+++ /dev/null
-Acess File System
-- Database Design
-
-== Data Strutures ==
-Blocks are of a size specified in the superblock
-- Superblock
- > Fixed offset: 1024 bytes
-- Field Table
- > Offset set in superblock
-- Index Table
-- Inode Table
-
-=== Superblock ===
-struct sSuperblock {
- Uint8 Magic[4]; // == '\xACFS'+Version
- Uint8 BlockSize; // TrueSize = 2^(7+BlockSize)
-};
-
-=== Field Table ===
-struct sFieldTableEntry {
- Uint16 Ident;
- Uint8 Type;
- Uint8 Length;
- char Text[];
-} FieldTable[SuperBlock.NFields];
-
-=== Index Table ==
-struct sIndexTableEntry {
- Uint16 Field;
- Uint16 CheckSum;
- Uint32 Block;
-} IndexTable[SuperBlock.NFields];
-
-=== Index Table entry ==
-struct {
- Uint32 NumEntries;
- Uint32 Links[];
-};
-
-=== Inode Table ===
-struct sInodeTable {
-
-};
-
-=== Inode ===
-struct sInodeEntry {
- Uint16 Name;
- Uint8 Size;
- Uint8 Checksum;
- Uint8 data[];
-};
-
-Each `sInodeEntry` defines an entry in a "database"
-
+++ /dev/null
-SHORTLOCK()
- cli; lock cmpxchg
-SHORTREL()
- lock and ; sti
-
-
-LONGLOCK()
- mov eax, 1
- lock cmpxchg lock.lock, eax
- if(eax) {
- SHORTLOCK(lock.listLock)
- // add to list (linked list, 4 static entries)
- SHORTREL(lock.listLock)
- for(;;)
- {
- check owner
- mov eax, 1
- lock cmpxchg lock.lock, eax
- if(!eax) break; // got lock
- Threads_Sleep();
- }
- }
-
-LONGREL()
- lock and lock.lock, 0
- pop off front of list, free entry, wake thread
+++ /dev/null
-select()
-- Implemented using a wait queue for every file descriptor
-- That requires waiting on sockets to be centeralised
-
-All wait tasks (reads on VTerm, Pipes, PTYs, network sockets) use the kernel
-version of select with an inifinite timeout.
-They then signal the VFS using their VFS node pointer as a reference
-
-The VFS function select()
-- Maintains a list of processes on each node (if select has been called on that node)
-- Each process maybe has a semaphore on it (to use the semaphore code to maintain the process list)
- > Could maybe use a mutex instead
-
-
-VFS_Select(int, fd_set* read, fd_set* write, fd_set* except)
-
-read is the set of sockets that we are waiting to read from
-write " " " be able to write to
-except " " " for possible errors on
-
-
-Hence, each VFS_Node has three listener lists (or just pointers)
-- One for when data is avaliable
-- One for when space is avaliable for writing
-- One for when an error occurs (closed pipe, etc ...)
+++ /dev/null
-
-== Read ==
-- UTF-8 / UCS-4 Character Stream
- > Selected with mode call
-
-== Write ==
-UTF-8 Emulation Text Mode:
-- Emuates a character device
- > VT-100/ANSI Control Codes
- > Characters/Symbols are sent as UTF-8
-
-/*
-Native Text Mode:
-- NxM 64-bit entries
- > UCS-32 Codepoint (if a diacritic is encountered, the previous character is modified)
- > 12-bit (16 encoded) Foreground
- > 12-bit (16 encoded) Background
-*/
-
-Framebuffer Graphics:
-- WxH 32-bit (3x 8-bit channels) framebuffer
-- Write to entire framebuffer
-
-Accellerated Graphics:
-- Command Stream
- > Each Node.Write call is a single command
- + NOP (-)
- + Direct (Uint16 X, Y, W, H, Uint32 Data[])
- + Blit (Uint16 W, H, SrcX, SrxY, DstX, DstY, Uint32 Data[])
- + Fill (Uint16 X, Y, W, H)
- + Rect (Uint16 X, Y, W, H)
- + Line (Uint16 X, Y, W, H)
- + Text (Uint16 X, Y, Size, Font)
- + ShowTile (Uint16 ID, Uint16 X, Y)
- + DelTile (Uint16 ID)
-- Extra IOCtls
- + int LoadFont(char *Path)
- + UnloadFont(int *ID)
- + int MakeTile(struct {Uint16 W, H, Uint32 Data[]} *Img)
- + DelTile(int *ID)
-- Allow fast switch between Accel/Framebuffer?
-- Min Reqd Tile Size 32x32
- > Tiles should be driver emulated if unavaliable by hardware
-
-3D Graphics: (Can be emulated if not avaliable, or just denied)
-- Command Stream
- >
- + NOP (-)
- + FlipBuffer (-)
- + LoadTexture(Uint16 ID, W, H, Uint32 Data[])
- + UnloadTexture(Uint16 ID)
- + SetTexture(Uint16 ID)
- + Triangle (Uint16 Texture, Uint32[3+3+2][3])
+++ /dev/null
-*.BuildNum.*
+++ /dev/null
-# Doxyfile 1.5.8
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME = Acess2
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = ../SrcDoc/Kernel
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
-# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
-# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
-# Spanish, Swedish, and Ukrainian.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it parses.
-# With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this tag.
-# The format is ext=language, where ext is a file extension, and language is one of
-# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
-# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
-# use: inc=Fortran f=C
-
-EXTENSION_MAPPING =
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING = YES
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT = YES
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
-# doxygen. The layout file controls the global structure of the generated output files
-# in an output format independent way. The create the layout file that represents
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name
-# of the layout file.
-
-LAYOUT_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT = .
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-
-FILE_PATTERNS = *.c *.h
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE = arch/archdoc.h
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET =
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
-
-GENERATE_DOCSET = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
-# are set, an additional index file will be generated that can be used as input for
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
-# HTML documentation.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE =
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
-# For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION =
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to FRAME, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
-# probably better off using the HTML help feature. Other possible values
-# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list;
-# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
-# disables this behavior completely. For backwards compatibility with previous
-# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
-# respectively.
-
-GENERATE_TREEVIEW = NONE
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH = 250
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE = 10
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX = YES
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT = NO
-
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
-
-DOT_FONTNAME = FreeSans
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP = YES
-
-#---------------------------------------------------------------------------
-# Options related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE = NO
+++ /dev/null
-# Doxyfile 1.7.5.1
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a hash (#) is considered a comment and will be ignored.
-# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
-
-PROJECT_NAME = Acess2
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF =
-
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
-
-PROJECT_LOGO =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = ../APIDoc
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
-
-EXTENSION_MAPPING =
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
-# pages) or section (for LaTeX and RTF).
-
-#INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT = YES
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES = NO
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
-
-STRICT_PROTO_MATCHING = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
-
-LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE.
-
-#CITE_BIB_FILES =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR = YES
-
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT = include/apidoc_mainpage.h \
- include/acess.h \
- include/hal_proc.h \
- include/binary.h \
- include/modules.h \
- include/vfs.h \
- include/vfs_ext.h \
- include/fs_devfs.h \
- include/fs_sysfs.h \
- include/iocache.h \
- arch/archdoc.h \
- include/apidoc/arch_x86.h \
- include/api_drv_common.h \
- include/api_drv_video.h \
- include/api_drv_terminal.h \
- include/api_drv_disk.h \
- include/api_drv_keyboard.h \
- include/api_drv_joystick.h \
- include/api_drv_network.h
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
-
-FILE_PATTERNS = api_drv_*.h
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE = NO
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to directory from which doxygen is run.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
-
-FILTER_SOURCE_PATTERNS =
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is adviced to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
-# the files will be copied as-is; there are no commands or markers available.
-
-HTML_EXTRA_FILES =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
-
-HTML_COLORSTYLE_HUE = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
-
-HTML_COLORSTYLE_SAT = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
-
-HTML_COLORSTYLE_GAMMA = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
-HTML_TIMESTAMP = YES
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-
-GENERATE_DOCSET = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE =
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
-
-GENERATE_ECLIPSEHELP = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
-
-ENUM_VALUES_PER_LINE = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-
-GENERATE_TREEVIEW = NONE
-
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
-
-FORMULA_TRANSPARENT = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
-
-USE_MATHJAX = NO
-
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the
-# mathjax.org site, so you can quickly see the result without installing
-# MathJax, but it is strongly recommended to install a local copy of MathJax
-# before deployment.
-
-MATHJAX_RELPATH = http://www.mathjax.org/mathjax
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
-
-#MATHJAX_EXTENSIONS =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
-SEARCHENGINE = NO
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvantages are that it is more difficult to setup
-# and does not have live searching capabilities.
-
-SERVER_BASED_SEARCH = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
-
-LATEX_FOOTER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
-
-LATEX_SOURCE_CODE = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
-
-#LATEX_BIB_STYLE = plain
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
-
-DOT_NUM_THREADS = 0
-
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
-
-DOT_FONTNAME = FreeSans
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
-
-DOT_IMAGE_FORMAT = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
-
-#INTERACTIVE_SVG = NO
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
-
-MSCFILE_DIRS =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP = YES
+++ /dev/null
-<doxygenlayout version="1.0">
- <!-- Navigation index tabs for HTML output -->
- <navindex>
- <tab type="mainpage" visible="yes" title=""/>
- <tab type="pages" visible="yes" title="" intro=""/>
- <tab type="modules" visible="yes" title="" intro=""/>
- <tab type="namespaces" visible="yes" title="">
- <tab type="namespaces" visible="yes" title="" intro=""/>
- <tab type="namespacemembers" visible="yes" title="" intro=""/>
- </tab>
- <tab type="classes" visible="yes" title="">
- <tab type="classes" visible="yes" title="" intro=""/>
- <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
- <tab type="hierarchy" visible="yes" title="" intro=""/>
- <tab type="classmembers" visible="yes" title="" intro=""/>
- </tab>
- <tab type="files" visible="yes" title="">
- <tab type="files" visible="yes" title="" intro=""/>
- <tab type="globals" visible="yes" title="" intro=""/>
- </tab>
- <tab type="dirs" visible="yes" title="" intro=""/>
- <tab type="examples" visible="yes" title="" intro=""/>
- </navindex>
-
- <!-- Layout definition for a class page -->
- <class>
- <briefdescription visible="yes"/>
- <includes visible="$SHOW_INCLUDE_FILES"/>
- <inheritancegraph visible="$CLASS_GRAPH"/>
- <collaborationgraph visible="$COLLABORATION_GRAPH"/>
- <allmemberslink visible="yes"/>
- <memberdecl>
- <nestedclasses visible="yes" title=""/>
- <publictypes title=""/>
- <publicslots title=""/>
- <signals title=""/>
- <publicmethods title=""/>
- <publicstaticmethods title=""/>
- <publicattributes title=""/>
- <publicstaticattributes title=""/>
- <protectedtypes title=""/>
- <protectedslots title=""/>
- <protectedmethods title=""/>
- <protectedstaticmethods title=""/>
- <protectedattributes title=""/>
- <protectedstaticattributes title=""/>
- <packagetypes title=""/>
- <packagemethods title=""/>
- <packagestaticmethods title=""/>
- <packageattributes title=""/>
- <packagestaticattributes title=""/>
- <properties title=""/>
- <events title=""/>
- <privatetypes title=""/>
- <privateslots title=""/>
- <privatemethods title=""/>
- <privatestaticmethods title=""/>
- <privateattributes title=""/>
- <privatestaticattributes title=""/>
- <friends title=""/>
- <related title="" subtitle=""/>
- <membergroups visible="yes"/>
- </memberdecl>
- <detaileddescription title=""/>
- <memberdef>
- <typedefs title=""/>
- <enums title=""/>
- <constructors title=""/>
- <functions title=""/>
- <related title=""/>
- <variables title=""/>
- <properties title=""/>
- <events title=""/>
- </memberdef>
- <usedfiles visible="$SHOW_USED_FILES"/>
- <authorsection visible="yes"/>
- </class>
-
- <!-- Layout definition for a namespace page -->
- <namespace>
- <briefdescription visible="yes"/>
- <memberdecl>
- <nestednamespaces visible="yes" title=""/>
- <classes visible="yes" title=""/>
- <typedefs title=""/>
- <enums title=""/>
- <functions title=""/>
- <variables title=""/>
- <membergroups visible="yes"/>
- </memberdecl>
- <detaileddescription title=""/>
- <memberdef>
- <typedefs title=""/>
- <enums title=""/>
- <functions title=""/>
- <variables title=""/>
- </memberdef>
- <authorsection visible="yes"/>
- </namespace>
-
- <!-- Layout definition for a file page -->
- <file>
- <briefdescription visible="yes"/>
- <includes visible="$SHOW_INCLUDE_FILES"/>
- <includegraph visible="$INCLUDE_GRAPH"/>
- <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
- <sourcelink visible="yes"/>
- <memberdecl>
- <classes visible="yes" title=""/>
- <namespaces visible="yes" title=""/>
- <defines title=""/>
- <typedefs title=""/>
- <enums title=""/>
- <functions title=""/>
- <variables title=""/>
- <membergroups visible="yes"/>
- </memberdecl>
- <detaileddescription title=""/>
- <memberdef>
- <defines title=""/>
- <typedefs title=""/>
- <enums title=""/>
- <functions title=""/>
- <variables title=""/>
- </memberdef>
- <authorsection/>
- </file>
-
- <!-- Layout definition for a group page -->
- <group>
- <briefdescription visible="yes"/>
- <groupgraph visible="$GROUP_GRAPHS"/>
- <memberdecl>
- <classes visible="yes" title=""/>
- <namespaces visible="yes" title=""/>
- <dirs visible="yes" title=""/>
- <nestedgroups visible="yes" title=""/>
- <files visible="yes" title=""/>
- <defines title=""/>
- <typedefs title=""/>
- <enums title=""/>
- <enumvalues title=""/>
- <functions title=""/>
- <variables title=""/>
- <signals title=""/>
- <publicslots title=""/>
- <protectedslots title=""/>
- <privateslots title=""/>
- <events title=""/>
- <properties title=""/>
- <friends title=""/>
- <membergroups visible="yes"/>
- </memberdecl>
- <detaileddescription title=""/>
- <memberdef>
- <pagedocs/>
- <inlineclasses title=""/>
- <defines title=""/>
- <typedefs title=""/>
- <enums title=""/>
- <enumvalues title=""/>
- <functions title=""/>
- <variables title=""/>
- <signals title=""/>
- <publicslots title=""/>
- <protectedslots title=""/>
- <privateslots title=""/>
- <events title=""/>
- <properties title=""/>
- <friends title=""/>
- </memberdef>
- <authorsection visible="yes"/>
- </group>
-
- <!-- Layout definition for a directory page -->
- <directory>
- <briefdescription visible="yes"/>
- <directorygraph visible="yes"/>
- <memberdecl>
- <dirs visible="yes"/>
- <files visible="yes"/>
- </memberdecl>
- <detaileddescription title=""/>
- </directory>
-</doxygenlayout>
+++ /dev/null
-<?php
-$gLines = file("syscalls.lst");
-
-$lSyscalls = array();
-$i = 0;
-foreach($gLines as $line)
-{
- $line = trim($line);
- if(empty($line)) continue;
-
- if( intVal($line) != 0)
- $i = $line;
- else
- $lSyscalls[$i++] = explode("\t", $line, 3);
-}
-$lMax = $i;
-
-$lAsmInc = "; Acess2
-; System Calls List
-;
-
-";
-$lHeader = "/*
- * AcessOS Microkernel Version
- * syscalls.h
- */
-#ifndef _SYSCALLS_H
-#define _SYSCALLS_H
-
-";
-$i = 0;
-foreach($lSyscalls as $num=>$call)
-{
- if($i != $num) {
- $lHeader .= "\n";
- $lAsmInc .= "\n";
- }
-
- $lHeader .= "#define {$call[0]}\t{$num}";
- $lHeader .= "\t// {$num} - {$call[1]}\n";
-
- $lAsmInc .= "%define {$call[0]}\t{$num}\t; {$call[1]}\n";
-
-
- if($i != $num)
- $i = $num+1;
- else
- $i ++;
-}
-$lHeader .= "#define NUM_SYSCALLS\t$i\n";
-$lHeader .= "#define SYS_DEBUG\t0x100 // 0x100 - Print a debug string\n";
-$lHeader .= "\n";
-$lHeader .= "#ifdef __GNUC__\n";
-$lHeader .= "static const char *cSYSCALL_NAMES[] = {\n\t";
-
-$j = 0;
-for($i=0;$i<$lMax;$i++)
-{
- if(!isset($lSyscalls[$i]))
- $lHeader .= "\"\",";
- else
- $lHeader .= "\"".$lSyscalls[$i][0]."\",";
- $j ++;
- if($j == 6) {
- $lHeader .= "\n\t";
- $j = 0;
- }
-}
-$lHeader .= "\"\"\n};\n"
-$lHeader .= "#endif\n";
-$lHeader .= "#endif\n";
-
-$fp = fopen("include/syscalls.h", "w"); fwrite($fp, $lHeader); fclose($fp);
-$fp = fopen("include/syscalls.inc.asm", "w"); fwrite($fp, $lAsmInc); fclose($fp);
-
-?>
+++ /dev/null
-#!/usr/bin/perl
-#
-#
-
-open(FILE, "syscalls.lst");
-
-$num = 0;
-@calls = ();
-while($_ = <FILE>)
-{
- if(/(\d+)/)
- {
- $num = $1;
- }
- elsif(/([A-Z_]+)\s+(.+)/)
- {
- push @calls, [$num, $1, $2];
- $num ++;
- }
-}
-
-close(FILE);
-
-# C header
-open(HEADER, ">include/syscalls.h");
-print HEADER "/*
- * Acess2
- * syscalls.h
- * - System Call List
- *
- * NOTE: Generated from Kernel/syscalls.lst
- */
-#ifndef _SYSCALLS_H
-#define _SYSCALLS_H
-
-";
-
-$lastid = -1;
-$i = 0;
-foreach my $call (@calls)
-{
- print HEADER "#define ", $call->[1], "\t", $call->[0], "\t// ", $call->[2], "\n";
- $i = $call->[0] + 1;
-}
-print HEADER "
-#define NUM_SYSCALLS ",$i,"
-#define SYS_DEBUG 0x100
-
-#ifndef __ASSEMBLER__
-static const char *cSYSCALL_NAMES[] = {
-";
-
-$lastid = -1;
-foreach $call (@calls)
-{
- while( $lastid + 1 < $call->[0] )
- {
- print HEADER "\t\"\",\n";
- $lastid = $lastid + 1;
- }
- print HEADER "\t\"", $call->[1], "\",\n";
- $lastid = $lastid + 1;
-}
-print HEADER "
-\t\"\"
-};
-#endif
-
-#endif
-";
-
-close(HEADER);
-
-# Assembly Header
-open(ASM, ">include/syscalls.inc.asm");
-print ASM "; Acess2
-; System Calls List
-;
-
-";
-foreach $call (@calls)
-{
- print ASM "%define ", $call->[1], "\t", $call->[0], "\t ;", $call->[2], "\n";
-}
-close(ASM);
+++ /dev/null
-# Acess2 Kernel
-# Root Makefile
-# NOTE: Does some voodoo to allow differing architecture builds to co-exist
-# - The built objects and dependency files are suffixed with the arch name
-# - The final binary is Acess2.<ARCH>.bin
-
--include ../Makefile.cfg
-
--include arch/$(ARCHDIR)/Makefile
-
--include Makefile.BuildNum.$(ARCH)
-
-ifeq ($(BUILD_NUM),)
-BUILD_NUM = 0
-endif
-
-KERNEL_VERSION = $(ACESS_VERSION)
-MAKEDEP = $(CC) -M
-
-ifeq ($(AS_SUFFIX),)
- AS_SUFFIX = S
-endif
-
-ASFLAGS += -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1
-CPPFLAGS += -I./include -I./arch/$(ARCHDIR)/include -D_MODULE_NAME_=\"Kernel\"
-CPPFLAGS += -D ARCH=$(ARCH) -D ARCHDIR=$(ARCHDIR) -D PLATFORM=\"$(PLATFORM)\" -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1
-CPPFLAGS += -D KERNEL_VERSION=$(KERNEL_VERSION)
-CFLAGS += -Wall -fno-stack-protector -Wstrict-prototypes -g
-CFLAGS += -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wuninitialized
-CFLAGS += -O3
-LDFLAGS += -T arch/$(ARCHDIR)/link.ld -g
-
-ifeq ($(PLATFORM),default)
- OBJDIR := obj-$(ARCH)/
- #OBJSUFFIX := .$(ARCH)
- BIN := ../Acess2.$(ARCH).bin
- GZBIN := ../Acess2.$(ARCH).gz
-else
- OBJDIR := obj-$(ARCH)-$(PLATFORM)/
- #OBJSUFFIX := .$(ARCH)-$(PLATFORM)
- BIN := ../Acess2.$(ARCH)-$(PLATFORM).bin
- GZBIN := ../Acess2.$(ARCH)-$(PLATFORM).gz
-endif
-
-ifeq ($(DEBUG_BUILD),yes)
- LDFLAGS += -g
- CFLAGS += -g
-endif
-
-BUILDINFO_OBJ := $(OBJDIR)buildinfo.o$(OBJSUFFIX)
-BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX)
-
-OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ))
-OBJ += heap.o drvutil.o logging.o debug.o lib.o adt.o time.o
-OBJ += messages.o modules.o syscalls.o system.o
-OBJ += threads.o mutex.o semaphore.o workqueue.o events.o
-OBJ += drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o
-OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o
-OBJ += binary.o bin/elf.o bin/pe.o
-OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o
-OBJ += vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o vfs/mmap.o
-OBJ += vfs/fs/root.o vfs/fs/devfs.o
-OBJ += $(addprefix drv/, $(addsuffix .o,$(DRIVERS)))
-
-OBJ := $(addsuffix $(OBJSUFFIX), $(OBJ))
-OBJ := $(addprefix $(OBJDIR), $(OBJ))
-
-MODS += $(addprefix ../Modules/, $(addsuffix .xo.$(ARCH),$(MODULES)))
-
-DEPFILES := $(OBJ:%$(OBJSUFFIX)=%.dep$(OBJSUFFIX))
-
-SRCFILES = $(OBJ:$(OBJDIR)%.o$(OBJSUFFIX)=%.c)
-SRCFILES := $(SRCFILES:$(OBJDIR)%.ao$(OBJSUFFIX)=%.$(AS_SUFFIX))
-
-OBJ += $(BUILDINFO_OBJ)
-
-.PHONY: all clean install apidoc
-
-all: $(BIN)
-
-clean:
- @$(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt
- @$(RM) -r $(OBJDIR) $(OBJ) $(DEPFILES) $(BUILDINFO_SRC)
-
-install: $(BIN)
- @cp $(BIN) $(BIN)_
- @$(STRIP) $(BIN)_
- @gzip -c $(BIN)_ > $(GZBIN)
- @$(RM) $(BIN)_
- $(xCP) $(GZBIN) $(DISTROOT)
-
-apidoc:
- doxygen Doxyfile.api
-
-$(BIN): $(OBJ) $(MODS) arch/$(ARCHDIR)/link.ld Makefile ../BuildConf/$(ARCH)/Makefile.cfg ../BuildConf/$(ARCH)/$(PLATFORM).mk
- @echo --- LD -o $(BIN)
- @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) $(MODS) --defsym __buildnum=$$(( $(BUILD_NUM) + 1 )) -Map ../Map.$(ARCH).txt
- @$(DISASM) -S $(BIN) > $(BIN).dsm
- @wc -l $(SRCFILES) include/*.h > LineCounts.$(ARCH).txt
- @echo BUILD_NUM = $$(( $(BUILD_NUM) + 1 )) > Makefile.BuildNum.$(ARCH)
- $(POSTBUILD)
-
-$(OBJDIR)%.ao$(OBJSUFFIX): %.$(AS_SUFFIX) Makefile
- @echo --- AS -o $@
- @mkdir -p $(dir $@)
- @$(AS) $(ASFLAGS) $< -o $@
-ifeq ($(AS_SUFFIX),S)
- @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.ao.dep$(OBJSUFFIX) $<
-endif
-
-$(OBJDIR)%.o$(OBJSUFFIX): %.c Makefile
-# if exists %*/Makefile
-# @make -C %*/ all
-# else
- @echo --- CC -o $@
- @mkdir -p $(dir $@)
- @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
- @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.o.dep$(OBJSUFFIX) $<
-# endif
-
-%.xo.$(ARCH):
- @BUILDTYPE=static make -C $* all
-
-include/syscalls.h include/syscalls.inc.asm: syscalls.lst Makefile GenSyscalls.pl
- perl GenSyscalls.pl
-
-Makefile: ../Makefile.cfg arch/$(ARCHDIR)/Makefile
-
-$(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) $(MODS) arch/$(ARCHDIR)/link.ld Makefile
- @echo "#include <acess.h>" > $@
- @echo "const char gsGitHash[] = \""`git log -n 1 | head -n 1 | awk '{print $$2}'`"\";" >> $@
- @echo "const int giBuildNumber = $(BUILD_NUM);" >> $@
-$(BUILDINFO_OBJ): $(BUILDINFO_SRC)
- @echo --- CC -o $@
- @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
-
-# Dependency Files
--include $(DEPFILES)
+++ /dev/null
-/*
- * Acess2 Kernel
- *
- * adt.c
- * - Complex data type code
- */
-#include <acess.h>
-#include <adt.h>
-
-
-// === CODE ===
-// --- Ring Buffers ---
-tRingBuffer *RingBuffer_Create(size_t Space)
-{
- tRingBuffer *ret = malloc(sizeof(tRingBuffer)+Space);
- ret->Start = 0;
- ret->Length = 0;
- ret->Space = Space;
- return ret;
-}
-
-size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length)
-{
- size_t tmpLen;
-
- tmpLen = Buffer->Length; // Changed in Write, so cache it for our read
-
- if(Length > tmpLen) Length = tmpLen;
-
- if( Buffer->Start + Length > Buffer->Space )
- {
- int endData = Buffer->Space - Buffer->Start;
- memcpy(Dest, &Buffer->Data[Buffer->Start], endData);
- memcpy((Uint8*)Dest + endData, Buffer->Data, Length - endData);
- }
- else
- {
- memcpy(Dest, &Buffer->Data[Buffer->Start], Length);
- }
-
- // Lock then modify
- SHORTLOCK( &Buffer->Lock );
- Buffer->Start += Length;
- if( Buffer->Start > Buffer->Space )
- Buffer->Start -= Buffer->Space;
- Buffer->Length -= Length;
- SHORTREL( &Buffer->Lock );
-
- return Length;
-}
-
-size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length)
-{
- size_t bufEnd, endSpace;
- size_t tmpLen, tmpStart;
-
- // Cache Start and Length because _Read can change these
- SHORTLOCK( &Buffer->Lock );
- tmpStart = Buffer->Start;
- tmpLen = Buffer->Length;
- SHORTREL( &Buffer->Lock );
-
- bufEnd = (tmpStart + Buffer->Length) % Buffer->Space;
- endSpace = Buffer->Space - bufEnd;
-
- // Force to bounds
- if(Length > Buffer->Space - tmpLen) Length = Buffer->Space - tmpLen;
-
- if(endSpace < Length)
- {
- memcpy( &Buffer->Data[bufEnd], Source, endSpace );
- memcpy( Buffer->Data, (Uint8*)Source + endSpace, Length - endSpace );
- }
- else
- {
- memcpy( &Buffer->Data[bufEnd], Source, Length );
- }
-
- // Lock then modify
- SHORTLOCK( &Buffer->Lock );
- Buffer->Length += Length;
- SHORTREL( &Buffer->Lock );
-
- return Length;
-}
-
+++ /dev/null
-/**
- * \file archdoc.h
- * \brief Documentation Definitions for the Acess 2 Architecture Interface
- * \author John Hodge (thePowersGang)
- *
- * Acess 2 allows different architecture builds to be made off almost
- * the same source tree. The difference between the trees is the inclusion
- * of a different directory from the "Kernel/arch/" directory that
- * contains the architecture specific intialisation and management code.
- * Each achitecture tree must provide all the definitions from this
- * document in some form or another (usually in the most efficient way
- * avaliable)
- * The values and types used in this documentation are a guide only and
- * will most probably be incorrect for most architectures.
- */
-#ifndef _ARCHDOC_H_
-#define _ARCHDOC_H_
-
-/**
- * \brief Maximum number of CPUs supported by this architecture driver
- * (in the current build)
- */
-#define MAX_CPUS 1
-/**
- * \brief Number of bits in a machine word (Uint)
- */
-#define BITS 32
-/**
- * \brief Number of valid bits in a \a tPAddr
- */
-#define PHYS_BITS 32
-
-/**
- * \name Syncronisation Primitives
- * \{
- */
-/**
- * \brief Spinlock type
- */
-typedef volatile int tSpinlock;
-/**
- * \brief Acquire a spinlock
- */
-#define LOCK(lockptr) do{while(*(tSpinlock*)lockptr)Threads_Yield();*(tSpinlock*)lockptr=1;}while(0)
-/**
- * \brief Release a held spinlock
- */
-#define RELEASE(lockptr) do{*(tSpinlock*)lockptr=0;}while(0)
-/**
- * \}
- */
-//#define HALT() __asm__ __volatile__ ("hlt")
-
-
-/**
- * \name Atomic Types
- * \{
- */
-typedef unsigned int Uint; //!< Unsigned machine native integer
-typedef unsigned char Uint8; //!< Unsigned 8-bit integer
-typedef unsigned short Uint16; //!< Unsigned 16-bit integer
-typedef unsigned long Uint32; //!< Unsigned 32-bit integer
-typedef unsigned long long Uint64; //!< Unsigned 64-bit integer
-typedef signed int Sint; //!< Signed Machine Native integer
-typedef signed char Sint8; //!< Signed 8-bit integer
-typedef signed short Sint16; //!< Signed 16-bit integer
-typedef signed long Sint32; //!< Signed 32-bit integer
-typedef signed long long Sint64; //!< Signed 16-bit integer
-/**
- * \}
- */
-
-typedef Uint size_t; //!< Counter/Byte count type
-typedef Uint32 tVAddr; //!< Virtual address type
-typedef Uint32 tPAddr; //!< Physical Address type
-
-/**
- * \brief Register state passed to the syscall handler
- *
- * The \a tSyscallRegs structure allows the system call handler to read
- * the user state to get the arguments for the call. It also allows the
- * handler to alter specific parts of the user state to reflect the
- * result of the call.
- * \note The fields shown here are need only be present in the actual
- * structure, in any order. The implementation may also add more
- * fields for padding purposes.
- */
-typedef struct {
- Uint Arg4; //!< Fourth argument
- Uint Arg3; //!< Third argument
- Uint Arg2; //!< Second argument
- union {
- Uint Arg1; //!< First arugment
- Uint RetHi; //!< High part of the return
- };
- union {
- Uint Num; //!< Call Number
- Uint Return; //!< Low return value
- };
-
- Uint StackPointer; //!< User stack pointer
-} tSyscallRegs;
-
-/**
- * \brief Opaque structure definining the MMU state for a task
- */
-typedef struct sMemoryState tMemoryState;
-
-/**
- * \brief Opque structure defining the CPU state for a task
- */
-typedef struct sTaskState tTaskState;
-
-
-/**
- * \name Memory Management
- * \{
- */
-/**
- * \}
- */
-
-
-#endif
+++ /dev/null
-#
-# Acess2 Kernel
-# arm7 Architecture Makefile
-# arch/arm7/Makefile
-
-CPPFLAGS =
-CFLAGS =
-ASFLAGS =
-
-CPPFLAGS += -DMMU_PRESENT=1
-LDFLAGS += `$(CC) --print-libgcc-file-name`
-
-A_OBJ = start.ao main.o lib.o lib.ao time.o pci.o debug.o
-A_OBJ += mm_phys.o mm_virt.o proc.o proc.ao
-
-#main.c: Makefile.BuildNum.$(ARCH)
-
-ifeq ($(PLATFORM),tegra2)
- POSTBUILD = arm-elf-objcopy $(BIN) -O binary $(BIN)
-endif
+++ /dev/null
-/**
- * Acess2
- * - By John Hodge (thePowersGang)
- *
- * arch/arm7/debug.c
- * - ARM7 Debug output
- * NOTE: Currently designed for the realview-pb-a8 emulated by Qemu
- */
-#include <acess.h>
-
-// === CONSTANTS ===
-//#define UART0_BASE 0x10009000
-#define UART0_BASE 0xF1000000 // Boot time mapped
-
-// === PROTOTYPES ===
-void KernelPanic_SetMode(void);
-void KernelPanic_PutChar(char Ch);
-void StartupPrint(const char *str);
-
-// === GLOBALS ===
- int giDebug_SerialInitialised = 0;
-
-// === CODE ===
-void Debug_PutCharDebug(char ch)
-{
- if(ch == '\n')
- Debug_PutCharDebug('\r');
-
- #if PLATFORM_is_tegra2
- // Tegra2
- while( !(*(volatile Uint32*)(UART0_BASE + 0x14) & (1 << 5)) )
- ;
- #endif
-
-// *(volatile Uint32*)(SERIAL_BASE + SERIAL_REG_DATA) = ch;
- *(volatile Uint32*)(UART0_BASE) = ch;
-}
-
-void Debug_PutStringDebug(const char *str)
-{
- for( ; *str; str++ )
- Debug_PutCharDebug( *str );
-}
-
-void KernelPanic_SetMode(void)
-{
-}
-
-void KernelPanic_PutChar(char ch)
-{
-// Debug_PutCharDebug(ch);
-}
-
-void StartupPrint(const char *str)
-{
-}
-
+++ /dev/null
-/*
- * Acess2
- * ARM7 Architecture Header
- */
-#ifndef _ARCH_H_
-#define _ARCH_H_
-
-// === CONSTANTS ===
-#define INVLPTR ((void*)-1)
-#define BITS 32
-#define PAGE_SIZE 0x1000
-#define KERNEL_BASE 0x80000000 // 2GiB
-
-// === TYPES ===
-typedef unsigned int Uint;
-typedef unsigned char Uint8;
-typedef unsigned short Uint16;
-typedef unsigned long Uint32;
-typedef unsigned long long Uint64;
-typedef signed int Sint;
-typedef signed char Sint8;
-typedef signed short Sint16;
-typedef signed long Sint32;
-typedef signed long long Sint64;
-
-typedef int size_t;
-typedef char BOOL;
-
-typedef Uint32 tVAddr;
-typedef Uint32 tPAddr;
-
-#include "lock.h"
-
-// --- Debug
-extern void Debug_PutCharDebug(char Ch);
-extern void Debug_PutStringDebug(const char *String);
-
-// This should be elsewhere, but CBF
-extern void MM_SetupPhys(void);
-extern int MM_InitialiseVirtual(void);
-
-#define NO_IO_BUS 1
-
-#endif
+++ /dev/null
-/*
- * Acess2 ARMv7
- * - By John Hodge (thePowersGang)
- *
- * arch/arm7/include/assembly.h
- * - Assembly specific macros
- */
-#ifndef _ASSEMBLY_H_
-#define _ASSEMBLY_H_
-
-#define PUSH_GPRS \
- str r0, [sp,#-1*4];\
- str r1, [sp,#-2*4];\
- str r2, [sp,#-3*4];\
- str r3, [sp,#-4*4];\
- str r4, [sp,#-5*4];\
- str r5, [sp,#-6*4];\
- str r6, [sp,#-7*4];\
- str r7, [sp,#-8*4];\
- str r8, [sp,#-9*4];\
- str r9, [sp,#-10*4];\
- str r10, [sp,#-11*4];\
- str r11, [sp,#-12*4];\
- str r12, [sp,#-13*4];\
- str sp, [sp,#-14*4];\
- str lr, [sp,#-15*4];\
- sub sp, #16*4
-
-#define POP_GPRS add sp, #16*4; \
- ldr r0, [sp,#-1*4]; \
- ldr r1, [sp,#-2*4]; \
- ldr r2, [sp,#-3*4]; \
- ldr r3, [sp,#-4*4]; \
- ldr r4, [sp,#-5*4]; \
- ldr r5, [sp,#-6*4]; \
- ldr r6, [sp,#-7*4]; \
- ldr r7, [sp,#-8*4]; \
- ldr r8, [sp,#-9*4]; \
- ldr r9, [sp,#-10*4]; \
- ldr r10, [sp,#-11*4]; \
- ldr r11, [sp,#-12*4]; \
- ldr r12, [sp,#-13*4]; \
- ldr lr, [sp,#-15*4];
-
-#endif
-
+++ /dev/null
-/*
- * Acess2
- * ARM7 Architecture
- *
- * lock.h - Hardware level spinlocks
- */
-#ifndef _LOCK_H_
-#define _LOCK_H_
-
-// === CODE ===
-struct sShortSpinlock {
- int Lock;
-};
-
-// --- Spinlocks ---
-static inline int IS_LOCKED(struct sShortSpinlock *Lock)
-{
- return !!Lock->Lock;
-}
-
-static inline int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
-{
- // TODO: Handle multiple CPUs
- return !!Lock->Lock;
-}
-
-static inline int SHORTLOCK(struct sShortSpinlock *Lock)
-{
- #if 0
- // Coped from linux, yes, but I know what it does now :)
- Uint tmp;
- __asm__ __volatile__ (
- "1: ldrex %0, [%1]\n" // Exclusive LOAD
- " teq %0, #0\n" // Check if zero
- " strexeq %0, %2, [%1]\n" // Set to one if it is zero (releasing lock on the memory)
- " teqeq %0, #0\n" // If the lock was avaliable, check if the write succeeded
- " bne 1b" // If the lock was unavaliable, or the write failed, loop
- : "=&r" (tmp) // Temp
- : "r" (&Lock->Lock), "r" (1)
- : "cc" // Condition codes clobbered
- );
- #elif 1
- while( *(volatile int*)&Lock->Lock ) ;
- Lock->Lock = 1;
- #else
- int v = 1;
- while( v )
- __asm__ __volatile__ (
- "swp %0, %0, [%1]"
- : "=r" (v) : "r" (&Lock->Lock)
- : "cc"
- );
- #endif
- return 1;
-}
-
-static inline void SHORTREL(struct sShortSpinlock *Lock)
-{
- Lock->Lock = 0;
-}
-
-#endif
-
+++ /dev/null
-/*
- * Acess2
- * ARM7 Virtual Memory Manager Header
- */
-#ifndef _MM_VIRT_H_
-#define _MM_VIRT_H_
-
-#include "options.h"
-
-#define USER_STACK_COMM 0x04000 // Pages to allocate up front
-#define USER_STACK_SIZE 0x10000 // Stack space
-#define USER_STACK_TOP 0x78000000
-
-#define MM_USER_MIN 0x00001000
-#define USER_LIB_MAX 0x70000000
-#define MM_PPD_HANDLES 0x7F800000
-#define MM_TABLE1USER 0x7FC00000 // 2 GiB - 4 MiB
-#define MM_TABLE0USER 0x7FE00000 // 2 GiB - 2 MiB
-#define MM_KSTACK_BASE 0x7FE00000
-#define MM_KSTACK_END 0x80000000
-
-// Page Blocks are 12-bits wide (12 address bits used)
-// Hence, the table is 16KiB large (and must be so aligned)
-// and each block addresses 1MiB of data
-
-// First level table is aligned to 16KiB (restriction of TTBR reg)
-// - VMSAv6 uses two TTBR regs, determined by bit 31
-
-//#define KERNEL_BASE 0x80000000 // 2GiB
-
-#define MM_KHEAP_BASE 0x80800000 // 8MiB of kernel code
-#define MM_KHEAP_MAX 0xC0000000 // ~1GiB of kernel heap
-
-#define MM_MODULE_MIN 0xC0000000 // - 0xD0000000
-#define MM_MODULE_MAX 0xCF000000
-
-#define MM_GLOBALSTACKS 0xCF000000 // Global stacks
-#define MM_GLOBALSTACKS_END 0xD0000000
-
-// PMM Data, giving it 256MiB is overkill, but it's unused atm
-#define MM_MAXPHYSPAGE (1024*1024)
-// 2^(32-12) max pages
-// 8.125 bytes per page (for bitmap allocation)
-// = 8.125 MiB
-#define MM_PMM_BASE 0xE0000000
-#define MM_PMM_END 0xF0000000
-
-#define MM_HWMAP_BASE 0xF0000000 // Ent 0xF00
-#define MM_HWMAP_END 0xFE000000
-#define MM_TMPMAP_BASE 0xFE000000
-#define MM_TMPMAP_END 0xFF000000
-
-#define MM_KERNEL_VFS 0xFF000000 //
-#define MM_TABLE1KERN 0xFF800000 // - 0x???????? 4MiB
-//#define MM_TABLE0KERN 0xFFC00000 // - 0xFFE04000 16KiB
-
-#endif
+++ /dev/null
-/*
- * Acess2 ARMv7 Port
- * - By John Hodge (thePowersGang)
- *
- * options.h
- * - C/ASM Shared constants
- */
-#ifndef _ARMV7_OPTIONS_H_
-#define _ARMV7_OPTIONS_H_
-
-#define KERNEL_BASE 0x80000000
-
-//#define PCI_PADDR 0x60000000 // Realview (Non-PB)
-
-#if PLATFORM_is_realview_pb
-# define UART0_PADDR 0x10009000 // Realview
-# define GICI_PADDR 0x1e000000
-# define GICD_PADDR 0x1e001000
-# define PL110_BASE 0x10020000 // Integrator
-
-#endif
-
-#if PLATFORM_is_tegra2 // Tegra2
-# define UART0_PADDR 0x70006000
-# define GICD_PADDR 0x50041000
-# define GICI_PADDR 0x50040100
-//# define PL110_BASE 0x10020000 // Integrator
-#endif
-
-#define MM_KSTACK_SIZE 0x2000 // 2 Pages
-
-#endif
-
+++ /dev/null
-/*
- * Acess2
- * ARM7 Architecture
- *
- * proc.h - Arch-Dependent Process Management
- */
-#ifndef _PROC_H_
-#define _PROC_H_
-
-#define MAX_CPUS 4
-#define USER_MAX 0x80000000
-
-// === STRUCTURES ===
-typedef struct {
- Uint32 IP, SP;
- Uint32 UserIP, UserSP;
-} tTaskState;
-
-typedef struct {
- Uint32 Base;
-} tMemoryState;
-
-typedef struct {
- union {
- Uint32 Num;
- Uint32 Error;
- };
- union {
- Uint32 Arg1;
- Uint32 Return;
- };
- union {
- Uint32 Arg2;
- Uint32 RetHi;
- };
- Uint32 Arg3;
- Uint32 Arg4;
- Uint32 Arg5;
- Uint32 Arg6; // R6
-} tSyscallRegs;
-
-// === MACROS ===
-#define HALT() do{}while(0)
-
-// === PROTOTYPES ===
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 ARM
- * - By John Hodge (thePowersGang)
- *
- * arch/arm7/lib.S
- * - Assembly editions of library functions
- */
-#include "include/assembly.h"
-
-.globl __memcpy_byte
-__memcpy_byte:
-1:
- tst r2, r2 @ Check counter
- moveq pc, lr @ Return if zero
- ldrb r3, [r1],#1 @ Read
- strb r3, [r0],#1 @ Write
- sub r2, #1
- b 1b
-
-@
-@ Pre-aligned memcpy (32-bit blocks)
-@
-.globl __memcpy_align4
-__memcpy_align4:
- push {r4}
- mvn r3, #3 @ Mask for checking length
-
- @ 4 byte chunk copies
-1: tst r2, r3
- ldrne r4, [r1],#4
- strne r4, [r0],#4
- subne r2, #4
- bne 1b
-
- @ single byte copies to finish off
-2: tst r2, #3
- beq 3f
- ldrb r4, [r1],#1
- strb r4, [r0],#1
- sub r2, #1
- b 2b
-
-3: pop {r4}
- mov pc, lr
-
-@
-@ Division
-@
-.globl __divmod32_asm
-__divmod32_asm:
- push {r4}
- mov r4, #0 @ Return value
- mov r3, #1 @ add value
-
- @ Scan up for first larger multiple of 2
-1: cmp r0, r1 @ N < D
- bmi 2f @ ^^
- lsl r1, r1, #1 @ D <<= 1
- lsls r3, r3, #1 @ add <<= 1
- beq .err @ result is zero
- b 1b
-
- @ Go back down
-2: lsrs r3, r3, #1 @ add >>= 1
- beq 3f @ Done (value is zero)
- lsr r1, r1, #1 @ D >>= 1
- cmp r0, r1 @ N < D
- bmi 2b
- sub r0, r1 @ N -= D
- add r4, r3 @ ret += add
- b 2b
-3:
- tst r2, r2 @ Remainder (if wanted)
- strne r0,[r2]
- mov r0, r4 @ Return value
- pop {r4}
- mov pc, lr
-.err:
- mov r0, #0
- tst r2, r2
- strne r0, [r2]
- pop {r4}
- mov pc, lr
-
+++ /dev/null
-/*
- * Acess2 ARM7 Port
- *
- * lib.c - Library Functions
- */
-#include <acess.h>
-#include "../helpers.h"
-
-// === IMPORTS ===
-extern void __memcpy_align4(void *_dest, const void *_src, size_t _length);
-extern void __memcpy_byte(void *_dest, const void *_src, size_t _length);
-extern Uint32 __divmod32_asm(Uint32 Num, Uint32 Den, Uint32 *Rem);
-
-// === PROTOTYPES ===
-Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
-Uint32 __divmod32(Uint32 Num, Uint32 Den, Uint32 *Rem);
-Uint64 __udivdi3(Uint64 Num, Uint64 Den);
-Uint64 __umoddi3(Uint64 Num, Uint64 Den);
-Uint32 __udivsi3(Uint32 Num, Uint32 Den);
-Uint32 __umodsi3(Uint32 Num, Uint32 Den);
-Sint32 __divsi3(Sint32 Num, Sint32 Den);
-Sint32 __modsi3(Sint32 Num, Sint32 Den);
-
-// === CODE ===
-void *memcpy(void *_dest, const void *_src, size_t _length)
-{
- Uint8 *dst8 = _dest;
- const Uint8 *src8 = _src;
-
- if( ((tVAddr)_dest & 3) == 0 && ((tVAddr)_src & 3) == 0 )
- {
- __memcpy_align4(_dest, _src, _length);
- return _dest;
- }
-
- // Handle small copies / Non-aligned
- if( _length < 4 || ((tVAddr)_dest & 3) != ((tVAddr)_src & 3) )
- {
- __memcpy_byte(_dest, _src, _length);
- return _dest;
- }
-
- // Force alignment
- while( (tVAddr)dst8 & 3 ) *dst8 ++ = *src8++, _length --;
-
- __memcpy_align4(dst8, src8, _length);
-
- return _dest;
-}
-
-int memcmp(const void *_m1, const void *_m2, size_t _length)
-{
- const Uint32 *m1, *m2;
- const Uint8 *m1_8 = _m1, *m2_8 = _m2;
-
- // Handle small copies / Non-aligned
- if( _length < 4 || ((tVAddr)_m1 & 3) != ((tVAddr)_m1 & 3) )
- {
- for( ; _length--; m1_8++,m2_8++ ) {
- if(*m1_8 != *m2_8) return *m1_8 - *m2_8;
- }
- return 0;
- }
-
- // Force alignment
- for( ; (tVAddr)m1_8 & 3; m1_8 ++, m2_8 ++) {
- if(*m1_8 != *m2_8) return *m1_8 - *m2_8;
- }
- m1 = (void *)m1_8; m2 = (void *)m2_8;
-
- // DWORD copies
- for( ; _length > 3; _length -= 4, m1++, m2++)
- if(*m1 != *m2) return *m1 - *m2;
-
- // Trailing bytes
- m1_8 = (void*)m1; m2_8 = (void*)m2;
- for( ; _length; _length --, m1_8++, m2_8++ )
- if(*m1_8 != *m2_8) return *m1_8 - *m2_8;
-
- return 0;
-}
-
-void *memset(void *_dest, int _value, size_t _length)
-{
- Uint32 *dst, val32;
- Uint8 *dst8 = _dest;
-
- _value = (Uint8)_value;
-
- // Handle small copies
- if( _length < 4 )
- {
- for( ; _length--; dst8++ )
- *dst8 = _value;
- return _dest;
- }
-
- val32 = _value;
- val32 |= val32 << 8;
- val32 |= val32 << 16;
-
- // Force alignment
- while( (tVAddr)dst8 & 3 ) *dst8 ++ = _value;
- dst = (void *)dst8;
-
- // DWORD copies
- for( ; _length > 3; _length -= 4)
- *dst++ = val32;
-
- // Trailing bytes
- dst8 = (void*)dst;
- for( ; _length; _length -- )
- *dst8 ++ = _value;
-
- return _dest;
-}
-
-DEF_DIVMOD(64)
-DEF_DIVMOD(32)
-
-Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem)
-{
- Uint64 ret;
- if(Den == 0) return 0; // TODO: #div0
- if(Num < Den) {
- if(Rem) *Rem = Num;
- return 0;
- }
- if(Num == 0) {
- if(Rem) *Rem = 0;
- return 0;
- }
- if(Den == 1) {
- if(Rem) *Rem = 0;
- return Num;
- }
- if(Den == 2) {
- if(Rem) *Rem = Num & 1;
- return Num >> 1;
- }
- if(Den == 16) {
- if(Rem) *Rem = Num & 0xF;
- return Num >> 4;
- }
- if(Den == 32) {
- if(Rem) *Rem = Num & 0x1F;
- return Num >> 5;
- }
- if(Den == 0x1000) {
- if(Rem) *Rem = Num & 0xFFF;
- return Num >> 12;
- }
-
- if( !(Den >> 32) && !(Num >> 32) ) {
- if(Rem) *Rem = 0; // Clear high bits
- return __divmod32_asm(Num, Den, (Uint32*)Rem);
- }
-
- ret = __divmod64(Num, Den, Rem);
- return ret;
-}
-
-// Unsigned Divide 64-bit Integer
-Uint64 __udivdi3(Uint64 Num, Uint64 Den)
-{
- return DivMod64U(Num, Den, NULL);
-}
-
-// Unsigned Modulus 64-bit Integer
-Uint64 __umoddi3(Uint64 Num, Uint64 Den)
-{
- Uint64 ret = 0;
- DivMod64U(Num, Den, &ret);
- return ret;
-}
-
-Uint32 __udivsi3(Uint32 Num, Uint32 Den)
-{
- return __divmod32_asm(Num, Den, NULL);
-}
-
-Uint32 __umodsi3(Uint32 Num, Uint32 Den)
-{
- Uint32 rem;
- __divmod32_asm(Num, Den, &rem);
- return rem;
-}
-
-static inline Sint32 DivMod32S(Sint32 Num, Sint32 Den, Sint32 *Rem)
-{
- Sint32 ret = 1;
- if( Num < 0 ) {
- ret = -ret;
- Num = -Num;
- }
- if( Den < 0 ) {
- ret = -ret;
- Den = -Den;
- }
- if(ret < 0)
- ret = -__divmod32(Num, Den, (Uint32*)Rem);
- else
- ret = __divmod32(Num, Den, (Uint32*)Rem);
- return ret;
-}
-
-Sint32 __divsi3(Sint32 Num, Sint32 Den)
-{
- return DivMod32S(Num, Den, NULL);
-}
-
-Sint32 __modsi3(Sint32 Num, Sint32 Den)
-{
- Sint32 rem;
- DivMod32S(Num, Den, &rem);
- return rem;
-}
+++ /dev/null
-ENTRY (_start)
-
-_kernel_base = 0x80000000;
-_usertext_vbase = 0xFFFFE000;
-
-SECTIONS
-{
- . = 0;
- .init :
- {
- *(.init)
- }
- . += _kernel_base;
- .text : AT( ADDR(.text) - _kernel_base )
- {
- *(.text*)
- *(.rodata*)
- }
-
-
- /* HACKS: User accesible .text section */
- . = ALIGN(0x1000);
- gUsertextPhysStart = . - _kernel_base;
- . = _usertext_vbase;
- .usertext : AT( gUsertextPhysStart )
- {
- *(.usertext)
- }
- . += gUsertextPhysStart + _kernel_base - _usertext_vbase;
-
- /* 0x4000 (4 pages) alignment needed for root table */
- .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base )
- {
- *(.padata)
- *(.data*)
-
- gKernelSymbols = .;
- *(KEXPORT)
- gKernelSymbolsEnd = .;
-
- gKernelModules = .;
- *(KMODULES)
- gKernelModulesEnd = .;
- }
- .bss : AT( ADDR(.bss) - _kernel_base )
- {
- bss_start = .;
- *(.bss*)
- *(COMMON*)
- . = ALIGN(0x1000);
- *(.pabss)
- bss_end = .;
- }
- gKernelEnd = .;
-}
+++ /dev/null
-/*
- * Acess2
- *
- * ARM7 Entrypoint
- * arch/arm7/main.c
- */
-#define DEBUG 0
-
-#include <acess.h>
-#include <modules.h>
-
-// === IMPORTS ===
-extern void Interrupts_Setup(void);
-extern void Arch_LoadBootModules(void);
-extern void Heap_Install(void);
-extern void Threads_Init(void);
-extern void System_Init(const char *Commandline);
-
-// === PROTOTYPES ===
- int kmain(void);
-Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args);
-
-// === CODE ===
-int kmain(void)
-{
- LogF("Acess2 ARMv7 v"EXPAND_STR(KERNEL_VERSION)"\n");
- LogF(" Git Hash %s\n", gsGitHash);
- LogF(" Build %i\n", BUILD_NUM);
-
- MM_SetupPhys();
-
- LogF("Heap Setup...\n");
- Heap_Install();
-
- LogF("Threads Init...\n");
- Threads_Init();
-
- LogF("VFS Init...\n");
- VFS_Init();
-
- // Boot modules?
- Module_EnsureLoaded("armv7_GIC");
-
- //
- LogF("Moving to arch-independent init\n");
- #if PLATFORM_is_tegra2
- System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=Tegra2Vid");
- #else
- System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=PL110");
- #endif
-// System_Init("Acess2.armv7.bin /Acess=initrd:");
- //TODO:
- LogF("End of kmain(), for(;;) Threads_Sleep();\n");
- for(;;)
- Threads_Sleep();
-}
-
-void Arch_LoadBootModules(void)
-{
-}
-
-Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args)
-{
- Uint32 ret = -1, err = 0;
- Uint32 addr;
- ENTER("iNum xArgs[0] xArgs[1] xArgs[2] xArgs[3]",
- Num, Args[0], Args[1], Args[2], Args[3]
- );
- switch(Num)
- {
- case 1:
-// Log_Debug("ARMv7", "__clear_cache(%p, %p)", Args[0], Args[1]);
- // Align
- Args[0] &= ~0xFFF;
- Args[1] += 0xFFF; Args[1] &= ~0xFFF;
- // Invalidate!
- for( addr = Args[0]; addr < Args[1]; addr += 0x1000 )
- {
- LOG("addr = %p", addr);
- __asm__ __volatile__ (
- "mcrlt p15, 0, %0, c7, c5, 1;\n\t"
- "mcrlt p15, 0, %0, c7, c6, 1;\n\t"
- :
- : "r" (addr)
- );
- }
- ret = 0;
- break;
- }
- Args[0] = ret; // RetLow
- Args[1] = 0; // RetHi
- Args[2] = err; // Errno
- LEAVE('x', ret);
- return ret;
-}
-
+++ /dev/null
-/*
- * Acess2
- *
- * ARM7 Physical Memory Manager
- * arch/arm7/mm_phys.c
- */
-#define DEBUG 0
-
-#include <acess.h>
-#include <mm_virt.h>
-
-#define MM_NUM_RANGES 1 // Single range
-#define MM_RANGE_MAX 0
-#define TRACE_ALLOCS 0
-
-#define NUM_STATIC_ALLOC 4
-
-char gStaticAllocPages[NUM_STATIC_ALLOC][PAGE_SIZE] __attribute__ ((section(".padata")));
-tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {
- (tPAddr)(&gStaticAllocPages[0]) - KERNEL_BASE,
- (tPAddr)(&gStaticAllocPages[1]) - KERNEL_BASE,
- (tPAddr)(&gStaticAllocPages[2]) - KERNEL_BASE,
- (tPAddr)(&gStaticAllocPages[3]) - KERNEL_BASE
-};
-extern char gKernelEnd[];
-
-
-#include <tpl_mm_phys_bitmap.h>
-
-//#define REALVIEW_LOWRAM_SIZE 0x10000000
-#define REALVIEW_LOWRAM_SIZE (32*1024*1024)
-
-void MM_SetupPhys(void)
-{
- LogF("MM_SetupPhys: ()\n");
- MM_Tpl_InitPhys( REALVIEW_LOWRAM_SIZE/0x1000, NULL );
-}
-
-int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length )
-{
- switch(Index)
- {
- case 0:
- *Start = ((tVAddr)&gKernelEnd - KERNEL_BASE + 0xFFF) & ~0xFFF;
- *Length = REALVIEW_LOWRAM_SIZE - *Start;
- return 1;
- default:
- return 0;
- }
-}
-
-/**
- * \brief Takes a physical address and returns the ID of its range
- * \param Addr Physical address of page
- * \return Range ID from eMMPhys_Ranges
- */
-int MM_int_GetRangeID( tPAddr Addr )
-{
- return MM_RANGE_MAX; // ARM doesn't need ranges
-}
+++ /dev/null
-/*
- * Acess2
- *
- * ARM7 Virtual Memory Manager
- * - arch/arm7/mm_virt.c
- */
-#define DEBUG 0
-#include <acess.h>
-#include <mm_virt.h>
-#include <hal_proc.h>
-
-#define TRACE_MAPS 0
-
-#define AP_KRW_ONLY 1 // Kernel page
-#define AP_KRO_ONLY 5 // Kernel RO page
-#define AP_RW_BOTH 3 // Standard RW
-#define AP_RO_BOTH 7 // COW Page
-#define AP_RO_USER 2 // User RO Page
-#define PADDR_MASK_LVL1 0xFFFFFC00
-
-// === IMPORTS ===
-extern Uint32 kernel_table0[];
-
-// === TYPES ===
-typedef struct
-{
- tPAddr PhysAddr;
- Uint8 Size;
- Uint8 Domain;
- BOOL bExecutable;
- BOOL bGlobal;
- BOOL bShared;
- int AP;
-} tMM_PageInfo;
-
-//#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)])
-#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)])
-#define USRFRACTAL(addr) (*((Uint32*)(0x7FDFF000) + ((addr)>>22)))
-#define TLBIALL() __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0))
-#define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" ((addr)&~0xFFF):"memory")
-#define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF))
-
-// === PROTOTYPES ===
-void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1);
- int MM_int_AllocateCoarse(tVAddr VAddr, int Domain);
- int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
- int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
-tVAddr MM_NewUserStack(void);
-tPAddr MM_AllocateZero(tVAddr VAddr);
-tPAddr MM_AllocateRootTable(void);
-void MM_int_CloneTable(Uint32 *DestEnt, int Table);
-tPAddr MM_Clone(void);
-tVAddr MM_NewKStack(int bGlobal);
-void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info);
-//void MM_DumpTables(tVAddr Start, tVAddr End);
-void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch);
-
-// === GLOBALS ===
-tPAddr giMM_ZeroPage;
-
-// === CODE ===
-int MM_InitialiseVirtual(void)
-{
- return 0;
-}
-
-void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1)
-{
- if(VAddr & 0x80000000) {
- *Table0 = (void*)&kernel_table0; // Level 0
- *Table1 = (void*)MM_TABLE1KERN; // Level 1
- }
- else {
- *Table0 = (void*)MM_TABLE0USER;
- *Table1 = (void*)MM_TABLE1USER;
- }
-}
-
-int MM_int_AllocateCoarse(tVAddr VAddr, int Domain)
-{
- Uint32 *table0, *table1;
- Uint32 *desc;
- tPAddr paddr;
-
- ENTER("xVAddr iDomain", VAddr, Domain);
-
- MM_int_GetTables(VAddr, &table0, &table1);
-
- VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page
-
- desc = &table0[ VAddr>>20];
- LOG("desc = %p", desc);
-
- // table0: 4 bytes = 1 MiB
-
- LOG("desc[0] = %x", desc[0]);
- LOG("desc[1] = %x", desc[1]);
- LOG("desc[2] = %x", desc[2]);
- LOG("desc[3] = %x", desc[3]);
-
- if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0
- || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 )
- {
- // Error?
- LEAVE('i', 1);
- return 1;
- }
-
- paddr = MM_AllocPhys();
- if( !paddr )
- {
- // Error
- LEAVE('i', 2);
- return 2;
- }
-
- *desc = paddr | (Domain << 5) | 1;
- desc[1] = desc[0] + 0x400;
- desc[2] = desc[0] + 0x800;
- desc[3] = desc[0] + 0xC00;
-
- if( VAddr < 0x80000000 ) {
- USRFRACTAL(VAddr) = paddr | 0x13;
- }
- else {
- FRACTAL(table1, VAddr) = paddr | 0x13;
- }
-
- // TLBIALL
- TLBIALL();
-
- memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 );
-
- LEAVE('i', 0);
- return 0;
-}
-
-int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
-{
- Uint32 *table0, *table1;
- Uint32 *desc;
-
- ENTER("pVAddr ppi", VAddr, pi);
-
- MM_int_GetTables(VAddr, &table0, &table1);
-
- desc = &table0[ VAddr >> 20 ];
- LOG("desc = %p", desc);
-
- switch(pi->Size)
- {
- case 12: // Small Page
- case 16: // Large Page
- LOG("Page");
- if( (*desc & 3) == 0 ) {
- MM_int_AllocateCoarse( VAddr, pi->Domain );
- }
- desc = &table1[ VAddr >> 12 ];
- LOG("desc (2) = %p", desc);
- if( pi->Size == 12 )
- {
- // Small page
- // - Error if overwriting a large page
- if( (*desc & 3) == 1 ) LEAVE_RET('i', 1);
- if( pi->PhysAddr == 0 ) {
- *desc = 0;
- TLBIMVA( VAddr );
- DCCMVAC( (tVAddr) desc );
- #warning "HACK: TLBIALL"
- TLBIALL();
- LEAVE('i', 0);
- return 0;
- }
-
- *desc = (pi->PhysAddr & 0xFFFFF000) | 2;
- if(!pi->bExecutable) *desc |= 1; // XN
- if(!pi->bGlobal) *desc |= 1 << 11; // nG
- if( pi->bShared) *desc |= 1 << 10; // S
- *desc |= (pi->AP & 3) << 4; // AP
- *desc |= ((pi->AP >> 2) & 1) << 9; // APX
- TLBIMVA( VAddr );
- #warning "HACK: TLBIALL"
- TLBIALL();
- DCCMVAC( (tVAddr) desc );
- LEAVE('i', 0);
- return 0;
- }
- else
- {
- // Large page
- Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo");
- }
- break;
- case 20: // Section or unmapped
- Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo");
- break;
- case 24: // Supersection
- // Error if not aligned
- if( VAddr & 0xFFFFFF ) {
- LEAVE('i', 1);
- return 1;
- }
- if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18))) )
- {
- if( pi->PhysAddr == 0 ) {
- *desc = 0;
- }
- else {
- // Apply
- *desc = pi->PhysAddr & 0xFF000000;
-// *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20;
-// *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5;
- *desc |= 2 | (1 << 18);
- }
- // TODO: Apply to all entries
- Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections");
- LEAVE('i', 0);
- return 0;
- }
- // TODO: What here?
- Log_Warning("MMVirt", "TODO: 24-bit not on supersection?");
- LEAVE('i', 1);
- return 1;
- }
-
- LEAVE('i', 1);
- return 1;
-}
-
-int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
-{
- Uint32 *table0, *table1;
- Uint32 desc;
-
-// LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi);
-
- MM_int_GetTables(VAddr, &table0, &table1);
-
- desc = table0[ VAddr >> 20 ];
-
-// if( VAddr > 0x90000000)
-// LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc);
-
- pi->bExecutable = 1;
- pi->bGlobal = 0;
- pi->bShared = 0;
- pi->AP = 0;
-
- switch( (desc & 3) )
- {
- // 0: Unmapped
- case 0:
- pi->PhysAddr = 0;
- pi->Size = 20;
- pi->Domain = 0;
- return 1;
-
- // 1: Coarse page table
- case 1:
- // Domain from top level table
- pi->Domain = (desc >> 5) & 7;
- // Get next level
- desc = table1[ VAddr >> 12 ];
-// LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc);
- switch( desc & 3 )
- {
- // 0: Unmapped
- case 0:
- pi->Size = 12;
- return 1;
- // 1: Large Page (64KiB)
- case 1:
- pi->Size = 16;
- pi->PhysAddr = desc & 0xFFFF0000;
- pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
- pi->bExecutable = !(desc & 0x8000);
- pi->bShared = (desc >> 10) & 1;
- return 0;
- // 2/3: Small page
- case 2:
- case 3:
- pi->Size = 12;
- pi->PhysAddr = desc & 0xFFFFF000;
- pi->bExecutable = !(desc & 1);
- pi->bGlobal = !(desc >> 11);
- pi->bShared = (desc >> 10) & 1;
- pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
- return 0;
- }
- return 1;
-
- // 2: Section (or Supersection)
- case 2:
- if( desc & (1 << 18) ) {
- // Supersection
- pi->PhysAddr = desc & 0xFF000000;
- pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32;
- pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36;
- pi->Size = 24;
- pi->Domain = 0; // Supersections default to zero
- pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
- return 0;
- }
-
- // Section
- pi->PhysAddr = desc & 0xFFF80000;
- pi->Size = 20;
- pi->Domain = (desc >> 5) & 7;
- pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
- return 0;
-
- // 3: Reserved (invalid)
- case 3:
- pi->PhysAddr = 0;
- pi->Size = 20;
- pi->Domain = 0;
- return 2;
- }
- return 2;
-}
-
-// --- Exports ---
-tPAddr MM_GetPhysAddr(tVAddr VAddr)
-{
- tMM_PageInfo pi;
- if( MM_int_GetPageInfo(VAddr, &pi) )
- return 0;
- return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1));
-}
-
-Uint MM_GetFlags(tVAddr VAddr)
-{
- tMM_PageInfo pi;
- int ret;
-
- if( MM_int_GetPageInfo(VAddr, &pi) )
- return 0;
-
- ret = 0;
-
- switch(pi.AP)
- {
- case 0:
- break;
- case AP_KRW_ONLY:
- ret |= MM_PFLAG_KERNEL;
- break;
- case AP_KRO_ONLY:
- ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO;
- break;
- case AP_RW_BOTH:
- break;
- case AP_RO_BOTH:
- ret |= MM_PFLAG_COW;
- break;
- case AP_RO_USER:
- ret |= MM_PFLAG_RO;
- break;
- }
-
- if( pi.bExecutable ) ret |= MM_PFLAG_EXEC;
- return ret;
-}
-
-void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
-{
- tMM_PageInfo pi;
- Uint curFlags;
-
- if( MM_int_GetPageInfo(VAddr, &pi) )
- return ;
-
- curFlags = MM_GetFlags(VAddr);
- if( (curFlags & Mask) == Flags )
- return ;
- curFlags &= ~Mask;
- curFlags |= Flags;
-
- if( curFlags & MM_PFLAG_COW )
- pi.AP = AP_RO_BOTH;
- else
- {
- switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) )
- {
- case 0:
- pi.AP = AP_RW_BOTH; break;
- case MM_PFLAG_KERNEL:
- pi.AP = AP_KRW_ONLY; break;
- case MM_PFLAG_RO:
- pi.AP = AP_RO_USER; break;
- case MM_PFLAG_KERNEL|MM_PFLAG_RO:
- pi.AP = AP_KRO_ONLY; break;
- }
- }
-
- pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC);
-
- MM_int_SetPageInfo(VAddr, &pi);
-}
-
-int MM_IsValidBuffer(tVAddr Addr, size_t Size)
-{
- tMM_PageInfo pi;
- int bUser = 0;
-
- Size += Addr & (PAGE_SIZE-1);
- Addr &= ~(PAGE_SIZE-1);
-
- if( MM_int_GetPageInfo(Addr, &pi) ) return 0;
- Addr += PAGE_SIZE;
-
- if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY)
- bUser = 1;
-
- while( Size >= PAGE_SIZE )
- {
- if( MM_int_GetPageInfo(Addr, &pi) )
- return 0;
- if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY))
- return 0;
- Addr += PAGE_SIZE;
- Size -= PAGE_SIZE;
- }
-
- return 1;
-}
-
-int MM_Map(tVAddr VAddr, tPAddr PAddr)
-{
- tMM_PageInfo pi = {0};
- #if TRACE_MAPS
- Log("MM_Map %P=>%p", PAddr, VAddr);
- #endif
-
- pi.PhysAddr = PAddr;
- pi.Size = 12;
- if(VAddr < USER_STACK_TOP)
- pi.AP = AP_RW_BOTH;
- else
- pi.AP = AP_KRW_ONLY; // Kernel Read/Write
- pi.bExecutable = 1;
- if( MM_int_SetPageInfo(VAddr, &pi) ) {
-// MM_DerefPhys(pi.PhysAddr);
- return 0;
- }
- return pi.PhysAddr;
-}
-
-tPAddr MM_Allocate(tVAddr VAddr)
-{
- tMM_PageInfo pi = {0};
-
- ENTER("pVAddr", VAddr);
-
- pi.PhysAddr = MM_AllocPhys();
- if( pi.PhysAddr == 0 ) LEAVE_RET('i', 0);
- pi.Size = 12;
- if(VAddr < USER_STACK_TOP)
- pi.AP = AP_RW_BOTH;
- else
- pi.AP = AP_KRW_ONLY;
- pi.bExecutable = 0;
- if( MM_int_SetPageInfo(VAddr, &pi) ) {
- MM_DerefPhys(pi.PhysAddr);
- LEAVE('i', 0);
- return 0;
- }
- LEAVE('x', pi.PhysAddr);
- return pi.PhysAddr;
-}
-
-tPAddr MM_AllocateZero(tVAddr VAddr)
-{
- if( !giMM_ZeroPage ) {
- giMM_ZeroPage = MM_Allocate(VAddr);
- MM_RefPhys(giMM_ZeroPage);
- memset((void*)VAddr, 0, PAGE_SIZE);
- }
- else {
- MM_RefPhys(giMM_ZeroPage);
- MM_Map(VAddr, giMM_ZeroPage);
- }
- MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
- return giMM_ZeroPage;
-}
-
-void MM_Deallocate(tVAddr VAddr)
-{
- tMM_PageInfo pi;
-
- if( MM_int_GetPageInfo(VAddr, &pi) ) return ;
- if( pi.PhysAddr == 0 ) return;
- MM_DerefPhys(pi.PhysAddr);
-
- pi.PhysAddr = 0;
- pi.AP = 0;
- pi.bExecutable = 0;
- MM_int_SetPageInfo(VAddr, &pi);
-}
-
-tPAddr MM_AllocateRootTable(void)
-{
- tPAddr ret;
-
- ret = MM_AllocPhysRange(2, -1);
- if( ret & 0x1000 ) {
- MM_DerefPhys(ret);
- MM_DerefPhys(ret+0x1000);
- ret = MM_AllocPhysRange(3, -1);
- if( ret & 0x1000 ) {
- MM_DerefPhys(ret);
- ret += 0x1000;
-// Log("MM_AllocateRootTable: Second try not aligned, %P", ret);
- }
- else {
- MM_DerefPhys(ret + 0x2000);
-// Log("MM_AllocateRootTable: Second try aligned, %P", ret);
- }
- }
-// else
-// Log("MM_AllocateRootTable: Got it in one, %P", ret);
- return ret;
-}
-
-void MM_int_CloneTable(Uint32 *DestEnt, int Table)
-{
- tPAddr table;
- Uint32 *tmp_map;
- Uint32 *cur = (void*)MM_TABLE1USER;
-// Uint32 *cur = &FRACTAL(MM_TABLE1USER,0);
- int i;
-
- table = MM_AllocPhys();
- if(!table) return ;
-
- cur += 256*Table;
-
- tmp_map = (void*)MM_MapTemp(table);
-
- for( i = 0; i < 1024; i ++ )
- {
-// Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]);
- switch(cur[i] & 3)
- {
- case 0: tmp_map[i] = 0; break;
- case 1:
- tmp_map[i] = 0;
- Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000);
- // Large page?
- break;
- case 2:
- case 3:
- // Small page
- // - If full RW
-// Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230);
- if( (cur[i] & 0x230) == 0x010 )
- {
- void *dst, *src;
- tPAddr newpage;
- newpage = MM_AllocPhys();
- src = (void*)( (Table*256+i)*0x1000 );
- dst = (void*)MM_MapTemp(newpage);
-// Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF);
- memcpy(dst, src, PAGE_SIZE);
- MM_FreeTemp( (tVAddr)dst );
- tmp_map[i] = newpage | (cur[i] & 0xFFF);
- }
- else
- {
- if( (cur[i] & 0x230) == 0x030 )
- cur[i] |= 0x200; // Set to full RO (Full RO=COW, User RO = RO)
- tmp_map[i] = cur[i];
- MM_RefPhys( tmp_map[i] & ~0xFFF );
- }
- break;
- }
- }
- MM_FreeTemp( (tVAddr) tmp_map );
-
- DestEnt[0] = table + 0*0x400 + 1;
- DestEnt[1] = table + 1*0x400 + 1;
- DestEnt[2] = table + 2*0x400 + 1;
- DestEnt[3] = table + 3*0x400 + 1;
-}
-
-tPAddr MM_Clone(void)
-{
- tPAddr ret;
- Uint32 *new_lvl1_1, *new_lvl1_2, *cur;
- Uint32 *tmp_map;
- int i;
-
-// MM_DumpTables(0, KERNEL_BASE);
-
- ret = MM_AllocateRootTable();
-
- cur = (void*)MM_TABLE0USER;
- new_lvl1_1 = (void*)MM_MapTemp(ret);
- new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000);
- tmp_map = new_lvl1_1;
- for( i = 0; i < 0x800-4; i ++ )
- {
- // HACK! Ignore the original identity mapping
- if( i == 0 && Threads_GetTID() == 0 ) {
- tmp_map[0] = 0;
- continue;
- }
- if( i == 0x400 )
- tmp_map = &new_lvl1_2[-0x400];
- switch( cur[i] & 3 )
- {
- case 0: tmp_map[i] = 0; break;
- case 1:
- MM_int_CloneTable(&tmp_map[i], i);
- i += 3; // Tables are alocated in blocks of 4
- break;
- case 2:
- case 3:
- Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i);
- tmp_map[i] = 0;
- break;
- }
- }
-
- // Allocate Fractal table
- {
- int j, num;
- tPAddr tmp = MM_AllocPhys();
- Uint32 *table = (void*)MM_MapTemp(tmp);
- Uint32 sp;
- register Uint32 __SP asm("sp");
-
- // Map table to last 4MiB of user space
- new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1;
- new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1;
- new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1;
- new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1;
-
- tmp_map = new_lvl1_1;
- for( j = 0; j < 512; j ++ )
- {
- if( j == 256 )
- tmp_map = &new_lvl1_2[-0x400];
- if( (tmp_map[j*4] & 3) == 1 )
- {
- table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00;
- table[j] |= 0x813; // nG, Kernel Only, Small page, XN
- }
- else
- table[j] = 0;
- }
- // Fractal
- table[j++] = (ret + 0x0000) | 0x813;
- table[j++] = (ret + 0x1000) | 0x813;
- // Nuke the rest
- for( ; j < 1024; j ++ )
- table[j] = 0;
-
- // Get kernel stack bottom
- sp = __SP & ~(MM_KSTACK_SIZE-1);
- j = (sp / 0x1000) % 1024;
- num = MM_KSTACK_SIZE/0x1000;
-
-// Log("num = %i, sp = %p, j = %i", num, sp, j);
-
- // Copy stack pages
- for(; num--; j ++, sp += 0x1000)
- {
- tVAddr page;
- void *tmp_page;
-
- page = MM_AllocPhys();
-// Log("page = %P", page);
- table[j] = page | 0x813;
-
- tmp_page = (void*)MM_MapTemp(page);
- memcpy(tmp_page, (void*)sp, 0x1000);
- MM_FreeTemp( (tVAddr) tmp_page );
- }
-
- MM_FreeTemp( (tVAddr)table );
- }
-
- MM_FreeTemp( (tVAddr)new_lvl1_1 );
- MM_FreeTemp( (tVAddr)new_lvl1_2 );
-
-// Log("MM_Clone: ret = %P", ret);
-
- return ret;
-}
-
-void MM_ClearUser(void)
-{
- int i, j;
- const int user_table_count = USER_STACK_TOP / (256*0x1000);
- Uint32 *cur = (void*)MM_TABLE0USER;
- Uint32 *tab;
-
-// MM_DumpTables(0, 0x80000000);
-
-// Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4);
-
- for( i = 0; i < user_table_count; i ++ )
- {
- switch( cur[i] & 3 )
- {
- case 0: break; // Already unmapped
- case 1: // Sub pages
- tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
- for( j = 0; j < 1024; j ++ )
- {
- switch( tab[j] & 3 )
- {
- case 0: break; // Unmapped
- case 1:
- Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser");
- break;
- case 2:
- case 3:
- MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) );
- break;
- }
- }
- MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) );
- cur[i+0] = 0;
- cur[i+1] = 0;
- cur[i+2] = 0;
- i += 3;
- break;
- case 2:
- case 3:
- Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser");
- break;
- }
- cur[i] = 0;
- }
-
- // Final block of 4 tables are KStack
- i = 0x800 - 4;
-
- // Clear out unused stacks
- {
- register Uint32 __SP asm("sp");
- int cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024;
-
- tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
-
- // First 512 is the Table1 mapping + 2 for Table0 mapping
- for( j = 512+2; j < 1024; j ++ )
- {
- // Skip current stack
- if( j == cur_stack_base ) {
- j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1;
- continue ;
- }
- if( !(tab[j] & 3) ) continue;
- ASSERT( (tab[j] & 3) == 2 );
- MM_DerefPhys( tab[j] & ~(PAGE_SIZE) );
- tab[j] = 0;
- }
- }
-
-
-// MM_DumpTables(0, 0x80000000);
-}
-
-tVAddr MM_MapTemp(tPAddr PAddr)
-{
- tVAddr ret;
- tMM_PageInfo pi;
-
- for( ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE )
- {
- if( MM_int_GetPageInfo(ret, &pi) == 0 )
- continue;
-
-// Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0));
- MM_RefPhys(PAddr); // Counter the MM_Deallocate in FreeTemp
- MM_Map(ret, PAddr);
-
- return ret;
- }
- Log_Warning("MMVirt", "MM_MapTemp: All slots taken");
- return 0;
-}
-
-void MM_FreeTemp(tVAddr VAddr)
-{
- if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) {
- Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr);
- return ;
- }
-
- MM_Deallocate(VAddr);
-}
-
-tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages)
-{
- tVAddr ret;
- int i;
- tMM_PageInfo pi;
-
- ENTER("xPAddr iNPages", PAddr, NPages);
-
- // Scan for a location
- for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE )
- {
-// LOG("checking %p", ret);
- // Check if there is `NPages` free pages
- for( i = 0; i < NPages; i ++ )
- {
- if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 )
- break;
- }
- // Nope, jump to after the used page found and try again
-// LOG("i = %i, ==? %i", i, NPages);
- if( i != NPages ) {
- ret += i * PAGE_SIZE;
- continue ;
- }
-
- // Map the pages
- for( i = 0; i < NPages; i ++ )
- MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE);
- // and return
- LEAVE('p', ret);
- return ret;
- }
- Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages);
- LEAVE('p', 0);
- return 0;
-}
-
-tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr)
-{
- tPAddr phys;
- tVAddr ret;
-
- phys = MM_AllocPhysRange(Pages, MaxBits);
- if(!phys) {
- Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages);
- return 0;
- }
-
- ret = MM_MapHWPages(phys, Pages);
- *PAddr = phys;
-
- return ret;
-}
-
-void MM_UnmapHWPages(tVAddr Vaddr, Uint Number)
-{
- Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages");
-}
-
-tVAddr MM_NewKStack(int bShared)
-{
- tVAddr min_addr, max_addr;
- tVAddr addr, ofs;
-
- if( bShared ) {
- min_addr = MM_GLOBALSTACKS;
- max_addr = MM_GLOBALSTACKS_END;
- }
- else {
- min_addr = MM_KSTACK_BASE;
- max_addr = MM_KSTACK_END;
- }
-
- // Locate a free slot
- for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE )
- {
- tMM_PageInfo pi;
- if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) ) break;
- }
-
- // Check for an error
- if(addr >= max_addr) {
- return 0;
- }
-
- // 1 guard page
- for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE )
- {
- if( MM_Allocate(addr + ofs) == 0 )
- {
- while(ofs)
- {
- ofs -= PAGE_SIZE;
- MM_Deallocate(addr + ofs);
- }
- Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate");
- return 0;
- }
- }
- return addr + ofs;
-}
-
-tVAddr MM_NewUserStack(void)
-{
- tVAddr addr, ofs;
-
- addr = USER_STACK_TOP - USER_STACK_SIZE;
- if( MM_GetPhysAddr(addr + PAGE_SIZE) ) {
- Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken",
- addr + PAGE_SIZE
- );
- return 0;
- }
-
- // 1 guard page
- for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE )
- {
- tPAddr rv;
- if(ofs >= USER_STACK_SIZE - USER_STACK_COMM)
- rv = MM_Allocate(addr + ofs);
- else
- rv = MM_AllocateZero(addr + ofs);
- if(rv == 0)
- {
- while(ofs)
- {
- ofs -= PAGE_SIZE;
- MM_Deallocate(addr + ofs);
- }
- Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate");
- return 0;
- }
- MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL);
- }
-// Log("Return %p", addr + ofs);
-// MM_DumpTables(0, 0x80000000);
- return addr + ofs;
-}
-
-void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info)
-{
- if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage )
- {
- Debug("%p => %8s - 0x%7x %i %x %s",
- Start, "ZERO", Len,
- Info->Domain, Info->AP,
- Info->bGlobal ? "G" : "nG"
- );
- }
- else
- {
- Debug("%p => %8x - 0x%7x %i %x %s",
- Start, Info->PhysAddr-Len, Len,
- Info->Domain, Info->AP,
- Info->bGlobal ? "G" : "nG"
- );
- }
-}
-
-void MM_DumpTables(tVAddr Start, tVAddr End)
-{
- tVAddr range_start = 0, addr;
- tMM_PageInfo pi, pi_old;
- int i = 0, inRange=0;
-
- memset(&pi_old, 0, sizeof(pi_old));
-
- Debug("Page Table Dump (%p to %p):", Start, End);
- range_start = Start;
- for( addr = Start; i == 0 || (addr && addr < End); i = 1 )
- {
- int rv;
-// Log("addr = %p", addr);
- rv = MM_int_GetPageInfo(addr, &pi);
- if( rv
- || pi.Size != pi_old.Size
- || pi.Domain != pi_old.Domain
- || pi.AP != pi_old.AP
- || pi.bGlobal != pi_old.bGlobal
- || pi_old.PhysAddr != pi.PhysAddr )
- {
- if(inRange) {
- MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old);
- }
- addr &= ~((1 << pi.Size)-1);
- range_start = addr;
- }
-
- pi_old = pi;
- // Handle the zero page
- if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage )
- pi_old.PhysAddr += 1 << pi_old.Size;
- addr += 1 << pi_old.Size;
- inRange = (rv == 0);
- }
- if(inRange)
- MM_int_DumpTableEnt(range_start, addr - range_start, &pi);
- Debug("Done");
-}
-
-// NOTE: Runs in abort context, not much difference, just a smaller stack
-void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch)
-{
- int rv;
- tMM_PageInfo pi;
-
- rv = MM_int_GetPageInfo(Addr, &pi);
-
- // Check for COW
- if( rv == 0 && pi.AP == AP_RO_BOTH )
- {
- pi.AP = AP_RW_BOTH;
- if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage )
- {
- tPAddr newpage;
- newpage = MM_AllocPhys();
- if( !newpage ) {
- Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO");
- for(;;);
- }
-
- #if TRACE_COW
- Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC,
- newpage, MM_GetRefCount(pi.PhysAddr));
- #endif
-
- MM_DerefPhys(pi.PhysAddr);
- pi.PhysAddr = newpage;
- pi.AP = AP_RW_BOTH;
- MM_int_SetPageInfo(Addr, &pi);
-
- memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE );
-
- return ;
- }
- else if( MM_GetRefCount(pi.PhysAddr) > 1 )
- {
- // Duplicate the page
- tPAddr newpage;
- void *dst, *src;
-
- newpage = MM_AllocPhys();
- if(!newpage) {
- Log_Error("MMVirt", "Unable to allocate new page for COW");
- for(;;);
- }
- dst = (void*)MM_MapTemp(newpage);
- src = (void*)(Addr & ~(PAGE_SIZE-1));
- memcpy( dst, src, PAGE_SIZE );
- MM_FreeTemp( (tVAddr)dst );
-
- #if TRACE_COW
- Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC,
- pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr));
- #endif
-
- MM_DerefPhys(pi.PhysAddr);
- pi.PhysAddr = newpage;
- }
- #if TRACE_COW
- else {
- Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P",
- Addr, PC, pi.PhysAddr);
- }
- #endif
- // Unset COW
- pi.AP = AP_RW_BOTH;
- MM_int_SetPageInfo(Addr, &pi);
- return ;
- }
-
-
- Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR,
- (bPrefetch ? " - Prefetch" : "")
- );
- if( Addr < 0x80000000 )
- MM_DumpTables(0, 0x80000000);
- else
- MM_DumpTables(0x80000000, -1);
- for(;;);
-}
-
+++ /dev/null
-/*
- *
- */
-#include <acess.h>
-#include <drv_pci_int.h>
-
-// Realview
-//#define PCI_BASE 0x60000000
-
-//#define PCI_BASE 0xF0400000 // VMM Mapping
-#define PCI_BASE 0
-
-// === CODE ===
-void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data)
-{
- #if PCI_BASE
- Uint32 address = PCI_BASE | Addr;
- *(Uint32*)(address) = Data;
- #else
- #endif
-}
-
-Uint32 PCI_CfgReadDWord(Uint32 Addr)
-{
- #if PCI_BASE
- Uint32 address = PCI_BASE | Addr;
- return *(Uint32*)address;
- #else
- return 0xFFFFFFFF;
- #endif
-}
-
+++ /dev/null
-/*
- * Acess2 ARM
- * - By John Hodge (thePowersGang)
- *
- * arch/arm7/proc.S
- * - Process management assembly
- */
-
-#include "include/assembly.h"
-
-.globl KernelThreadHeader
-@ SP+12: Argument 1
-@ SP+8: Argument Count
-@ SP+4: Function
-@ SP+0: Thread Pointer
-KernelThreadHeader:
- ldr r0, [sp],#4
- @ TODO: Do something with the thread pointer
-
- ldr r4, [sp],#4 @ Function
- @ Get argument
- ldr r0, [sp],#4
-
- blx r4
-
- ldr r0, =0
- bl Threads_Exit
- b .
-
-.globl SwitchTask
-@ R0: New stack
-@ R1: Pointer to where to save old stack
-@ R2: New IP
-@ R3: Pointer to save old IP
-@ SP+0: New address space
-SwitchTask:
- push {r4-r12,lr}
-
- @ Save IP
- ldr r4, =.return
- str r4, [r3]
- @ Save SP
- str sp, [r1]
-
- @ Only update TTBR0 if the task has an explicit address space
- ldr r1, [sp,#4*10]
- tst r1, r1
- mcrne p15, 0, r1, c2, c0, 0 @ Set TTBR0 to r0
- mov r1, #0
- mcrne p15, 0, r1, c8, c7, 0 @ TLBIALL - Invalidate all
-
- @ Restore SP
- mov sp, r0
-
- bx r2
-
-.return:
- pop {r4-r12,pc}
-
-.extern MM_Clone
-.extern MM_DumpTables
-.globl Proc_CloneInt
-Proc_CloneInt:
- @ R0: SP Destination
- @ R1: Mem Destination
- push {r4-r12,lr}
- mov r4, r1 @ Save mem destination
- str sp, [r0] @ Save SP to SP dest
-
- bl MM_Clone
- str r0, [r4] @ Save clone return to Mem Dest
-
- ldr r0, =Proc_CloneInt_new
- pop {r4-r12,pc}
-Proc_CloneInt_new:
- mov r0, #0
- pop {r4-r12,pc}
-
-@ R0: New user SP
-@ Return: Old user SP
-.globl Proc_int_SwapUserSP
-Proc_int_SwapUserSP:
- cps #31 @ Go to system mode
- mov r1, sp
- tst r0, r0 @ Only update if non-zero
- movne sp, r0
- mov r0, r1
- cps #19
- mov pc, lr
-
-.section .usertext, "ax"
-.globl Proc_int_DropToUser
-@ R0: User IP
-@ R1: User SP
-Proc_int_DropToUser:
- cps #16
- mov sp, r1
- mov pc, r0
-
-.section .rodata
-csProc_CloneInt_NewTaskMessage:
- .asciz "New task PC=%p, R4=%p, sp=%p"
-csProc_CloneInt_OldTaskMessage:
- .asciz "Parent task PC=%p, R4=%p, SP=%p"
+++ /dev/null
-/*
- * Acess2
- * - By John Hodge (thePowersGang)
- *
- * arch/arm7/proc.c
- * - ARM7 Process Switching
- */
-#include <acess.h>
-#include <threads_int.h>
-#include <hal_proc.h>
-
-// === IMPORTS ===
-extern tThread gThreadZero;
-extern tProcess gProcessZero;
-extern void SwitchTask(Uint32 NewSP, Uint32 *OldSP, Uint32 NewIP, Uint32 *OldIP, Uint32 MemPtr);
-extern void KernelThreadHeader(void); // Actually takes args on stack
-extern void Proc_int_DropToUser(Uint32 IP, Uint32 SP) NORETURN __attribute__((long_call));
-extern Uint32 Proc_int_SwapUserSP(Uint32 NewSP);
-extern Uint32 Proc_CloneInt(Uint32 *SP, Uint32 *MemPtr);
-extern tVAddr MM_NewKStack(int bGlobal); // TODO: Move out into a header
-extern tVAddr MM_NewUserStack(void);
-extern char kernel_table0[];
-
-// === PROTOTYPES ===
-void Proc_IdleThread(void *unused);
-
-// === GLOBALS ===
-tThread *gpCurrentThread = &gThreadZero;
-tThread *gpIdleThread = NULL;
-
-// === CODE ===
-void ArchThreads_Init(void)
-{
- gProcessZero.MemState.Base = (tPAddr)&kernel_table0 - KERNEL_BASE;
-}
-
-void Proc_IdleThread(void *unused)
-{
- Threads_SetPriority(gpIdleThread, -1);
- for(;;) {
- Proc_Reschedule();
- __asm__ __volatile__ ("wfi");
- }
-}
-
-void Proc_Start(void)
-{
- tTID tid;
-
- tid = Proc_NewKThread( Proc_IdleThread, NULL );
- gpIdleThread = Threads_GetThread(tid);
- gpIdleThread->ThreadName = (char*)"Idle Thread";
-}
-
-int GetCPUNum(void)
-{
- return 0;
-}
-
-tThread *Proc_GetCurThread(void)
-{
- return gpCurrentThread;
-}
-
-void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
-{
- Uint32 *usr_sp;
- int i;
- const char **envp;
- tVAddr delta;
-
-// Log_Debug("Proc", "Proc_StartUser: (Entrypoint=%p, Base=%p, ArgC=%i, ArgV=%p, DataSize=0x%x)",
-// Entrypoint, Base, ArgC, ArgV, DataSize);
-
- // Write data to the user's stack
- usr_sp = (void*)MM_NewUserStack();
- usr_sp -= (DataSize+3)/4;
- memcpy(usr_sp, ArgV, DataSize);
- free(ArgV);
-
- // Adjust user's copy of the arguments
- delta = (tVAddr)usr_sp - (tVAddr)ArgV;
- ArgV = (void*)usr_sp;
- for(i = 0; ArgV[i]; i ++) ArgV[i] += delta;
- envp = &ArgV[i+1];
- for(i = 0; envp[i]; i ++) envp[i] += delta;
-
- *--usr_sp = (Uint32)envp;
- *--usr_sp = (Uint32)ArgV;
- *--usr_sp = (Uint32)ArgC;
- *--usr_sp = Base;
-
- // Drop to user code
- Log_Debug("Proc", "Proc_int_DropToUser(%p, %p)", Entrypoint, usr_sp);
- Proc_int_DropToUser(Entrypoint, (Uint32)usr_sp);
-}
-
-void Proc_ClearProcess(tProcess *Process)
-{
- Log_Warning("Proc", "TODO: Nuke address space etc");
-}
-
-void Proc_ClearThread(tThread *Thread)
-{
-}
-
-tTID Proc_Clone(Uint Flags)
-{
- tThread *new;
- Uint32 pc, sp, mem;
-
- new = Threads_CloneTCB(Flags);
- if(!new) return -1;
-
- // Actual clone magic
- pc = Proc_CloneInt(&sp, &mem);
- if(pc == 0) {
- Log("Proc_Clone: In child");
- return 0;
- }
-
- new->SavedState.IP = pc;
- new->SavedState.SP = sp;
- new->SavedState.UserSP = Proc_int_SwapUserSP(0);
- new->SavedState.UserIP = Proc_GetCurThread()->SavedState.UserIP;
- new->Process->MemState.Base = mem;
-
- Threads_AddActive(new);
-
- return new->TID;
-}
-
-int Proc_SpawnWorker( void (*Fnc)(void*), void *Ptr )
-{
- tThread *new;
- Uint32 sp;
-
- new = Threads_CloneThreadZero();
- if(!new) return -1;
- if(new->ThreadName) free(new->ThreadName);
- new->ThreadName = NULL;
-
- new->KernelStack = MM_NewKStack(1);
- if(!new->KernelStack) {
- // TODO: Delete thread
- Log_Error("Proc", "Unable to allocate kernel stack");
- return -1;
- }
-
- sp = new->KernelStack;
-
- *(Uint32*)(sp -= 4) = (Uint)Ptr;
- *(Uint32*)(sp -= 4) = (Uint)Fnc;
- *(Uint32*)(sp -= 4) = (Uint)new;
-
- new->SavedState.SP = sp;
- new->SavedState.IP = (Uint)KernelThreadHeader;
-
- Threads_AddActive(new);
-
- return new->TID;
-}
-
-tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr )
-{
- tThread *new;
- Uint32 sp;
-
- new = Threads_CloneTCB(0);
- if(!new) return -1;
- free(new->ThreadName);
- new->ThreadName = NULL;
-
- // TODO: Non-shared stack
- new->KernelStack = MM_NewKStack(1);
- if(!new->KernelStack) {
- // TODO: Delete thread
- Log_Error("Proc", "Unable to allocate kernel stack");
- return -1;
- }
-
- sp = new->KernelStack;
-
- *(Uint32*)(sp -= 4) = (Uint)Ptr;
- *(Uint32*)(sp -= 4) = (Uint)Fnc;
- *(Uint32*)(sp -= 4) = (Uint)new;
-
- new->SavedState.SP = sp;
- new->SavedState.IP = (Uint)KernelThreadHeader;
-
- Threads_AddActive(new);
-
- return new->TID;
-}
-
-void Proc_CallFaultHandler(tThread *Thread)
-{
-
-}
-
-void Proc_Reschedule(void)
-{
- tThread *cur, *next;
-
- cur = gpCurrentThread;
-
- next = Threads_GetNextToRun(0, cur);
- if(!next) next = gpIdleThread;
- if(!next || next == cur) return;
-
- Log("Switching to %p (%i %s) IP=%p SP=%p TTBR0=%p UsrSP=%p",
- next, next->TID, next->ThreadName,
- next->SavedState.IP, next->SavedState.SP, next->Process->MemState.Base,
- next->SavedState.UserSP
- );
-
- Log("Requested by %p", __builtin_return_address(0));
-
- gpCurrentThread = next;
-
- cur->SavedState.UserSP = Proc_int_SwapUserSP( next->SavedState.UserSP );
-
- SwitchTask(
- next->SavedState.SP, &cur->SavedState.SP,
- next->SavedState.IP, &cur->SavedState.IP,
- next->Process->MemState.Base
- );
-
-}
-
-void Proc_DumpThreadCPUState(tThread *Thread)
-{
-
-}
-
+++ /dev/null
-
-#include "include/assembly.h"
-#include "include/options.h"
-
-@
-@ Exception defs taken from ARM DDI 0406B
-@
-.section .init
-interrupt_vector_table:
-ivt_reset: b _start @ 0x00 Reset
-ivt_undef: b Undef_Handler @ 0x04 #UD
-ivt_svc: b SVC_Handler @ 0x08 SVC (used to be called SWI)
-ivt_prefetch: b PrefetchAbort @ 0x0C Prefetch abort
-ivt_data: b DataAbort @ 0x10 Data abort
-ivt_unused: b . @ 0x14 Not Used
-ivt_irq: b IRQHandler @ 0x18 IRQ
-ivt_fiq: b . @ 0x1C FIQ (Fast interrupt)
-
-.globl _start
-_start:
- ldr r2, =UART0_PADDR
- mov r1, #'A'
- str r1, [r2]
-
- ldr r0, =kernel_table0-KERNEL_BASE
- mcr p15, 0, r0, c2, c0, 1 @ Set TTBR1 to r0
- mcr p15, 0, r0, c2, c0, 0 @ Set TTBR0 to r0 too (for identity)
-
- mov r1, #'c'
- str r1, [r2]
-
- mov r0, #1
- mcr p15, 0, r0, c2, c0, 2 @ Set TTCR to 1 (50/50 split)
-
- mov r1, #'e'
- str r1, [r2]
-
- mov r0, #3
- mcr p15, 0, r0, c3, c0, 0 @ Set Domain 0 to Manager
-
- mov r1, #'s'
- str r1, [r2]
-
- @ Enable VMSA
- mrc p15, 0, r0, c1, c0, 0
- orr r0, r0, #1
- orr r0, r0, #1 << 23
- mvn r1, #1 << 2
- and r0, r0, r1
- mcr p15, 0, r0, c1, c0, 0
-
- @ HACK! Disable caching
- mrc p15, 0, r1, c1, c0, 0
-
- ldr r2, =0xF1000000
- mov r1, #'s'
- str r1, [r2]
-
- @ Enable access faults on domains 0 & 1
- mov r0, #0x55 @ 01010101b
- mcr p15, 0, r0, c3, c0, 0
-
- mov r1, #'2'
- str r1, [r2]
-
- @
- @ Check for security extensions
- @
- mrc p15, 0, r0, c0, c1, 1
- and r0, #0xF0
- @ - Present
- ldrne r0,=KERNEL_BASE
- mcrne p15, 0, r0, c12, c0, 0 @ Set the VBAR (brings exceptions into high memory)
- @ - Absent
- mrceq p15, 0, r0, c1, c0, 0 @ Set SCTLR.V
- orreq r0, #0x2000
- mcreq p15, 0, r0, c1, c0, 0
-
- mov r1, #'-'
- str r1, [r2]
-
- @ Prepare for interrupts
- cps #18 @ IRQ Mode
- ldr sp, =irqstack+0x1000 @ Set up stack
- cps #23 @ Abort Mode
- ldr sp, =abortstack+0x1000
- cps #19
-
- mov r1, #'a'
- str r1, [r2]
- mov r1, #'r'
- str r1, [r2]
- mov r1, #'m'
- str r1, [r2]
- mov r1, #13
- str r1, [r2]
- mov r1, #10
- str r1, [r2]
-
-.extern bss_start
-.extern bss_size_div_4
-.zero_bss:
- ldr r0, =bss_start
- ldr r1, =bss_end
- mov r3, #0
-.zero_bss_loop:
- str r3, [r0],#4
- cmp r0, r1
- bls .zero_bss_loop
-
-.goto_c:
- ldr sp, =0x80000000-8 @ Set up stack (top of user range)
- ldr r0, =kmain
- mov pc, r0
-1: b 1b @ Infinite loop
-
-.comm irqstack, 0x1000 @ ; 4KiB Stack
-.comm abortstack, 0x1000 @ ; 4KiB Stack
-
-.extern SyscallHandler
-SVC_Handler:
-@ sub lr, #4
- srsdb sp!, #19 @ Save state to stack
- cpsie ifa, #19 @ Ensure we're in supervisor with interrupts enabled (should already be there)
- push {r0-r12}
-
- ldr r4, [lr,#-4]
- mvn r5, #0xFF000000
- and r4, r5
-
- tst r4, #0x1000
- bne .arm_specifics
-
- push {r4}
-
- mov r0, sp
- ldr r4, =SyscallHandler
- blx r4
-
-@ ldr r0, =csSyscallPrintRetAddr
-@ ldr r1, [sp,#9*4+5*4]
-@ ldr r4, =Log
-@ blx r4
-
- pop {r2} @ errno
- pop {r0,r1} @ Ret/RetHi
- add sp, #2*4 @ Saved r2/r3
-
- pop {r4-r12}
- rfeia sp! @ Pop state (actually RFEFD)
-.arm_specifics:
- and r4, #0xFF
- mov r0, r4 @ Number
- mov r1, sp @ Arguments
-
- ldr r4, =ARMv7_int_HandleSyscalls
- blx r4
-
- add sp, #4*4
- pop {r4-r12}
- rfeia sp!
-
-
-.globl gpIRQHandler
-gpIRQHandler: .long 0
-IRQ_saved_sp: .long 0
-IRQ_saved_lr: .long 0
-.globl IRQHandler
-IRQHandler:
- sub lr, #4 @ Adjust LR to the correct value
- srsdb sp!, #19 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
- cps #19
-
- PUSH_GPRS
-
-@ ldr r0, =csIRQ_Tag
-@ ldr r1, =csIRQ_Fmt
-@ ldr r4, =Log_Debug
-@ blx r4
-
- @ Call the registered handler
- ldr r0, gpIRQHandler
- blx r0
-
- @ Restore CPU state
- POP_GPRS
- cpsie i
- rfeia sp! @ Pop state (actually RFEFD)
- bx lr
-
-.globl DataAbort
-DataAbort:
- sub lr, #8 @ Adjust LR to the correct value
- srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
-@ cpsid ifa, #19
- PUSH_GPRS
-
- mov r3, #0 @ not a prefetch abort
- mrc p15, 0, r2, c5, c0, 0 @ Read DFSR (Data Fault Status Register) to R2
- mrc p15, 0, r1, c6, c0, 0 @ Read DFAR (Data Fault Address Register) into R1
- mov r0, lr @ PC
- ldr r4, =MM_PageFault
- blx r4
-
- POP_GPRS
- rfeia sp! @ Pop state (actually RFEFD)
-
-.globl PrefetchAbort
-PrefetchAbort:
- sub lr, #4 @ Adjust LR to the correct value
- srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
-@ cpsid ifa, #19
- PUSH_GPRS
-
- ldr r0, =csAbort_Tag
- ldr r1, =csPrefetchAbort_Fmt
-# mov r2, lr
- mrc p15, 0, r2, c6, c0, 2 @ Read IFAR (Instruction Fault Address Register) into R3
- mrc p15, 0, r3, c5, c0, 1 @ Read IFSR (Instruction Fault Status Register) into R3
- ldr r5, =Log_Error
- blx r5
-
-.loop:
- wfi
- b .loop
-.globl Undef_Handler
-Undef_Handler:
- wfi
- b Undef_Handler
-
-
-
-.section .rodata
-csIRQ_Tag:
-csAbort_Tag:
- .asciz "ARMv7"
-csIRQ_Fmt:
- .asciz "IRQ"
-csDataAbort_Fmt:
- .asciz "Data Abort - %p accessed %p, DFSR=%x Unk:%x Unk:%x"
-csPrefetchAbort_Fmt:
- .asciz "Prefetch Abort at %p, IFSR=%x"
-csSyscallPrintRetAddr:
- .asciz "Syscall ret to %p"
-
-.section .padata
-.globl kernel_table0
-
-kernel_table0:
- .long 0x00000402 @ Identity map the first 1 MiB
- .rept 0x7FC - 1
- .long 0
- .endr
- .long user_table1_map + 0x000 - KERNEL_BASE + 1 @ 0x7FC00000
- .long user_table1_map + 0x400 - KERNEL_BASE + 1 @ 0x7FD00000
- .long user_table1_map + 0x800 - KERNEL_BASE + 1 @ KStacks
- .long user_table1_map + 0xC00 - KERNEL_BASE + 1
- @ 0x80000000 - User/Kernel split
- .long 0x00000402 @ Map first 4 MiB to 2GiB (KRW only)
- .long 0x00100402 @
- .long 0x00200402 @
- .long 0x00300402 @
- .rept 0xF00 - 0x800 - 4
- .long 0
- .endr
-#if PCI_PADDR
- .long PCI_PADDR + 0*(1 << 20) + 0x402 @ Map PCI config space
- .long PCI_PADDR + 1*(1 << 20) + 0x402
- .long PCI_PADDR + 2*(1 << 20) + 0x402
- .long PCI_PADDR + 3*(1 << 20) + 0x402
- .long PCI_PADDR + 4*(1 << 20) + 0x402
- .long PCI_PADDR + 5*(1 << 20) + 0x402
- .long PCI_PADDR + 6*(1 << 20) + 0x402
- .long PCI_PADDR + 7*(1 << 20) + 0x402
- .long PCI_PADDR + 8*(1 << 20) + 0x402
- .long PCI_PADDR + 9*(1 << 20) + 0x402
- .long PCI_PADDR + 10*(1 << 20) + 0x402
- .long PCI_PADDR + 11*(1 << 20) + 0x402
- .long PCI_PADDR + 12*(1 << 20) + 0x402
- .long PCI_PADDR + 13*(1 << 20) + 0x402
- .long PCI_PADDR + 14*(1 << 20) + 0x402
- .long PCI_PADDR + 15*(1 << 20) + 0x402
-#else
- .rept 16
- .long 0
- .endr
-#endif
- .long hwmap_table_0 + 0x000 - KERNEL_BASE + 1
- .long hwmap_table_0 + 0x400 - KERNEL_BASE + 1
- .long hwmap_table_0 + 0x800 - KERNEL_BASE + 1
- .long hwmap_table_0 + 0xC00 - KERNEL_BASE + 1
- .rept 0xFF8 - 0xF00 - 16 - 4
- .long 0
- .endr
- @ Page fractals
- .long kernel_table1_map + 0x000 - KERNEL_BASE + 1
- .long kernel_table1_map + 0x400 - KERNEL_BASE + 1
- .long kernel_table1_map + 0x800 - KERNEL_BASE + 1
- .long kernel_table1_map + 0xC00 - KERNEL_BASE + 1
- .long kernel_exception_map + 0x000 - KERNEL_BASE + 1
- .long kernel_exception_map + 0x400 - KERNEL_BASE + 1
- .long kernel_exception_map + 0x800 - KERNEL_BASE + 1
- .long kernel_exception_map + 0xC00 - KERNEL_BASE + 1
-
-@ PID0 user table
-.globl user_table1_map
-@ User table1 data table (only the first half is needed)
-@ - Abused to provide kernel stacks in the unused half of the table
-user_table1_map: @ Size = 4KiB (only 2KiB used)
- .rept 0x800/4-1
- .long 0
- .endr
- .long user_table1_map - KERNEL_BASE + 0x13 @ ...1FF000 = 0x7FDFF000
- @ Kernel stack zone
- .long kernel_table0 + 0x0000 - KERNEL_BASE + 0x13 @ ...200000 = 0x7FE00000
- .long kernel_table0 + 0x1000 - KERNEL_BASE + 0x13 @ ...201000 = 0x7FE01000
- .rept (0x800/4)-(MM_KSTACK_SIZE/0x1000)-2
- .long 0
- .endr
- #if MM_KSTACK_SIZE != 0x2000
- #error Kernel stack size not changed in start.S
- #endif
- .long stack + 0x0000 - KERNEL_BASE + 0x13 @ Kernel Stack
- .long stack + 0x1000 - KERNEL_BASE + 0x13 @
-
-.globl kernel_table1_map
-kernel_table1_map: @ Size = 4KiB
- .rept (0xF00+16)/4
- .long 0
- .endr
- .long hwmap_table_0 - KERNEL_BASE + 0x13
- .rept 0xFF8/4 - (0xF00+16)/4 - 1
- .long 0
- .endr
- .long kernel_table1_map - KERNEL_BASE + 0x13
- .long kernel_exception_map - KERNEL_BASE + 0x13
-
-@ Hardware mappings
-.globl hwmap_table_0
-hwmap_table_0:
- .long UART0_PADDR + 0x13 @ UART0
- .rept 1024 - 1
- .long 0
- .endr
-.globl kernel_exception_map
-kernel_exception_map:
- @ Padding
- .rept 1024-256
- .long 0
- .endr
- @ Align to nearly the end
- .rept 256-16
- .long 0
- .endr
- .long 0x212 @ Map first page for exceptions (Kernel RO, Execute)
- .rept 16-1-2
- .long 0
- .endr
- .long gUsertextPhysStart + 0x22 @ User .text (User RO, Kernel RW, because both is COW)
- .long 0
-
-.section .padata
-stack:
- .space MM_KSTACK_SIZE, 0 @ Original kernel stack
-
-// vim: ts=8 ft=armv7
-
+++ /dev/null
-/*
- * Acess2
- *
- * ARM7 Time code
- * arch/arm7/time.c
- */
-#include <acess.h>
-
-// === GLOBALS ===
-tTime giTimestamp;
-
-// === CODE ===
-tTime now(void)
-{
- return giTimestamp;
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * arch/helpers.h
- * - Misc helper functions for the arch code
- */
-#ifndef _ARCH_HELPERS_H_
-#define _ARCH_HELPERS_H_
-
-// Divide
-// - Find what power of two times Den is > Num
-// - Iterate down in bit significance
-// > If the `N` value is greater than `D`, we can set this bit
-#define DEF_DIVMOD(s) Uint##s __divmod##s(Uint##s N, Uint##s D, Uint##s*Rem){\
- Uint##s ret=0,add=1;\
- while(N>=D&&add) {D<<=1;add<<=1;}\
- while(add>1){\
- add>>=1;D>>=1;\
- if(N>=D){ret+=add;N-=D;}\
- }\
- if(Rem)*Rem = N;\
- return ret;\
-}
-
-#endif
-
+++ /dev/null
-#
-# Acess2 Kernel
-# m68k Architecture Makefile
-# arch/m68k/Makefile
-
-CFLAGS =
-
-A_OBJ = main.o debug.o lib.o time.o proc.o mm_virt.o mm_phys.o
-
-LDFLAGS += `$(CC) --print-libgcc-file-name`
-
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/debug.c
- * - Debugging output
- */
-#include <acess.h>
-
-// === PROTOTYPES ===
-void StartupPrint(const char *Str);
-void KernelPanic_SetMode(void);
-void KernelPanic_PutChar(char ch);
-
-// === CODE ===
-void StartupPrint(const char *Str)
-{
-}
-
-void KernelPanic_SetMode(void)
-{
-}
-
-void KernelPanic_PutChar(char ch)
-{
-}
-
+++ /dev/null
-/*
- * Acess2 M68000 port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/include/arch.h
- * - Architectre config
- */
-#ifndef _M68K_ARCH_H_
-#define _M68K_ARCH_H_
-
-#define INVLPTR ((void*)-1)
-#define BITS 32
-
-typedef unsigned long long Uint64;
-typedef unsigned long Uint32;
-typedef unsigned short Uint16;
-typedef unsigned char Uint8;
-
-typedef signed long long Sint64;
-typedef signed long Sint32;
-typedef signed short Sint16;
-typedef signed char Sint8;
-
-typedef unsigned int Uint;
-typedef unsigned int size_t;
-
-typedef char BOOL;
-
-typedef Uint32 tVAddr;
-typedef Uint32 tPAddr;
-
-struct sShortSpinlock
-{
- int v;
-};
-
-// Non preemptive and no SMP, no need for these
-#define SHORTLOCK(lock) do{}while(0)
-#define SHORTREL(lock) do{}while(0)
-#define IS_LOCKED(lock) (0)
-#define CPU_HAS_LOCK(lock) (0)
-
-#define Debug_PutCharDebug(ch) do{}while(0)
-#define Debug_PutStringDebug(ch) do{}while(0)
-
-#define HALT() do{}while(0)
-
-#define USER_MAX 0
-
-#define PAGE_SIZE 0x1000
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 M68000 port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/include/mm_virt.h
- * - Virtual memory addresses
- */
-#ifndef _M68K_MM_VIRT_H_
-#define _M68K_MM_VIRT_H_
-
-#define MM_KHEAP_BASE 0
-#define MM_KHEAP_MAX 0
-
-#define MM_USER_MIN 0
-#define USER_LIB_MAX 0
-#define MM_MODULE_MIN 0
-#define MM_MODULE_MAX 0
-
-#define MM_PPD_HANDLES 0
-#define MM_KERNEL_VFS 0
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 M68000 port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/include/proc.h
- * - Task management defs
- */
-#ifndef _M68K_PROC_H_
-#define _M68K_PROC_H_
-
-#define MAX_CPUS 1
-
-typedef int tMemoryState; // Unused
-
-typedef struct {
- Uint32 IP;
- Uint32 SP;
-} tTaskState;
-
-typedef struct {
- Uint32 Num;
- union {
- Uint32 Arg1;
- Uint32 Return;
- };
- union {
- Uint32 Arg2;
- Uint32 RetHi;
- };
- union {
- Uint32 Arg3;
- Uint32 Error;
- };
- Uint32 Arg4;
- Uint32 Arg5;
- Uint32 Arg6;
-} tSyscallRegs;
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/lib.c
- * - Library functions
- */
-#include <acess.h>
-#include "../helpers.h"
-#include <drv_pci_int.h>
-
-Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
-Uint64 __udivdi3(Uint64 Num, Uint64 Den);
-Uint64 __umoddi3(Uint64 Num, Uint64 Den);
-
-// === CODE ===
-void *memcpy(void *__dest, const void *__src, size_t __len)
-{
- register Uint8 *dest = __dest;
- register const Uint8 *src = __src;
-
- while(__len --)
- *dest++ = *src++;
-
- return __dest;
-}
-
-void *memset(void *__dest, int __val, size_t __count)
-{
- register Uint8 *dest = __dest;
-
- while(__count --)
- *dest = __val;
-
- return __dest;
-}
-
-int memcmp(const void *__p1, const void *__p2, size_t __maxlen)
-{
- const char *m1 = __p1, *m2 = __p2;
- while( __maxlen-- )
- {
- if(*m1 != *m2) return *m1 - *m2;
- }
- return 0;
-}
-
-DEF_DIVMOD(64)
-
-Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem)
-{
- Uint64 ret;
- if(Den == 0) return 0; // TODO: #div0
- if(Num < Den) {
- if(Rem) *Rem = Num;
- return 0;
- }
- if(Num == 0) {
- if(Rem) *Rem = 0;
- return 0;
- }
- if(Den == 1) {
- if(Rem) *Rem = 0;
- return Num;
- }
- if(Den == 2) {
- if(Rem) *Rem = Num & 1;
- return Num >> 1;
- }
- if(Den == 16) {
- if(Rem) *Rem = Num & 0xF;
- return Num >> 4;
- }
- if(Den == 32) {
- if(Rem) *Rem = Num & 0x1F;
- return Num >> 5;
- }
- if(Den == 0x1000) {
- if(Rem) *Rem = Num & 0xFFF;
- return Num >> 12;
- }
-
- if( !(Den >> 32) && !(Num >> 32) ) {
- if(Rem) *Rem = (Uint32)Num % (Uint32)Den; // Clear high bits
- return (Uint32)Num / (Uint32)Den;
- }
-
- ret = __divmod64(Num, Den, Rem);
- return ret;
-}
-
-// Unsigned Divide 64-bit Integer
-Uint64 __udivdi3(Uint64 Num, Uint64 Den)
-{
- return DivMod64U(Num, Den, NULL);
-}
-
-// Unsigned Modulus 64-bit Integer
-Uint64 __umoddi3(Uint64 Num, Uint64 Den)
-{
- Uint64 ret = 0;
- DivMod64U(Num, Den, &ret);
- return ret;
-}
-
-// ---- PCI (stubbed)
-Uint32 PCI_CfgReadDWord(Uint32 Addr)
-{
- return 0xFFFFFFFF;
-}
-void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data)
-{
-}
-
+++ /dev/null
-ENTRY (_start)
-
-_kernel_base = 0x0;
-
-SECTIONS
-{
- . = 0;
- .init :
- {
- *(.init)
- }
- . += _kernel_base;
- .text : AT( ADDR(.text) - _kernel_base )
- {
- *(.text*)
- *(.rodata*)
- }
- /* 0x4000 (4 pages) alignment needed for root table */
- .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base )
- {
- *(.padata)
- *(.data*)
-
- gKernelSymbols = .;
- *(KEXPORT)
- gKernelSymbolsEnd = .;
-
- gKernelModules = .;
- *(KMODULES)
- gKernelModulesEnd = .;
- }
- .bss : AT( ADDR(.bss) - _kernel_base )
- {
- *(.bss*)
- *(COMMON*)
- . = ALIGN(0x1000);
- *(.pabss)
- }
- gKernelEnd = .;
-}
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/main.c
- * - C entrypoint
- */
-#include <acess.h>
-#include <init.h>
-
-// === PROTOTYPES ===
-void kmain(void);
-
-// === CODE ===
-void kmain(void)
-{
- LogF("Acess2 m68k v"EXPAND_STR(KERNEL_VERSION)"\n");
- LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash);
-
-}
-
-void Arch_LoadBootModules(void)
-{
-
-}
-
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/mm_phys.c
- * - Stubbed physical memory management
- */
-#include <acess.h>
-
-// === CODE ===
-void MM_RefPhys(tPAddr Page)
-{
- // TODO: Refcount pages
- Log_Warning("MMPhys", "TODO: Implement MM_RefPhys");
-}
-
-int MM_SetPageNode(tPAddr Page, void *Node)
-{
- Log_Warning("MMPhys", "TODO: Implement MM_SetPageNode");
- return -1;
-}
-
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/mm_virt.c
- * - Stubbed virtual memory management (no MMU)
- */
-#include <acess.h>
-#include <mm_virt.h>
-#include <hal_proc.h>
-
-// === CODE ===
-tPAddr MM_GetPhysAddr(tVAddr Addr)
-{
- return Addr;
-}
-
-void MM_SetFlags(tVAddr Addr, Uint Val, Uint Mask)
-{
- return ;
-}
-
-Uint MM_GetFlags(tVAddr Addr)
-{
- return 0;
-}
-
-int MM_Map(tVAddr Dest, tPAddr Src)
-{
- Dest &= (PAGE_SIZE-1);
- Src &= (PAGE_SIZE-1);
- if(Dest != Src)
- memcpy((void*)Dest, (void*)Src, PAGE_SIZE);
- return 0;
-}
-
-tPAddr MM_Allocate(tVAddr Dest)
-{
- return Dest;
-}
-
-void MM_Deallocate(tVAddr Addr)
-{
-}
-
-void MM_ClearUser(void)
-{
-}
-
-void MM_DumpTables(tVAddr Start, tVAddr End)
-{
-
-}
-
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/proc.c
- * - Multithreading
- */
-#include <acess.h>
-#include <threads_int.h>
-#include <hal_proc.h>
-
-// === IMPORTS ===
-extern tThread gThreadZero;
-
-// === GLOBALS ===
-tThread *gpCurrentThread = &gThreadZero;
-
-// === CODE ===
-void ArchThreads_Init(void)
-{
-}
-
-void Proc_Start(void)
-{
-}
-
-tThread *Proc_GetCurThread(void)
-{
- return gpCurrentThread;
-}
-
-int GetCPUNum(void)
-{
- return 0;
-}
-
-tTID Proc_Clone(Uint Flags)
-{
- UNIMPLEMENTED();
- return -1;
-}
-
-tTID Proc_NewKThread(tThreadFunction Fcn, void *Arg)
-{
- UNIMPLEMENTED();
- return -1;
-}
-
-tTID Proc_SpawnWorker(tThreadFunction Fcn, void *Arg)
-{
- UNIMPLEMENTED();
- return -1;
-}
-
-void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize)
-{
- Log_KernelPanic("Proc", "TODO: Implement Proc_StartUser");
- for(;;);
-}
-
-void Proc_CallFaultHandler(tThread *Thread)
-{
-
-}
-
-void Proc_DumpThreadCPUState(tThread *Thread)
-{
-
-}
-
-void Proc_Reschedule(void)
-{
- Log_Notice("Proc", "TODO: Implement Proc_Reschedule");
-}
-
+++ /dev/null
-/*
- * Acess2 M68K port
- * - By John Hodge (thePowersGang)
- *
- * arch/m68k/time.c
- * - Timekeeping
- */
-#include <acess.h>
-
-// === CODE ===
-Sint64 now(void)
-{
- return 0;
-}
+++ /dev/null
-#
-# Acess2 Kernel
-# i386 Architecture Makefile
-# arch/i386/Makefile
-
-AS_SUFFIX = asm
-
-CPPFLAGS =
-CFLAGS =
-ASFLAGS = -f elf
-
-USE_MP=0
-
-ifeq ($(PLATFORM),default)
- USE_MP=0
-else ifeq ($(PLATFORM),smp)
- USE_MP=1
-endif
-
-ASFLAGS += -D USE_MP=$(USE_MP)
-CPPFLAGS += -DUSE_MP=$(USE_MP)
-
-A_OBJ = start.ao main.o lib.o desctab.ao errors.o irq.o
-A_OBJ += mm_phys.o mm_virt.o
-A_OBJ += proc.o proc.ao time.o vm8086.o
-A_OBJ += kpanic.o pci.o
+++ /dev/null
-; AcessOS Microkernel Version
-;
-; desctab.asm
-[BITS 32]
-
-
-[section .data]
-; IDT
-ALIGN 8
-[global gIDT]
-gIDT:
- ; CS = 0x08, Type = 32-bit Interrupt (0xE = 1 110)
- times 256 dd 0x00080000,0x00000E00
-[global gIDTPtr]
-gIDTPtr:
- dw 256 * 16 - 1 ; Limit
- dd gIDT ; Base
-
-[section .text]
-
-[global Desctab_Install]
-Desctab_Install:
- ; Set up IDT
- ; Helper Macros
- ; - Set an IDT entry to an ISR
-%macro SETISR 1
- mov eax, Isr%1
- mov WORD [gIDT + %1*8], ax
- shr eax, 16
- mov WORD [gIDT + %1*8+6], ax
- ; Enable
- mov ax, WORD [gIDT + %1*8 + 4]
- or ax, 0x8000
- mov WORD [gIDT + %1*8 + 4], ax
-%endmacro
- ; Enable user calling of an ISR
-%macro SET_USER 1
- or WORD [gIDT + %1*8 + 4], 0x6000
-%endmacro
- ; Set an ISR as a trap (leaves interrupts enabled when invoked)
-%macro SET_TRAP 1
- or WORD [gIDT + %1*8 + 4], 0x0100
-%endmacro
-
- ; Error handlers
- %assign i 0
- %rep 32
- SETISR i
- %assign i i+1
- %endrep
-
- ; User Syscall
- SETISR 0xAC
- SET_USER 0xAC
- SET_TRAP 0xAC ; Interruptable
-
- ; MP ISRs
- %if USE_MP
- SETISR 0xED ; 0xED Inter-processor HALT
- SETISR 0xEE ; 0xEE Timer
- SETISR 0xEF ; 0xEF Spurious Interrupt
- %endif
-
- ; IRQs
- %assign i 0xF0
- %rep 16
- SETISR i
- %assign i i+1
- %endrep
-
- ; Load IDT
- lidt [gIDTPtr]
-
- ; Remap PIC
- push edx ; Save EDX
- mov dx, 0x20
- mov al, 0x11
- out dx, al ; Init Command
- mov dx, 0x21
- mov al, 0xF0
- out dx, al ; Offset (Start of IDT Range)
- mov al, 0x04
- out dx, al ; IRQ connected to Slave (00000100b) = IRQ2
- mov al, 0x01
- out dx, al ; Set Mode
- mov al, 0x00
- out dx, al ; Set Mode
-
- mov dx, 0xA0
- mov al, 0x11
- out dx, al ; Init Command
- mov dx, 0xA1
- mov al, 0xF8
- out dx, al ; Offset (Start of IDT Range)
- mov al, 0x02
- out dx, al ; IRQ Line connected to master
- mov al, 0x01
- out dx, al ; Set Mode
- mov dl, 0x00
- out dx, al ; Set Mode
- pop edx
-
- ret
-
-
-; ===============
-; = Define ISRs =
-; ===============
-%macro ISR_ERRNO 1
-[global Isr%1]
-Isr%1:
- ;xchg bx, bx
- push %1
- jmp ErrorCommon
-%endmacro
-%macro ISR_NOERR 1
-[global Isr%1]
-Isr%1:
- ;xchg bx, bx
- push 0
- push %1
- jmp ErrorCommon
-%endmacro
-
-%macro DEF_SYSCALL 1
-[global Isr%1]
-Isr%1:
- push 0
- push %1
- jmp SyscallCommon
-%endmacro
-
-%macro DEF_IRQ 1
-[global Isr%1]
-Isr%1:
- push 0
- push %1
- jmp IRQCommon
-%endmacro
-
-ISR_NOERR 0; 0: Divide By Zero Exception
-ISR_NOERR 1; 1: Debug Exception
-ISR_NOERR 2; 2: Non Maskable Interrupt Exception
-ISR_NOERR 3; 3: Int 3 Exception
-ISR_NOERR 4; 4: INTO Exception
-ISR_NOERR 5; 5: Out of Bounds Exception
-ISR_NOERR 6; 6: Invalid Opcode Exception
-ISR_NOERR 7; 7: Coprocessor Not Available Exception
-ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!)
-ISR_NOERR 9; 9: Coprocessor Segment Overrun Exception
-ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!)
-ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!)
-ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!)
-ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!)
-ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!)
-ISR_NOERR 15; 15: Reserved Exception
-ISR_NOERR 16; 16: Floating Point Exception
-ISR_NOERR 17; 17: Alignment Check Exception
-ISR_NOERR 18; 18: Machine Check Exception
-ISR_NOERR 19; 19: Reserved
-ISR_NOERR 20; 20: Reserved
-ISR_NOERR 21; 21: Reserved
-ISR_NOERR 22; 22: Reserved
-ISR_NOERR 23; 23: Reserved
-ISR_NOERR 24; 24: Reserved
-ISR_NOERR 25; 25: Reserved
-ISR_NOERR 26; 26: Reserved
-ISR_NOERR 27; 27: Reserved
-ISR_NOERR 28; 28: Reserved
-ISR_NOERR 29; 29: Reserved
-ISR_NOERR 30; 30: Reserved
-ISR_NOERR 31; 31: Reserved
-
-DEF_SYSCALL 0xAC ; Acess System Call
-
-%if USE_MP
-[global Isr0xED]
-; 0xED - Interprocessor HALT
-Isr0xED:
- cli
-.jmp: hlt
- jmp .jmp
-
-[global Isr0xEE]
-[extern SchedulerBase]
-; AP's Timer Interrupt
-Isr0xEE:
- push eax ; Line up with interrupt number
- mov eax, dr1 ; CPU Number
- push eax
- mov eax, [esp+4] ; Load EAX back
- jmp SchedulerBase
-; Spurious Interrupt
-[global Isr0xEF]
-Isr0xEF:
- xchg bx, bx ; MAGIC BREAK
- iret
-%endif
-
-; IRQs
-; - Timer
-[global Isr240]
-[global Isr240.jmp]
-[extern SchedulerBase]
-[extern SetAPICTimerCount]
-Isr240:
- push 0 ; Line up with Argument in errors
- push 0 ; CPU Number
- ;xchg bx, bx ; MAGIC BREAK
-Isr240.jmp:
- %if USE_MP
- jmp SetAPICTimerCount ; This is reset once the bus speed has been calculated
- %else
- jmp SchedulerBase
- %endif
-; - Assignable
-%assign i 0xF1
-%rep 16
- DEF_IRQ i
-%assign i i+1
-%endrep
-
-; ---------------------
-; Common error handling
-; ---------------------
-[extern ErrorHandler]
-ErrorCommon:
- ;xchg bx, bx ; MAGIC BREAK
-
- pusha
- push ds
- push es
- push fs
- push gs
-
- ; Clear TF
-; pushf
-; and WORD [esp], 0xFEFF
-; popf
-
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- push esp
- call ErrorHandler
- add esp, 4
-
- pop gs
- pop fs
- pop es
- pop ds
- popa
- add esp, 8 ; Error Code and ID
- iret
-
-; --------------------------
-; Common System Call Handler
-; --------------------------
-[extern SyscallHandler]
-SyscallCommon:
- pusha
- push ds
- push es
- push fs
- push gs
-
- push esp
- call SyscallHandler
- add esp, 4
-
- ; Pass changes to TF on to the user
- ; EFLAGS is stored at ESP[4+8+2+2]
- ; 4 Segment Registers
- ; 8 GPRs
- ; 2 Error Code / Interrupt ID
- ; 2 CS/EIP
- pushf
- pop eax
- and eax, 0x100 ; 0x100 = Trace Flag
- and WORD [esp+(4+8+2+2)*4], ~0x100 ; Clear
- or DWORD [esp+(4+8+2+2)*4], eax ; Set for user
-
- pop gs
- pop fs
- pop es
- pop ds
- popa
- add esp, 8 ; Error Code and ID
- iret
-
-; ------------
-; IRQ Handling
-; ------------
-[extern IRQ_Handler]
-[global IRQCommon]
-[global IRQCommon_handled]
-IRQCommon_handled equ IRQCommon.handled
-IRQCommon:
- pusha
- push ds
- push es
- push fs
- push gs
-
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- push esp
- call IRQ_Handler
-.handled:
- add esp, 4
-
- pop gs
- pop fs
- pop es
- pop ds
- popa
- add esp, 8 ; Error Code and ID
- iret
-
-; vim: ft=nasm ts=8
+++ /dev/null
-/*
- * Acess2 - x86 Architecture
- * arch/x86/errors.c
- * - CPU Error Handler
- */
-#include <acess.h>
-#include <proc.h>
-#include <mm_virt.h>
-
-// === CONSTANTS ===
-#define MAX_BACKTRACE 8 //!< Maximum distance to trace the stack backwards
-
-// === IMPORTS ===
-extern void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs);
-extern void VM8086_GPF(tRegs *Regs);
-extern void Threads_Dump(void);
-extern void Threads_Fault(int Num);
-extern int GetCPUNum(void);
-extern void MM_DumpTables(tVAddr, tVAddr);
-extern void Proc_EnableSSE(void);
-extern void Proc_RestoreSSE(Uint32 Data);
-
-// === PROTOTYPES ===
-void __stack_chk_fail(void);
-void ErrorHandler(tRegs *Regs);
-void Proc_PrintBacktrace(void);
-void Error_Backtrace(Uint eip, Uint ebp);
-void StartupPrint(char *Str);
-
-// === GLOBALS ===
-const char *csaERROR_NAMES[] = {
- "Divide By Zero", "Debug", "NMI Exception", "INT3",
- "INTO Instr - Overflow", "BOUND Instr - Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable",
- "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present",
- "Stack Fault Exception", "GPF", "#PF", "Reserved",
- "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved",
- "Reserved", "Reserved", "Reserved", "Reserved",
- "Reserved", "Reserved", "Reserved", "Reserved",
- "Reserved", "Reserved", "Reserved", "Reserved"
- };
-
-// === CODE ===
-/**
- * \brief Keeps GCC happy
- */
-void __stack_chk_fail(void)
-{
- Panic("FATAL ERROR: Stack Check Failed\n");
- for(;;);
-}
-
-/**
- * \fn void ErrorHandler(tRegs *Regs)
- * \brief General Error Handler
- * \param Regs Register state at error
- */
-void ErrorHandler(tRegs *Regs)
-{
- Uint cr;
-
- //if( Regs && !(Regs->int_num == 13 && Regs->eflags & 0x20000) )
- // __asm__ __volatile__ ("xchg %bx, %bx");
- //Log_Debug("X86", "Regs = %p", Regs);
- //Log_Debug("X86", "Error %i at 0x%08x", Regs->int_num, Regs->eip);
-
- __asm__ __volatile__ ("cli");
-
- // Debug exception (used for single-stepping)
- if(Regs->int_num == 1)
- {
- static Uint32 lastEIP = 0;
- tThread *thread = Proc_GetCurThread();
- if( Regs->eip == lastEIP )
- return;
- Log("%p(%i %s) IP=%08x", thread, thread->TID, thread->ThreadName, Regs->eip);
- lastEIP = Regs->eip;
- return ;
- }
-
- // Page Fault
- if(Regs->int_num == 14)
- {
- __asm__ __volatile__ ("sti"); // Should be OK, TODO: Test
- __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
- MM_PageFault( cr, Regs->err_code, Regs );
- return ;
- }
-
- // #NM - Coprocessor unavaliable
- if(Regs->int_num == 7)
- {
- tThread *thread = Proc_GetCurThread();
- if(!thread->SavedState.bSSEModified)
- {
- Proc_EnableSSE();
- if(!thread->SavedState.SSE)
- thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF);
- else
- Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF );
- thread->SavedState.bSSEModified = 1;
- __asm__ __volatile__ ("sti");
- return ;
- }
- // oops, SSE enabled but a #NM, bad news
- }
-
- // VM8086 GPF
- if(Regs->int_num == 13 && Regs->eflags & 0x20000)
- {
- VM8086_GPF(Regs);
- return ;
- }
-
- // Check if it's a user mode fault
- if( (Regs->cs & 3) == 3 ) {
- Log_Warning("Arch", "User Fault - %s, Code: 0x%x",
- csaERROR_NAMES[Regs->int_num], Regs->err_code);
- Log_Warning("Arch", "at CS:EIP %04x:%08x",
- Regs->cs, Regs->eip);
- MM_DumpTables(0, KERNEL_BASE);
- switch( Regs->int_num )
- {
- // Division by Zero
- case 0: Threads_Fault(FAULT_DIV0); break;
- // Invalid opcode
- case 6: Threads_Fault(FAULT_OPCODE); break;
- // GPF
- case 13: Threads_Fault(FAULT_ACCESS); break;
- // Floating Point Exception
- case 16: Threads_Fault(FAULT_FLOAT); break;
-
- default: Threads_Fault(FAULT_MISC); break;
- }
- return ;
- }
-
- Debug_KernelPanic();
-
- LogF("CPU %i Error %i - %s, Code: 0x%x - At %08x\n",
- GetCPUNum(),
- Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code,
- Regs->eip);
-
- //Warning("CPU Error %i - %s, Code: 0x%x",
- // Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code);
- //Warning(" CS:EIP = 0x%04x:%08x", Regs->cs, Regs->eip);
- __ASM__ ("xchg %bx, %bx");
- if(Regs->cs == 0x08)
- Warning(" SS:ESP = 0x0010:%08x", (Uint)Regs+sizeof(tRegs));
- else
- Warning(" SS:ESP = 0x%04x:%08x", Regs->ss, Regs->esp);
- Warning(" EFLAGS = 0x%08x", Regs->eflags);
- Warning(" EAX %08x ECX %08x EDX %08x EBX %08x",
- Regs->eax, Regs->ecx, Regs->edx, Regs->ebx);
- Warning(" ESP %08x EBP %08x ESI %08x EDI %08x",
- Regs->esp, Regs->ebp, Regs->esi, Regs->edi);
- Warning(" DS %04x ES %04x FS %04x GS %04x",
- Regs->ds, Regs->es, Regs->fs, Regs->gs);
-
- // Control Registers
- __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr));
- Warning(" CR0 0x%08x", cr);
- __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
- Warning(" CR2 0x%08x", cr);
- __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr));
- Warning(" CR3 0x%08x", cr);
-
- switch( Regs->int_num )
- {
- case 6: // #UD
- Warning(" Offending bytes: %02x %02x %02x %02x",
- *(Uint8*)(Regs->eip+0), *(Uint8*)(Regs->eip+1),
- *(Uint8*)(Regs->eip+2), *(Uint8*)(Regs->eip+3));
- break;
- }
-
- // Print Stack Backtrace
- Error_Backtrace(Regs->eip, Regs->ebp);
-
- // Dump running threads
- Threads_Dump();
-
- for(;;) __asm__ __volatile__ ("hlt");
-}
-
-void Proc_PrintBacktrace(void)
-{
- Uint32 ebp;
- __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp));
- Error_Backtrace( *(Uint32*)(ebp+4), *(Uint32*)ebp );
-}
-
-/**
- * \fn void Error_Backtrace(Uint eip, Uint ebp)
- * \brief Unrolls the stack to trace execution
- * \param eip Current Instruction Pointer
- * \param ebp Current Base Pointer (Stack Frame)
- */
-void Error_Backtrace(Uint eip, Uint ebp)
-{
- int i = 0;
-// Uint delta = 0;
-// char *str = NULL;
-
- //if(eip < 0xC0000000 && eip > 0x1000)
- //{
- // LogF("Backtrace: User - 0x%x\n", eip);
- // return;
- //}
-
- #if 0
- if(eip > 0xE0000000)
- {
- LogF("Backtrace: Data Area - 0x%x\n", eip);
- return;
- }
-
- if(eip > 0xC8000000)
- {
- LogF("Backtrace: Kernel Module - 0x%x\n", eip);
- return;
- }
- #endif
-
- //str = Debug_GetSymbol(eip, &delta);
-// if(str == NULL)
- LogF("Backtrace: 0x%x", eip);
-// else
-// LogF("Backtrace: %s+0x%x", str, delta);
- if(!MM_GetPhysAddr(ebp))
- {
- LogF("\nBacktrace: Invalid EBP, stopping\n");
- return;
- }
-
-
- while( MM_GetPhysAddr(ebp) && i < MAX_BACKTRACE )
- {
- if( ebp >= MM_KERNEL_STACKS_END ) break;
- //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta);
-// if(str == NULL)
- LogF(" >> 0x%x", *(Uint*)(ebp+4));
-// else
-// LogF(" >> %s+0x%x", str, delta);
- ebp = *(Uint*)ebp;
- i++;
- }
- LogF("\n");
-}
-
-/**
- * \fn void StartupPrint(char *Str)
- * \brief Str String to print
- * \note WHY IS THIS HERE?!?!
- */
-void StartupPrint(char *Str)
-{
- Uint16 *buf = (void*)0xC00B8000;
- int i = 0;
- static int line = 0;
- while(*Str)
- {
- buf[line*80 + i++] = *Str | 0x0700;
- Str ++;
- }
-
- // Clear the rest of the line
- while(i < 80)
- buf[line*80 + i++] = 0x0720;
-
- line ++;
- if(line == 25)
- {
- line --;
- memcpy(buf, &buf[80], 80*24*2);
- memset(&buf[80*24], 0, 80*2);
- }
-}
-
-// === EXPORTS ===
-EXPORT(__stack_chk_fail);
+++ /dev/null
-/*
- * Acess2
- * - x86 Architecture
- * arch/x86/include/arch.h
- */
-#ifndef _ARCH_H_
-#define _ARCH_H_
-
-// - Base Defintions
-#define KERNEL_BASE 0xC0000000
-#define BITS 32
-#define PAGE_SIZE 0x1000
-
-#define INVLPTR ((void*)-1)
-
-// Allow nested spinlocks?
-#define LOCK_DISABLE_INTS 1
-
-// - Processor/Machine Specific Features
-#if ARCH != x86 && ARCH != x86_smp
-# error "Unknown architecture '" #ARCH "'"
-#endif
-
-#if USE_MP
-# define MAX_CPUS 8
-#else
-# define MAX_CPUS 1
-#endif
-
-#if USE_PAE
-# define PHYS_BITS 48
-#else
-# define PHYS_BITS 32
-#endif
-
-#define __ASM__ __asm__ __volatile__
-
-// === Spinlocks ===
-/**
- * \brief Short Spinlock structure
- */
-struct sShortSpinlock {
- volatile int Lock; //!< Lock value
-
- #if LOCK_DISABLE_INTS
- int IF; //!< Interrupt state on call to SHORTLOCK
- #endif
-};
-
-// === MACROS ===
-/**
- * \brief Halt the CPU (shorter version of yield)
- */
-#if 1
-#define HALT() do { \
- Uint32 flags; \
- __asm__ __volatile__ ("pushf;pop %0" : "=a"(flags)); \
- if( !(flags & 0x200) ) Panic("HALT called with interrupts disabled"); \
- __asm__ __volatile__ ("hlt"); \
-} while(0)
-#else
-#define HALT() __asm__ __volatile__ ("hlt")
-#endif
-/**
- * \brief Fire a magic breakpoint (bochs)
- */
-#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx")
-
-// === TYPES ===
-typedef unsigned int Uint; // Unsigned machine native integer
-typedef unsigned char Uint8;
-typedef unsigned short Uint16;
-typedef unsigned long Uint32;
-typedef unsigned long long Uint64;
-typedef signed int Sint; // Signed Machine Native integer
-typedef signed char Sint8;
-typedef signed short Sint16;
-typedef signed long Sint32;
-typedef signed long long Sint64;
-typedef Uint size_t;
-typedef char BOOL;
-
-typedef Uint32 tPAddr;
-typedef Uint32 tVAddr;
-
-typedef struct {
- Uint32 gs, fs, es, ds;
- Uint32 edi, esi, ebp, kesp;
- Uint32 ebx, edx, ecx, eax;
- Uint32 int_num, err_code;
- Uint32 eip, cs;
- Uint32 eflags, esp, ss;
-} tRegs;
-
-typedef struct {
- Uint Resvd1[4]; // GS, FS, ES, DS
- Uint Arg4, Arg5; // EDI, ESI
- Uint Arg6; // EBP
- Uint Resvd2[1]; // Kernel ESP
- union {
- Uint Arg1;
- Uint Error;
- }; // EBX
- union {
- Uint Arg3;
- Uint RetHi; // High 32 bits of ret
- }; // EDX
- Uint Arg2; // ECX
- union {
- Uint Num;
- Uint Return;
- }; // EAX
- Uint Resvd3[5]; // Int, Err, Eip, CS, ...
- Uint StackPointer; // ESP
- Uint Resvd4[1]; // SS
-} tSyscallRegs;
-
-// === FUNCTIONS ===
-extern void Debug_PutCharDebug(char ch);
-extern void Debug_PutStringDebug(const char *String);
-
-extern int IS_LOCKED(struct sShortSpinlock *Lock);
-extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock);
-extern void SHORTLOCK(struct sShortSpinlock *Lock);
-extern void SHORTREL(struct sShortSpinlock *Lock);
-
-#endif // !defined(_ARCH_H_)
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * x86 Arch - Internal Definitions
- * - arch/x86/include/arch_int.h
- */
-#ifndef _ARCH_INT_H_
-#define _ARCH_INT_H_
-
-/**
- * \brief Spinlock primative atomic set-if-zero loop
- */
-extern void __AtomicTestSetLoop(Uint *Ptr, Uint Value);
-
-/**
- * \brief Clear and free an address space
- */
-extern void MM_ClearSpace(Uint32 CR3);
-
-#endif
-
+++ /dev/null
-/**
- */
-#ifndef _DESCTAB_H_
-#define _DESCTAB_H_
-
-typedef struct {
- Uint16 LimitLow;
- Uint16 BaseLow;
- Uint8 BaseMid;
- Uint8 Access;
- struct {
- unsigned LimitHi: 4;
- unsigned Flags: 4;
- } __attribute__ ((packed));
- Uint8 BaseHi;
-} __attribute__ ((packed)) tGDT;
-
-typedef struct {
- Uint16 OffsetLo;
- Uint16 CS;
- Uint16 Flags;
- Uint16 OffsetHi;
-} __attribute__ ((packed)) tIDT;
-
-#endif
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * mm_phys.h
- */
-#ifndef _MM_PHYS_H
-#define _MM_PHYS_H
-
-// === FUNCTIONS ===
-//extern tPAddr MM_AllocPhys(void);
-//extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
-//extern void MM_RefPhys(tPAddr PAddr);
-//extern void MM_DerefPhys(tPAddr PAddr);
-//extern int MM_GetRefCount(tPAddr Addr);
-
-#endif
+++ /dev/null
-/*
- * Acess2
- * - Virtual Memory Manager (Header)
- */
-#ifndef _MM_VIRT_H
-#define _MM_VIRT_H
-
-// NOTES:
-// - 1PD is 0x400000
-
-// - Memory Layout
-#define MM_USER_MIN 0x00200000
-#define USER_STACK_SZ 0x00020000 // 128 KiB
-#define USER_STACK_TOP 0x00800000
-#define USER_LIB_MAX 0xBC000000
-#define MM_USER_MAX 0xBC000000 // Top load address for user libraries
-#define MM_PPD_MIN 0xBC000000 // Per-Process Data base
-#define MM_PPD_HANDLES 0xBC000000 // - VFS Handles (Practically unlimited)
-#define MM_PPD_MMAP 0xBD000000 // - MMap Entries (24b each = 0x2AAAA max)
-#define MM_PPD_UNALLOC 0xBE000000 //
-#define MM_PPD_CFG 0xBFFFF000 // - Per-process config entries
-#define MM_PPD_MAX 0xC0000000 //
-
-#define MM_KHEAP_BASE 0xC0400000 // C+4MiB
-#define MM_KHEAP_MAX 0xCF000000 //
-#define MM_KERNEL_VFS 0xCF000000 //
-#define MM_KUSER_CODE 0xCFFF0000 // 16 Pages
-#define MM_MODULE_MIN 0xD0000000 // Lowest Module Address
-#define MM_MODULE_MAX 0xE0000000 // 128 MiB
-
-// Page Info (Which VFS node owns each physical page)
-// 2^32/2^12*16
-// = 2^24 = 16 MiB = 0x4000000
-// 256 items per page
-#define MM_PAGENODE_BASE 0xE0000000
-
-// Needs (2^32/2^12*4 bytes)
-// - 2^22 bytes max = 4 MiB = 0x1000000
-// 1024 items per page
-#define MM_REFCOUNT_BASE 0xE4000000
-
-#define MM_KERNEL_STACKS 0xF0000000
-#define MM_KERNEL_STACK_SIZE 0x00008000
-#define MM_KERNEL_STACKS_END 0xFC000000
-
-// === FUNCTIONS ===
-extern void MM_FinishVirtualInit(void);
-extern void MM_SetCR3(Uint CR3);
-extern tPAddr MM_Clone(int bCloneUser);
-extern tVAddr MM_NewKStack(void);
-extern tVAddr MM_NewWorkerStack(Uint *InitialStack, size_t StackSize);
-
-#endif
+++ /dev/null
-/*
- */
-#ifndef _MP_H
-#define _MP_H
-
-#define MPPTR_IDENT ('_'|('M'<<8)|('P'<<16)|('_'<<24))
-#define MPTABLE_IDENT ('P'|('C'<<8)|('M'<<16)|('P'<<24))
-
-typedef struct sMPInfo {
- Uint32 Sig; // '_MP_'
- Uint32 MPConfig;
- Uint8 Length;
- Uint8 Version;
- Uint8 Checksum;
- Uint8 Features[5]; // 2-4 are unused
-} tMPInfo;
-
-typedef union uMPTable_Ent {
- Uint8 Type;
- struct {
- Uint8 Type;
- Uint8 APICID;
- Uint8 APICVer;
- Uint8 CPUFlags; // bit 0: Enabled, bit 1: Boot Processor
- Uint32 CPUSignature; // Stepping, Model, Family
- Uint32 FeatureFlags;
- Uint32 Reserved[2];
- } __attribute__((packed)) Proc; // 0x00
- struct {
- Uint8 Type;
- Uint8 ID;
- char TypeString[6];
- } __attribute__((packed)) Bus; // 0x01
- struct {
- Uint8 Type;
- Uint8 ID;
- Uint8 Version;
- Uint8 Flags; // bit 0: Enabled
- Uint32 Addr;
- } __attribute__((packed)) IOAPIC; // 0x02
- struct {
- Uint8 Type;
- Uint8 IntType;
- Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode
- Uint8 SourceBusID;
- Uint8 SourceBusIRQ;
- Uint8 DestAPICID;
- Uint8 DestAPICIRQ;
- } __attribute__((packed)) IOInt;
- struct {
- Uint8 Type;
- Uint8 IntType;
- Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode
- Uint8 SourceBusID;
- Uint8 SourceBusIRQ;
- Uint8 DestLocalAPICID;
- Uint8 DestLocalAPICIRQ;
- } __attribute__((packed)) LocalInt;
-} __attribute__((packed)) tMPTable_Ent;
-
-typedef struct sMPTable {
- Uint32 Sig;
- Uint16 BaseTableLength;
- Uint8 SpecRev;
- Uint8 Checksum;
-
- char OemID[8];
- char ProductID[12];
-
- Uint32 OEMTablePtr;
- Uint16 OEMTableSize;
- Uint16 EntryCount;
-
- Uint32 LocalAPICMemMap; //!< Address used to access the local APIC
- Uint16 ExtendedTableLen;
- Uint8 ExtendedTableChecksum;
- Uint8 Reserved;
-
- tMPTable_Ent Entries[];
-} tMPTable;
-
-typedef volatile struct {
- Uint32 Addr;
- Uint32 Resvd1[3];
- union {
- Uint8 Byte;
- Uint16 Word;
- Uint32 DWord;
- } Value;
- Uint32 Resvd2[3];
-} tIOAPIC;
-
-typedef struct {
- Uint32 Val;
- Uint32 Padding[3];
-} volatile tReg;
-
-typedef volatile struct {
- tReg _unused1[2];
- tReg ID;
- tReg Version;
- tReg _unused2[4];
- tReg TPR; // Task Priority Register
- tReg APR; // Arbitration Priority Register (RO)
- tReg PPR; // Processor Priority Register (RO)
- tReg EOI; // EOI Register (Write Only)
- tReg _unused3[1];
- tReg LogDest; // Logical Destination Register
- tReg DestFmt; // Destination Format Register (0-27: RO, 28-31: RW)
- tReg SIV; // Spurious Interrupt Vector Register (0-8: RW, 9-31: RO)
- tReg ISR[8]; // In-Service Register - Total 256 Bits (RO)
- tReg TMR[8]; // Trigger Mode Register - Total 256 Bits (RO)
- tReg IRR[8]; // Interrupt Request Register - Total 256 Bits (RO)
- tReg ErrorStatus; // Error Status Register (RO)
- tReg _unused4[6];
- tReg LVTCMI; // LVT CMI Registers
- // 0x300
- tReg ICR[2]; // Interrupt Command Register (RW)
- // LVT Registers (Controls Local Vector Table)
- // Structure:
- // 0-7: Vector - IDT Vector for the interrupt
- // 12: Delivery Status (0: Idle, 1: Send Pending)
- // 16: Mask (0: Enabled, 1: Disabled)
- // 0x320
- tReg LVTTimer; // LVT Timer Register (RW)
- tReg LVTThermalSensor; // LVT Thermal Sensor Register (RW)
- tReg LVTPerfMonCounters; // LVT Performance Monitor Counters Register (RW)
- tReg LVTLInt0; // LVT Local Interrupt (LINT) #0 Register (RW);
- tReg LVTLInt1; // LVT Local Interrupt (LINT) #1 Register (RW);
- tReg LVTError; // LVT Error Register (RW);
- // 0x380
- tReg InitialCount; // Initial Count Register (Used for the timer) (RW)
- tReg CurrentCount; // Current Count Register (Used for the timer) (RW)
- tReg _unused5[4];
- // 0x3E0
- tReg DivideConifg; // Divide Configuration Register (RW)
- tReg _unused6[1];
-} volatile tAPIC;
-
-#endif
+++ /dev/null
-/*
- * Acess 2
- * By John Hodge (thePowersGang)
- *
- * multiboot2.h
- * - Multiboot 2 Header
- */
-#ifndef _MULTIBOOT2_H_
-#define _MULTIBOOT2_H_
-
-#define MULTIBOOT2_MAGIC 0x36D76289
-
-typedef struct sMultiboot2Info
-{
- Uint32 TotalSize;
- Uint32 Reserved; // SBZ
-} tMultiboot2Info;
-
-#endif
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * proc.h
- */
-#ifndef _PROC_H
-#define _PROC_H
-
-// === TYPES ==
-typedef struct sTSS {
- Uint32 Link;
- Uint32 ESP0, SS0;
- Uint32 ESP1, SS1;
- Uint32 ESP2, SS2;
- Uint32 CR3;
- Uint32 EIP;
- Uint32 EFLAGS;
- Uint32 EAX, ECX, EDX, EBX;
- Uint32 ESP, EBP, ESI, EDI;
- Uint32 ES, CS, DS, SS, FS, GS;
- Uint32 LDTR;
- Uint16 Resvd, IOPB; // IO Permissions Bitmap
-} __attribute__((packed)) tTSS;
-
-typedef struct {
- #if USE_PAE
- Uint PDPT[4];
- #else
- Uint32 CR3;
- #endif
-} tMemoryState;
-
-// 512 bytes, 16 byte aligned
-typedef struct sSSEState
-{
- char data[512];
-} tSSEState;
-
-typedef struct {
- Uint EIP, ESP;
- Uint32 UserCS, UserEIP;
- tSSEState *SSE;
- int bSSEModified;
-} tTaskState;
-
-#include <threads_int.h>
-
-#define USER_MAX KERNEL_BASE
-
-#endif
+++ /dev/null
-/*
- * Acess2 VM8086 BIOS Interface
- * - By John Hodge (thePowersGang)
- *
- * vm8086.h
- * - Core Header
- */
-#ifndef _VM80806_H_
-#define _VM80806_H_
-
-// === TYPES ===
-/**
- * \note Semi-opaque - Past \a .IP, the implementation may add any data
- * it needs to the state.
- */
-typedef struct sVM8086
-{
- Uint16 AX, CX, DX, BX;
- Uint16 BP, SP, SI, DI;
-
- Uint16 SS, DS, ES;
-
- Uint16 CS, IP;
-
- struct sVM8086_InternalData *Internal;
-} tVM8086;
-
-// === FUNCTIONS ===
-/**
- * \brief Create an instance of the VM8086 Emulator
- * \note Do not free this pointer with ::free, instead use ::VM8086_Free
- * \return Pointer to a tVM8086 structure, this structure may be larger than
- * tVM8086 due to internal data.
- */
-extern tVM8086 *VM8086_Init(void);
-/**
- * \brief Free an allocated tVM8086 structure
- * \param State Emulator state to free
- */
-extern void VM8086_Free(tVM8086 *State);
-/**
- * \brief Allocate a piece of memory in the emulated address space and
- * return a host and emulated pointer to it.
- * \param State Emulator state
- * \param Size Size of memory block
- * \param Segment Pointer to location to store the allocated memory's segment
- * \param Offset Pointet to location to store the allocated memory's offset
- * \return Host pointer to the allocated memory
- */
-extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset);
-/**
- * \brief Gets a pointer to a piece of emulated memory
- * \todo Only 1 machine page is garenteed to be contiguous
- * \param State Emulator State
- * \param Segment Source Segment
- * \param Offset Source Offset
- * \return Host pointer to the emulated memory
- */
-extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset);
-/**
- * \brief Calls a real-mode interrupt described by the current state of the IVT.
- * \param State Emulator State
- * \param Interrupt BIOS Interrupt to call
- */
-extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt);
-
-#endif
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * irq.c
- */
-#include <acess.h>
-
-// === CONSTANTS ===
-#define MAX_CALLBACKS_PER_IRQ 4
-#define TRACE_IRQS 0
-
-// === TYPES ===
-typedef void (*tIRQ_Callback)(int, void *);
-
-// === PROTOTYPES ===
-void IRQ_Handler(tRegs *Regs);
-
-// === GLOBALS ===
-tIRQ_Callback gIRQ_Handlers[16][MAX_CALLBACKS_PER_IRQ];
-void *gaIRQ_DataPointers[16][MAX_CALLBACKS_PER_IRQ];
-
-// === CODE ===
-/**
- * \fn void IRQ_Handler(tRegs *Regs)
- * \brief Handle an IRQ
- */
-void IRQ_Handler(tRegs *Regs)
-{
- int i, irq = Regs->int_num - 0xF0;
-
- //Log("IRQ_Handler: (Regs={int_num:%i})", Regs->int_num);
-
- for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ )
- {
- if( gIRQ_Handlers[irq][i] ) {
- gIRQ_Handlers[irq][i](irq, gaIRQ_DataPointers[irq][i]);
- #if TRACE_IRQS
- if( irq != 8 )
- Log("IRQ %i: Call %p", Regs->int_num, gIRQ_Handlers[Regs->int_num][i]);
- #endif
- }
- }
-
- //Log(" IRQ_Handler: Resetting");
- if(irq >= 8)
- outb(0xA0, 0x20); // ACK IRQ (Secondary PIC)
- outb(0x20, 0x20); // ACK IRQ
- //Log("IRQ_Handler: RETURN");
-}
-
-/**
- * \fn int IRQ_AddHandler( int Num, void (*Callback)(int) )
- */
-int IRQ_AddHandler( int Num, void (*Callback)(int, void*), void *Ptr )
-{
- int i;
- for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ )
- {
- if( gIRQ_Handlers[Num][i] == NULL ) {
- Log_Log("IRQ", "Added IRQ%i Cb#%i %p", Num, i, Callback);
- gIRQ_Handlers[Num][i] = Callback;
- gaIRQ_DataPointers[Num][i] = Ptr;
- return 1;
- }
- }
-
- Log_Warning("IRQ", "No free callbacks on IRQ%i", Num);
- return 0;
-}
+++ /dev/null
-/*
- * Acess 2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * kpanic.c
- * - x86 Kernel Panic Handler
- */
-
-#include <acess.h>
-#include <proc.h>
-
-// === CONSTANTS ===
-#define FB ((Uint16 *)(KERNEL_BASE|0xB8000))
-#define BGC 0x4F00 // White on Red
-//#define BGC 0xC000 // Black on Bright Red
-//#define BGC 0x1F00 // White on Blue (BSOD!)
-
-// === IMPORTS ===
-extern Uint32 GetEIP(void);
-extern void Error_Backtrace(Uint32 eip, Uint32 ebp);
-#if USE_MP
-extern void MP_SendIPIVector(int CPU, Uint8 Vector);
-extern int giNumCPUs;
-extern int GetCPUNum(void);
-#endif
-
-// === PROTOTYPES ===
-void KernelPanic_SetMode(void);
-void KernelPanic_PutChar(char Ch);
-
-// === CONSTANTS ===
-const struct {
- Uint16 IdxPort;
- Uint16 DatPort;
- Uint8 Index;
- Uint8 Value;
-} caRegValues[] = {
- //{0x3C0, 0x3C0, 0x10, 0x0C}, // Mode Control (Blink Enabled)
- {0x3C0, 0x3C0, 0x10, 0x04}, // Mode Control (Blink Disabled)
- {0x3C0, 0x3C0, 0x11, 0x00}, // Overscan Register
- {0x3C0, 0x3C0, 0x12, 0x0F}, // Color Plane Enable
- {0x3C0, 0x3C0, 0x13, 0x08}, // Horizontal Panning
- {0x3C0, 0x3C0, 0x14, 0x00}, // Color Select
- {0 , 0x3C2, 0 , 0x67}, // Miscellaneous Output Register
- {0x3C4, 0x3C5, 0x01, 0x00}, // Clock Mode Register
- {0x3C4, 0x3C5, 0x03, 0x00}, // Character select
- {0x3C4, 0x3C5, 0x04, 0x07}, // Memory Mode Register
- {0x3CE, 0x3CF, 0x05, 0x10}, // Mode Register
- {0x3CE, 0x3CF, 0x06, 0x0E}, // Miscellaneous Register
- {0x3D4, 0x3D5, 0x00, 0x5F}, // Horizontal Total
- {0x3D4, 0x3D5, 0x01, 0x4F}, // Horizontal Display Enable End
- {0x3D4, 0x3D5, 0x02, 0x50}, // Horizontal Blank Start
- {0x3D4, 0x3D5, 0x03, 0x82}, // Horizontal Blank End
- {0x3D4, 0x3D5, 0x04, 0x55}, // Horizontal Retrace Start
- {0x3D4, 0x3D5, 0x05, 0x81}, // Horizontal Retrace End
- {0x3D4, 0x3D5, 0x06, 0xBF}, // Vertical Total
- {0x3D4, 0x3D5, 0x07, 0x1F}, // Overflow Register
- {0x3D4, 0x3D5, 0x08, 0x00}, // Preset row scan
- {0x3D4, 0x3D5, 0x09, 0x4F}, // Maximum Scan Line
- {0x3D4, 0x3D5, 0x10, 0x9C}, // Vertical Retrace Start
- {0x3D4, 0x3D5, 0x11, 0x8E}, // Vertical Retrace End
- {0x3D4, 0x3D5, 0x12, 0x8F}, // Vertical Display Enable End
- {0x3D4, 0x3D5, 0x13, 0x28}, // Logical Width
- {0x3D4, 0x3D5, 0x14, 0x1F}, // Underline Location
- {0x3D4, 0x3D5, 0x15, 0x96}, // Vertical Blank Start
- {0x3D4, 0x3D5, 0x16, 0xB9}, // Vertical Blank End
- {0x3D4, 0x3D5, 0x17, 0xA3} // CRTC Mode Control
-};
-#define NUM_REGVALUES (sizeof(caRegValues)/sizeof(caRegValues[0]))
-
-// === GLOBALS ===
- int giKP_Pos = 0;
-
-// === CODE ===
-/**
- * \brief Sets the screen mode for a kernel panic
- */
-void KernelPanic_SetMode(void)
-{
- int i;
-
- // This function is called by Panic(), but MM_PageFault and the
- // CPU exception handers also call it, so let's not clear the screen
- // twice
- if( giKP_Pos ) return ;
-
- // Restore VGA 0xB8000 text mode
- #if 1
- for( i = 0; i < NUM_REGVALUES; i++ )
- {
- // Reset Flip-Flop
- if( caRegValues[i].IdxPort == 0x3C0 ) inb(0x3DA);
-
- if( caRegValues[i].IdxPort )
- outb(caRegValues[i].IdxPort, caRegValues[i].Index);
- outb(caRegValues[i].DatPort, caRegValues[i].Value);
- }
-
- inb(0x3DA);
- outb(0x3C0, 0x20);
- #endif
-
- #if USE_MP
- // Send halt to all processors
- for( i = 0; i < giNumCPUs; i ++ )
- {
- if(i == GetCPUNum()) continue ;
- FB[i] = BGC|('A'+i);
- MP_SendIPIVector(i, 0xED);
- }
- #endif
-
- // Clear Screen
- for( i = 0; i < 80*25; i++ )
- {
- FB[i] = BGC;
- }
-
- {
- Uint32 eip = GetEIP();
- Uint32 ebp;
- __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp));
- Error_Backtrace(eip, ebp);
- }
-}
-
-void KernelPanic_PutChar(char Ch)
-{
- if( giKP_Pos > 80*25 ) return ;
- switch(Ch)
- {
- case '\t':
- do {
- FB[giKP_Pos] &= 0xFF00;
- FB[giKP_Pos++] |= ' ';
- } while(giKP_Pos & 7);
- break;
-
- case '\n':
- giKP_Pos += 80;
- case '\r':
- giKP_Pos -= giKP_Pos % 80;
- break;
-
- default:
- if(' ' <= Ch && Ch < 0x7F)
- {
- FB[giKP_Pos] &= 0xFF00;
- FB[giKP_Pos] |= Ch;
- }
- giKP_Pos ++;
- break;
- }
-}
+++ /dev/null
-/*
- * Acess2
- *
- * arch/x86/lib.c
- * - General arch-specific stuff
- */
-#include <acess.h>
-#include <threads_int.h>
-#include <arch_int.h>
-#include <hal_proc.h> // GetCPUNum
-
-#define TRACE_LOCKS 0
-
-#define DEBUG_TO_E9 1
-#define DEBUG_TO_SERIAL 1
-#define SERIAL_PORT 0x3F8
-#define GDB_SERIAL_PORT 0x2F8
-
-// === IMPRORTS ===
-#if TRACE_LOCKS
-extern struct sShortSpinlock glDebug_Lock;
-extern tMutex glPhysAlloc;
-#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glThreadListLock && Lock != &glPhysAlloc.Protector)
-//#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glPhysAlloc.Protector)
-#endif
-
-// === PROTOTYPES ==
-Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
-Uint64 __udivdi3(Uint64 Num, Uint64 Den);
-Uint64 __umoddi3(Uint64 Num, Uint64 Den);
-
-// === GLOBALS ===
- int gbDebug_SerialSetup = 0;
- int gbGDB_SerialSetup = 0;
-
-// === CODE ===
-/**
- * \brief Determine if a short spinlock is locked
- * \param Lock Lock pointer
- */
-int IS_LOCKED(struct sShortSpinlock *Lock)
-{
- return !!Lock->Lock;
-}
-
-/**
- * \brief Check if the current CPU has the lock
- * \param Lock Lock pointer
- */
-int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
-{
- return Lock->Lock == GetCPUNum() + 1;
-}
-
-void __AtomicTestSetLoop(Uint *Ptr, Uint Value)
-{
- __ASM__(
- "1:\n\t"
- "xor %%eax, %%eax;\n\t"
- "lock cmpxchgl %0, (%1);\n\t"
- "jnz 1b;\n\t"
- :: "r"(Value), "r"(Ptr)
- : "eax" // EAX clobbered
- );
-}
-/**
- * \brief Acquire a Short Spinlock
- * \param Lock Lock pointer
- *
- * This type of mutex should only be used for very short sections of code,
- * or in places where a Mutex_* would be overkill, such as appending
- * an element to linked list (usually two assignement lines in C)
- *
- * \note This type of lock halts interrupts, so ensure that no timing
- * functions are called while it is held. As a matter of fact, spend as
- * little time as possible with this lock held
- * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested
- */
-void SHORTLOCK(struct sShortSpinlock *Lock)
-{
- int IF;
- int cpu = GetCPUNum() + 1;
-
- // Save interrupt state
- __ASM__ ("pushf;\n\tpop %0" : "=r"(IF));
- IF &= 0x200; // AND out all but the interrupt flag
-
- #if TRACE_LOCKS
- if( TRACE_LOCK_COND )
- {
- //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0));
- Debug("%i %p obtaining %p (Called by %p)", cpu-1, __builtin_return_address(0), Lock, __builtin_return_address(1));
- }
- #endif
-
- __ASM__("cli");
-
- // Wait for another CPU to release
- __AtomicTestSetLoop( (Uint*)&Lock->Lock, cpu );
- Lock->IF = IF;
-
- #if TRACE_LOCKS
- if( TRACE_LOCK_COND )
- {
- //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0));
- Debug("%i %p locked by %p\t%p", cpu-1, Lock, __builtin_return_address(0), __builtin_return_address(1));
-// Debug("got it");
- }
- #endif
-}
-/**
- * \brief Release a short lock
- * \param Lock Lock pointer
- */
-void SHORTREL(struct sShortSpinlock *Lock)
-{
- #if TRACE_LOCKS
- if( TRACE_LOCK_COND )
- {
- //Log_Log("LOCK", "%p released by %p", Lock, __builtin_return_address(0));
- Debug("Lock %p released by %p\t%p", Lock, __builtin_return_address(0), __builtin_return_address(1));
- }
- #endif
-
- // Lock->IF can change anytime once Lock->Lock is zeroed
- if(Lock->IF) {
- Lock->Lock = 0;
- __ASM__ ("sti");
- }
- else {
- Lock->Lock = 0;
- }
-}
-
-// === DEBUG IO ===
-#if USE_GDB_STUB
-int putDebugChar(char ch)
-{
- if(!gbGDB_SerialSetup) {
- outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
- outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
- outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
- outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte)
- outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1)
- outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
- outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
- gbGDB_SerialSetup = 1;
- }
- while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 );
- outb(GDB_SERIAL_PORT, ch);
- return 0;
-}
-int getDebugChar(void)
-{
- if(!gbGDB_SerialSetup) {
- outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
- outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
- outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
- outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte)
- outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
- outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
- outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
- gbGDB_SerialSetup = 1;
- }
- while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ;
- return inb(GDB_SERIAL_PORT);
-}
-#endif /* USE_GDB_STUB */
-
-void Debug_PutCharDebug(char ch)
-{
- #if DEBUG_TO_E9
- __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) );
- #endif
-
- #if DEBUG_TO_SERIAL
- if(!gbDebug_SerialSetup) {
- outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts
- outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
- outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
- outb(SERIAL_PORT + 1, 0x00); // (hi byte)
- outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
- outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
- outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
- gbDebug_SerialSetup = 1;
- }
- while( (inb(SERIAL_PORT + 5) & 0x20) == 0 );
- outb(SERIAL_PORT, ch);
- #endif
-}
-
-void Debug_PutStringDebug(const char *String)
-{
- while(*String)
- Debug_PutCharDebug(*String++);
-}
-
-// === IO Commands ===
-void outb(Uint16 Port, Uint8 Data)
-{
- __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data));
-}
-void outw(Uint16 Port, Uint16 Data)
-{
- __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data));
-}
-void outd(Uint16 Port, Uint32 Data)
-{
- __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data));
-}
-Uint8 inb(Uint16 Port)
-{
- Uint8 ret;
- __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port));
- return ret;
-}
-Uint16 inw(Uint16 Port)
-{
- Uint16 ret;
- __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port));
- return ret;
-}
-Uint32 ind(Uint16 Port)
-{
- Uint32 ret;
- __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port));
- return ret;
-}
-
-/**
- * \fn void *memset(void *Dest, int Val, size_t Num)
- * \brief Do a byte granuality set of Dest
- */
-void *memset(void *Dest, int Val, size_t Num)
-{
- Uint32 val = Val&0xFF;
- val |= val << 8;
- val |= val << 16;
- __asm__ __volatile__ (
- "rep stosl;\n\t"
- "mov %3, %%ecx;\n\t"
- "rep stosb"
- :: "D" (Dest), "a" (val), "c" (Num/4), "r" (Num&3));
- return Dest;
-}
-/**
- * \brief Set double words
- */
-void *memsetd(void *Dest, Uint32 Val, size_t Num)
-{
- __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num));
- return Dest;
-}
-
-/**
- * \fn int memcmp(const void *m1, const void *m2, size_t Num)
- * \brief Compare two pieces of memory
- */
-int memcmp(const void *m1, const void *m2, size_t Num)
-{
- const Uint8 *d1 = m1;
- const Uint8 *d2 = m2;
- if( Num == 0 ) return 0; // No bytes are always identical
-
- while(Num--)
- {
- if(*d1 != *d2)
- return *d1 - *d2;
- d1 ++;
- d2 ++;
- }
- return 0;
-}
-
-/**
- * \fn void *memcpy(void *Dest, const void *Src, size_t Num)
- * \brief Copy \a Num bytes from \a Src to \a Dest
- */
-void *memcpy(void *Dest, const void *Src, size_t Num)
-{
- tVAddr dst = (tVAddr)Dest;
- tVAddr src = (tVAddr)Src;
- if( (dst & 3) != (src & 3) )
- {
- __asm__ __volatile__ ("rep movsb" :: "D" (dst), "S" (src), "c" (Num));
-// Debug("\nmemcpy:Num=0x%x by %p (UA)", Num, __builtin_return_address(0));
- }
- #if 1
- else if( Num > 128 && (dst & 15) == (src & 15) )
- {
- char tmp[16+15]; // Note, this is a hack to save/restor xmm0
- int count = 16 - (dst & 15);
-// Debug("\nmemcpy:Num=0x%x by %p (SSE)", Num, __builtin_return_address(0));
- if( count < 16 )
- {
- Num -= count;
- __asm__ __volatile__ ("rep movsb" : "=D"(dst),"=S"(src): "0"(dst), "1"(src), "c"(count));
- }
-
- count = Num / 16;
- __asm__ __volatile__ (
- "movdqa 0(%5), %%xmm0;\n\t"
- "1:\n\t"
- "movdqa 0(%1), %%xmm0;\n\t"
- "movdqa %%xmm0, 0(%0);\n\t"
- "add $16,%0;\n\t"
- "add $16,%1;\n\t"
- "loop 1b;\n\t"
- "movdqa %%xmm0, 0(%5);\n\t"
- : "=r"(dst),"=r"(src)
- : "0"(dst), "1"(src), "c"(count), "r" (((tVAddr)tmp+15)&~15)
- );
-
- count = Num & 15;
- if(count)
- __asm__ __volatile__ ("rep movsb" :: "D"(dst), "S"(src), "c"(count));
- }
- #endif
- else
- {
-// Debug("\nmemcpy:Num=0x%x by %p", Num, __builtin_return_address(0));
- __asm__ __volatile__ (
- "rep movsl;\n\t"
- "mov %3, %%ecx;\n\t"
- "rep movsb"
- :: "D" (Dest), "S" (Src), "c" (Num/4), "r" (Num&3));
- }
- return Dest;
-}
-
-/**
- * \fn void *memcpyd(void *Dest, const void *Src, size_t Num)
- * \brief Copy \a Num DWORDs from \a Src to \a Dest
- */
-void *memcpyd(void *Dest, const void *Src, size_t Num)
-{
- __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num));
- return Dest;
-}
-
-#include "../helpers.h"
-
-DEF_DIVMOD(64);
-
-Uint64 DivMod64U(Uint64 Num, Uint64 Div, Uint64 *Rem)
-{
- if( Div < 0x100000000ULL && Num < 0xFFFFFFFF * Div ) {
- Uint32 rem, ret_32;
- __asm__ __volatile__(
- "div %4"
- : "=a" (ret_32), "=d" (rem)
- : "a" ( (Uint32)(Num & 0xFFFFFFFF) ), "d" ((Uint32)(Num >> 32)), "r" (Div)
- );
- if(Rem) *Rem = rem;
- return ret_32;
- }
-
- return __divmod64(Num, Div, Rem);
-}
-
-/**
- * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den)
- * \brief Divide two 64-bit integers
- */
-Uint64 __udivdi3(Uint64 Num, Uint64 Den)
-{
- if(Den == 0) {
- __asm__ __volatile__ ("int $0x0");
- return -1;
- }
- // Common speedups
- if(Num <= 0xFFFFFFFF && Den <= 0xFFFFFFFF)
- return (Uint32)Num / (Uint32)Den;
- if(Den == 1) return Num;
- if(Den == 2) return Num >> 1; // Speed Hacks
- if(Den == 4) return Num >> 2; // Speed Hacks
- if(Den == 8) return Num >> 3; // Speed Hacks
- if(Den == 16) return Num >> 4; // Speed Hacks
- if(Den == 32) return Num >> 5; // Speed Hacks
- if(Den == 1024) return Num >> 10; // Speed Hacks
- if(Den == 2048) return Num >> 11; // Speed Hacks
- if(Den == 4096) return Num >> 12;
- if(Num < Den) return 0;
- if(Num < Den*2) return 1;
- if(Num == Den*2) return 2;
-
- return __divmod64(Num, Den, NULL);
-}
-
-/**
- * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den)
- * \brief Get the modulus of two 64-bit integers
- */
-Uint64 __umoddi3(Uint64 Num, Uint64 Den)
-{
- Uint64 ret = 0;
- if(Den == 0) {
- __asm__ __volatile__ ("int $0x0"); // Call Div by Zero Error
- return -1;
- }
- if(Den == 1) return 0; // Speed Hacks
- if(Den == 2) return Num & 1; // Speed Hacks
- if(Den == 4) return Num & 3; // Speed Hacks
- if(Den == 8) return Num & 7; // Speed Hacks
- if(Den == 16) return Num & 15; // Speed Hacks
- if(Den == 32) return Num & 31; // Speed Hacks
- if(Den == 1024) return Num & 1023; // Speed Hacks
- if(Den == 2048) return Num & 2047; // Speed Hacks
- if(Den == 4096) return Num & 4095; // Speed Hacks
-
- if(Num >> 32 == 0 && Den >> 32 == 0)
- return (Uint32)Num % (Uint32)Den;
-
- __divmod64(Num, Den, &ret);
- return ret;
-}
-
-
-// --- EXPORTS ---
-EXPORT(memcpy); EXPORT(memset);
-EXPORT(memcmp);
-//EXPORT(memcpyw); EXPORT(memsetw);
-EXPORT(memcpyd); EXPORT(memsetd);
-EXPORT(inb); EXPORT(inw); EXPORT(ind);
-EXPORT(outb); EXPORT(outw); EXPORT(outd);
-EXPORT(__udivdi3); EXPORT(__umoddi3);
-
-EXPORT(SHORTLOCK);
-EXPORT(SHORTREL);
-EXPORT(IS_LOCKED);
+++ /dev/null
-/*
- * AcessMicro Kernel
- * Linker Script
- */
-
-lowStart = start - 0xC0000000;
-ENTRY(lowStart)
-OUTPUT_FORMAT(elf32-i386)
-
-SECTIONS {
- . = 0x100000;
- __load_addr = .;
- .multiboot : AT(ADDR(.multiboot)) {
- *(.multiboot)
- }
-
- . += 0xC0000000;
-
- .text ALIGN(0x1000): AT(ADDR(.text) - 0xC0000000) {
- *(.text)
- }
-
- .usertext ALIGN(0x1000): AT(ADDR(.usertext) - 0xC0000000) {
- _UsertextBase = .;
- *(.usertext)
- }
- _UsertextEnd = .;
-
- .rodata ALIGN(0x1000): AT(ADDR(.rodata) - 0xC0000000) {
- *(.initpd)
- *(.rodata)
- *(.rdata)
- gKernelModules = .;
- *(KMODULES)
- gKernelModulesEnd = .;
- . = ALIGN(4);
- gKernelSymbols = .;
- *(KEXPORT)
- gKernelSymbolsEnd = .;
-
-
- }
- /*
- .debug_abbrev : { *(.debug_abbrev) }
- .debug_info : { *(.debug_info) }
- .debug_line : { *(.debug_line) }
- .debug_loc : { *(.debug_loc) }
- .debug_pubnames : { *(.debug_pubnames) }
- .debug_aranges : { *(.debug_aranges) }
- .debug_ranges : { *(.debug_ranges) }
- .debug_str : { *(.debug_str) }
- .debug_frame : { *(.debug_frame) }
- */
-
- .padata ALIGN (0x1000) : AT(ADDR(.padata) - 0xC0000000) {
- *(.padata)
- }
-
- .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
- *(.data)
- }
-
- __bss_start = .;
- .bss : AT(ADDR(.bss) - 0xC0000000) {
- _sbss = .;
- *(COMMON)
- *(.bss)
- _ebss = .;
- }
- gKernelEnd = (. + 0xFFF)&0xFFFFF000;
-}
+++ /dev/null
-/*
- * Acess 2
- * x86 Kernel Main
- * arch/x86/main.c
- */
-#include <acess.h>
-#include <mboot.h>
-#include <multiboot2.h>
-#include <init.h>
-#include <mm_virt.h>
-#include <mp.h>
-
-#define VGA_ERRORS 0
-
-#define MAX_ARGSTR_POS (0x400000-0x2000)
-
-// === IMPORTS ===
-extern void Heap_Install(void);
-extern void Desctab_Install(void);
-extern void MM_PreinitVirtual(void);
-extern void MM_Install(tMBoot_Info *MBoot);
-extern void MM_InstallVirtual(void);
-extern void Threads_Init(void);
-extern int Time_Setup(void);
-// --- Core ---
-extern void System_Init(char *Commandline);
-
-// === PROTOTYPES ===
- int kmain(Uint MbMagic, void *MbInfoPtr);
-
-// === GLOBALS ===
-char *gsBootCmdLine = NULL;
-struct {
- Uint32 PBase;
- void *Base;
- Uint Size;
- char *ArgString;
-} *gaArch_BootModules;
- int giArch_NumBootModules = 0;
-
-// === CODE ===
-int kmain(Uint MbMagic, void *MbInfoPtr)
-{
- int i;
- tMBoot_Module *mods;
- tMBoot_Info *mbInfo;
-
- LogF("Acess2 x86-"PLATFORM" v"EXPAND_STR(KERNEL_VERSION)"\n");
- LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash);
-
- Log("MbMagic = %08x, MbInfoPtr = %p", MbMagic, MbInfoPtr);
-
- // Set up non-boot info dependent stuff
- Desctab_Install(); // Set up GDT and IDT
- MM_PreinitVirtual(); // Initialise virtual mappings
-
- switch(MbMagic)
- {
- // Multiboot 1
- case MULTIBOOT_MAGIC:
- // Adjust Multiboot structure address
- mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE );
- gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE);
-
- MM_Install( mbInfo ); // Set up physical memory manager
- break;
-
- // Multiboot 2
- case MULTIBOOT2_MAGIC:
- Panic("Multiboot 2 not yet supported");
- //MM_InstallMBoot2( MbInfo ); // Set up physical memory manager
- return 0;
- break;
-
- default:
- Panic("Multiboot magic invalid %08x, expected %08x or %08x\n",
- MbMagic, MULTIBOOT_MAGIC, MULTIBOOT2_MAGIC);
- return 0;
- }
-
- MM_InstallVirtual(); // Clean up virtual address space
- Heap_Install(); // Create initial heap
-
- // Start Multitasking
- Threads_Init();
-
- // Start Timers
- Time_Setup();
-
- Log_Log("Arch", "Starting VFS...");
- // Load Virtual Filesystem
- VFS_Init();
-
- // Load initial modules
- mods = (void*)( mbInfo->Modules + KERNEL_BASE );
- giArch_NumBootModules = mbInfo->ModuleCount;
- gaArch_BootModules = malloc( giArch_NumBootModules * sizeof(*gaArch_BootModules) );
- for( i = 0; i < mbInfo->ModuleCount; i ++ )
- {
- int ofs;
-
- Log_Log("Arch", "Multiboot Module at 0x%08x, 0x%08x bytes (String at 0x%08x)",
- mods[i].Start, mods[i].End-mods[i].Start, mods[i].String);
-
- gaArch_BootModules[i].PBase = mods[i].Start;
- gaArch_BootModules[i].Size = mods[i].End - mods[i].Start;
-
- // Always HW map the module data
- ofs = mods[i].Start&0xFFF;
- gaArch_BootModules[i].Base = (void*)( MM_MapHWPages(mods[i].Start,
- (gaArch_BootModules[i].Size+ofs+0xFFF) / 0x1000
- ) + ofs );
-
- // Only map the string if needed
- if( (tVAddr)mods[i].String > MAX_ARGSTR_POS )
- {
- // Assumes the string is < 4096 bytes long)
- gaArch_BootModules[i].ArgString = (void*)(
- MM_MapHWPages(mods[i].String, 2) + (mods[i].String&0xFFF)
- );
- }
- else
- gaArch_BootModules[i].ArgString = (char *)mods[i].String + KERNEL_BASE;
- }
-
- // Pass on to Independent Loader
- Log_Log("Arch", "Starting system");
- System_Init(gsBootCmdLine);
-
- // Sleep forever (sleeping beauty)
- for(;;)
- Threads_Sleep();
- return 0;
-}
-
-void Arch_LoadBootModules(void)
-{
- int i, j, numPages;
- for( i = 0; i < giArch_NumBootModules; i ++ )
- {
- Log_Log("Arch", "Loading '%s'", gaArch_BootModules[i].ArgString);
-
- if( !Module_LoadMem( gaArch_BootModules[i].Base, gaArch_BootModules[i].Size, gaArch_BootModules[i].ArgString ) )
- {
- Log_Warning("Arch", "Unable to load module");
- }
-
- // Unmap and free
- numPages = (gaArch_BootModules[i].Size + ((Uint)gaArch_BootModules[i].Base&0xFFF) + 0xFFF) >> 12;
- MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].Base, numPages );
-
- for( j = 0; j < numPages; j++ )
- MM_DerefPhys( gaArch_BootModules[i].PBase + (j << 12) );
-
- if( (tVAddr) gaArch_BootModules[i].ArgString > MAX_ARGSTR_POS )
- MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].ArgString, 2 );
- }
- Log_Log("Arch", "Boot modules loaded");
- free( gaArch_BootModules );
-}
+++ /dev/null
-/*
- * Acess2
- * - Physical memory manager
- */
-#define DEBUG 0
-#include <acess.h>
-#include <mboot.h>
-#include <mm_virt.h>
-
-//#define USE_STACK 1
-#define TRACE_ALLOCS 0 // Print trace messages on AllocPhys/DerefPhys
-
-
-// === IMPORTS ===
-extern char gKernelEnd[];
-extern void Proc_PrintBacktrace(void);
-
-// === PROTOTYPES ===
-void MM_Install(tMBoot_Info *MBoot);
-//tPAddr MM_AllocPhys(void);
-//tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
-//void MM_RefPhys(tPAddr PAddr);
-//void MM_DerefPhys(tPAddr PAddr);
-// int MM_GetRefCount(tPAddr PAddr);
-
-// === GLOBALS ===
-tMutex glPhysAlloc;
-Uint64 giPhysAlloc = 0; // Number of allocated pages
-Uint64 giPageCount = 0; // Total number of pages
-Uint64 giLastPossibleFree = 0; // Last possible free page (before all pages are used)
-
-Uint32 gaSuperBitmap[1024]; // Blocks of 1024 Pages
-Uint32 gaPageBitmap[1024*1024/32]; // Individual pages
- int *gaPageReferences;
-void **gaPageNodes = (void*)MM_PAGENODE_BASE;
-#define REFENT_PER_PAGE (0x1000/sizeof(gaPageReferences[0]))
-
-// === CODE ===
-void MM_Install(tMBoot_Info *MBoot)
-{
- Uint kernelPages, num;
- Uint i;
- Uint64 maxAddr = 0;
- tMBoot_Module *mods;
- tMBoot_MMapEnt *ent;
-
- // --- Find largest address
- MBoot->MMapAddr |= KERNEL_BASE;
- ent = (void *)( MBoot->MMapAddr );
- while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
- {
- // Adjust for size
- ent->Size += 4;
-
- // If entry is RAM and is above `maxAddr`, change `maxAddr`
- if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
- maxAddr = ent->Base + ent->Length;
- // Go to next entry
- ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
- }
-
- if(maxAddr == 0) {
- giPageCount = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
- }
- else {
- giPageCount = maxAddr >> 12;
- }
- giLastPossibleFree = giPageCount - 1;
-
- memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32);
-
- // Set up allocateable space
- ent = (void *)( MBoot->MMapAddr );
- while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
- {
- memsetd( &gaPageBitmap[ent->Base/(4096*32)], 0, ent->Length/(4096*32) );
- ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
- }
-
- // Get used page count (Kernel)
- kernelPages = (Uint)&gKernelEnd - KERNEL_BASE - 0x100000;
- kernelPages += 0xFFF; // Page Align
- kernelPages >>= 12;
-
- // Fill page bitmap
- num = kernelPages/32;
- memsetd( &gaPageBitmap[0x100000/(4096*32)], -1, num );
- gaPageBitmap[ 0x100000/(4096*32) + num ] = (1 << (kernelPages & 31)) - 1;
-
- // Fill Superpage bitmap
- num = kernelPages/(32*32);
- memsetd( &gaSuperBitmap[0x100000/(4096*32*32)], -1, num );
- gaSuperBitmap[ 0x100000/(4096*32*32) + num ] = (1 << ((kernelPages / 32) & 31)) - 1;
-
- // Mark Multiboot's pages as taken
- // - Structure
- MM_RefPhys( (Uint)MBoot - KERNEL_BASE );
- // - Module List
- for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; )
- MM_RefPhys( MBoot->Modules + (i << 12) );
- // - Modules
- mods = (void*)(MBoot->Modules + KERNEL_BASE);
- for(i = 0; i < MBoot->ModuleCount; i++)
- {
- num = (mods[i].End - mods[i].Start + 0xFFF) >> 12;
- while(num--)
- MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) );
- }
-
- gaPageReferences = (void*)MM_REFCOUNT_BASE;
-
- Log_Log("PMem", "Physical memory set up");
-}
-
-/**
- * \fn tPAddr MM_AllocPhys(void)
- * \brief Allocates a physical page from the general pool
- */
-tPAddr MM_AllocPhys(void)
-{
- // int a, b, c;
- int indx = -1;
- tPAddr ret;
-
- ENTER("");
-
- Mutex_Acquire( &glPhysAlloc );
-
- // Classful scan
- #if 1
- {
- const int addrClasses[] = {0,16,20,24,32,64};
- const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]);
- int i;
- int first, last;
- for( i = numAddrClasses; i -- > 1; )
- {
- first = 1UL << (addrClasses[i-1] - 12);
- last = (1UL << (addrClasses[i] - 12)) - 1;
- // Range is above the last free page
- if( first > giLastPossibleFree )
- continue;
- // Last possible free page is in the range
- if( last > giLastPossibleFree )
- last = giLastPossibleFree;
-
- // Scan the range
- for( indx = first; indx < last; )
- {
- if( gaSuperBitmap[indx>>10] == -1 ) {
- indx += 1024;
- continue;
- }
-
- if( gaPageBitmap[indx>>5] == -1 ) {
- indx += 32;
- continue;
- }
-
- if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
- indx ++;
- continue;
- }
- break;
- }
- if( indx < last ) break;
-
- giLastPossibleFree = first; // Well, we couldn't find any in this range
- }
- // Out of memory?
- if( i <= 1 ) indx = -1;
- }
- #elif 0
- // Find free page
- // Scan downwards
- LOG("giLastPossibleFree = %i", giLastPossibleFree);
- for( indx = giLastPossibleFree; indx >= 0; )
- {
- if( gaSuperBitmap[indx>>10] == -1 ) {
- indx -= 1024;
- continue;
- }
-
- if( gaPageBitmap[indx>>5] == -1 ) {
- indx -= 32;
- continue;
- }
-
- if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
- indx --;
- continue;
- }
- break;
- }
- if( indx >= 0 )
- giLastPossibleFree = indx;
- LOG("indx = %i", indx);
- #else
- c = giLastPossibleFree % 32;
- b = (giLastPossibleFree / 32) % 32;
- a = giLastPossibleFree / 1024;
-
- LOG("a=%i,b=%i,c=%i", a, b, c);
- for( ; gaSuperBitmap[a] == -1 && a >= 0; a-- );
- if(a < 0) {
- Mutex_Release( &glPhysAlloc );
- Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used",
- __builtin_return_address(0), giPhysAlloc, giPageCount);
- LEAVE('i', 0);
- return 0;
- }
- for( ; gaSuperBitmap[a] & (1<<b); b-- );
- for( ; gaPageBitmap[a*32+b] & (1<<c); c-- );
- LOG("a=%i,b=%i,c=%i", a, b, c);
- indx = (a << 10) | (b << 5) | c;
- if( indx >= 0 )
- giLastPossibleFree = indx;
- #endif
-
- if( indx < 0 ) {
- Mutex_Release( &glPhysAlloc );
- Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)",
- __builtin_return_address(0), giPhysAlloc, giPageCount, indx);
- Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree);
- LEAVE('i', 0);
- return 0;
- }
-
- if( indx > 0xFFFFF ) {
- Panic("The fuck? Too many pages! (indx = 0x%x)", indx);
- }
-
- if( indx >= giPageCount ) {
- Mutex_Release( &glPhysAlloc );
- Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount);
- LEAVE('i', 0);
- return 0;
- }
-
- // Mark page used
- if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[indx] ) )
- gaPageReferences[indx] = 1;
- gaPageBitmap[ indx>>5 ] |= 1 << (indx&31);
-
- giPhysAlloc ++;
-
- // Get address
- ret = indx << 12;
-
- // Mark used block
- if(gaPageBitmap[ indx>>5 ] == -1) {
- gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31);
- }
-
- // Release Spinlock
- Mutex_Release( &glPhysAlloc );
-
- LEAVE('X', ret);
- #if TRACE_ALLOCS
- if( now() > 4000 ) {
- Log_Debug("PMem", "MM_AllocPhys: RETURN %P (%i free)", ret, giPageCount-giPhysAlloc);
- Proc_PrintBacktrace();
- }
- #endif
- return ret;
-}
-
-/**
- * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
- * \brief Allocate a range of physical pages
- * \param Pages Number of pages to allocate
- * \param MaxBits Maximum number of address bits to use
- */
-tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
-{
- int a, b;
- int i, idx, sidx;
- tPAddr ret;
-
- ENTER("iPages iMaxBits", Pages, MaxBits);
-
- // Sanity Checks
- if(MaxBits < 0) {
- LEAVE('i', 0);
- return 0;
- }
- if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS;
-
- // Lock
- Mutex_Acquire( &glPhysAlloc );
-
- // Set up search state
- if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) {
- sidx = (tPAddr)1 << (MaxBits-12);
- }
- else {
- sidx = giLastPossibleFree;
- }
- idx = sidx / 32;
- sidx %= 32;
- b = idx % 32;
- a = idx / 32;
-
- #if 0
- LOG("a=%i, b=%i, idx=%i, sidx=%i", a, b, idx, sidx);
-
- // Find free page
- for( ; gaSuperBitmap[a] == -1 && a --; ) b = 31;
- if(a < 0) {
- Mutex_Release( &glPhysAlloc );
- Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
- LEAVE('i', 0);
- return 0;
- }
- LOG("a = %i", a);
- for( ; gaSuperBitmap[a] & (1 << b); b-- ) sidx = 31;
- LOG("b = %i", b);
- idx = a * 32 + b;
- for( ; gaPageBitmap[idx] & (1 << sidx); sidx-- )
- LOG("gaPageBitmap[%i] = 0x%08x", idx, gaPageBitmap[idx]);
-
- LOG("idx = %i, sidx = %i", idx, sidx);
- #else
-
- #endif
-
- // Check if the gap is large enough
- while( idx >= 0 )
- {
- // Find a free page
- for( ; ; )
- {
- // Bulk Skip
- if( gaPageBitmap[idx] == -1 ) {
- idx --;
- sidx = 31;
- continue;
- }
-
- if( gaPageBitmap[idx] & (1 << sidx) ) {
- sidx --;
- if(sidx < 0) { sidx = 31; idx --; }
- if(idx < 0) break;
- continue;
- }
- break;
- }
- if( idx < 0 ) break;
-
- // Check if it is a free range
- for( i = 0; i < Pages; i++ )
- {
- // Used page? break
- if( gaPageBitmap[idx] & (1 << sidx) )
- break;
-
- sidx --;
- if(sidx < 0) { sidx = 31; idx --; }
- if(idx < 0) break;
- }
-
- if( i == Pages )
- break;
- }
-
- // Check if an address was found
- if( idx < 0 ) {
- Mutex_Release( &glPhysAlloc );
- Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
- LEAVE('i', 0);
- return 0;
- }
-
- // Mark pages used
- for( i = 0; i < Pages; i++ )
- {
- if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[idx*32+sidx] ) )
- gaPageReferences[idx*32+sidx] = 1;
- gaPageBitmap[ idx ] |= 1 << sidx;
- sidx ++;
- giPhysAlloc ++;
- if(sidx == 32) { sidx = 0; idx ++; }
- }
-
- // Get address
- ret = (idx << 17) | (sidx << 12);
-
- // Mark used block
- if(gaPageBitmap[ idx ] == -1) gaSuperBitmap[idx/32] |= 1 << (idx%32);
-
- // Release Spinlock
- Mutex_Release( &glPhysAlloc );
-
- LEAVE('X', ret);
- #if TRACE_ALLOCS
- Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)",
- ret, ret + (1<<Pages)-1, giPageCount-giPhysAlloc);
- #endif
- return ret;
-}
-
-/**
- * \fn void MM_RefPhys(tPAddr PAddr)
- */
-void MM_RefPhys(tPAddr PAddr)
-{
- // Get page number
- PAddr >>= 12;
-
- // We don't care about non-ram pages
- if(PAddr >= giPageCount) return;
-
- // Lock Structures
- Mutex_Acquire( &glPhysAlloc );
-
- // Reference the page
- if( gaPageReferences )
- {
- if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 )
- {
- int i, base;
- tVAddr addr = ((tVAddr)&gaPageReferences[PAddr]) & ~0xFFF;
-// Log_Debug("PMem", "MM_RefPhys: Allocating info for %X", PAddr);
- Mutex_Release( &glPhysAlloc );
- if( MM_Allocate( addr ) == 0 ) {
- Log_KernelPanic("PMem", "MM_RefPhys: Out of physical memory allocating info for %X", PAddr*PAGE_SIZE);
- }
- Mutex_Acquire( &glPhysAlloc );
-
- base = PAddr & ~(1024-1);
- for( i = 0; i < 1024; i ++ ) {
- gaPageReferences[base + i] = (gaPageBitmap[(base+i)/32] & (1 << (base+i)%32)) ? 1 : 0;
- }
- }
- gaPageReferences[ PAddr ] ++;
- }
-
- // Mark as used
- gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31);
-
- // Mark used block
- if(gaPageBitmap[ PAddr / 32 ] == -1)
- gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31);
-
- // Release Spinlock
- Mutex_Release( &glPhysAlloc );
-}
-
-/**
- * \fn void MM_DerefPhys(tPAddr PAddr)
- * \brief Dereferences a physical page
- */
-void MM_DerefPhys(tPAddr PAddr)
-{
- // Get page number
- PAddr >>= 12;
-
- // We don't care about non-ram pages
- if(PAddr >= giPageCount) return;
-
- // Check if it is freed
- if( !(gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ) {
- Log_Warning("MMVirt", "MM_DerefPhys - Non-referenced memory dereferenced");
- return;
- }
-
- // Lock Structures
- Mutex_Acquire( &glPhysAlloc );
-
- if( giLastPossibleFree < PAddr )
- giLastPossibleFree = PAddr;
-
- // Dereference
- if( !MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) || (-- gaPageReferences[PAddr]) == 0 )
- {
- #if TRACE_ALLOCS
- Log_Debug("PMem", "MM_DerefPhys: Free'd %P (%i free)", PAddr<<12, giPageCount-giPhysAlloc);
- Proc_PrintBacktrace();
- #endif
- //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0));
- giPhysAlloc --;
- gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31));
- if(gaPageBitmap[ PAddr / 32 ] == 0)
- gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31));
-
- if( MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) )
- {
- gaPageNodes[PAddr] = NULL;
- // TODO: Free Node Page when fully unused
- }
- }
-
- // Release spinlock
- Mutex_Release( &glPhysAlloc );
-}
-
-/**
- * \fn int MM_GetRefCount(tPAddr Addr)
- */
-int MM_GetRefCount(tPAddr PAddr)
-{
- // Get page number
- PAddr >>= 12;
-
- // We don't care about non-ram pages
- if(PAddr >= giPageCount) return -1;
-
- if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 )
- return (gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ? 1 : 0;
-
- // Check if it is freed
- return gaPageReferences[ PAddr ];
-}
-
-int MM_SetPageNode(tPAddr PAddr, void *Node)
-{
- tVAddr block_addr;
-
- if( MM_GetRefCount(PAddr) == 0 ) return 1;
-
- PAddr /= PAGE_SIZE;
-
- block_addr = (tVAddr) &gaPageNodes[PAddr];
- block_addr &= ~(PAGE_SIZE-1);
-
- if( !MM_GetPhysAddr( block_addr ) )
- {
- if( !MM_Allocate( block_addr ) ) {
- Log_Warning("PMem", "Unable to allocate Node page");
- return -1;
- }
- memset( (void*)block_addr, 0, PAGE_SIZE );
- }
-
- gaPageNodes[PAddr] = Node;
-// Log("gaPageNodes[0x%x] = %p", PAddr, Node);
- return 0;
-}
-
-int MM_GetPageNode(tPAddr PAddr, void **Node)
-{
- if( MM_GetRefCount(PAddr) == 0 ) return 1;
-
- PAddr /= PAGE_SIZE;
- if( !MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) {
- *Node = NULL;
- return 0;
- }
- *Node = gaPageNodes[PAddr];
- return 0;
-}
-
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * mm_virt.c
- *
- * Memory Map
- * 0xE0 - Kernel Base
- * 0xF0 - Kernel Stacks
- * 0xFD - Fractals
- * 0xFE - Unused
- * 0xFF - System Calls / Kernel's User Code
- */
-#define DEBUG 0
-#define SANITY 1
-#include <acess.h>
-#include <mm_virt.h>
-#include <mm_phys.h>
-#include <proc.h>
-#include <hal_proc.h>
-#include <arch_int.h>
-
-#define TAB 22
-
-#define WORKER_STACKS 0x00100000 // Thread0 Only!
-#define WORKER_STACK_SIZE MM_KERNEL_STACK_SIZE
-#define WORKER_STACKS_END 0xB0000000
-#define NUM_WORKER_STACKS ((WORKER_STACKS_END-WORKER_STACKS)/WORKER_STACK_SIZE)
-
-#define PAE_PAGE_TABLE_ADDR 0xFC000000 // 16 MiB
-#define PAE_PAGE_DIR_ADDR 0xFCFC0000 // 16 KiB
-#define PAE_PAGE_PDPT_ADDR 0xFCFC3F00 // 32 bytes
-#define PAE_TMP_PDPT_ADDR 0xFCFC3F20 // 32 bytes
-#define PAE_TMP_DIR_ADDR 0xFCFE0000 // 16 KiB
-#define PAE_TMP_TABLE_ADDR 0xFD000000 // 16 MiB
-
-#define PAGE_TABLE_ADDR 0xFC000000
-#define PAGE_DIR_ADDR 0xFC3F0000
-#define PAGE_CR3_ADDR 0xFC3F0FC0
-#define TMP_CR3_ADDR 0xFC3F0FC4 // Part of core instead of temp
-#define TMP_DIR_ADDR 0xFC3F1000 // Same
-#define TMP_TABLE_ADDR 0xFC400000
-
-#define HW_MAP_ADDR 0xFE000000
-#define HW_MAP_MAX 0xFFEF0000
-#define NUM_HW_PAGES ((HW_MAP_MAX-HW_MAP_ADDR)/0x1000)
-#define TEMP_MAP_ADDR 0xFFEF0000 // Allows 16 "temp" pages
-#define NUM_TEMP_PAGES 16
-#define LAST_BLOCK_ADDR 0xFFFF0000 // Free space for kernel provided user code/ *(-1) protection
-
-#define PF_PRESENT 0x1
-#define PF_WRITE 0x2
-#define PF_USER 0x4
-#define PF_GLOBAL 0x80
-#define PF_COW 0x200
-#define PF_NOPAGE 0x400
-
-#define INVLPG(addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(addr))
-
-#define GET_TEMP_MAPPING(cr3) do { \
- __ASM__("cli"); \
- __AtomicTestSetLoop( (Uint *)gpTmpCR3, cr3 | 3 ); \
-} while(0)
-#define REL_TEMP_MAPPING() do { \
- *gpTmpCR3 = 0; \
- __ASM__("sti"); \
-} while(0)
-
-typedef Uint32 tTabEnt;
-
-// === IMPORTS ===
-extern char _UsertextEnd[], _UsertextBase[];
-extern Uint32 gaInitPageDir[1024];
-extern Uint32 gaInitPageTable[1024];
-extern void Threads_SegFault(tVAddr Addr);
-extern void Error_Backtrace(Uint eip, Uint ebp);
-
-// === PROTOTYPES ===
-void MM_PreinitVirtual(void);
-void MM_InstallVirtual(void);
-void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
-//void MM_DumpTables(tVAddr Start, tVAddr End);
-//void MM_ClearUser(void);
-tPAddr MM_DuplicatePage(tVAddr VAddr);
-
-// === GLOBALS ===
-#define gaPageTable ((tTabEnt*)PAGE_TABLE_ADDR)
-#define gaPageDir ((tTabEnt*)PAGE_DIR_ADDR)
-#define gaTmpTable ((tTabEnt*)TMP_TABLE_ADDR)
-#define gaTmpDir ((tTabEnt*)TMP_DIR_ADDR)
-#define gpPageCR3 ((tTabEnt*)PAGE_CR3_ADDR)
-#define gpTmpCR3 ((tTabEnt*)TMP_CR3_ADDR)
-
-#define gaPAE_PageTable ((tTabEnt*)PAE_PAGE_TABLE_ADDR)
-#define gaPAE_PageDir ((tTabEnt*)PAE_PAGE_DIR_ADDR)
-#define gaPAE_MainPDPT ((tTabEnt*)PAE_PAGE_PDPT_ADDR)
-#define gaPAE_TmpTable ((tTabEnt*)PAE_TMP_DIR_ADDR)
-#define gaPAE_TmpDir ((tTabEnt*)PAE_TMP_DIR_ADDR)
-#define gaPAE_TmpPDPT ((tTabEnt*)PAE_TMP_PDPT_ADDR)
- int gbUsePAE = 0;
-tMutex glTempMappings;
-tMutex glTempFractal;
-Uint32 gWorkerStacks[(NUM_WORKER_STACKS+31)/32];
- int giLastUsedWorker = 0;
-struct sPageInfo {
- void *Node;
- tVAddr Base;
- Uint64 Offset;
- int Length;
- int Flags;
-} *gaMappedRegions; // sizeof = 24 bytes
-
-// === CODE ===
-/**
- * \fn void MM_PreinitVirtual(void)
- * \brief Maps the fractal mappings
- */
-void MM_PreinitVirtual(void)
-{
- gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((tTabEnt)&gaInitPageDir - KERNEL_BASE) | 3;
- INVLPG( PAGE_TABLE_ADDR );
-}
-
-/**
- * \fn void MM_InstallVirtual(void)
- * \brief Sets up the constant page mappings
- */
-void MM_InstallVirtual(void)
-{
- int i;
-
- // --- Pre-Allocate kernel tables
- for( i = KERNEL_BASE>>22; i < 1024; i ++ )
- {
- if( gaPageDir[ i ] ) continue;
- // Skip stack tables, they are process unique
- if( i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) {
- gaPageDir[ i ] = 0;
- continue;
- }
- // Preallocate table
- gaPageDir[ i ] = MM_AllocPhys() | 3;
- INVLPG( &gaPageTable[i*1024] );
- memset( &gaPageTable[i*1024], 0, 0x1000 );
- }
-
- // Unset kernel on the User Text pages
- for( i = ((tVAddr)&_UsertextEnd-(tVAddr)&_UsertextBase+0xFFF)/4096; i--; ) {
- MM_SetFlags( (tVAddr)&_UsertextBase + i*4096, 0, MM_PFLAG_KERNEL );
- }
-
- *gpTmpCR3 = 0;
-}
-
-/**
- * \brief Cleans up the SMP required mappings
- */
-void MM_FinishVirtualInit(void)
-{
- gaInitPageDir[ 0 ] = 0;
-}
-
-/**
- * \fn void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
- * \brief Called on a page fault
- */
-void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
-{
- //ENTER("xAddr bErrorCode", Addr, ErrorCode);
-
- // -- Check for COW --
- if( gaPageDir [Addr>>22] & PF_PRESENT && gaPageTable[Addr>>12] & PF_PRESENT
- && gaPageTable[Addr>>12] & PF_COW )
- {
- tPAddr paddr;
- if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
- {
- gaPageTable[Addr>>12] &= ~PF_COW;
- gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
- }
- else
- {
- //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr);
- paddr = MM_DuplicatePage( Addr );
- MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
- gaPageTable[Addr>>12] &= PF_USER;
- gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
- }
-
-// Log_Debug("MMVirt", "COW for %p (%P)", Addr, gaPageTable[Addr>>12]);
-
- INVLPG( Addr & ~0xFFF );
- return;
- }
-
- // Disable instruction tracing
- __ASM__("pushf; andw $0xFEFF, 0(%esp); popf");
- Proc_GetCurThread()->bInstrTrace = 0;
-
- // If it was a user, tell the thread handler
- if(ErrorCode & 4) {
- Log_Warning("MMVirt", "User %s %s memory%s",
- (ErrorCode&2?"write to":"read from"),
- (ErrorCode&1?"bad/locked":"non-present"),
- (ErrorCode&16?" (Instruction Fetch)":"")
- );
- Log_Warning("MMVirt", "Instruction %04x:%08x accessed %p", Regs->cs, Regs->eip, Addr);
- __ASM__("sti"); // Restart IRQs
- #if 1
- Error_Backtrace(Regs->eip, Regs->ebp);
- #endif
- Threads_SegFault(Addr);
- return ;
- }
-
- Debug_KernelPanic();
-
- // -- Check Error Code --
- if(ErrorCode & 8)
- Warning("Reserved Bits Trashed!");
- else
- {
- Warning("Kernel %s %s memory%s",
- (ErrorCode&2?"write to":"read from"),
- (ErrorCode&1?"bad/locked":"non-present"),
- (ErrorCode&16?" (Instruction Fetch)":"")
- );
- }
-
- Log("CPU %i - Code at %p accessed %p", GetCPUNum(), Regs->eip, Addr);
- // Print Stack Backtrace
- Error_Backtrace(Regs->eip, Regs->ebp);
-
- #if 0
- Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]);
- if( gaPageDir[Addr>>22] & PF_PRESENT )
- Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]);
- #endif
- //MM_DumpTables(0, -1);
-
- // Register Dump
- Log("EAX %08x ECX %08x EDX %08x EBX %08x", Regs->eax, Regs->ecx, Regs->edx, Regs->ebx);
- Log("ESP %08x EBP %08x ESI %08x EDI %08x", Regs->esp, Regs->ebp, Regs->esi, Regs->edi);
- //Log("SS:ESP %04x:%08x", Regs->ss, Regs->esp);
- Log("CS:EIP %04x:%08x", Regs->cs, Regs->eip);
- Log("DS %04x ES %04x FS %04x GS %04x", Regs->ds, Regs->es, Regs->fs, Regs->gs);
- {
- Uint dr0, dr1;
- __ASM__ ("mov %%dr0, %0":"=r"(dr0):);
- __ASM__ ("mov %%dr1, %0":"=r"(dr1):);
- Log("DR0 %08x DR1 %08x", dr0, dr1);
- }
-
- Panic("Page Fault at 0x%x (Accessed 0x%x)", Regs->eip, Addr);
-}
-
-/**
- * \fn void MM_DumpTables(tVAddr Start, tVAddr End)
- * \brief Dumps the layout of the page tables
- */
-void MM_DumpTables(tVAddr Start, tVAddr End)
-{
- tVAddr rangeStart = 0;
- tPAddr expected = 0;
- void *expected_node = NULL, *tmpnode = NULL;
- tVAddr curPos;
- Uint page;
- const tPAddr MASK = ~0xF78;
-
- Start >>= 12; End >>= 12;
-
- #if 0
- Log("Directory Entries:");
- for(page = Start >> 10;
- page < (End >> 10)+1;
- page ++)
- {
- if(gaPageDir[page])
- {
- Log(" 0x%08x-0x%08x :: 0x%08x",
- page<<22, ((page+1)<<22)-1,
- gaPageDir[page]&~0xFFF
- );
- }
- }
- #endif
-
- Log("Table Entries:");
- for(page = Start, curPos = Start<<12;
- page < End;
- curPos += 0x1000, page++)
- {
- if( !(gaPageDir[curPos>>22] & PF_PRESENT)
- || !(gaPageTable[page] & PF_PRESENT)
- || (gaPageTable[page] & MASK) != expected
- || (tmpnode=NULL,MM_GetPageNode(expected, &tmpnode), tmpnode != expected_node))
- {
- if(expected) {
- tPAddr orig = gaPageTable[rangeStart>>12];
- Log(" 0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p",
- rangeStart,
- orig & ~0xFFF,
- curPos - rangeStart,
- (orig & PF_NOPAGE ? "P" : "-"),
- (orig & PF_COW ? "C" : "-"),
- (orig & PF_GLOBAL ? "G" : "-"),
- (orig & PF_USER ? "U" : "-"),
- (orig & PF_WRITE ? "W" : "-"),
- expected_node
- );
- expected = 0;
- }
- if( !(gaPageDir[curPos>>22] & PF_PRESENT) ) continue;
- if( !(gaPageTable[curPos>>12] & PF_PRESENT) ) continue;
-
- expected = (gaPageTable[page] & MASK);
- MM_GetPageNode(expected, &expected_node);
- rangeStart = curPos;
- }
- if(expected) expected += 0x1000;
- }
-
- if(expected) {
- tPAddr orig = gaPageTable[rangeStart>>12];
- Log("0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p",
- rangeStart,
- orig & ~0xFFF,
- curPos - rangeStart,
- (orig & PF_NOPAGE ? "p" : "-"),
- (orig & PF_COW ? "C" : "-"),
- (orig & PF_GLOBAL ? "G" : "-"),
- (orig & PF_USER ? "U" : "-"),
- (orig & PF_WRITE ? "W" : "-"),
- expected_node
- );
- expected = 0;
- }
-}
-
-/**
- * \fn tPAddr MM_Allocate(tVAddr VAddr)
- */
-tPAddr MM_Allocate(tVAddr VAddr)
-{
- tPAddr paddr;
- //ENTER("xVAddr", VAddr);
- //__ASM__("xchg %bx,%bx");
- // Check if the directory is mapped
- if( gaPageDir[ VAddr >> 22 ] == 0 )
- {
- // Allocate directory
- paddr = MM_AllocPhys();
- if( paddr == 0 ) {
- Warning("MM_Allocate - Out of Memory (Called by %p)", __builtin_return_address(0));
- //LEAVE('i',0);
- return 0;
- }
- // Map and mark as user (if needed)
- gaPageDir[ VAddr >> 22 ] = paddr | 3;
- if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER;
-
- INVLPG( &gaPageDir[ VAddr >> 22 ] );
- memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 );
- }
- // Check if the page is already allocated
- else if( gaPageTable[ VAddr >> 12 ] != 0 ) {
- Warning("MM_Allocate - Allocating to used address (%p)", VAddr);
- //LEAVE('X', gaPageTable[ VAddr >> 12 ] & ~0xFFF);
- return gaPageTable[ VAddr >> 12 ] & ~0xFFF;
- }
-
- // Allocate
- paddr = MM_AllocPhys();
- //LOG("paddr = 0x%llx", paddr);
- if( paddr == 0 ) {
- Warning("MM_Allocate - Out of Memory when allocating at %p (Called by %p)",
- VAddr, __builtin_return_address(0));
- //LEAVE('i',0);
- return 0;
- }
- // Map
- gaPageTable[ VAddr >> 12 ] = paddr | 3;
- // Mark as user
- if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER;
- // Invalidate Cache for address
- INVLPG( VAddr & ~0xFFF );
-
- //LEAVE('X', paddr);
- return paddr;
-}
-
-/**
- * \fn void MM_Deallocate(tVAddr VAddr)
- */
-void MM_Deallocate(tVAddr VAddr)
-{
- if( gaPageDir[ VAddr >> 22 ] == 0 ) {
- Warning("MM_Deallocate - Directory not mapped");
- return;
- }
-
- if(gaPageTable[ VAddr >> 12 ] == 0) {
- Warning("MM_Deallocate - Page is not allocated");
- return;
- }
-
- // Dereference page
- MM_DerefPhys( gaPageTable[ VAddr >> 12 ] & ~0xFFF );
- // Clear page
- gaPageTable[ VAddr >> 12 ] = 0;
-}
-
-/**
- * \fn tPAddr MM_GetPhysAddr(tVAddr Addr)
- * \brief Checks if the passed address is accesable
- */
-tPAddr MM_GetPhysAddr(tVAddr Addr)
-{
- if( !(gaPageDir[Addr >> 22] & 1) )
- return 0;
- if( !(gaPageTable[Addr >> 12] & 1) )
- return 0;
- return (gaPageTable[Addr >> 12] & ~0xFFF) | (Addr & 0xFFF);
-}
-
-/**
- * \fn void MM_SetCR3(Uint CR3)
- * \brief Sets the current process space
- */
-void MM_SetCR3(Uint CR3)
-{
- __ASM__("mov %0, %%cr3"::"r"(CR3));
-}
-
-/**
- * \fn int MM_Map(tVAddr VAddr, tPAddr PAddr)
- * \brief Map a physical page to a virtual one
- */
-int MM_Map(tVAddr VAddr, tPAddr PAddr)
-{
- //ENTER("xVAddr xPAddr", VAddr, PAddr);
- // Sanity check
- if( PAddr & 0xFFF || VAddr & 0xFFF ) {
- Log_Warning("MM_Virt", "MM_Map - Physical or Virtual Addresses are not aligned");
- //LEAVE('i', 0);
- return 0;
- }
-
- // Align addresses
- PAddr &= ~0xFFF; VAddr &= ~0xFFF;
-
- // Check if the directory is mapped
- if( gaPageDir[ VAddr >> 22 ] == 0 )
- {
- tPAddr tmp = MM_AllocPhys();
- if( tmp == 0 )
- return 0;
- gaPageDir[ VAddr >> 22 ] = tmp | 3;
-
- // Mark as user
- if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER;
-
- INVLPG( &gaPageTable[ (VAddr >> 12) & ~0x3FF ] );
- memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 );
- }
- // Check if the page is already allocated
- else if( gaPageTable[ VAddr >> 12 ] != 0 ) {
- Warning("MM_Map - Allocating to used address");
- //LEAVE('i', 0);
- return 0;
- }
-
- // Map
- gaPageTable[ VAddr >> 12 ] = PAddr | 3;
- // Mark as user
- if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER;
-
- //LOG("gaPageTable[ 0x%x ] = (Uint)%p = 0x%x",
- // VAddr >> 12, &gaPageTable[ VAddr >> 12 ], gaPageTable[ VAddr >> 12 ]);
-
- // Reference
- MM_RefPhys( PAddr );
-
- //LOG("INVLPG( 0x%x )", VAddr);
- INVLPG( VAddr );
-
- //LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \brief Clear user's address space
- */
-void MM_ClearUser(void)
-{
- Uint i, j;
-
- for( i = 0; i < (MM_USER_MAX>>22); i ++ )
- {
- // Check if directory is not allocated
- if( !(gaPageDir[i] & PF_PRESENT) ) {
- gaPageDir[i] = 0;
- continue;
- }
-
- // Deallocate tables
- for( j = 0; j < 1024; j ++ )
- {
- if( gaPageTable[i*1024+j] & 1 )
- MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF );
- gaPageTable[i*1024+j] = 0;
- }
-
- // Deallocate directory
- MM_DerefPhys( gaPageDir[i] & ~0xFFF );
- gaPageDir[i] = 0;
- INVLPG( &gaPageTable[i*1024] );
- }
- INVLPG( gaPageDir );
-}
-
-/**
- * \brief Deallocate an address space
- */
-void MM_ClearSpace(Uint32 CR3)
-{
- int i, j;
-
- if(CR3 == (*gpPageCR3 & ~0xFFF)) {
- Log_Error("MMVirt", "Can't clear current address space");
- return ;
- }
-
- if( MM_GetRefCount(CR3) > 1 ) {
- MM_DerefPhys(CR3);
- Log_Log("MMVirt", "CR3 %P is still referenced, not cleaning (but dereferenced)", CR3);
- return ;
- }
-
- Log_Debug("MMVirt", "Clearing out address space 0x%x from 0x%x", CR3, *gpPageCR3);
-
- GET_TEMP_MAPPING(CR3);
- INVLPG( gaTmpDir );
-
- for( i = 0; i < 1024; i ++ )
- {
- Uint32 *table = &gaTmpTable[i*1024];
- if( !(gaTmpDir[i] & PF_PRESENT) )
- continue ;
-
- INVLPG( table );
-
- if( i < 768 || (i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) )
- {
- for( j = 0; j < 1024; j ++ )
- {
- if( !(table[j] & 1) )
- continue;
- MM_DerefPhys( table[j] & ~0xFFF );
- }
- }
-
- if( i != (PAGE_TABLE_ADDR >> 22) )
- {
- MM_DerefPhys( gaTmpDir[i] & ~0xFFF );
- }
- }
-
-
- MM_DerefPhys( CR3 );
-
- REL_TEMP_MAPPING();
-}
-
-/**
- * \fn tPAddr MM_Clone(void)
- * \brief Clone the current address space
- */
-tPAddr MM_Clone(int bNoUserCopy)
-{
- Uint i, j;
- tPAddr ret;
- Uint page = 0;
- tVAddr kStackBase = Proc_GetCurThread()->KernelStack - MM_KERNEL_STACK_SIZE;
- void *tmp;
-
- // Create Directory Table
- ret = MM_AllocPhys();
- if( ret == 0 ) {
- return 0;
- }
-
- // Map
- GET_TEMP_MAPPING( ret );
- INVLPG( gaTmpDir );
- memsetd( gaTmpDir, 0, 1024 );
-
- if( Threads_GetPID() != 0 && !bNoUserCopy )
- {
- // Copy Tables
- for( i = 0; i < 768; i ++)
- {
- // Check if table is allocated
- if( !(gaPageDir[i] & PF_PRESENT) ) {
- gaTmpDir[i] = 0;
- page += 1024;
- continue;
- }
-
- // Allocate new table
- gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7);
- INVLPG( &gaTmpTable[page] );
- // Fill
- for( j = 0; j < 1024; j ++, page++ )
- {
- if( !(gaPageTable[page] & PF_PRESENT) ) {
- gaTmpTable[page] = 0;
- continue;
- }
-
- // Refrence old page
- MM_RefPhys( gaPageTable[page] & ~0xFFF );
- // Add to new table
- if(gaPageTable[page] & PF_WRITE) {
- gaTmpTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
- gaPageTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
- INVLPG( page << 12 );
- }
- else
- gaTmpTable[page] = gaPageTable[page];
- }
- }
- }
-
- // Map in kernel tables (and make fractal mapping)
- for( i = 768; i < 1024; i ++ )
- {
- // Fractal
- if( i == (PAGE_TABLE_ADDR >> 22) ) {
- gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gpTmpCR3;
- continue;
- }
- if( i == (TMP_TABLE_ADDR >> 22) ) {
- gaTmpDir[ TMP_TABLE_ADDR >> 22 ] = 0;
- continue ;
- }
-
- if( gaPageDir[i] == 0 ) {
- gaTmpDir[i] = 0;
- continue;
- }
-
- //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]);
- MM_RefPhys( gaPageDir[i] & ~0xFFF );
- gaTmpDir[i] = gaPageDir[i];
- }
-
- // Allocate kernel stack
- for(i = MM_KERNEL_STACKS >> 22; i < MM_KERNEL_STACKS_END >> 22; i ++ )
- {
- // Check if directory is allocated
- if( (gaPageDir[i] & 1) == 0 ) {
- gaTmpDir[i] = 0;
- continue;
- }
-
- // We don't care about other kernel stacks, just the current one
- if( i != kStackBase >> 22 ) {
- MM_DerefPhys( gaPageDir[i] & ~0xFFF );
- gaTmpDir[i] = 0;
- continue;
- }
-
- // Create a copy
- gaTmpDir[i] = MM_AllocPhys() | 3;
- INVLPG( &gaTmpTable[i*1024] );
- for( j = 0; j < 1024; j ++ )
- {
- // Is the page allocated? If not, skip
- if( !(gaPageTable[i*1024+j] & 1) ) {
- gaTmpTable[i*1024+j] = 0;
- continue;
- }
-
- // We don't care about other kernel stacks
- if( ((i*1024+j)*4096 & ~(MM_KERNEL_STACK_SIZE-1)) != kStackBase ) {
- gaTmpTable[i*1024+j] = 0;
- continue;
- }
-
- // Allocate page
- gaTmpTable[i*1024+j] = MM_AllocPhys() | 3;
-
- MM_RefPhys( gaTmpTable[i*1024+j] & ~0xFFF );
-
- tmp = (void *) MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF );
- memcpy( tmp, (void *)( (i*1024+j)*0x1000 ), 0x1000 );
- MM_FreeTemp( (Uint)tmp );
- }
- }
-
- REL_TEMP_MAPPING();
-
- //LEAVE('x', ret);
- return ret;
-}
-
-/**
- * \fn tVAddr MM_NewKStack(void)
- * \brief Create a new kernel stack
- */
-tVAddr MM_NewKStack(void)
-{
- tVAddr base;
- Uint i;
- for(base = MM_KERNEL_STACKS; base < MM_KERNEL_STACKS_END; base += MM_KERNEL_STACK_SIZE)
- {
- // Check if space is free
- if(MM_GetPhysAddr(base) != 0) continue;
- // Allocate
- //for(i = MM_KERNEL_STACK_SIZE; i -= 0x1000 ; )
- for(i = 0; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
- {
- if( MM_Allocate(base+i) == 0 )
- {
- // On error, print a warning and return error
- Warning("MM_NewKStack - Out of memory");
- // - Clean up
- //for( i += 0x1000 ; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
- // MM_Deallocate(base+i);
- return 0;
- }
- }
- // Success
-// Log("MM_NewKStack - Allocated %p", base + MM_KERNEL_STACK_SIZE);
- return base+MM_KERNEL_STACK_SIZE;
- }
- // No stacks left
- Log_Warning("MMVirt", "MM_NewKStack - No address space left");
- return 0;
-}
-
-/**
- * \fn tVAddr MM_NewWorkerStack()
- * \brief Creates a new worker stack
- */
-tVAddr MM_NewWorkerStack(Uint *StackContents, size_t ContentsSize)
-{
- Uint base, addr;
- tVAddr tmpPage;
- tPAddr page;
-
- // TODO: Thread safety
- // Find a free worker stack address
- for(base = giLastUsedWorker; base < NUM_WORKER_STACKS; base++)
- {
- // Used block
- if( gWorkerStacks[base/32] == -1 ) {
- base += 31; base &= ~31;
- base --; // Counteracted by the base++
- continue;
- }
- // Used stack
- if( gWorkerStacks[base/32] & (1 << base) ) {
- continue;
- }
- break;
- }
- if(base >= NUM_WORKER_STACKS) {
- Warning("Uh-oh! Out of worker stacks");
- return 0;
- }
-
- // It's ours now!
- gWorkerStacks[base/32] |= (1 << base);
- // Make life easier for later calls
- giLastUsedWorker = base;
- // We have one
- base = WORKER_STACKS + base * WORKER_STACK_SIZE;
- //Log(" MM_NewWorkerStack: base = 0x%x", base);
-
- // Set the temp fractals to TID0's address space
- GET_TEMP_MAPPING( ((Uint)gaInitPageDir - KERNEL_BASE) );
- INVLPG( gaTmpDir );
-
- // Check if the directory is mapped (we are assuming that the stacks
- // will fit neatly in a directory)
- //Log(" MM_NewWorkerStack: gaTmpDir[ 0x%x ] = 0x%x", base>>22, gaTmpDir[ base >> 22 ]);
- if(gaTmpDir[ base >> 22 ] == 0) {
- gaTmpDir[ base >> 22 ] = MM_AllocPhys() | 3;
- INVLPG( &gaTmpTable[ (base>>12) & ~0x3FF ] );
- }
-
- // Mapping Time!
- for( addr = 0; addr < WORKER_STACK_SIZE; addr += 0x1000 )
- {
- page = MM_AllocPhys();
- gaTmpTable[ (base + addr) >> 12 ] = page | 3;
- }
-
- // Release temporary fractal
- REL_TEMP_MAPPING();
-
- // NOTE: Max of 1 page
- // `page` is the last allocated page from the previious for loop
- tmpPage = MM_MapTemp( page );
- memcpy( (void*)( tmpPage + (0x1000 - ContentsSize) ), StackContents, ContentsSize);
- MM_FreeTemp(tmpPage);
-
- //Log("MM_NewWorkerStack: RETURN 0x%x", base);
- return base + WORKER_STACK_SIZE;
-}
-
-/**
- * \fn void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
- * \brief Sets the flags on a page
- */
-void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
-{
- tTabEnt *ent;
- if( !(gaPageDir[VAddr >> 22] & 1) ) return ;
- if( !(gaPageTable[VAddr >> 12] & 1) ) return ;
-
- ent = &gaPageTable[VAddr >> 12];
-
- // Read-Only
- if( Mask & MM_PFLAG_RO )
- {
- if( Flags & MM_PFLAG_RO ) {
- *ent &= ~PF_WRITE;
- }
- else {
- gaPageDir[VAddr >> 22] |= PF_WRITE;
- *ent |= PF_WRITE;
- }
- }
-
- // Kernel
- if( Mask & MM_PFLAG_KERNEL )
- {
- if( Flags & MM_PFLAG_KERNEL ) {
- *ent &= ~PF_USER;
- }
- else {
- gaPageDir[VAddr >> 22] |= PF_USER;
- *ent |= PF_USER;
- }
- }
-
- // Copy-On-Write
- if( Mask & MM_PFLAG_COW )
- {
- if( Flags & MM_PFLAG_COW ) {
- *ent &= ~PF_WRITE;
- *ent |= PF_COW;
- }
- else {
- *ent &= ~PF_COW;
- *ent |= PF_WRITE;
- }
- }
-
- //Log("MM_SetFlags: *ent = 0x%08x, gaPageDir[%i] = 0x%08x",
- // *ent, VAddr >> 22, gaPageDir[VAddr >> 22]);
-}
-
-/**
- * \brief Get the flags on a page
- */
-Uint MM_GetFlags(tVAddr VAddr)
-{
- tTabEnt *ent;
- Uint ret = 0;
-
- // Validity Check
- if( !(gaPageDir[VAddr >> 22] & 1) ) return 0;
- if( !(gaPageTable[VAddr >> 12] & 1) ) return 0;
-
- ent = &gaPageTable[VAddr >> 12];
-
- // Read-Only
- if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO;
- // Kernel
- if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
- // Copy-On-Write
- if( *ent & PF_COW ) ret |= MM_PFLAG_COW;
-
- return ret;
-}
-
-/**
- * \brief Check if the provided buffer is valid
- * \return Boolean valid
- */
-int MM_IsValidBuffer(tVAddr Addr, size_t Size)
-{
- int bIsUser;
- int dir, tab;
-
- Size += Addr & (PAGE_SIZE-1);
- Addr &= ~(PAGE_SIZE-1);
-
- dir = Addr >> 22;
- tab = Addr >> 12;
-
-// Debug("Addr = %p, Size = 0x%x, dir = %i, tab = %i", Addr, Size, dir, tab);
-
- if( !(gaPageDir[dir] & 1) ) return 0;
- if( !(gaPageTable[tab] & 1) ) return 0;
-
- bIsUser = !!(gaPageTable[tab] & PF_USER);
-
- while( Size >= PAGE_SIZE )
- {
- if( (tab & 1023) == 0 )
- {
- dir ++;
- if( !(gaPageDir[dir] & 1) ) return 0;
- }
-
- if( !(gaPageTable[tab] & 1) ) return 0;
- if( bIsUser && !(gaPageTable[tab] & PF_USER) ) return 0;
-
- tab ++;
- Size -= PAGE_SIZE;
- }
- return 1;
-}
-
-/**
- * \fn tPAddr MM_DuplicatePage(tVAddr VAddr)
- * \brief Duplicates a virtual page to a physical one
- */
-tPAddr MM_DuplicatePage(tVAddr VAddr)
-{
- tPAddr ret;
- Uint temp;
- int wasRO = 0;
-
- //ENTER("xVAddr", VAddr);
-
- // Check if mapped
- if( !(gaPageDir [VAddr >> 22] & PF_PRESENT) ) return 0;
- if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) ) return 0;
-
- // Page Align
- VAddr &= ~0xFFF;
-
- // Allocate new page
- ret = MM_AllocPhys();
- if( !ret ) {
- return 0;
- }
-
- // Write-lock the page (to keep data constistent), saving its R/W state
- wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1);
- gaPageTable[VAddr >> 12] &= ~PF_WRITE;
- INVLPG( VAddr );
-
- // Copy Data
- temp = MM_MapTemp(ret);
- memcpy( (void*)temp, (void*)VAddr, 0x1000 );
- MM_FreeTemp(temp);
-
- // Restore Writeable status
- if(!wasRO) gaPageTable[VAddr >> 12] |= PF_WRITE;
- INVLPG(VAddr);
-
- //LEAVE('X', ret);
- return ret;
-}
-
-/**
- * \fn Uint MM_MapTemp(tPAddr PAddr)
- * \brief Create a temporary memory mapping
- * \todo Show Luigi Barone (C Lecturer) and see what he thinks
- */
-tVAddr MM_MapTemp(tPAddr PAddr)
-{
- int i;
-
- //ENTER("XPAddr", PAddr);
-
- PAddr &= ~0xFFF;
-
- //LOG("glTempMappings = %i", glTempMappings);
-
- for(;;)
- {
- Mutex_Acquire( &glTempMappings );
-
- for( i = 0; i < NUM_TEMP_PAGES; i ++ )
- {
- // Check if page used
- if(gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] & 1) continue;
- // Mark as used
- gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] = PAddr | 3;
- INVLPG( TEMP_MAP_ADDR + (i << 12) );
- //LEAVE('p', TEMP_MAP_ADDR + (i << 12));
- Mutex_Release( &glTempMappings );
- return TEMP_MAP_ADDR + (i << 12);
- }
- Mutex_Release( &glTempMappings );
- Threads_Yield(); // TODO: Use a sleep queue here instead
- }
-}
-
-/**
- * \fn void MM_FreeTemp(tVAddr PAddr)
- * \brief Free's a temp mapping
- */
-void MM_FreeTemp(tVAddr VAddr)
-{
- int i = VAddr >> 12;
- //ENTER("xVAddr", VAddr);
-
- if(i >= (TEMP_MAP_ADDR >> 12))
- gaPageTable[ i ] = 0;
-
- //LEAVE('-');
-}
-
-/**
- * \fn tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
- * \brief Allocates a contigous number of pages
- */
-tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
-{
- int i, j;
-
- PAddr &= ~0xFFF;
-
- // Scan List
- for( i = 0; i < NUM_HW_PAGES; i ++ )
- {
- // Check if addr used
- if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 )
- continue;
-
- // Check possible region
- for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ )
- {
- // If there is an allocated page in the region we are testing, break
- if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 ) break;
- }
- // Is it all free?
- if( j == Number )
- {
- // Allocate
- for( j = 0; j < Number; j++ ) {
- MM_RefPhys( PAddr + (j<<12) );
- gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3;
- }
- return HW_MAP_ADDR + (i<<12);
- }
- }
- // If we don't find any, return NULL
- return 0;
-}
-
-/**
- * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
- * \brief Allocates DMA physical memory
- * \param Pages Number of pages required
- * \param MaxBits Maximum number of bits the physical address can have
- * \param PhysAddr Pointer to the location to place the physical address allocated
- * \return Virtual address allocate
- */
-tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
-{
- tPAddr maxCheck = (1 << MaxBits);
- tPAddr phys;
- tVAddr ret;
-
- ENTER("iPages iMaxBits pPhysAddr", Pages, MaxBits, PhysAddr);
-
- // Sanity Check
- if(MaxBits < 12 || !PhysAddr) {
- LEAVE('i', 0);
- return 0;
- }
-
- // Bound
- if(MaxBits >= PHYS_BITS) maxCheck = -1;
-
- // Fast Allocate
- if(Pages == 1 && MaxBits >= PHYS_BITS)
- {
- phys = MM_AllocPhys();
- if( !phys ) {
- *PhysAddr = 0;
- LEAVE_RET('i', 0);
- }
- *PhysAddr = phys;
- ret = MM_MapHWPages(phys, 1);
- if(ret == 0) {
- MM_DerefPhys(phys);
- LEAVE('i', 0);
- return 0;
- }
- LEAVE('x', ret);
- return ret;
- }
-
- // Slow Allocate
- phys = MM_AllocPhysRange(Pages, MaxBits);
- // - Was it allocated?
- if(phys == 0) {
- LEAVE('i', 0);
- return 0;
- }
-
- // Allocated successfully, now map
- ret = MM_MapHWPages(phys, Pages);
- if( ret == 0 ) {
- // If it didn't map, free then return 0
- for(;Pages--;phys+=0x1000)
- MM_DerefPhys(phys);
- LEAVE('i', 0);
- return 0;
- }
-
- *PhysAddr = phys;
- LEAVE('x', ret);
- return ret;
-}
-
-/**
- * \fn void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
- * \brief Unmap a hardware page
- */
-void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
-{
- int i, j;
-
- //Log_Debug("VirtMem", "MM_UnmapHWPages: (VAddr=0x%08x, Number=%i)", VAddr, Number);
-
- // Sanity Check
- if(VAddr < HW_MAP_ADDR || VAddr+Number*0x1000 > HW_MAP_MAX) return;
-
- i = VAddr >> 12;
-
- Mutex_Acquire( &glTempMappings ); // Temp and HW share a directory, so they share a lock
-
- for( j = 0; j < Number; j++ )
- {
- MM_DerefPhys( gaPageTable[ i + j ] & ~0xFFF );
- gaPageTable[ i + j ] = 0;
- }
-
- Mutex_Release( &glTempMappings );
-}
-
+++ /dev/null
-/*\r
- * Acess2\r
- * arch/x86/pci.h - x86 PCI Bus Access\r
- */\r
-#define DEBUG 0\r
-#include <acess.h>\r
-#include <drv_pci_int.h>\r
-\r
-// === CODE ===\r
-Uint32 PCI_CfgReadDWord(Uint32 Address)\r
-{\r
- Address |= 0x80000000;\r
- outd(0xCF8, Address);\r
- return ind(0xCFC);\r
-}\r
-\r
-void PCI_CfgWriteDWord(Uint32 Address, Uint32 Data)\r
-{\r
- Address |= 0x80000000;\r
- outd(0xCF8, Address);\r
- outd(0xCFC, Data);\r
-}\r
+++ /dev/null
-; AcessOS Microkernel Version
-; Start.asm
-
-[bits 32]
-
-%define SAVEFLAG_FPU 0x1
-
-KERNEL_BASE equ 0xC0000000
-
-KSTACK_USERSTATE_SIZE equ (4+8+1+5)*4 ; SRegs, GPRegs, CPU, IRET
-
-[section .text]
-
-[global NewTaskHeader]
-NewTaskHeader:
- mov eax, [esp]
- mov dr0, eax
-
- mov eax, [esp+4]
- add esp, 12 ; Thread, Function, Arg Count
- call eax
-
- push eax ; Ret val
- push 0 ; 0 = This Thread
- call Threads_Exit
-
-[extern MM_Clone]
-[global Proc_CloneInt]
-Proc_CloneInt:
- pusha
- ; Save RSP
- mov eax, [esp+0x20+4]
- mov [eax], esp
- push DWORD [esp+0x20+12]
- call MM_Clone
- add esp, 4
- ; Save CR3
- mov esi, [esp+0x20+8]
- mov [esi], eax
- ; Undo the pusha
- add esp, 0x20
- mov eax, .newTask
- ret
-.newTask:
- popa
- xor eax, eax
- ret
-
-[global SwitchTasks]
-; + 4 = New RSP
-; + 8 = Old RSP save loc
-; +12 = New RIP
-; +16 = Old RIP save loc
-; +20 = CR3
-SwitchTasks:
- pusha
-
- ; Old IP
- mov eax, [esp+0x20+16]
- test eax, eax
- jz .nosave
- mov DWORD [eax], .restore
- ; Old SP
- mov eax, [esp+0x20+8]
- mov [eax], esp
-
-.nosave:
- mov ecx, [esp+0x20+12] ; New IP
- mov eax, [esp+0x20+20] ; New CR3
- mov esp, [esp+0x20+ 4] ; New SP
-
- test eax, eax
- jz .setState
- mov cr3, eax
- invlpg [esp]
- invlpg [esp+0x1000]
-.setState:
- jmp ecx
-
-.restore:
- popa
- xor eax, eax
- ret
-
-[global Proc_InitialiseSSE]
-Proc_InitialiseSSE:
- mov eax, cr4
- or eax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT
- mov cr4, eax
- mov eax, cr0
- and ax, ~(1 << 2) ; Clear EM
- or eax, (1 << 1) ; Set MP
- mov eax, cr0
- ret
-[global Proc_DisableSSE]
-Proc_DisableSSE:
- mov eax, cr0
- or ax, 1 << 3 ; Set TS
- mov cr0, eax
- ret
-[global Proc_EnableSSE]
-Proc_EnableSSE:
- mov eax, cr0
- and ax, ~(1 << 3) ; Clear TS
- mov cr0, eax
- ret
-
-[global Proc_SaveSSE]
-Proc_SaveSSE:
- mov eax, [esp+4]
- fxsave [eax]
- ret
-[global Proc_RestoreSSE]
-Proc_RestoreSSE:
- mov eax, [esp+4]
- fxrstor [eax]
- ret
-
-%if USE_MP
-[extern giMP_TimerCount]
-[extern gpMP_LocalAPIC]
-[extern Isr240.jmp]
-[global SetAPICTimerCount]
-SetAPICTimerCount:
- pusha
- push ds
- push es
- push fs
- push gs
-
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- mov eax, [gpMP_LocalAPIC]
- mov ecx, [eax+0x320]
- test ecx, 0x00010000
- jz .setTime
- mov DWORD [eax+0x380], 0xFFFFFFFF ; Set Initial Count
- mov DWORD [eax+0x320], 0x000000F0 ; Enable the timer on IVT#0xEF (One Shot)
- jmp .ret
-
-.setTime:
- ; Get Timer Count
- mov ecx, 0xFFFFFFFF
- sub ecx, [eax+0x390]
- mov DWORD [giMP_TimerCount], ecx
- ; Disable APIC Timer
- mov DWORD [eax+0x320], 0x000100EF
- mov DWORD [eax+0x380], 0
-
- ; Update Timer IRQ to the IRQ code
- mov eax, SchedulerBase
- sub eax, Isr240.jmp+5
- mov DWORD [Isr240.jmp+1], eax
-
- ;xchg bx, bx ; MAGIC BREAK
-.ret:
- mov dx, 0x20
- mov al, 0x20
- out dx, al ; ACK IRQ
- pop gs
- pop fs
- pop es
- pop ds
- popa
- add esp, 8 ; CPU ID / Error Code
- iret
-%endif
-; --------------
-; Task Scheduler
-; --------------
-[extern Proc_Scheduler]
-[global SchedulerBase]
-SchedulerBase:
- pusha
- push ds
- push es
- push fs
- push gs
-
- pushf
- and BYTE [esp+1], 0xFE ; Clear Trap Flag
- popf
-
- mov eax, dr0
- push eax ; Debug Register 0, Current Thread
-
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- %if USE_MP
- call GetCPUNum
- mov ebx, eax
- push eax ; Push as argument
- %else
- push 0
- %endif
-
- call Proc_Scheduler
-[global scheduler_return]
-scheduler_return: ; Used by some hackery in Proc_DumpThreadCPUState
-
- add esp, 4 ; Remove CPU Number (thread is poped later)
-
- %if USE_MP
- test ebx, ebx
- jnz .sendEOI
- %endif
-
- mov al, 0x20
- out 0x20, al ; ACK IRQ
- %if USE_MP
- jmp .ret
-
-.sendEOI:
- mov eax, DWORD [gpMP_LocalAPIC]
- mov DWORD [eax+0x0B0], 0
- %endif
-.ret:
- pop eax ; Debug Register 0, Current Thread
- mov dr0, eax
-
- pop gs
- pop fs
- pop es
- pop ds
-
- popa
- add esp, 4*2 ; CPU ID + Dummy error code
- ; No Error code / int num
- iret
-
-[extern Proc_Clone]
-[extern Threads_Exit]
-[global SpawnTask]
-SpawnTask:
- ; Call Proc_Clone with Flags=0
- xor eax, eax
-; push eax
- push eax
- call Proc_Clone
- add esp, 8 ; Remove arguments from stack
-
- test eax, eax
- jnz .parent
-
- ; In child, so now set up stack frame
- mov ebx, [esp+4] ; Child Function
- mov edx, [esp+8] ; Argument
- ; Child Function
- push edx ; Argument
- call ebx ; Function
- ; Kill thread once done
- push eax ; Exit Code
- push 0 ; Kill this thread
- call Threads_Exit ; Kill Thread
-
-.parent:
- ret
-
-; void Proc_ReturnToUser(void *Method, Uint Parameter, tVAddr KernelStack)
-; Calls a user fault handler
-;
-[global Proc_ReturnToUser]
-[extern Proc_GetCurThread]
-Proc_ReturnToUser:
- push ebp
- mov ebp, esp
- ; [EBP+8]: handler to use
- ; [EBP+12]: parameter
- ; [EBP+16]: kernel stack top
-
- ; Get kernel stack
- mov eax, [ebp+16]
- sub eax, KSTACK_USERSTATE_SIZE
-
- ;
- ; NOTE: This can cause corruption if the signal happens while the user
- ; has called a kernel operation.
- ; Good thing this can only be called on a user fault.
- ;
-
- ; Validate user ESP
- ; - Page Table
- mov edx, [eax+KSTACK_USERSTATE_SIZE-12] ; User ESP is at top of kstack - 3*4
- mov ecx, edx
- shr ecx, 22
- test BYTE [0xFC3F0000+ecx*4], 1
- jnz .justKillIt
- ; - Page
- mov ecx, edx
- shr ecx, 12
- test BYTE [0xFC000000+ecx*4], 1
- jnz .justKillIt
- ; Adjust
- sub edx, 8
- ; - Page Table
- mov ecx, edx
- shr ecx, 22
- test BYTE [0xFC3F0000+ecx*4], 1
- jnz .justKillIt
- ; - Page
- mov ecx, edx
- shr ecx, 12
- test BYTE [0xFC000000+ecx*4], 1
- jnz .justKillIt
-
- ; Get and alter User SP
- mov edi, edx
- mov edx, [ebp+12] ; Get parameter
- mov [edi+4], edx ; save to user stack
- mov [edi], DWORD User_Syscall_RetAndExit ; Return Address
-
- ; Restore Segment Registers
- mov ax, 0x23
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- push 0x23 ; SS
- push edi ; ESP
- push 0x202 ; EFLAGS (IP and Rsvd)
- push 0x1B ; CS
- mov eax, [ebp+8] ; Method to call
- push eax ; EIP
-
- iret
-
- ; Just kill the bleeding thing
- ; (I know it calls int 0xAC in kernel mode, but meh)
-.justKillIt:
- xor eax, eax
- xor ebx, ebx
- dec ebx ; EBX = -1
- int 0xAC
-
-[global GetCPUNum]
-GetCPUNum: ; TODO: Store in debug registers
- mov eax, dr1
- ret
-
-[extern GetEIP]
-[global GetEIP_Sched]
-[global GetEIP_Sched_ret]
-GetEIP_Sched_ret equ GetEIP_Sched.ret
-GetEIP_Sched:
- call GetEIP
-GetEIP_Sched.ret:
- ret
-
-; Usermode code exported by the kernel
-[section .usertext]
-; Export a place for the user to jump to to call a syscall
-; - Allows the kernel to change the method easily
-User_Syscall:
- xchg bx, bx ; MAGIC BREAKPOINT
- int 0xAC
-
-; A place to return to and exit
-User_Syscall_RetAndExit:
- push eax
- call User_Syscall_Exit
-User_Syscall_Exit:
- xor eax, eax
- mov ebx, [esp+4]
- int 0xAC
-
-; vim: ft=nasm ts=8
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * proc.c
- */
-#include <acess.h>
-#include <threads.h>
-#include <proc.h>
-#include <desctab.h>
-#include <mm_virt.h>
-#include <errno.h>
-#if USE_MP
-# include <mp.h>
-#endif
-#include <hal_proc.h>
-#include <arch_int.h>
-
-// === FLAGS ===
-#define DEBUG_TRACE_SWITCH 0
-#define DEBUG_DISABLE_DOUBLEFAULT 1
-#define DEBUG_VERY_SLOW_PERIOD 0
-
-// === CONSTANTS ===
-// Base is 1193182
-#define TIMER_BASE 1193182
-#if DEBUG_VERY_SLOW_PERIOD
-# define TIMER_DIVISOR 1193 //~10Hz switch, with 10 quantum = 1s per thread
-#else
-# define TIMER_DIVISOR 11932 //~100Hz
-#endif
-
-// === TYPES ===
-typedef struct sCPU
-{
- Uint8 APICID;
- Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
- Uint16 Resvd;
- tThread *Current;
-} tCPU;
-
-// === IMPORTS ===
-extern tGDT gGDT[];
-extern tIDT gIDT[];
-extern void APWait(void); // 16-bit AP pause code
-extern void APStartup(void); // 16-bit AP startup code
-extern Uint GetEIP(void); // start.asm
-extern Uint GetEIP_Sched(void); // proc.asm
-extern void NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...); // Actually takes cdecl args
-extern Uint Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone);
-extern Uint32 gaInitPageDir[1024]; // start.asm
-extern char Kernel_Stack_Top[];
-extern int giNumCPUs;
-extern int giNextTID;
-extern tThread gThreadZero;
-extern tProcess gProcessZero;
-extern void Isr8(void); // Double Fault
-extern void Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack);
-extern char scheduler_return[]; // Return address in SchedulerBase
-extern char IRQCommon[]; // Common IRQ handler code
-extern char IRQCommon_handled[]; // IRQCommon call return location
-extern char GetEIP_Sched_ret[]; // GetEIP call return location
-extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3);
-extern void Proc_InitialiseSSE(void);
-extern void Proc_SaveSSE(Uint DestPtr);
-extern void Proc_DisableSSE(void);
-
-// === PROTOTYPES ===
-//void ArchThreads_Init(void);
-#if USE_MP
-void MP_StartAP(int CPU);
-void MP_SendIPIVector(int CPU, Uint8 Vector);
-void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
-#endif
-void Proc_IdleThread(void *Ptr);
-//void Proc_Start(void);
-//tThread *Proc_GetCurThread(void);
-void Proc_ChangeStack(void);
-// int Proc_NewKThread(void (*Fcn)(void*), void *Data);
-// int Proc_Clone(Uint *Err, Uint Flags);
-Uint Proc_MakeUserStack(void);
-//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
-void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN;
- int Proc_Demote(Uint *Err, int Dest, tRegs *Regs);
-//void Proc_CallFaultHandler(tThread *Thread);
-//void Proc_DumpThreadCPUState(tThread *Thread);
-void Proc_Scheduler(int CPU);
-
-// === GLOBALS ===
-// --- Multiprocessing ---
-#if USE_MP
-volatile int giNumInitingCPUs = 0;
-tMPInfo *gMPFloatPtr = NULL;
-volatile Uint32 giMP_TimerCount; // Start Count for Local APIC Timer
-tAPIC *gpMP_LocalAPIC = NULL;
-Uint8 gaAPIC_to_CPU[256] = {0};
- int giProc_BootProcessorID = 0;
-tTSS gaTSSs[MAX_CPUS]; // TSS Array
-#endif
-tCPU gaCPUs[MAX_CPUS] = {
- {.Current = &gThreadZero}
- };
-tTSS *gTSSs = NULL; // Pointer to TSS array
-tTSS gTSS0 = {0};
-// --- Error Recovery ---
-char gaDoubleFaultStack[1024] __attribute__ ((section(".padata")));
-tTSS gDoubleFault_TSS = {
- .ESP0 = (Uint)&gaDoubleFaultStack[1024],
- .SS0 = 0x10,
- .CR3 = (Uint)gaInitPageDir - KERNEL_BASE,
- .EIP = (Uint)Isr8,
- .ESP = (Uint)&gaDoubleFaultStack[1024],
- .CS = 0x08, .SS = 0x10,
- .DS = 0x10, .ES = 0x10,
- .FS = 0x10, .GS = 0x10,
-};
-
-// === CODE ===
-/**
- * \fn void ArchThreads_Init(void)
- * \brief Starts the process scheduler
- */
-void ArchThreads_Init(void)
-{
- Uint pos = 0;
-
- #if USE_MP
- tMPTable *mptable;
-
- // Mark BSP as active
- gaCPUs[0].State = 2;
-
- // -- Initialise Multiprocessing
- // Find MP Floating Table
- // - EBDA/Last 1Kib (640KiB)
- for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) {
- if( *(Uint*)(pos) == MPPTR_IDENT ) {
- Log("Possible %p", pos);
- if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
- gMPFloatPtr = (void*)pos;
- break;
- }
- }
- // - Last KiB (512KiB base mem)
- if(!gMPFloatPtr) {
- for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) {
- if( *(Uint*)(pos) == MPPTR_IDENT ) {
- Log("Possible %p", pos);
- if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
- gMPFloatPtr = (void*)pos;
- break;
- }
- }
- }
- // - BIOS ROM
- if(!gMPFloatPtr) {
- for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) {
- if( *(Uint*)(pos) == MPPTR_IDENT ) {
- Log("Possible %p", pos);
- if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
- gMPFloatPtr = (void*)pos;
- break;
- }
- }
- }
-
- // If the MP Table Exists, parse it
- if(gMPFloatPtr)
- {
- int i;
- tMPTable_Ent *ents;
- #if DUMP_MP_TABLE
- Log("gMPFloatPtr = %p", gMPFloatPtr);
- Log("*gMPFloatPtr = {");
- Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig);
- Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig);
- Log("\t.Length = 0x%02x", gMPFloatPtr->Length);
- Log("\t.Version = 0x%02x", gMPFloatPtr->Version);
- Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum);
- Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]",
- gMPFloatPtr->Features[0], gMPFloatPtr->Features[1],
- gMPFloatPtr->Features[2], gMPFloatPtr->Features[3],
- gMPFloatPtr->Features[4]
- );
- Log("}");
- #endif
-
- mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig );
- #if DUMP_MP_TABLE
- Log("mptable = %p", mptable);
- Log("*mptable = {");
- Log("\t.Sig = 0x%08x", mptable->Sig);
- Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength);
- Log("\t.SpecRev = 0x%02x", mptable->SpecRev);
- Log("\t.Checksum = 0x%02x", mptable->Checksum);
- Log("\t.OEMID = '%8c'", mptable->OemID);
- Log("\t.ProductID = '%8c'", mptable->ProductID);
- Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr);
- Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize);
- Log("\t.EntryCount = 0x%04x", mptable->EntryCount);
- Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap);
- Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen);
- Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum);
- Log("}");
- #endif
-
- gpMP_LocalAPIC = (void*)MM_MapHWPages(mptable->LocalAPICMemMap, 1);
-
- ents = mptable->Entries;
- giNumCPUs = 0;
-
- for( i = 0; i < mptable->EntryCount; i ++ )
- {
- int entSize = 0;
- switch( ents->Type )
- {
- case 0: // Processor
- entSize = 20;
- #if DUMP_MP_TABLE
- Log("%i: Processor", i);
- Log("\t.APICID = %i", ents->Proc.APICID);
- Log("\t.APICVer = 0x%02x", ents->Proc.APICVer);
- Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags);
- Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature);
- Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags);
- #endif
-
- if( !(ents->Proc.CPUFlags & 1) ) {
- Log("DISABLED");
- break;
- }
-
- // Check if there is too many processors
- if(giNumCPUs >= MAX_CPUS) {
- giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped
- break;
- }
-
- // Initialise CPU Info
- gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs;
- gaCPUs[giNumCPUs].APICID = ents->Proc.APICID;
- gaCPUs[giNumCPUs].State = 0;
- giNumCPUs ++;
-
- // Set BSP Variable
- if( ents->Proc.CPUFlags & 2 ) {
- giProc_BootProcessorID = giNumCPUs-1;
- }
-
- break;
-
- #if DUMP_MP_TABLE >= 2
- case 1: // Bus
- entSize = 8;
- Log("%i: Bus", i);
- Log("\t.ID = %i", ents->Bus.ID);
- Log("\t.TypeString = '%6C'", ents->Bus.TypeString);
- break;
- case 2: // I/O APIC
- entSize = 8;
- Log("%i: I/O APIC", i);
- Log("\t.ID = %i", ents->IOAPIC.ID);
- Log("\t.Version = 0x%02x", ents->IOAPIC.Version);
- Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags);
- Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr);
- break;
- case 3: // I/O Interrupt Assignment
- entSize = 8;
- Log("%i: I/O Interrupt Assignment", i);
- Log("\t.IntType = %i", ents->IOInt.IntType);
- Log("\t.Flags = 0x%04x", ents->IOInt.Flags);
- Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID);
- Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ);
- Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID);
- Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ);
- break;
- case 4: // Local Interrupt Assignment
- entSize = 8;
- Log("%i: Local Interrupt Assignment", i);
- Log("\t.IntType = %i", ents->LocalInt.IntType);
- Log("\t.Flags = 0x%04x", ents->LocalInt.Flags);
- Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID);
- Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ);
- Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID);
- Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ);
- break;
- default:
- Log("%i: Unknown (%i)", i, ents->Type);
- break;
- #endif
- }
- ents = (void*)( (Uint)ents + entSize );
- }
-
- if( giNumCPUs > MAX_CPUS ) {
- Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS);
- giNumCPUs = MAX_CPUS;
- }
- gTSSs = gaTSSs;
- }
- else {
- Log("No MP Table was found, assuming uniprocessor\n");
- giNumCPUs = 1;
- gTSSs = &gTSS0;
- }
- #else
- giNumCPUs = 1;
- gTSSs = &gTSS0;
- #endif
-
- #if !DEBUG_DISABLE_DOUBLEFAULT
- // Initialise Double Fault TSS
- gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF;
- gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16;
- gGDT[5].BaseHi = (Uint)&gDoubleFault_TSS >> 24;
-
- // Set double fault IDT to use the new TSS
- gIDT[8].OffsetLo = 0;
- gIDT[8].CS = 5<<3;
- gIDT[8].Flags = 0x8500;
- gIDT[8].OffsetHi = 0;
- #endif
-
- // Set timer frequency
- outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
- outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
- outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte
-
- Log_Debug("Proc", "PIT Frequency %i.%03i Hz",
- TIMER_BASE/TIMER_DIVISOR,
- ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000
- );
-
- #if USE_MP
- // Get the count setting for APIC timer
- Log("Determining APIC Count");
- __asm__ __volatile__ ("sti");
- while( giMP_TimerCount == 0 ) __asm__ __volatile__ ("hlt");
- __asm__ __volatile__ ("cli");
- Log("APIC Count %i", giMP_TimerCount);
- {
- Uint64 freq = giMP_TimerCount;
- freq *= TIMER_BASE;
- freq /= TIMER_DIVISOR;
- if( (freq /= 1000) < 2*1000)
- Log("Bus Frequency %i KHz", freq);
- else if( (freq /= 1000) < 2*1000)
- Log("Bus Frequency %i MHz", freq);
- else if( (freq /= 1000) < 2*1000)
- Log("Bus Frequency %i GHz", freq);
- else
- Log("Bus Frequency %i THz", freq);
- }
-
- // Initialise Normal TSS(s)
- for(pos=0;pos<giNumCPUs;pos++)
- {
- #else
- pos = 0;
- #endif
- gTSSs[pos].SS0 = 0x10;
- gTSSs[pos].ESP0 = 0; // Set properly by scheduler
- gGDT[6+pos].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
- gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos]) >> 16) & 0xFFFF;
- gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
- #if USE_MP
- }
- #endif
-
- // Load the BSP's TSS
- __asm__ __volatile__ ("ltr %%ax"::"a"(0x30));
- // Set Current Thread and CPU Number in DR0 and DR1
- __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero));
- __asm__ __volatile__ ("mov %0, %%db1"::"r"(0));
-
- gaCPUs[0].Current = &gThreadZero;
- gThreadZero.CurCPU = 0;
-
- gProcessZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE;
-
- // Create Per-Process Data Block
- if( !MM_Allocate(MM_PPD_CFG) )
- {
- Panic("OOM - No space for initial Per-Process Config");
- }
-
- // Initialise SSE support
- Proc_InitialiseSSE();
-
- // Change Stacks
- Proc_ChangeStack();
-}
-
-#if USE_MP
-/**
- * \brief Start an AP
- */
-void MP_StartAP(int CPU)
-{
- Log_Log("Proc", "Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
-
- // Set location of AP startup code and mark for a warm restart
- *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
- *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
- outb(0x70, 0x0F); outb(0x71, 0x0A); // Set warm reset flag
- MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); // Init IPI
-
- // Delay
- inb(0x80); inb(0x80); inb(0x80); inb(0x80);
-
- // TODO: Use a better address, preferably registered with the MM
- // - MM_AllocDMA mabye?
- // Create a far jump
- *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA; // Far JMP
- *(Uint16*)(KERNEL_BASE|0x11001) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0); // IP
- *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF; // CS
- // Send a Startup-IPI to make the CPU execute at 0x11000 (which we
- // just filled)
- MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6); // StartupIPI
-
- giNumInitingCPUs ++;
-}
-
-void MP_SendIPIVector(int CPU, Uint8 Vector)
-{
- MP_SendIPI(gaCPUs[CPU].APICID, Vector, 0);
-}
-
-/**
- * \brief Send an Inter-Processor Interrupt
- * \param APICID Processor's Local APIC ID
- * \param Vector Argument of some kind
- * \param DeliveryMode Type of signal
- * \note 3A 10.5 "APIC/Handling Local Interrupts"
- */
-void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
-{
- Uint32 val;
-
- // Hi
- val = (Uint)APICID << 24;
-// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val);
- gpMP_LocalAPIC->ICR[1].Val = val;
- // Low (and send)
- val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
-// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val);
- gpMP_LocalAPIC->ICR[0].Val = val;
-}
-#endif
-
-void Proc_IdleThread(void *Ptr)
-{
- tCPU *cpu = &gaCPUs[GetCPUNum()];
- cpu->Current->ThreadName = strdup("Idle Thread");
- Threads_SetPriority( cpu->Current, -1 ); // Never called randomly
- cpu->Current->Quantum = 1; // 1 slice quantum
- for(;;) {
- __asm__ __volatile__ ("sti"); // Make sure interrupts are enabled
- __asm__ __volatile__ ("hlt"); // Make sure interrupts are enabled
- Proc_Reschedule();
- }
-}
-
-/**
- * \fn void Proc_Start(void)
- * \brief Start process scheduler
- */
-void Proc_Start(void)
-{
- int tid;
- #if USE_MP
- int i;
- #endif
-
- #if USE_MP
- // Start APs
- for( i = 0; i < giNumCPUs; i ++ )
- {
- if(i) gaCPUs[i].Current = NULL;
-
- // Create Idle Task
- tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[i]);
-
- // Start the AP
- if( i != giProc_BootProcessorID ) {
- MP_StartAP( i );
- }
- }
-
- // BSP still should run the current task
- gaCPUs[0].Current = &gThreadZero;
-
- // Start interrupts and wait for APs to come up
- Log_Debug("Proc", "Waiting for APs to come up");
- __asm__ __volatile__ ("sti");
- while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
- #else
- // Create Idle Task
- tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[0]);
-// gaCPUs[0].IdleThread = Threads_GetThread(tid);
-
- // Set current task
- gaCPUs[0].Current = &gThreadZero;
-
- // Start Interrupts (and hence scheduler)
- __asm__ __volatile__("sti");
- #endif
- MM_FinishVirtualInit();
-}
-
-/**
- * \fn tThread *Proc_GetCurThread(void)
- * \brief Gets the current thread
- */
-tThread *Proc_GetCurThread(void)
-{
- #if USE_MP
- return gaCPUs[ GetCPUNum() ].Current;
- #else
- return gaCPUs[ 0 ].Current;
- #endif
-}
-
-/**
- * \fn void Proc_ChangeStack(void)
- * \brief Swaps the current stack for a new one (in the proper stack reigon)
- */
-void Proc_ChangeStack(void)
-{
- Uint esp, ebp;
- Uint tmpEbp, oldEsp;
- Uint curBase, newBase;
-
- __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
- __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
-
- oldEsp = esp;
-
- // Create new KStack
- newBase = MM_NewKStack();
- // Check for errors
- if(newBase == 0) {
- Panic("What the?? Unable to allocate space for initial kernel stack");
- return;
- }
-
- curBase = (Uint)&Kernel_Stack_Top;
-
- LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
-
- // Get ESP as a used size
- esp = curBase - esp;
- LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp );
- // Copy used stack
- memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp );
- // Get ESP as an offset in the new stack
- esp = newBase - esp;
- // Adjust EBP
- ebp = newBase - (curBase - ebp);
-
- // Repair EBPs & Stack Addresses
- // Catches arguments also, but may trash stack-address-like values
- for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4)
- {
- if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase)
- *(Uint*)tmpEbp += newBase - curBase;
- }
-
- Proc_GetCurThread()->KernelStack = newBase;
-
- __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp));
- __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp));
-}
-
-void Proc_ClearProcess(tProcess *Process)
-{
- MM_ClearSpace(Process->MemState.CR3);
-}
-
-void Proc_ClearThread(tThread *Thread)
-{
- if(Thread->SavedState.SSE) {
- free(Thread->SavedState.SSE);
- Thread->SavedState.SSE = NULL;
- }
-}
-
-tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
-{
- Uint esp;
- tThread *newThread, *cur;
-
- cur = Proc_GetCurThread();
- newThread = Threads_CloneTCB(0);
- if(!newThread) return -1;
-
- // Create new KStack
- newThread->KernelStack = MM_NewKStack();
- // Check for errors
- if(newThread->KernelStack == 0) {
- free(newThread);
- return -1;
- }
-
- esp = newThread->KernelStack;
- *(Uint*)(esp-=4) = (Uint)Data; // Data (shadowed)
- *(Uint*)(esp-=4) = 1; // Number of params
- *(Uint*)(esp-=4) = (Uint)Fcn; // Function to call
- *(Uint*)(esp-=4) = (Uint)newThread; // Thread ID
-
- newThread->SavedState.ESP = esp;
- newThread->SavedState.EIP = (Uint)&NewTaskHeader;
- newThread->SavedState.SSE = NULL;
-// Log("New (KThread) %p, esp = %p", newThread->SavedState.EIP, newThread->SavedState.ESP);
-
-// MAGIC_BREAK();
- Threads_AddActive(newThread);
-
- return newThread->TID;
-}
-
-/**
- * \fn int Proc_Clone(Uint *Err, Uint Flags)
- * \brief Clone the current process
- */
-tPID Proc_Clone(Uint Flags)
-{
- tThread *newThread;
- tThread *cur = Proc_GetCurThread();
- Uint eip;
-
- // Sanity, please
- if( !(Flags & CLONE_VM) ) {
- Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead");
- return -1;
- }
-
- // New thread
- newThread = Threads_CloneTCB(Flags);
- if(!newThread) return -1;
-
- newThread->KernelStack = cur->KernelStack;
-
- // Clone state
- eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER);
- if( eip == 0 ) {
- return 0;
- }
- newThread->SavedState.EIP = eip;
- newThread->SavedState.SSE = NULL;
- newThread->SavedState.bSSEModified = 0;
-
- // Check for errors
- if( newThread->Process->MemState.CR3 == 0 ) {
- Log_Error("Proc", "Proc_Clone: MM_Clone failed");
- Threads_Delete(newThread);
- return -1;
- }
-
- // Add the new thread to the run queue
- Threads_AddActive(newThread);
- return newThread->TID;
-}
-
-/**
- * \fn int Proc_SpawnWorker(void)
- * \brief Spawns a new worker thread
- */
-int Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
-{
- tThread *new;
- Uint stack_contents[4];
-
- // Create new thread
- new = Threads_CloneThreadZero();
- if(!new) {
- Warning("Proc_SpawnWorker - Out of heap space!\n");
- return -1;
- }
-
- // Create the stack contents
- stack_contents[3] = (Uint)Data;
- stack_contents[2] = 1;
- stack_contents[1] = (Uint)Fcn;
- stack_contents[0] = (Uint)new;
-
- // Create a new worker stack (in PID0's address space)
- new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents));
-
- // Save core machine state
- new->SavedState.ESP = new->KernelStack - sizeof(stack_contents);
- new->SavedState.EIP = (Uint)NewTaskHeader;
- new->SavedState.SSE = NULL;
- new->SavedState.bSSEModified = 0;
-
- // Mark as active
- new->Status = THREAD_STAT_PREINIT;
- Threads_AddActive( new );
-
- return new->TID;
-}
-
-/**
- * \fn Uint Proc_MakeUserStack(void)
- * \brief Creates a new user stack
- */
-Uint Proc_MakeUserStack(void)
-{
- int i;
- Uint base = USER_STACK_TOP - USER_STACK_SZ;
-
- // Check Prospective Space
- for( i = USER_STACK_SZ >> 12; i--; )
- if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
- break;
-
- if(i != -1) return 0;
-
- // Allocate Stack - Allocate incrementally to clean up MM_Dump output
- for( i = 0; i < USER_STACK_SZ/0x1000; i++ )
- {
- if( !MM_Allocate( base + (i<<12) ) )
- {
- Warning("OOM: Proc_MakeUserStack");
- return 0;
- }
- }
-
- return base + USER_STACK_SZ;
-}
-
-void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
-{
- Uint *stack;
- int i;
- const char **envp = NULL;
- Uint16 ss, cs;
-
- // Copy data to the user stack and free original buffer
- stack = (void*)Proc_MakeUserStack();
- stack -= (DataSize+sizeof(*stack)-1)/sizeof(*stack);
- memcpy( stack, ArgV, DataSize );
- free(ArgV);
-
- // Adjust Arguments and environment
- if( DataSize )
- {
- Uint delta = (Uint)stack - (Uint)ArgV;
- ArgV = (const char**)stack;
- for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta;
- envp = &ArgV[i+1];
- for( i = 0; envp[i]; i++ ) envp[i] += delta;
- }
-
- // User Mode Segments
- ss = 0x23; cs = 0x1B;
-
- // Arguments
- *--stack = (Uint)envp;
- *--stack = (Uint)ArgV;
- *--stack = (Uint)ArgC;
- *--stack = Base;
-
- Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
-}
-
-void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
-{
- Uint *stack = (void*)Stack;
- *--stack = SS; //Stack Segment
- *--stack = Stack; //Stack Pointer
- *--stack = Flags; //EFLAGS (Resvd (0x2) and IF (0x20))
- *--stack = CS; //Code Segment
- *--stack = IP; //EIP
- //PUSHAD
- *--stack = 0xAAAAAAAA; // eax
- *--stack = 0xCCCCCCCC; // ecx
- *--stack = 0xDDDDDDDD; // edx
- *--stack = 0xBBBBBBBB; // ebx
- *--stack = 0xD1D1D1D1; // edi
- *--stack = 0x54545454; // esp - NOT POPED
- *--stack = 0x51515151; // esi
- *--stack = 0xB4B4B4B4; // ebp
- //Individual PUSHs
- *--stack = SS; // ds
- *--stack = SS; // es
- *--stack = SS; // fs
- *--stack = SS; // gs
-
- __asm__ __volatile__ (
- "mov %%eax,%%esp;\n\t" // Set stack pointer
- "pop %%gs;\n\t"
- "pop %%fs;\n\t"
- "pop %%es;\n\t"
- "pop %%ds;\n\t"
- "popa;\n\t"
- "iret;\n\t" : : "a" (stack));
- for(;;);
-}
-
-/**
- * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
- * \brief Demotes a process to a lower permission level
- * \param Err Pointer to user's errno
- * \param Dest New Permission Level
- * \param Regs Pointer to user's register structure
- */
-int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
-{
- int cpl = Regs->cs & 3;
- // Sanity Check
- if(Dest > 3 || Dest < 0) {
- *Err = -EINVAL;
- return -1;
- }
-
- // Permission Check
- if(cpl > Dest) {
- *Err = -EACCES;
- return -1;
- }
-
- // Change the Segment Registers
- Regs->cs = (((Dest+1)<<4) | Dest) - 8;
- Regs->ss = ((Dest+1)<<4) | Dest;
- // Check if the GP Segs are GDT, then change them
- if(!(Regs->ds & 4)) Regs->ds = ((Dest+1)<<4) | Dest;
- if(!(Regs->es & 4)) Regs->es = ((Dest+1)<<4) | Dest;
- if(!(Regs->fs & 4)) Regs->fs = ((Dest+1)<<4) | Dest;
- if(!(Regs->gs & 4)) Regs->gs = ((Dest+1)<<4) | Dest;
-
- return 0;
-}
-
-/**
- * \brief Calls a signal handler in user mode
- * \note Used for signals
- */
-void Proc_CallFaultHandler(tThread *Thread)
-{
- // Rewinds the stack and calls the user function
- // Never returns
- Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack );
- for(;;);
-}
-
-void Proc_DumpThreadCPUState(tThread *Thread)
-{
- if( Thread->CurCPU > -1 )
- {
- int maxBacktraceDistance = 6;
- tRegs *regs = NULL;
- Uint32 *stack;
-
- if( Thread->CurCPU != GetCPUNum() ) {
- Log(" Currently running");
- return ;
- }
-
- // Backtrace to find the IRQ entrypoint
- // - This will usually only be called by an IRQ, so this should
- // work
- __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (stack));
- while( maxBacktraceDistance -- )
- {
- // [ebp] = oldEbp
- // [ebp+4] = retaddr
-
- if( stack[1] == (tVAddr)&IRQCommon_handled ) {
- regs = (void*)stack[2];
- break;
- }
-
- stack = (void*)stack[0];
- }
-
- if( !regs ) {
- Log(" Unable to find IRQ Entry");
- return ;
- }
-
- Log(" at %04x:%08x", regs->cs, regs->eip);
- return ;
- }
-
- tVAddr diffFromScheduler = Thread->SavedState.EIP - (tVAddr)SwitchTasks;
- tVAddr diffFromClone = Thread->SavedState.EIP - (tVAddr)Proc_CloneInt;
- tVAddr diffFromSpawn = Thread->SavedState.EIP - (tVAddr)NewTaskHeader;
-
- if( diffFromClone > 0 && diffFromClone < 40 ) // When I last checked, .newTask was at .+27
- {
- Log(" Creating process");
- return ;
- }
-
- if( diffFromSpawn == 0 )
- {
- Log(" Creating thread");
- return ;
- }
-
- if( diffFromScheduler > 0 && diffFromScheduler < 128 ) // When I last checked, GetEIP was at .+0x30
- {
- // Scheduled out
- Log(" At %04x:%08x", Thread->SavedState.UserCS, Thread->SavedState.UserEIP);
- return ;
- }
-
- Log(" Just created (unknown %p)", Thread->SavedState.EIP);
-}
-
-void Proc_Reschedule(void)
-{
- tThread *nextthread, *curthread;
- int cpu = GetCPUNum();
-
- // TODO: Wait for the lock?
- if(IS_LOCKED(&glThreadListLock)) return;
-
- curthread = Proc_GetCurThread();
-
- nextthread = Threads_GetNextToRun(cpu, curthread);
-
- if(!nextthread || nextthread == curthread)
- return ;
-
- #if DEBUG_TRACE_SWITCH
- // HACK: Ignores switches to the idle threads
- if( nextthread->TID == 0 || nextthread->TID > giNumCPUs )
- {
- LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
- GetCPUNum(),
- nextthread, nextthread->TID, nextthread->ThreadName,
- nextthread->Process->MemState.CR3,
- nextthread->SavedState.EIP,
- nextthread->SavedState.ESP
- );
- LogF("OldCR3 = %P\n", curthread->Process->MemState.CR3);
- }
- #endif
-
- // Update CPU state
- gaCPUs[cpu].Current = nextthread;
- gTSSs[cpu].ESP0 = nextthread->KernelStack-4;
- __asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
-
- // Save FPU/MMX/XMM/SSE state
- if( curthread && curthread->SavedState.SSE )
- {
- Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
- curthread->SavedState.bSSEModified = 0;
- Proc_DisableSSE();
- }
-
- if( curthread )
- {
- SwitchTasks(
- nextthread->SavedState.ESP, &curthread->SavedState.ESP,
- nextthread->SavedState.EIP, &curthread->SavedState.EIP,
- nextthread->Process->MemState.CR3
- );
- }
- else
- {
- SwitchTasks(
- nextthread->SavedState.ESP, 0,
- nextthread->SavedState.EIP, 0,
- nextthread->Process->MemState.CR3
- );
- }
-
- return ;
-}
-
-/**
- * \fn void Proc_Scheduler(int CPU)
- * \brief Swap current thread and clears dead threads
- */
-void Proc_Scheduler(int CPU)
-{
-#if 0
- tThread *thread;
-
- // If the spinlock is set, let it complete
- if(IS_LOCKED(&glThreadListLock)) return;
-
- // Get current thread
- thread = gaCPUs[CPU].Current;
-
- if( thread )
- {
- tRegs *regs;
- Uint ebp;
- // Reduce remaining quantum and continue timeslice if non-zero
- if( thread->Remaining-- )
- return;
- // Reset quantum for next call
- thread->Remaining = thread->Quantum;
-
- // TODO: Make this more stable somehow
- __asm__ __volatile__("mov %%ebp, %0" : "=r" (ebp));
- regs = (tRegs*)(ebp+(2+2)*4); // EBP,Ret + CPU,CurThread
- thread->SavedState.UserCS = regs->cs;
- thread->SavedState.UserEIP = regs->eip;
-
- if(thread->bInstrTrace) {
- regs->eflags |= 0x100; // Set TF
- Log("%p De-scheduled", thread);
- }
- else
- regs->eflags &= ~0x100; // Clear TF
- }
-
- // TODO: Ack timer?
- #if USE_MP
- if( GetCPUNum() )
- gpMP_LocalAPIC->EOI.Val = 0;
- else
- #endif
- outb(0x20, 0x20);
- __asm__ __volatile__ ("sti");
- Proc_Reschedule();
-#endif
-}
-
-// === EXPORTS ===
-EXPORT(Proc_SpawnWorker);
+++ /dev/null
-; AcessOS Microkernel Version
-; Start.asm
-
-[bits 32]
-
-KERNEL_BASE equ 0xC0000000
-%define MAX_CPUS 16
-
-[extern __load_addr]
-[extern __bss_start]
-[extern gKernelEnd]
-[section .multiboot]
-mboot:
- ; Multiboot macros to make a few lines later more readable
- MULTIBOOT_PAGE_ALIGN equ 1<<0
- MULTIBOOT_MEMORY_INFO equ 1<<1
- MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
- MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
- MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
-
- ; This is the GRUB Multiboot header. A boot signature
- dd MULTIBOOT_HEADER_MAGIC
- dd MULTIBOOT_HEADER_FLAGS
- dd MULTIBOOT_CHECKSUM
- dd mboot; - KERNEL_BASE ;Location of Multiboot Header
-
-; Multiboot 2 Header
-;mboot2:
-; MULTIBOOT2_HEADER_MAGIC equ 0xE85250D6
-; MULTIBOOT2_HEADER_ARCH equ 0
-; MULTIBOOT2_HEADER_LENGTH equ (mboot2_end-mboot2)
-; MULTIBOOT2_CHECKSUM equ -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_HEADER_ARCH + MULTIBOOT2_HEADER_LENGTH)
-;
-; dd MULTIBOOT2_HEADER_MAGIC
-; dd MULTIBOOT2_HEADER_ARCH
-; dd MULTIBOOT2_HEADER_LENGTH
-; dd MULTIBOOT2_CHECKSUM
-; ; MBoot2 Address Header
-; dw 2, 0
-; dd 8 + 16
-; dd mboot2 ; Location of Multiboot Header
-; dd __load_addr - KERNEL_BASE ; Kernel Load base
-; dd __bss_start - KERNEL_BASE ; Kernel Data End
-; dd gKernelEnd - KERNEL_BASE ; Kernel BSS End
-; ; MBoot2 Entry Point Tag
-; dw 3, 0
-; dd 8 + 4
-; dd start - KERNEL_BASE
-; ; MBoot2 Module Alignment Tag
-; dw 6, 0
-; dd 12 ; ???
-; dd 0 ; Search me, seems it wants padding
-; ; Terminator
-; dw 0, 0
-; dd 8
-;mboot2_end:
-
-[section .text]
-[extern kmain]
-[global start]
-start:
- ; Just show we're here
- mov WORD [0xB8000], 0x0741 ; 'A'
-
- ; Set up stack
- mov esp, Kernel_Stack_Top
-
- ; Start Paging
- mov ecx, gaInitPageDir - KERNEL_BASE
- mov cr3, ecx
- mov ecx, cr0
- or ecx, 0x80010000 ; PG and WP
- mov cr0, ecx
-
- mov WORD [0xB8002], 0x0763 ; 'c'
-
- ; Set GDT
- lgdt [gGDTPtr]
- mov cx, 0x10 ; PL0 Data
- mov ss, cx
- mov ds, cx
- mov es, cx
- mov gs, cx
- mov fs, cx
- mov WORD [0xB8004], 0x0765 ; 'e'
- jmp 0x08:.higher_half
-.higher_half:
-
- mov WORD [0xB8006], 0x0773 ; 's'
- mov WORD [0xB8008], 0x0773 ; 's'
-
- ; Call the kernel
- push ebx ; Multiboot Info
- push eax ; Multiboot Magic Value
- mov WORD [0xB800A], 0x0732 ; '2'
- call kmain
-
- ; Halt the Machine
- cli
-.hlt:
- hlt
- jmp .hlt
-
-;
-; Multiprocessing AP Startup Code (Must be within 0 - 0x10FFF0)
-;
-%if USE_MP
-[extern gIDTPtr]
-[extern gpMP_LocalAPIC]
-[extern giMP_TimerCount]
-[extern gaAPIC_to_CPU]
-[extern gaCPUs]
-[extern giNumInitingCPUs]
-[extern MM_NewKStack]
-[extern Proc_InitialiseSSE]
-
-lGDTPtr: ; Local GDT Pointer
- dw 3*8-1
- dd gGDT-KERNEL_BASE
-
-[bits 16]
-[global APWait]
-APWait:
- ;xchg bx, bx
-.hlt:
- ;hlt
- jmp .hlt
-[extern Proc_Reschedule]
-[global APStartup]
-APStartup:
- ;xchg bx, bx ; MAGIC BREAK!
- ; Load initial GDT
- mov ax, 0xFFFF
- mov ds, ax
- lgdt [DWORD ds:lGDTPtr-KERNEL_BASE-0xFFFF0]
- ; Enable PMode in CR0
- mov eax, cr0
- or al, 1
- mov cr0, eax
- ; Jump into PMode
- jmp 08h:DWORD .ProtectedMode-KERNEL_BASE
-[bits 32]
-.ProtectedMode:
- ; Load segment registers
- mov ax, 0x10
- mov ss, ax
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
- ; Start Paging
- mov eax, gaInitPageDir - KERNEL_BASE
- mov cr3, eax
- mov eax, cr0
- or eax, 0x80010000 ; PG and WP
- mov cr0, eax
- ; Jump to higher half
- lea eax, [.higherHalf]
- jmp eax
-.higherHalf:
- ; Load True GDT & IDT
- lgdt [gGDTPtr]
- lidt [gIDTPtr]
-
- mov ebp, [gpMP_LocalAPIC]
- mov ebx, [ebp+0x20] ; Read ID
- shr ebx, 24
- ;xchg bx, bx ; MAGIC BREAK
- ; BL is now local APIC ID
- mov cl, BYTE [gaAPIC_to_CPU+ebx]
- xor ebx, ebx
- mov bl, cl
- ; BL is now the CPU ID
- mov dr1, ebx ; Save the CPU number to a debug register
- ; Mark the CPU as up
- mov BYTE [gaCPUs+ebx*8+1], 1
- ; Decrement the remaining CPU count
- dec DWORD [giNumInitingCPUs]
-
- ; Create a stack
- lea edx, [ebx+1]
- shl edx, 5+2 ; *32 *4
- lea esp, [gInitAPStacks+edx]
- call MM_NewKStack
- mov esp, eax
-
- ; Set TSS
- lea ecx, [ebx*8+0x30]
- ltr cx
-
- ;xchg bx, bx ; MAGIC_BREAK
- ; Enable Local APIC
- mov DWORD [ebp+0x0F0], 0x000001EF ; Spurious Interrupt Vector (0xEF)
- mov ecx, DWORD [giMP_TimerCount]
- mov DWORD [ebp+0x380], ecx ; Set Initial Count
- mov DWORD [ebp+0x320], 0x000200EE ; Enable timer on IVT#0xEE
- mov DWORD [ebp+0x330], 0x000100E0 ; ##Enable thermal sensor on IVT#0xE0
- mov DWORD [ebp+0x340], 0x000100D0 ; ##Enable performance counters on IVT#0xD0
- mov DWORD [ebp+0x350], 0x000100D1 ; ##Enable LINT0 on IVT#0xD1
- mov DWORD [ebp+0x360], 0x000100D2 ; ##Enable LINT1 on IVT#0xD2
- mov DWORD [ebp+0x370], 0x000100E1 ; ##Enable Error on IVT#0xE1
- mov DWORD [ebp+0x0B0], 0 ; Send an EOI (just in case)
-
- ; Initialise SSE support
- call Proc_InitialiseSSE
-
- ; CPU is now marked as initialised
-
-.hlt:
- sti
- call Proc_Reschedule
- hlt
- jmp .hlt
-%endif
-
-[global GetEIP]
-GetEIP:
- mov eax, [esp]
- ret
-
-; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args)
-; Call a function passing the array as arguments
-[global CallWithArgArray]
-CallWithArgArray:
- push ebp
- mov ebp, esp
- mov ecx, [ebp+12] ; Get NArgs
- mov edx, [ebp+16]
-
-.top:
- mov eax, [edx+ecx*4-4]
- push eax
- loop .top
-
- mov eax, [ebp+8]
- call eax
- lea esp, [ebp]
- pop ebp
- ret
-
-[section .data]
-; GDT
-GDT_SIZE equ (1+2*2+1+MAX_CPUS)*8
-[global gGDT]
-gGDT:
- ; PL0 - Kernel
- ; PL3 - User
- dd 0x00000000, 0x00000000 ; 00 NULL Entry
- dd 0x0000FFFF, 0x00CF9A00 ; 08 PL0 Code
- dd 0x0000FFFF, 0x00CF9200 ; 10 PL0 Data
- dd 0x0000FFFF, 0x00CFFA00 ; 18 PL3 Code
- dd 0x0000FFFF, 0x00CFF200 ; 20 PL3 Data
- dd 26*4-1, 0x00408900 ; 28 Double Fault TSS
- times MAX_CPUS dd 26*4-1, 0x00408900 ; 30+ TSSes
-[global gGDTPtr]
-gGDTPtr:
- dw GDT_SIZE-1
- dd gGDT
-
-[section .initpd]
-[global gaInitPageDir]
-[global gaInitPageTable]
-align 4096
-gaInitPageDir:
- dd gaInitPageTable-KERNEL_BASE+3 ; 0x000 - Low kernel
- times 0x300-0x000-1 dd 0
- dd gaInitPageTable-KERNEL_BASE+3 ; 0xC00 - High kernel
- times 0x3F0-0x300-1 dd 0
- dd gaInitPageDir-KERNEL_BASE+3 ; 0xFC0 - Fractal
- times 0x400-0x3F0-1 dd 0
-align 4096
-gaInitPageTable:
- %assign i 0
- %rep 1024
- dd i*0x1000+3
- %assign i i+1
- %endrep
-[global Kernel_Stack_Top]
-ALIGN 4096
- times 1024 dd 0
-Kernel_Stack_Top:
-gInitAPStacks:
- times 32*MAX_CPUS dd 0
-
-; vim: ft=nasm ts=8
+++ /dev/null
-/*
- * Acess2 Kernel
- * Timekeeping
- * arch/x86/time.c
- */
-#include <acess.h>
-
-// === MACROS ===
-#define TIMER_QUANTUM 100
-// 2^(15-rate), 14: 2Hz, 5: 1024Hz, 2: 8192Hz
-// (Max: 14, Min: 2) - 14 = 2Hz, 13 = 4Hz, 12 = 8Hz, 11 = 16Hz 10 = 32Hz, 2 = 8192Hz
-//#define TIMER_RATE 10 // 32 Hz
-//#define TIMER_RATE 12 // 8 Hz
-#define TIMER_RATE 14 // 2 Hz - Lowest
-#define TIMER_FREQ (0x8000>>TIMER_RATE) //Hz
-#define MS_PER_TICK_WHOLE (1000/(TIMER_FREQ))
-#define MS_PER_TICK_FRACT ((0x80000000*(1000%TIMER_FREQ))/TIMER_FREQ)
-
-// === IMPORTS ===
-extern volatile Sint64 giTimestamp;
-extern volatile Uint64 giTicks;
-extern volatile Uint64 giPartMiliseconds;
-extern void Timer_CallTimers(void);
-
-// === GLOBALS ===
-volatile Uint64 giTime_TSCAtLastTick = 0;
-volatile Uint64 giTime_TSCPerTick = 0;
-
-// === PROTOTYPES ===
-//Sint64 now(void);
- int Time_Setup(void);
-void Time_Interrupt(int IRQ, void *Ptr);
-Uint64 Time_ReadTSC(void);
-
-// === CODE ===
-/**
- * \fn Sint64 now()
- * \brief Return the current timestamp
- */
-Sint64 now(void)
-{
- Uint64 tsc = Time_ReadTSC();
- tsc -= giTime_TSCAtLastTick;
- tsc *= MS_PER_TICK_WHOLE;
- if( giTime_TSCPerTick ) {
- tsc /= giTime_TSCPerTick;
- }
- else
- tsc = 0;
- return giTimestamp + tsc;
-}
-
-/**
- * \fn int Time_Setup(void)
- * \brief Sets the system time from the Realtime-Clock
- */
-int Time_Setup(void)
-{
- Uint8 val;
-
- Log_Log("Timer", "RTC Timer firing at %iHz (%i divisor), %i.0x%08x",
- TIMER_FREQ, TIMER_RATE, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT);
-
- outb(0x70, inb(0x70)&0x7F); // Disable NMIs
- __asm__ __volatile__ ("cli"); // Disable normal interrupts
-
- // Set IRQ8 firing rate
- outb(0x70, 0x0A); // Set the index to register A
- val = inb(0x71); // Get the current value of register A
- val &= 0xF0;
- val |= TIMER_RATE+1;
- outb(0x70, 0x0A); // Reset index to A
- outb(0x71, val); // Update the timer rate
-
- // Enable IRQ8
- outb(0x70, 0x0B); // Set the index to register B
- val = inb(0x71); // Read the current value of register B
- outb(0x70, 0x0B); // Set the index again (a read will reset the index to register D)
- outb(0x71, val | 0x40); // Write the previous value or'd with 0x40. This turns on bit 6 of register D
-
- __asm__ __volatile__ ("sti"); // Re-enable normal interrupts
- outb(0x70, inb(0x70)|0x80); // Re-enable NMIs
-
- // Install IRQ Handler
- IRQ_AddHandler(8, Time_Interrupt, NULL);
-
- // Make sure the RTC actually fires
- outb(0x70, 0x0C); // Select register C
- inb(0x71); // Just throw away contents.
-
- return 0;
-}
-
-/**
- * \brief Called on the timekeeping IRQ
- */
-void Time_Interrupt(int IRQ, void *Ptr)
-{
- Uint64 curTSC = Time_ReadTSC();
-
- if( giTime_TSCAtLastTick )
- {
- giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick;
- }
- giTime_TSCAtLastTick = curTSC;
-
- giTicks ++;
- giTimestamp += MS_PER_TICK_WHOLE;
- giPartMiliseconds += MS_PER_TICK_FRACT;
- if(giPartMiliseconds > 0x80000000) {
- giTimestamp ++;
- giPartMiliseconds -= 0x80000000;
- }
-
- Timer_CallTimers();
-
- // Make sure the RTC Fires again
- outb(0x70, 0x0C); // Select register C
- inb(0x71); // Just throw away contents.
-}
-
-Uint64 Time_ReadTSC(void)
-{
- Uint32 a, d;
- __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d));
- return ((Uint64)d << 32) | a;
-}
+++ /dev/null
-/*
- * Acess2 VM8086 Driver
- * - By John Hodge (thePowersGang)
- */
-#define DEBUG 0
-#include <acess.h>
-#include <vm8086.h>
-#include <modules.h>
-#include <hal_proc.h>
-#include <semaphore.h>
-
-// === CONSTANTS ===
-#define VM8086_MAGIC_CS 0xFFFF
-#define VM8086_MAGIC_IP 0x0010
-#define VM8086_STACK_SEG 0x9F00
-#define VM8086_STACK_OFS 0x0AFE
-enum eVM8086_Opcodes
-{
- VM8086_OP_PUSHF = 0x9C,
- VM8086_OP_POPF = 0x9D,
- VM8086_OP_INT_I = 0xCD,
- VM8086_OP_IRET = 0xCF,
- VM8086_OP_IN_AD = 0xEC,
- VM8086_OP_IN_ADX = 0xED,
- VM8086_OP_OUT_AD = 0xEE,
- VM8086_OP_OUT_ADX = 0xEF
-};
-#define VM8086_PAGES_PER_INST 4
-
-#define VM8086_BLOCKSIZE 128
-#define VM8086_BLOCKCOUNT ((0x9F000-0x10000)/VM8086_BLOCKSIZE)
-
-// === TYPES ===
-struct sVM8086_InternalData
-{
- struct {
- Uint32 Bitmap; // 32 sections = 128 byte blocks
- tVAddr VirtBase;
- tPAddr PhysAddr;
- } AllocatedPages[VM8086_PAGES_PER_INST];
-};
-
-// === PROTOTYPES ===
- int VM8086_Install(char **Arguments);
-void VM8086_GPF(tRegs *Regs);
-//tVM8086 *VM8086_Init(void);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL);
-tMutex glVM8086_Process;
-tSemaphore gVM8086_TaskComplete;
-tSemaphore gVM8086_TasksToDo;
-tPID gVM8086_WorkerPID;
-tTID gVM8086_CallingThread;
-tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions
-Uint32 gaVM8086_MemBitmap[VM8086_BLOCKCOUNT/32];
-
-// === FUNCTIONS ===
-int VM8086_Install(char **Arguments)
-{
- tPID pid;
-
- Semaphore_Init(&gVM8086_TasksToDo, 0, 10, "VM8086", "TasksToDo");
-
- // Lock to avoid race conditions
- Mutex_Acquire( &glVM8086_Process );
-
- // Create BIOS Call process
- pid = Proc_Clone(CLONE_VM);
- Log_Debug("VM8086", "pid = %i", pid);
- if(pid == -1)
- {
- Log_Error("VM8086", "Unable to clone kernel into VM8086 worker");
- return MODULE_ERR_MISC;
- }
- if(pid == 0)
- {
- Uint * volatile stacksetup; // Initialising Stack
- Uint16 * volatile rmstack; // Real Mode Stack
- int i;
-
- Log_Debug("VM8086", "Initialising worker");
-
- // Set Image Name
- Threads_SetName("VM8086");
-
- // Map ROM Area
- for(i=0xA0;i<0x100;i++) {
- MM_Map( i * 0x1000, i * 0x1000 );
- }
- MM_Map( 0, 0 ); // IVT / BDA
- // Map (but allow allocation) of 0x1000 - 0x9F000
- // - So much hack, it isn't funny
- for(i=1;i<0x9F;i++) {
- MM_Map( i * 0x1000, i * 0x1000 );
- MM_DerefPhys( i * 0x1000 ); // Above
- while(MM_GetRefCount(i*0x1000))
- MM_DerefPhys( i * 0x1000 ); // Phys setup
- }
- MM_Map( 0x9F000, 0x9F000 ); // Stack / EBDA
- // System Stack / Stub
- if( MM_Allocate( 0x100000 ) == 0 ) {
- Log_Error("VM8086", "Unable to allocate memory for stack/stub");
- gVM8086_WorkerPID = 0;
- Threads_Exit(0, 1);
- }
-
- *(Uint8*)(0x100000) = VM8086_OP_IRET;
- *(Uint8*)(0x100001) = 0x07; // POP ES
- *(Uint8*)(0x100002) = 0x1F; // POP DS
- *(Uint8*)(0x100003) = 0xCB; // RET FAR
-
- rmstack = (Uint16*)(VM8086_STACK_SEG*16 + VM8086_STACK_OFS);
- rmstack--; *rmstack = 0xFFFF; //CS
- rmstack--; *rmstack = 0x0010; //IP
-
- // Setup Stack
- stacksetup = (Uint*)0x101000;
- stacksetup--; *stacksetup = VM8086_STACK_SEG; // GS
- stacksetup--; *stacksetup = VM8086_STACK_SEG; // FS
- stacksetup--; *stacksetup = VM8086_STACK_SEG; // DS
- stacksetup--; *stacksetup = VM8086_STACK_SEG; // ES
- stacksetup--; *stacksetup = VM8086_STACK_SEG; // SS
- stacksetup--; *stacksetup = VM8086_STACK_OFS-2; // SP
- stacksetup--; *stacksetup = 0x20202; // FLAGS
- stacksetup--; *stacksetup = 0xFFFF; // CS
- stacksetup--; *stacksetup = 0x10; // IP
- stacksetup--; *stacksetup = 0xAAAA; // AX
- stacksetup--; *stacksetup = 0xCCCC; // CX
- stacksetup--; *stacksetup = 0xDDDD; // DX
- stacksetup--; *stacksetup = 0xBBBB; // BX
- stacksetup--; *stacksetup = 0x5454; // SP
- stacksetup--; *stacksetup = 0xB4B4; // BP
- stacksetup--; *stacksetup = 0x5151; // SI
- stacksetup--; *stacksetup = 0xD1D1; // DI
- stacksetup--; *stacksetup = 0x20|3; // DS - Kernel
- stacksetup--; *stacksetup = 0x20|3; // ES - Kernel
- stacksetup--; *stacksetup = 0x20|3; // FS
- stacksetup--; *stacksetup = 0x20|3; // GS
- __asm__ __volatile__ (
- "mov %%eax,%%esp;\n\t" // Set stack pointer
- "pop %%gs;\n\t"
- "pop %%fs;\n\t"
- "pop %%es;\n\t"
- "pop %%ds;\n\t"
- "popa;\n\t"
- "iret;\n\t" : : "a" (stacksetup));
- for(;;); // Shouldn't be reached
- }
-
- gVM8086_WorkerPID = pid;
-
- // It's released when the GPF fires
- Mutex_Acquire( &glVM8086_Process );
- Mutex_Release( &glVM8086_Process );
-
- // Worker killed itself
- if( gVM8086_WorkerPID != pid ) {
- return MODULE_ERR_MISC;
- }
-
- return MODULE_ERR_OK;
-}
-
-void VM8086_GPF(tRegs *Regs)
-{
- Uint8 opcode;
-
-// Log_Log("VM8086", "GPF - %04x:%04x", Regs->cs, Regs->eip);
-
- if(Regs->eip == VM8086_MAGIC_IP && Regs->cs == VM8086_MAGIC_CS
- && Threads_GetPID() == gVM8086_WorkerPID)
- {
- if( gpVM8086_State == (void*)-1 ) {
- Log_Log("VM8086", "Worker thread ready and waiting");
- gpVM8086_State = NULL;
- Mutex_Release( &glVM8086_Process ); // Release lock obtained in VM8086_Install
- }
-// Log_Log("VM8086", "gpVM8086_State = %p, gVM8086_CallingThread = %i",
-// gpVM8086_State, gVM8086_CallingThread);
- if( gpVM8086_State ) {
- gpVM8086_State->AX = Regs->eax; gpVM8086_State->CX = Regs->ecx;
- gpVM8086_State->DX = Regs->edx; gpVM8086_State->BX = Regs->ebx;
- gpVM8086_State->BP = Regs->ebp;
- gpVM8086_State->SI = Regs->esi; gpVM8086_State->DI = Regs->edi;
- gpVM8086_State->DS = Regs->ds; gpVM8086_State->ES = Regs->es;
- gpVM8086_State = NULL;
- // Wake the caller
- Semaphore_Signal(&gVM8086_TaskComplete, 1);
- }
-
- //Log_Log("VM8086", "Waiting for something to do");
- __asm__ __volatile__ ("sti");
- Semaphore_Wait(&gVM8086_TasksToDo, 1);
-
- //Log_Log("VM8086", "We have a task (%p)", gpVM8086_State);
- Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_CS;
- Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_IP;
- Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->CS;
- Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->IP;
- Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->DS;
- Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->ES;
-
- // Set Registers
- Regs->eip = 0x11; Regs->cs = 0xFFFF;
- Regs->eax = gpVM8086_State->AX; Regs->ecx = gpVM8086_State->CX;
- Regs->edx = gpVM8086_State->DX; Regs->ebx = gpVM8086_State->BX;
- Regs->esi = gpVM8086_State->SI; Regs->edi = gpVM8086_State->DI;
- Regs->ebp = gpVM8086_State->BP;
- Regs->ds = 0x23; Regs->es = 0x23;
- Regs->fs = 0x23; Regs->gs = 0x23;
- return ;
- }
-
- opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip) );
- Regs->eip ++;
- switch(opcode)
- {
- case VM8086_OP_PUSHF: //PUSHF
- Regs->esp -= 2;
- *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eflags & 0xFFFF;
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated PUSHF");
- #endif
- break;
- case VM8086_OP_POPF: //POPF
- // Changing IF is not allowed
- Regs->eflags &= 0xFFFF0202;
- Regs->eflags |= *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) );
- Regs->esp += 2;
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated POPF");
- #endif
- break;
-
- case VM8086_OP_INT_I: //INT imm8
- {
- int id;
- id = *(Uint8*)( Regs->cs*16 +(Regs->eip&0xFFFF));
- Regs->eip ++;
-
- Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->cs;
- Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eip;
-
- Regs->cs = *(Uint16*)(4*id + 2);
- Regs->eip = *(Uint16*)(4*id);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated INT 0x%x", id);
- #endif
- }
- break;
-
- case VM8086_OP_IRET: //IRET
- Regs->eip = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2;
- Regs->cs = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2;
- #if TRACE_EMU
- Log_Debug("VM8086", "IRET to %04x:%04x", Regs->cs, Regs->eip);
- #endif
- break;
-
-
- case VM8086_OP_IN_AD: //IN AL, DX
- Regs->eax &= 0xFFFFFF00;
- Regs->eax |= inb(Regs->edx&0xFFFF);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated IN AL, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
- #endif
- break;
- case VM8086_OP_IN_ADX: //IN AX, DX
- Regs->eax &= 0xFFFF0000;
- Regs->eax |= inw(Regs->edx&0xFFFF);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated IN AX, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
- #endif
- break;
-
- case VM8086_OP_OUT_AD: //OUT DX, AL
- outb(Regs->edx&0xFFFF, Regs->eax&0xFF);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated OUT DX, AL (*0x%04x = 0x%02x)\n", Regs->edx&0xFFFF, Regs->eax&0xFF);
- #endif
- break;
- case VM8086_OP_OUT_ADX: //OUT DX, AX
- outw(Regs->edx&0xFFFF, Regs->eax&0xFFFF);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated OUT DX, AX (*0x%04x = 0x%04x)\n", Regs->edx&0xFFFF, Regs->eax&0xFFFF);
- #endif
- break;
-
- // TODO: Decide on allowing VM8086 Apps to enable/disable interrupts
- case 0xFA: //CLI
- break;
- case 0xFB: //STI
- break;
-
- case 0x66:
- opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip&0xFFFF));
- switch( opcode )
- {
- case VM8086_OP_IN_ADX: //IN AX, DX
- Regs->eax = ind(Regs->edx&0xFFFF);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated IN EAX, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
- #endif
- break;
- case VM8086_OP_OUT_ADX: //OUT DX, AX
- outd(Regs->edx&0xFFFF, Regs->eax);
- #if TRACE_EMU
- Log_Debug("VM8086", "Emulated OUT DX, EAX (*0x%04x = 0x%08x)\n", Regs->edx&0xFFFF, Regs->eax);
- #endif
- break;
- default:
- Log_Error("VM8086", "Error - Unknown opcode 66 %02x caused a GPF at %04x:%04x",
- Regs->cs, Regs->eip,
- opcode
- );
- // Force an end to the call
- Regs->cs = VM8086_MAGIC_CS;
- Regs->eip = VM8086_MAGIC_IP;
- break;
- }
- break;
-
- default:
- Log_Error("VM8086", "Error - Unknown opcode %02x caused a GPF at %04x:%04x",
- opcode, Regs->cs, Regs->eip);
- // Force an end to the call
- Regs->cs = VM8086_MAGIC_CS;
- Regs->eip = VM8086_MAGIC_IP;
- break;
- }
-}
-
-/**
- * \brief Create an instance of the VM8086 Emulator
- */
-tVM8086 *VM8086_Init(void)
-{
- tVM8086 *ret;
- ret = calloc( 1, sizeof(tVM8086) + sizeof(struct sVM8086_InternalData) );
- ret->Internal = (void*)((tVAddr)ret + sizeof(tVM8086));
- return ret;
-}
-
-void VM8086_Free(tVM8086 *State)
-{
- int i;
- for( i = VM8086_PAGES_PER_INST; i --; )
- MM_UnmapHWPages( State->Internal->AllocatedPages[i].VirtBase, 1);
- free(State);
-}
-
-void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset)
-{
- int i, j, base = 0;
- int nBlocks, rem;
-
- Size = (Size + 127) & ~127;
- nBlocks = Size / 128;
-
- if(Size > 4096) return NULL;
-
- for( i = 0; i < VM8086_PAGES_PER_INST; i++ )
- {
- if( State->Internal->AllocatedPages[i].VirtBase == 0 ) continue;
-
-
- //Log_Debug("VM8086", "AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap);
-
- rem = nBlocks;
- base = 0;
- // Scan the bitmap for a free block
- for( j = 0; j < 32; j++ ) {
- if( State->Internal->AllocatedPages[i].Bitmap & (1 << j) ) {
- base = j+1;
- rem = nBlocks;
- }
-
- rem --;
- if(rem == 0) // Goodie, there's a gap
- {
- for( j = 0; j < nBlocks; j++ )
- State->Internal->AllocatedPages[i].Bitmap |= 1 << (base + j);
- *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16 + base * 8;
- *Offset = 0;
- LOG("Allocated at #%i,%04x", i, base*128);
- LOG(" - %x:%x", *Segment, *Offset);
- return (void*)( State->Internal->AllocatedPages[i].VirtBase + base * 128 );
- }
- }
- }
-
- // No pages with free space?, allocate a new one
- for( i = 0; i < VM8086_PAGES_PER_INST; i++ )
- {
- if( State->Internal->AllocatedPages[i].VirtBase == 0 ) break;
- }
- // Darn, we can't allocate any more
- if( i == VM8086_PAGES_PER_INST ) {
- Log_Warning("VM8086", "Out of pages in %p", State);
- return NULL;
- }
-
- State->Internal->AllocatedPages[i].VirtBase = MM_AllocDMA(
- 1, 20, &State->Internal->AllocatedPages[i].PhysAddr);
- State->Internal->AllocatedPages[i].Bitmap = 0;
-
- for( j = 0; j < nBlocks; j++ )
- State->Internal->AllocatedPages[i].Bitmap |= 1 << j;
- LOG("AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap);
- *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16;
- *Offset = 0;
- LOG(" - %x:%x", *Segment, *Offset);
- return (void*) State->Internal->AllocatedPages[i].VirtBase;
-}
-
-void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset)
-{
- return (void*)( KERNEL_BASE + Segment*16 + Offset );
-}
-
-void VM8086_Int(tVM8086 *State, Uint8 Interrupt)
-{
- State->IP = *(Uint16*)(KERNEL_BASE+4*Interrupt);
- State->CS = *(Uint16*)(KERNEL_BASE+4*Interrupt+2);
-
-// Log_Debug("VM8086", "Software interrupt %i to %04x:%04x", Interrupt, State->CS, State->IP);
-
- Mutex_Acquire( &glVM8086_Process );
-
- gpVM8086_State = State;
- gVM8086_CallingThread = Threads_GetTID();
- Semaphore_Signal(&gVM8086_TasksToDo, 1);
-
- Semaphore_Wait(&gVM8086_TaskComplete, 1);
-
- Mutex_Release( &glVM8086_Process );
-}
+++ /dev/null
-#
-# Acess2 Kernel
-# i386 Architecture Makefile
-# arch/i386/Makefile
-
-MAX_CPUS := 4
-
-AS_SUFFIX = asm
-
-CPPFLAGS := -DMAX_CPUS=$(MAX_CPUS) -D USE_MP=0
-CFLAGS := $(KERNEL_CFLAGS) -mno-sse -mno-mmx
-ASFLAGS := -f elf64 -D MAX_CPUS=$(MAX_CPUS) -D USE_MP=0
-LDFLAGS := -nostdlib -nodefaultlibs
-
-ifeq ($(ARCH),amd64)
- ASFLAGS += -D AMD64=1
- CPPFLAGS += -DAMD64=1
-else
- ifeq ($(ARCH),x86_64)
- ASFLAGS += -D AMD64=0 -D X86_64=1
- CPPFLAGS += -DAMD64=0 -DX86_64=1
- endif
-endif
-
-
-A_OBJ := start32.ao start64.ao desctab.ao proc.ao
-A_OBJ += main.o lib.o proc.o mm_virt.o mm_phys.o
-A_OBJ += kernelpanic.o errors.o time.o pci.o
-A_OBJ += vm8086.o
-# rme.o
-
-POSTBUILD = objcopy $(BIN) -F elf32-i386 $(BIN)
+++ /dev/null
-;
-;
-;
-%include "arch/x86_64/include/common.inc.asm"
-[BITS 64]
-
-[extern Log]
-[extern gGDTPtr]
-[extern gGDT]
-
-%define NUM_IRQ_CALLBACKS 4
-
-MM_LOCALAPIC equ 0xFFFFFD0000000000
-
-[section .text]
-[global Desctab_Init]
-Desctab_Init:
- ; Save to make following instructions smaller
- mov rdi, gIDT
-
- ; Set an IDT entry to a callback
- %macro SETIDT 2
- mov rax, %2
- mov WORD [rdi + %1*16], ax
- shr rax, 16
- mov WORD [rdi + %1*16 + 6], ax
- shr rax, 16
- mov DWORD [rdi + %1*16 + 8], eax
- ; Enable
- mov ax, WORD [rdi + %1*16 + 4]
- or ax, 0x8000
- mov WORD [rdi + %1*16 + 4], ax
- %endmacro
-
- ; Install error handlers
- %macro SETISR 1
- SETIDT %1, Isr%1
- %endmacro
-
- %assign i 0
- %rep 32
- SETISR i
- %assign i i+1
- %endrep
-
- ; Install IRQs
- SETIDT 0xF0, PIT_IRQ
- SETIDT 0xF1, Irq1
- SETIDT 0xF2, Irq2
- SETIDT 0xF3, Irq3
- SETIDT 0xF4, Irq4
- SETIDT 0xF5, Irq5
- SETIDT 0xF6, Irq6
- SETIDT 0xF7, Irq7
- SETIDT 0xF8, Irq8
- SETIDT 0xF9, Irq9
- SETIDT 0xFA, Irq10
- SETIDT 0xFB, Irq11
- SETIDT 0xFC, Irq12
- SETIDT 0xFD, Irq13
- SETIDT 0xFE, Irq14
- SETIDT 0xFF, Irq15
-
- ; Remap PIC
- push rdx ; Save RDX
- mov dx, 0x20
- mov al, 0x11
- out dx, al ; Init Command
- mov dx, 0x21
- mov al, 0xF0
- out dx, al ; Offset (Start of IDT Range)
- mov al, 0x04
- out dx, al ; IRQ connected to Slave (00000100b) = IRQ2
- mov al, 0x01
- out dx, al ; Set Mode
- mov al, 0x00
- out dx, al ; Set Mode
-
- mov dx, 0xA0
- mov al, 0x11
- out dx, al ; Init Command
- mov dx, 0xA1
- mov al, 0xF8
- out dx, al ; Offset (Start of IDT Range)
- mov al, 0x02
- out dx, al ; IRQ Line connected to master
- mov al, 0x01
- out dx, al ; Set Mode
- mov dl, 0x00
- out dx, al ; Set Mode
- pop rdx
-
-
- ; Install IDT
- mov rax, gIDTPtr
- lidt [rax]
-
- ; Re-install GDT (in higher address space)
- mov rax, gGDTPtr
- mov rcx, gGDT
- mov QWORD [rax+2], rcx
- lgdt [rax]
-
- ; Start interrupts
- sti
-
- ; Set IA32_LSTAR (RIP of handler)
- mov ecx, 0xC0000082 ; IA32_LSTAR
- mov eax, SyscallStub - 0xFFFFFFFF00000000
- mov edx, 0xFFFFFFFF
- wrmsr
- ; Set IA32_FMASK (flags mask)
- mov ecx, 0xC0000084
- rdmsr
- mov eax, ~0x202
- wrmsr
- ; Set IA32_STAR (Kernel/User CS)
- mov ecx, 0xC0000081
- rdmsr
- mov edx, 0x8 | (0x1B << 16) ; Kernel CS (and Kernel DS/SS - 8), User CS
- wrmsr
-
- ret
-
-; int IRQ_AddHandler(int IRQ, void (*Handler)(int IRQ), void *Ptr)
-; Return Values:
-; 0 on Success
-; -1 on an invalid IRQ Number
-; -2 when no slots are avaliable
-[global IRQ_AddHandler]
-IRQ_AddHandler:
- ; RDI - IRQ Number
- ; RSI - Callback
- ; RDX - Ptr
-
- ; Check for RDI >= 16
- cmp rdi, 16
- jb .numOK
- xor rax, rax
- dec rax
- jmp .ret
-.numOK:
-
- ; Get handler base into RAX
- lea rax, [rdi*4]
- mov rcx, gaIRQ_Handlers
- lea rax, [rcx+rax*8]
-
- ; Find a free callback slot
- %rep NUM_IRQ_CALLBACKS
- mov rcx, [rax]
- test rcx, rcx
- jz .assign
- add rax, 8
- %endrep
- ; None found, return -2
- xor rax, rax
- dec rax
- dec rax
- jmp .ret
-
- ; Assign the IRQ Callback
-.assign:
- ; A little bit of debug
- push rdi
- push rsi
- push rax
- push rdx
- sub rsp, 8
- mov rcx, rdi ; IRQ Number
- mov rdx, rsi ; Callback
- mov rsi, rax ; Pointer
- mov rdi, csIRQ_Assigned
- call Log
- add rsp, 8
- pop rdx
- pop rax
- pop rsi
- pop rdi
-
- ; Assign and return
- mov [rax], rsi
- add rax, gaIRQ_DataPtrs - gaIRQ_Handlers
- mov [rax], rdx
- xor rax, rax
-
-.ret:
- ret
-
-[section .rodata]
-csIRQ_Assigned:
- db "IRQ %p := %p (IRQ %i)",0
-csIRQ_Fired:
- db "IRQ %i fired",0
-[section .text]
-
-%macro ISR_NOERRNO 1
-Isr%1:
- push QWORD 0
- push QWORD %1
- jmp ErrorCommon
-%endmacro
-%macro ISR_ERRNO 1
-Isr%1:
- push QWORD %1
- jmp ErrorCommon
-%endmacro
-
-ISR_NOERRNO 0; 0: Divide By Zero Exception
-ISR_NOERRNO 1; 1: Debug Exception
-ISR_NOERRNO 2; 2: Non Maskable Interrupt Exception
-ISR_NOERRNO 3; 3: Int 3 Exception
-ISR_NOERRNO 4; 4: INTO Exception
-ISR_NOERRNO 5; 5: Out of Bounds Exception
-ISR_NOERRNO 6; 6: Invalid Opcode Exception
-ISR_NOERRNO 7; 7: Coprocessor Not Available Exception
-ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!)
-ISR_NOERRNO 9; 9: Coprocessor Segment Overrun Exception
-ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!)
-ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!)
-ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!)
-ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!)
-ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!)
-ISR_NOERRNO 15; 15: Reserved Exception
-ISR_NOERRNO 16; 16: Floating Point Exception
-ISR_NOERRNO 17; 17: Alignment Check Exception
-ISR_NOERRNO 18; 18: Machine Check Exception
-ISR_NOERRNO 19; 19: Reserved
-ISR_NOERRNO 20; 20: Reserved
-ISR_NOERRNO 21; 21: Reserved
-ISR_NOERRNO 22; 22: Reserved
-ISR_NOERRNO 23; 23: Reserved
-ISR_NOERRNO 24; 24: Reserved
-ISR_NOERRNO 25; 25: Reserved
-ISR_NOERRNO 26; 26: Reserved
-ISR_NOERRNO 27; 27: Reserved
-ISR_NOERRNO 28; 28: Reserved
-ISR_NOERRNO 29; 29: Reserved
-ISR_NOERRNO 30; 30: Reserved
-ISR_NOERRNO 31; 31: Reserved
-
-[extern Error_Handler]
-[global ErrorCommon]
-ErrorCommon:
- PUSH_GPR
- push gs
- push fs
- ;PUSH_FPU
- ;PUSH_XMM
-
- mov rdi, rsp
-; xchg bx, bx
- call Error_Handler
-
- ;POP_XMM
- ;POP_FPU
- pop fs
- pop gs
- POP_GPR
- add rsp, 2*8
- iretq
-
-%macro DEFIRQ 1
-Irq%1:
- push 0
- push %1
- jmp IrqCommon
-%endmacro
-
-%assign i 0
-%rep 16
-DEFIRQ i
-%assign i i+1
-%endrep
-
-[global IrqCommon]
-IrqCommon:
- PUSH_GPR
- push gs
- push fs
-
-; mov rdi, csIRQ_Fired
-; mov rsi, [rsp+(16+2)*8]
-; call Log
-
- mov ebx, [rsp+(16+2)*8] ; Get interrupt number (16 GPRS + 2 SRs)
- shl ebx, 2 ; *4
- mov rax, gaIRQ_Handlers
- lea rbx, [rax+rbx*8]
-
- ; Check all callbacks
- sub rsp, 8 ; Shadow of argument
- %assign i 0
- %rep NUM_IRQ_CALLBACKS
- ; Get callback address
- mov rax, [rbx]
- test rax, rax ; Check if it exists
- jz .skip.%[i]
- ; Set RDI to IRQ number
- mov rdi, [rsp+(16+2+1)*8] ; Get IRQ number
- mov rsi, [rbx-gaIRQ_Handlers+gaIRQ_DataPtrs]
- call rax ; Call
-.skip.%[i]:
- add rbx, 8 ; Next!
- %assign i i+1
- %endrep
- add rsp, 8
-
- ; ACK
- mov al, 0x20
- mov rdi, [rsp+(16+2)*8] ; Get IRQ number
- cmp rdi, 8
- jb .skipAckSecondary
- out 0xA0, al
-.skipAckSecondary:
- out 0x20, al
-
- pop fs
- pop gs
- POP_GPR
- add rsp, 8*2
- iretq
-
-[extern Time_UpdateTimestamp]
-
-%if USE_MP
-[global APIC_Timer_IRQ]
-APIC_Timer_IRQ:
- PUSH_GPR
- push gs
- push fs
-
- ; TODO: What to do?
-
- mov eax, DWORD [gpMP_LocalAPIC]
- mov DWORD [eax+0x0B0], 0
-
- pop fs
- pop gs
- POP_GPR
- iretq
-%endif
-
-[global PIT_IRQ]
-PIT_IRQ:
- PUSH_GPR
- ;PUSH_FPU
- ;PUSH_XMM
-
- call Time_UpdateTimestamp
-
- %if 0
-[section .rodata]
-csUserSS: db "User SS: 0x%x",0
-[section .text]
- mov rdi, csUserSS
- mov rsi, [rsp+0x80+0x20]
- call Log
- %endif
-
- ; Send EOI
- mov al, 0x20
- out 0x20, al ; ACK IRQ
-
- ;POP_XMM
- ;POP_FPU
- POP_GPR
- iretq
-
-[extern ci_offsetof_tThread_KernelStack]
-[extern SyscallHandler]
-[global SyscallStub]
-SyscallStub:
- mov rbp, dr0
- mov ebx, [rel ci_offsetof_tThread_KernelStack]
- mov rbp, [rbp+rbx] ; Get kernel stack
- xchg rbp, rsp ; Swap stacks
-
- push rbp ; Save User RSP
- push rcx ; RIP
- push r11 ; RFLAGS
-
- ; RDI
- ; RSI
- ; RDX
- ; R10 (RCX for non syscall)
- ; R8
- ; R9
- sub rsp, (6+2)*8
- mov [rsp+0x00], rax ; Number
-; mov [rsp+0x08], rax ; Errno (output only)
- mov [rsp+0x10], rdi ; Arg1
- mov [rsp+0x18], rsi ; Arg2
- mov [rsp+0x20], rdx ; Arg3
- mov [rsp+0x28], r10 ; Arg4
- mov [rsp+0x30], r8 ; Arg5
- mov [rsp+0x38], r9 ; Arg6
-
- mov rdi, rsp
- sub rsp, 8
- call SyscallHandler
-
- %if 0
-[section .rodata]
-csSyscallReturn: db "Syscall Return: 0x%x",0
-[section .text]
- mov rdi, csSyscallReturn
- mov rsi, [rsp+0+8]
- call Log
- %endif
-
- add rsp, 8
- mov ebx, [rsp+8] ; Get errno
- mov rax, [rsp+0] ; Get return
- add rsp, (6+2)*8
-
- pop r11
- pop rcx
- pop rsp ; Change back to user stack
- ; TODO: Determine if user is 64 or 32 bit
-
- db 0x48 ; REX, nasm doesn't have a sysretq opcode
- sysret
-
-[section .data]
-gIDT:
- ; 64-bit Interrupt Gate, CS = 0x8, IST0 (Disabled)
- times 256 dd 0x00080000, 0x00000E00, 0, 0
-gIDTPtr:
- dw 256*16-1
- dq gIDT
-
-gaIRQ_Handlers:
- times 16*NUM_IRQ_CALLBACKS dq 0
-gaIRQ_DataPtrs:
- times 16*NUM_IRQ_CALLBACKS dq 0
-
-; vim: ft=nasm
+++ /dev/null
-/*
- * Acess2 x86_64 Project
- * - Error Handling
- */
-#include <acess.h>
-#include <proc.h>
-#include <mm_virt.h>
-#include <threads_int.h> // Needed for SSE handling
-
-#define MAX_BACKTRACE 6
-
-// === IMPORTS ===
-extern int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
-extern void Error_Backtrace(Uint IP, Uint BP);
-extern void Proc_EnableSSE(void);
-extern void Proc_RestoreSSE(Uint32 Data);
-
-// === PROTOTYPES ===
-void Error_Handler(tRegs *Regs);
-
-// === GLOBALS ==
-const char * const csaERROR_NAMES[] = {
- "Divide By Zero", "Debug", "NMI Exception", "INT3",
- "INTO", "Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable",
- "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present",
- "Stack Fault Exception", "GPF", "#PF", "Reserved",
- "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved",
- "Reserved", "Reserved", "Reserved", "Reserved",
- "Reserved", "Reserved", "Reserved", "Reserved",
- "Reserved", "Reserved", "Reserved", "Reserved"
- };
-
-// === CODE ===
-void Error_Handler(tRegs *Regs)
-{
- Uint cr;
-
- if( Regs->IntNum == 7 )
- {
- tThread *thread = Proc_GetCurThread();
- if(!thread->SavedState.bSSEModified)
- {
- Proc_EnableSSE();
- if(!thread->SavedState.SSE)
- thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF);
- else
- Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF );
- thread->SavedState.bSSEModified = 1;
-// __asm__ __volatile__ ("sti");
- return ;
- }
- // oops, SSE enabled but a #NM, bad news
- }
-
- if( Regs->IntNum == 14 ) {
- __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
- if( MM_PageFault(cr, Regs->ErrorCode, Regs) == 0 )
- return ;
- }
- else {
- Debug_KernelPanic();
-
- Error_Backtrace(Regs->RIP, Regs->RBP);
- }
-
- Log("CPU Error %x, Code: 0x%x", Regs->IntNum, Regs->ErrorCode);
- Log(" - %s", csaERROR_NAMES[Regs->IntNum]);
- Log(" CS:RIP = 0x%04x:%016llx", Regs->CS, Regs->RIP);
- Log(" SS:RSP = 0x%04x:%016llx", Regs->SS, Regs->RSP);
- Log(" RFLAGS = 0x%016llx", Regs->RFlags);
-
- Log(" RAX %016llx RCX %016llx RDX %016llx RBX %016llx",
- Regs->RAX, Regs->RCX, Regs->RDX, Regs->RBX);
- Log(" RSP %016llx RBP %016llx RSI %016llx RDI %016llx",
- Regs->RSP, Regs->RBP, Regs->RSP, Regs->RDI);
- Log(" R8 %016llx R9 %016llx R10 %016llx R11 %016llx",
- Regs->R8, Regs->R9, Regs->R10, Regs->R11);
- Log(" R12 %016llx R13 %016llx R14 %016llx R15 %016llx",
- Regs->R12, Regs->R13, Regs->R14, Regs->R15);
- Log(" FS %04x GS %04x", Regs->FS, Regs->GS);
-
-
- // Control Registers
- __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr));
- Warning(" CR0 0x%08x", cr);
- __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
- Warning(" CR2 0x%016llx", cr);
- __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr));
- Warning(" CR3 0x%016llx", cr);
- __asm__ __volatile__ ("mov %%cr4, %0":"=r"(cr));
- Warning(" CR4 0x%08x", cr);
-
- switch( Regs->IntNum )
- {
- case 6: // #UD
- Warning(" Offending bytes: %02x %02x %02x %02x",
- *(Uint8*)(Regs->RIP+0), *(Uint8*)(Regs->RIP+1),
- *(Uint8*)(Regs->RIP+2), *(Uint8*)(Regs->RIP+3)
- );
- break;
- }
-
- __asm__ __volatile__ ("cli");
- for(;;)
- __asm__ __volatile__ ("hlt");
-}
-
-/**
- * \fn void Error_Backtrace(Uint eip, Uint ebp)
- * \brief Unrolls the stack to trace execution
- * \param eip Current Instruction Pointer
- * \param ebp Current Base Pointer (Stack Frame)
- */
-void Error_Backtrace(Uint IP, Uint BP)
-{
- int i = 0;
-
- //if(eip < 0xC0000000 && eip > 0x1000)
- //{
- // LogF("Backtrace: User - 0x%x\n", eip);
- // return;
- //}
-
- if( IP > USER_MAX && IP < MM_KERNEL_CODE
- && (MM_MODULE_MIN > IP || IP > MM_MODULE_MAX)
- )
- {
- LogF("Backtrace: Data Area - %p\n", IP);
- return;
- }
-
- //str = Debug_GetSymbol(eip, &delta);
- //if(str == NULL)
- LogF("Backtrace: %p", IP);
- //else
- // LogF("Backtrace: %s+0x%x", str, delta);
- if( !MM_GetPhysAddr(BP) )
- {
- LogF("\nBacktrace: Invalid BP, stopping\n");
- return;
- }
-
-
- while( MM_GetPhysAddr(BP) && MM_GetPhysAddr(BP+8+7) && i < MAX_BACKTRACE )
- {
- //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta);
- //if(str == NULL)
- LogF(" >> 0x%llx", ((Uint*)BP)[1]);
- //else
- // LogF(" >> %s+0x%x", str, delta);
- BP = ((Uint*)BP)[0];
- i++;
- }
- LogF("\n");
-}
+++ /dev/null
-/*
- * Acess2 x86-64 Architecure Module
- * - By John Hodge (thePowersGang)
- */
-#ifndef _ARCH_H_
-#define _ARCH_H_
-
-//#include <stdint.h>
-#define USER_MAX 0x00007FFF##FFFFF000
-#define KERNEL_BASE 0xFFFFFFFF##80000000
-#define BITS 64
-#define PAGE_SIZE 0x1000
-
-#define STACKED_LOCKS 2 // 0: No, 1: Per-CPU, 2: Per-Thread
-#define LOCK_DISABLE_INTS 0
-
-#define INVLPTR ((void*)0x0FFFFFFFFFFFFFFFULL)
-
-//#define INT_MAX 0x7FFFFFFF
-//#define UINT_MAX 0xFFFFFFFF
-
-// === Core Types ===
-typedef signed char Sint8;
-typedef unsigned char Uint8;
-typedef signed short Sint16;
-typedef unsigned short Uint16;
-typedef signed int Sint32;
-typedef unsigned int Uint32;
-#if __WORDSIZE == 64
-typedef signed long int Sint64;
-typedef unsigned long int Uint64;
-#else
-typedef signed long long int Sint64;
-typedef unsigned long long int Uint64;
-#endif
-
-typedef Sint64 Sint;
-typedef Uint64 Uint;
-typedef Uint64 tPAddr;
-typedef Uint64 tVAddr;
-
-typedef Uint64 size_t;
-typedef char BOOL;
-
-#define __ASM__ __asm__ __volatile__
-
-// === MACROS ===
-/**
- * \brief Halt the CPU
- */
-#define HALT() __asm__ __volatile__ ("sti;\n\thlt")
-/**
- * \brief Fire a magic breakpoint (bochs)
- */
-#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx")
-
-// Systemcall Registers
-// TODO: Fix this structure
-typedef struct sSyscallRegs
-{
- union {
- Uint Num;
- Uint Return;
- }; // RAX
- Uint Error; // RBX
- Uint Arg1; // RDI
- Uint Arg2; // RSI
- Uint Arg3; // RDX
- Uint Arg4; // RCX
- Uint Arg5; // R8
- Uint Arg6; // R9
- Uint _Flags;
- Uint _IP;
- Uint StackPointer; // RSP
-
-} tSyscallRegs;
-
-/**
- * \brief Short Spinlock structure
- */
-struct sShortSpinlock {
- #if STACKED_LOCKS == 2
- volatile void *Lock; //!< Lock value
- #else
- volatile int Lock; //!< Lock value
- #endif
-
- #if LOCK_DISABLE_INTS
- int IF; //!< Interrupt state on call to SHORTLOCK
- #endif
- #if STACKED_LOCKS
- int Depth;
- #endif
-};
-
-// === FUNCTIONS ===
-extern int IS_LOCKED(struct sShortSpinlock *Lock);
-extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock);
-extern void SHORTLOCK(struct sShortSpinlock *Lock);
-extern void SHORTREL(struct sShortSpinlock *Lock);
-
-extern void Debug_PutCharDebug(char ch);
-extern void Debug_PutStringDebug(const char *Str);
-
-// TODO: Move this to acess.h
-extern tPAddr MM_AllocateZero(tVAddr VAddr);
-
-#endif
-
+++ /dev/null
-/*
- */
-#ifndef _ARCH_CONFIG_H_
-#define _ARCH_CONFIG_H_
-
-
-#define PIT_TIMER_BASE_N 3579545
-#define PIT_TIMER_BASE_D 3
-// PIT Ticks at 1.1931816666666 MHz
-// Base is 1193182 HZ
-#define PIT_TIMER_DIVISOR 11931 //~100Hz
-
-#endif
+++ /dev/null
-
-%define INITIAL_KSTACK_SIZE 8
-
-%macro SAVE_GPR 1
- mov [%1-0x08], r15
- mov [%1-0x10], r14
- mov [%1-0x18], r13
- mov [%1-0x20], r12
- mov [%1-0x28], r11
- mov [%1-0x30], r10
- mov [%1-0x38], r9
- mov [%1-0x40], r8
- mov [%1-0x48], rdi
- mov [%1-0x50], rsi
- mov [%1-0x58], rbp
- mov [%1-0x60], rsp
- mov [%1-0x68], rbx
- mov [%1-0x70], rdx
- mov [%1-0x78], rcx
- mov [%1-0x80], rax
-%endmacro
-
-%macro PUSH_GPR 0
- SAVE_GPR rsp
- sub rsp, 0x80
-%endmacro
-
-%macro RESTORE_GPR 1
- mov r15, [%1-0x08]
- mov r14, [%1-0x10]
- mov r13, [%1-0x18]
- mov r12, [%1-0x20]
- mov r11, [%1-0x28]
- mov r10, [%1-0x30]
- mov r9, [%1-0x38]
- mov r8, [%1-0x40]
- mov rdi, [%1-0x48]
- mov rsi, [%1-0x50]
- mov rbp, [%1-0x58]
- ;mov rsp, [%1-0x60]
- mov rbx, [%1-0x68]
- mov rdx, [%1-0x70]
- mov rcx, [%1-0x78]
- mov rax, [%1-0x80]
-%endmacro
-
-%macro POP_GPR 0
- add rsp, 0x80
- RESTORE_GPR rsp
-%endmacro
+++ /dev/null
-/**
- */
-#ifndef _DESCTAB_H_
-#define _DESCTAB_H_
-
-typedef struct {
- union
- {
- struct
- {
- Uint16 LimitLow;
- Uint16 BaseLow;
- Uint8 BaseMid;
- Uint8 Access;
- struct {
- unsigned LimitHi: 4;
- unsigned Flags: 4;
- } __attribute__ ((packed));
- Uint8 BaseHi;
- };
- Uint32 DWord[2];
- };
-} __attribute__ ((packed)) tGDT;
-
-typedef struct {
- Uint16 OffsetLo;
- Uint16 CS;
- Uint16 Flags; // 0-2: IST, 3-7: 0, 8-11: Type, 12: 0, 13-14: DPL, 15: Present
- Uint16 OffsetMid;
- Uint32 OffsetHi;
- Uint32 Reserved;
-} __attribute__ ((packed)) tIDT;
-
-typedef struct {
- Uint32 Rsvd1;
-
- Uint64 RSP0;
- Uint64 RSP1;
- Uint64 RSP2;
-
- Uint32 Rsvd2[2];
-
- // Interrupt Stack Table Pointers
- Uint64 IST1;
- Uint64 IST2;
- Uint64 IST3;
- Uint64 IST4;
- Uint64 IST5;
- Uint64 IST6;
- Uint64 IST7;
-
- Uint32 Rsvd3[2];
- Uint16 Rsvd4;
- Uint16 IOMapBase;
-} __attribute__ ((packed)) tTSS;
-
-#endif
+++ /dev/null
-/*
- * Acess2 x86_64 Architecture Code
- *
- * This file is published under the terms of the Acess Licence.
- * See the file COPYING for more details
- *
- * vmem.h - Virtual Memory Functions & Definitions
- */
-#ifndef _VMEM_H_
-#define _VMEM_H_
-
-#include <arch.h>
-
-#define PAGE_SIZE 0x1000
-
-// === Memory Location Definitions ===
-/*
- * Userland - Lower Half
- * Kernel land - Upper Half
- *
- * START ADDRESS END ADDRESS BITS SIZE NAME
- * 0x00000000 00000000 - 0x00007FFF FFFFFFFF 47 128 TiB User Space
- * 0x00008000 00000000 - 0xFFFF7FFF FFFFFFFF --- SIGN EXTENSION NULL ZONE
- * 0xFFFF8000 00000000 - 0xFFFFFFFF FFFFFFFF 47 128 TiB Kernel Range
- * 8000 00000000 - 9000 00000000 42 16 TiB Kernel Heap
- * 9000 00000000 - 9800 00000000 43 8 TiB Module Space
- * 9800 00000000 - 9A00 00000000 41 2 TiB Kernel VFS
- * ---- GAP ---- 6 TiB
- * A000 00000000 - B000 00000000 44 16 TiB Kernel Stacks
- * C000 00000000 - D000 00000000 44 16 TiB Hardware Mappings
- * D000 00000000 - D080 00000000 39 512 GiB Per-Process Data
- * D080 00000000 - D100 00000000 39 512 GiB Kernel Supplied User Code
- * ---- GAP ---- 15 TiB
- * E000 00000000 - E800 00000000 43 8 TiB Physical Page Nodes (2**40 pages * 8 bytes)
- * E800 00000000 - EC00 00000000 42 4 TiB Physical Page Reference Counts (2**40 pg * 4 bytes)
- * EC00 00000000 - EC80 00000000 39 512 GiB Physical Page Bitmap (1 page per bit)
- * EC80 00000000 - ED00 00000000 39 512 GiB Physical Page DblAlloc Bitmap (1 page per bit)
- * ED00 00000000 - ED00 80000000 31 2 GiB Physical Page Super Bitmap (64 pages per bit)
- * ---- GAP ---- 9 TiB
- * FE00 00000000 - FE80 00000000 39 512 GiB Fractal Mapping (PML4 508)
- * FE80 00000000 - FF00 00000000 39 512 GiB Temp Fractal Mapping
- * FF00 00000000 - FF80 00000000 39 512 GiB Temporary page mappings
- * FF80 00000000 - FF80 80000000 31 2 GiB Local APIC
- * ---- GAP ---- 506 GiB
- * FFFF 00000000 - FFFF 80000000 31 2 GiB User Code
- * FFFF 80000000 - FFFF FFFFFFFF 31 2 GiB Kernel code / data
- */
-
-#define MM_USER_MIN 0x00000000##00010000
-#define USER_LIB_MAX 0x00007000##00000000
-#define USER_STACK_PREALLOC 0x00000000##00002000 // 8 KiB
-#define USER_STACK_SZ 0x00000000##00020000 // 64 KiB
-#define USER_STACK_TOP 0x00008000##00000000
-#define MM_KERNEL_RANGE 0xFFFF8000##00000000
-#define MM_KHEAP_BASE (MM_KERNEL_RANGE|(0x8000##00000000))
-#define MM_KHEAP_MAX (MM_KERNEL_RANGE|(0x9000##00000000))
-#define MM_MODULE_MIN (MM_KERNEL_RANGE|(0x9000##00000000))
-#define MM_MODULE_MAX (MM_KERNEL_RANGE|(0x9800##00000000))
-#define MM_KERNEL_VFS (MM_KERNEL_RANGE|(0x9800##00000000))
-#define MM_KSTACK_BASE (MM_KERNEL_RANGE|(0xA000##00000000))
-#define MM_KSTACK_TOP (MM_KERNEL_RANGE|(0xB000##00000000))
-
-#define MM_HWMAP_BASE (MM_KERNEL_RANGE|(0xC000##00000000))
-#define MM_HWMAP_TOP (MM_KERNEL_RANGE|(0xD000##00000000))
-#define MM_PPD_BASE (MM_KERNEL_RANGE|(0xD000##00000000))
-#define MM_PPD_CFG MM_PPD_BASE
-#define MM_PPD_HANDLES (MM_KERNEL_RANGE|(0xD008##00000000))
-#define MM_USER_CODE (MM_KERNEL_RANGE|(0xD080##00000000))
-
-#define MM_PAGE_NODES (MM_KERNEL_RANGE|(0xE000##00000000))
-#define MM_PAGE_COUNTS (MM_KERNEL_RANGE|(0xE800##00000000))
-#define MM_PAGE_BITMAP (MM_KERNEL_RANGE|(0xEC00##00000000))
-#define MM_PAGE_DBLBMP (MM_KERNEL_RANGE|(0xEC00##00000000))
-#define MM_PAGE_SUPBMP (MM_KERNEL_RANGE|(0xED00##00000000))
-
-#define MM_FRACTAL_BASE (MM_KERNEL_RANGE|(0xFE00##00000000))
-#define MM_TMPFRAC_BASE (MM_KERNEL_RANGE|(0xFE80##00000000))
-#define MM_TMPMAP_BASE (MM_KERNEL_RANGE|(0xFF00##00000000))
-#define MM_TMPMAP_END (MM_KERNEL_RANGE|(0xFF80##00000000))
-#define MM_LOCALAPIC (MM_KERNEL_RANGE|(0xFF80##00000000))
-#define MM_KERNEL_CODE (MM_KERNEL_RANGE|(0xFFFF##80000000))
-
-
-// === FUNCTIONS ===
-void MM_FinishVirtualInit(void);
-tVAddr MM_NewKStack(void);
-tVAddr MM_Clone(void);
-tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize);
-
-#endif
+++ /dev/null
-/*
- * Acess2 x86_64 Port
- *
- * proc.h - Process/Thread management code
- */
-#ifndef _PROC_H_
-#define _PROC_H_
-
-#include <arch.h>
-
-// Register Structure
-// TODO: Rebuild once IDT code is done
-typedef struct {
- // MMX
- // FPU
- Uint FS, GS;
-
- Uint RAX, RCX, RDX, RBX;
- Uint KernelRSP, RBP, RSI, RDI;
- Uint R8, R9, R10, R11;
- Uint R12, R13, R14, R15;
-
- Uint IntNum, ErrorCode;
- Uint RIP, CS;
- Uint RFlags, RSP, SS;
-} tRegs;
-
-/**
- * \brief Memory State for thread handler
- */
-typedef struct sMemoryState
-{
- tPAddr CR3;
-} tMemoryState;
-
-// 512 bytes, 16 byte aligned
-typedef struct sSSEState
-{
- char data[512];
-} tSSEState;
-
-/**
- * \brief Task state for thread handler
- */
-typedef struct sTaskState
-{
- Uint RIP, RSP;
- Uint64 UserRIP, UserCS;
- tSSEState *SSE;
- int bSSEModified;
-} tTaskState;
-
-// === CONSTANTS ===
-#define KERNEL_STACK_SIZE 0x8000 // 32 KiB
-//#define KERNEL_STACK_SIZE 0x10000 // 64 KiB
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 VM8086 BIOS Interface
- * - By John Hodge (thePowersGang)
- *
- * vm8086.h
- * - Core Header
- */
-#ifndef _VM80806_H_
-#define _VM80806_H_
-
-// === TYPES ===
-/**
- * \note Semi-opaque - Past \a .IP, the implementation may add any data
- * it needs to the state.
- */
-typedef struct sVM8086
-{
- Uint16 AX, CX, DX, BX;
- Uint16 BP, SP, SI, DI;
-
- Uint16 SS, DS, ES;
-
- Uint16 CS, IP;
-
- struct sVM8086_InternalData *Internal;
-} tVM8086;
-
-// === FUNCTIONS ===
-/**
- * \brief Create an instance of the VM8086 Emulator
- * \note Do not free this pointer with ::free, instead use ::VM8086_Free
- * \return Pointer to a tVM8086 structure, this structure may be larger than
- * tVM8086 due to internal data.
- */
-extern tVM8086 *VM8086_Init(void);
-/**
- * \brief Free an allocated tVM8086 structure
- * \param State Emulator state to free
- */
-extern void VM8086_Free(tVM8086 *State);
-/**
- * \brief Allocate a piece of memory in the emulated address space and
- * return a host and emulated pointer to it.
- * \param State Emulator state
- * \param Size Size of memory block
- * \param Segment Pointer to location to store the allocated memory's segment
- * \param Offset Pointet to location to store the allocated memory's offset
- * \return Host pointer to the allocated memory
- */
-extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset);
-/**
- * \brief Gets a pointer to a piece of emulated memory
- * \todo Only 1 machine page is garenteed to be contiguous
- * \param State Emulator State
- * \param Segment Source Segment
- * \param Offset Source Offset
- * \return Host pointer to the emulated memory
- */
-extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset);
-/**
- * \brief Calls a real-mode interrupt described by the current state of the IVT.
- * \param State Emulator State
- * \param Interrupt BIOS Interrupt to call
- */
-extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt);
-
-#endif
+++ /dev/null
-/*
- * Acess2 x86_64 port
- * - Kernel Panic output
- */
-#include <acess.h>
-
-// === PROTOTYPES ===
-void KernelPanic_SetMode(void);
-void KernelPanic_PutChar(char ch);
-
-// === GLOBALS ===
-Uint16 *gpKernelPanic_Buffer = (void*)( KERNEL_BASE|0xB8000 );
- int giKernelPanic_CurPos = 0;
-
-// === CODE ===
-void KernelPanic_SetMode(void)
-{
- giKernelPanic_CurPos = 0;
-}
-
-void KernelPanic_PutChar(char ch)
-{
- switch(ch)
- {
- case '\n':
- giKernelPanic_CurPos += 80;
- case '\r':
- giKernelPanic_CurPos /= 80;
- giKernelPanic_CurPos *= 80;
- break;
-
- default:
- if(' ' <= ch && ch <= 0x7F)
- gpKernelPanic_Buffer[giKernelPanic_CurPos] = 0x4F00|ch;
- giKernelPanic_CurPos ++;
- break;
- }
-}
+++ /dev/null
-/*
- */
-#include <acess.h>
-#include <arch.h>
-
-#define DEBUG_TO_E9 1
-#define DEBUG_TO_SERIAL 1
-#define SERIAL_PORT 0x3F8
-#define GDB_SERIAL_PORT 0x2F8
-
-
-// === IMPORTS ===
-extern int GetCPUNum(void);
-extern void *Proc_GetCurThread(void);
-
-// === GLOBALS ===
- int gbDebug_SerialSetup = 0;
- int gbGDB_SerialSetup = 0;
-
-// === PROTOTYPEs ===
- int putDebugChar(char ch);
-
-// === CODE ===
-/**
- * \brief Determine if a short spinlock is locked
- * \param Lock Lock pointer
- */
-int IS_LOCKED(struct sShortSpinlock *Lock)
-{
- return !!Lock->Lock;
-}
-
-/**
- * \brief Check if the current CPU has the lock
- * \param Lock Lock pointer
- */
-int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
-{
- #if STACKED_LOCKS == 1
- return Lock->Lock == GetCPUNum() + 1;
- #elif STACKED_LOCKS == 2
- return Lock->Lock == Proc_GetCurThread();
- #else
- return 0;
- #endif
-}
-
-/**
- * \brief Acquire a Short Spinlock
- * \param Lock Lock pointer
- *
- * This type of mutex should only be used for very short sections of code,
- * or in places where a Mutex_* would be overkill, such as appending
- * an element to linked list (usually two assignement lines in C)
- *
- * \note This type of lock halts interrupts, so ensure that no timing
- * functions are called while it is held. As a matter of fact, spend as
- * little time as possible with this lock held
- * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested
- */
-void SHORTLOCK(struct sShortSpinlock *Lock)
-{
- int v = 1;
- #if LOCK_DISABLE_INTS
- int IF;
- #endif
- #if STACKED_LOCKS == 1
- int cpu = GetCPUNum() + 1;
- #elif STACKED_LOCKS == 2
- void *thread = Proc_GetCurThread();
- #endif
-
- #if LOCK_DISABLE_INTS
- // Save interrupt state and clear interrupts
- __ASM__ ("pushf;\n\tpop %0" : "=r"(IF));
- IF &= 0x200; // AND out all but the interrupt flag
- #endif
-
- #if STACKED_LOCKS == 1
- if( Lock->Lock == cpu ) {
- Lock->Depth ++;
- return ;
- }
- #elif STACKED_LOCKS == 2
- if( Lock->Lock == thread ) {
- Lock->Depth ++;
- return ;
- }
- #endif
-
- // Wait for another CPU to release
- while(v) {
- // CMPXCHG:
- // If r/m32 == EAX, set ZF and set r/m32 = r32
- // Else, clear ZF and set EAX = r/m32
- #if STACKED_LOCKS == 1
- __ASM__("lock cmpxchgl %2, (%3)"
- : "=a"(v)
- : "a"(0), "r"(cpu), "r"(&Lock->Lock)
- );
- #elif STACKED_LOCKS == 2
- __ASM__("lock cmpxchgq %2, (%3)"
- : "=a"(v)
- : "a"(0), "r"(thread), "r"(&Lock->Lock)
- );
- #else
- __ASM__("xchgl %0, (%2)":"=a"(v):"a"(1),"D"(&Lock->Lock));
- #endif
-
- #if LOCK_DISABLE_INTS
- if( v ) __ASM__("sti"); // Re-enable interrupts
- #endif
- }
-
- #if LOCK_DISABLE_INTS
- __ASM__("cli");
- Lock->IF = IF;
- #endif
-}
-/**
- * \brief Release a short lock
- * \param Lock Lock pointer
- */
-void SHORTREL(struct sShortSpinlock *Lock)
-{
- #if STACKED_LOCKS
- if( Lock->Depth ) {
- Lock->Depth --;
- return ;
- }
- #endif
-
- #if LOCK_DISABLE_INTS
- // Lock->IF can change anytime once Lock->Lock is zeroed
- if(Lock->IF) {
- Lock->Lock = 0;
- __ASM__ ("sti");
- }
- else {
- Lock->Lock = 0;
- }
- #else
- Lock->Lock = 0;
- #endif
-}
-
-// === DEBUG IO ===
-#if USE_GDB_STUB
-int putDebugChar(char ch)
-{
- if(!gbGDB_SerialSetup) {
- outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
- outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
- outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
- outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte)
- outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1)
- outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
- outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
- gbDebug_SerialSetup = 1;
- }
- while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 );
- outb(GDB_SERIAL_PORT, ch);
- return 0;
-}
-int getDebugChar(void)
-{
- if(!gbGDB_SerialSetup) {
- outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
- outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
- outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
- outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte)
- outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
- outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
- outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
- gbDebug_SerialSetup = 1;
- }
- while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ;
- return inb(GDB_SERIAL_PORT);
-}
-#endif
-
-void Debug_PutCharDebug(char ch)
-{
- #if DEBUG_TO_E9
- __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) );
- #endif
-
- #if DEBUG_TO_SERIAL
- if(!gbDebug_SerialSetup) {
- outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts
- outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
- outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
- outb(SERIAL_PORT + 1, 0x00); // (hi byte)
- outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
- outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
- outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
- gbDebug_SerialSetup = 1;
- }
- while( (inb(SERIAL_PORT + 5) & 0x20) == 0 );
- outb(SERIAL_PORT, ch);
- #endif
-}
-
-void Debug_PutStringDebug(const char *String)
-{
- while(*String)
- Debug_PutCharDebug(*String++);
-}
-
-// === PORT IO ===
-void outb(Uint16 Port, Uint8 Data)
-{
- __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data));
-}
-void outw(Uint16 Port, Uint16 Data)
-{
- __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data));
-}
-void outd(Uint16 Port, Uint32 Data)
-{
- __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data));
-}
-Uint8 inb(Uint16 Port)
-{
- Uint8 ret;
- __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port));
- return ret;
-}
-Uint16 inw(Uint16 Port)
-{
- Uint16 ret;
- __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port));
- return ret;
-}
-Uint32 ind(Uint16 Port)
-{
- Uint32 ret;
- __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port));
- return ret;
-}
-
-// === Endianness ===
-/*
-Uint32 BigEndian32(Uint32 Value)
-{
- Uint32 ret;
- ret = (Value >> 24);
- ret |= ((Value >> 16) & 0xFF) << 8;
- ret |= ((Value >> 8) & 0xFF) << 16;
- ret |= ((Value >> 0) & 0xFF) << 24;
- return ret;
-}
-
-Uint16 BigEndian16(Uint16 Value)
-{
- return (Value>>8)|(Value<<8);
-}
-*/
-
-// === Memory Manipulation ===
-int memcmp(const void *__dest, const void *__src, size_t __count)
-{
- if( ((tVAddr)__dest & 7) != ((tVAddr)__src & 7) ) {
- const Uint8 *src = __src, *dst = __dest;
- while(__count)
- {
- if( *src != *dst )
- return *dst - *src;
- src ++; dst ++; __count --;
- }
- return 0;
- }
- else {
- const Uint8 *src = __src;
- const Uint8 *dst = __dest;
- const Uint64 *src64, *dst64;
-
- while( (tVAddr)src & 7 && __count ) {
- if( *src != *dst )
- return *dst - *src;
- dst ++; src ++; __count --;
- }
-
- src64 = (void*)src;
- dst64 = (void*)dst;
-
- while( __count >= 8 )
- {
- if( *src64 != *dst64 )
- {
- src = (void*)src64;
- dst = (void*)dst64;
- if(src[0] != dst[0]) return dst[0]-src[0];
- if(src[1] != dst[1]) return dst[1]-src[1];
- if(src[2] != dst[2]) return dst[2]-src[2];
- if(src[3] != dst[3]) return dst[3]-src[3];
- if(src[4] != dst[4]) return dst[4]-src[4];
- if(src[5] != dst[5]) return dst[5]-src[5];
- if(src[6] != dst[6]) return dst[6]-src[6];
- if(src[7] != dst[7]) return dst[7]-src[7];
- return -1; // This should never happen
- }
- __count -= 8;
- src64 ++;
- dst64 ++;
- }
-
- src = (void*)src64;
- dst = (void*)dst64;
- while( __count-- )
- {
- if(*dst != *src) return *dst - *src;
- dst ++;
- src ++;
- }
- }
- return 0;
-}
-
-void *memcpy(void *__dest, const void *__src, size_t __count)
-{
- tVAddr dst = (tVAddr)__dest, src = (tVAddr)__src;
- if( (dst & 7) != (src & 7) )
- {
- __asm__ __volatile__ ("rep movsb" : : "D"(dst),"S"(src),"c"(__count));
- }
- else
- {
- while( (src & 7) && __count ) {
- *(char*)dst++ = *(char*)src++;
- __count --;
- }
-
- __asm__ __volatile__ ("rep movsq" : "=D"(dst),"=S"(src) : "0"(dst),"1"(src),"c"(__count/8));
- __count = __count & 7;
- while( __count-- )
- *(char*)dst++ = *(char*)src++;
- }
- return __dest;
-}
-
-void *memset(void *__dest, int __val, size_t __count)
-{
- if( __val != 0 || ((tVAddr)__dest & 7) != 0 )
- __asm__ __volatile__ ("rep stosb" : : "D"(__dest),"a"(__val),"c"(__count));
- else {
- Uint8 *dst = __dest;
-
- __asm__ __volatile__ ("rep stosq" : : "D"(dst),"a"(0),"c"(__count/8));
- dst += __count & ~7;
- __count = __count & 7;
- while( __count-- )
- *dst++ = 0;
- }
- return __dest;
-}
-
-void *memsetd(void *__dest, Uint32 __val, size_t __count)
-{
- __asm__ __volatile__ ("rep stosl" : : "D"(__dest),"a"(__val),"c"(__count));
- return __dest;
-}
-
-Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem)
-{
- Uint64 ret, rem;
- __asm__ __volatile__(
- "div %4"
- : "=a" (ret), "=d" (rem)
- : "a" ( Num ), "d" (0), "r" (Den)
- );
- if(Rem) *Rem = rem;
- return ret;
-}
-
+++ /dev/null
-/*
- * Acess2 x86_64 Kernel
- * Linker Script
- */
-
-/* _kernel_base = 0xFFFF800000000000; */
-/* -2 GiB */
-_kernel_base = 0xFFFFFFFF80000000;
-
-/*
-OUTPUT_FORMAT(elf32-i386)
-OUTPUT_ARCH(i386:x86-64)
-*/
-OUTPUT_FORMAT(elf64-x86-64)
-ENTRY(start)
-
-SECTIONS {
- . = 0x100000;
- gKernelBase = .;
- . += SIZEOF_HEADERS;
- __load_addr = .;
- .multiboot : AT(ADDR(.multiboot)) {
- *(.multiboot)
- }
-
- . += _kernel_base;
-
- .text ALIGN(0x1000): AT(ADDR(.text) - _kernel_base) {
- *(.text)
- }
-
- .usertext ALIGN(0x1000): AT(ADDR(.usertext) - _kernel_base) {
- _UsertextBase = .;
- *(.usertext)
- _UsertextEnd = .;
- }
-
- .rodata ALIGN(0x1000): AT(ADDR(.rodata) - _kernel_base) {
- *(.initpd)
- *(.rodata .rodata.*)
- *(.rdata)
-
- . = ALIGN(0x10);
- gKernelModules = .;
- *(KMODULES)
- gKernelModulesEnd = .;
- . = ALIGN(0x10);
- gKernelSymbols = .;
- *(KEXPORT)
- gKernelSymbolsEnd = .;
- }
-
- .data ALIGN (0x1000) : AT(ADDR(.data) - _kernel_base) {
- *(.padata)
- *(.data)
- }
-
- __bss_start = .;
- .bss : AT(ADDR(.bss) - _kernel_base) {
- *(COMMON)
- *(.bss)
- }
- gKernelEnd = (. + 0xFFF)&0xFFFFFFFFFFFFF000;
-}
+++ /dev/null
-/*
- * Acess2 x86_64 Project
- */
-#include <acess.h>
-#include <mboot.h>
-#include <init.h>
-
-// === IMPORTS ===
-extern void Desctab_Init(void);
-extern void MM_InitVirt(void);
-extern void Heap_Install(void);
-extern void Threads_Init(void);
-extern int Time_Setup(void);
-extern void System_Init(char *Commandline);
-
-extern void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
-
-// === PROTOTYPES ===
-void kmain(Uint MbMagic, void *MbInfoPtr);
-
-// === GLOBALS ==
-char *gsBootCmdLine = NULL;
-
-// === CODE ===
-void kmain(Uint MbMagic, void *MbInfoPtr)
-{
- tMBoot_Info *mbInfo;
-
- LogF("Acess2 x86_64 v"EXPAND_STR(KERNEL_VERSION)"\n");
- LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash);
-
- Desctab_Init();
-
- MM_InitVirt();
- *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'C';
-
- switch(MbMagic)
- {
- // Multiboot 1
- case MULTIBOOT_MAGIC:
- // Adjust Multiboot structure address
- mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE );
- gsBootCmdLine = (char*)( (Uint)mbInfo->CommandLine + KERNEL_BASE);
- MM_InitPhys_Multiboot( mbInfo ); // Set up physical memory manager
- break;
- default:
- Panic("Multiboot magic invalid %08x, expected %08x\n",
- MbMagic, MULTIBOOT_MAGIC);
- return ;
- }
-
- Log("gsBootCmdLine = '%s'", gsBootCmdLine);
-
- *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'D';
- Heap_Install();
-
- *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'E';
- Threads_Init();
-
- Time_Setup();
- *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'F';
-
- // Load Virtual Filesystem
- Log_Log("Arch", "Starting VFS...");
- VFS_Init();
-
- *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'Z';
-
- // Pass on to Independent Loader
- Log_Log("Arch", "Starting system");
- System_Init(gsBootCmdLine);
-
- // Sleep forever (sleeping beauty)
- for(;;)
- Threads_Sleep();
-}
-
-void Arch_LoadBootModules(void)
-{
-
-}
-
-void StartupPrint(const char *String)
-{
-
-}
+++ /dev/null
-/*
- * Acess2 x86_64 Port
- *
- * Physical Memory Manager
- */
-#define DEBUG 0
-#include <acess.h>
-#include <mboot.h>
-#include <mm_virt.h>
-
-#define TRACE_REF 0
-
-enum eMMPhys_Ranges
-{
- MM_PHYS_16BIT, // Does anything need this?
- MM_PHYS_20BIT, // Real-Mode
- MM_PHYS_24BIT, // ISA DMA
- MM_PHYS_32BIT, // x86 Hardware
- MM_PHYS_MAX, // Doesn't care
- NUM_MM_PHYS_RANGES
-};
-
-// === IMPORTS ===
-extern char gKernelBase[];
-extern char gKernelEnd[];
-
-// === PROTOTYPES ===
-void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
-//tPAddr MM_AllocPhysRange(int Num, int Bits);
-//tPAddr MM_AllocPhys(void);
-//void MM_RefPhys(tPAddr PAddr);
-//void MM_DerefPhys(tPAddr PAddr);
- int MM_int_GetRangeID( tPAddr Addr );
-
-// === MACROS ===
-#define PAGE_ALLOC_TEST(__page) (gaMainBitmap[(__page)>>6] & (1ULL << ((__page)&63)))
-#define PAGE_ALLOC_SET(__page) do{gaMainBitmap[(__page)>>6] |= (1ULL << ((__page)&63));}while(0)
-#define PAGE_ALLOC_CLEAR(__page) do{gaMainBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0)
-//#define PAGE_MULTIREF_TEST(__page) (gaMultiBitmap[(__page)>>6] & (1ULL << ((__page)&63)))
-//#define PAGE_MULTIREF_SET(__page) do{gaMultiBitmap[(__page)>>6] |= 1ULL << ((__page)&63);}while(0)
-//#define PAGE_MULTIREF_CLEAR(__page) do{gaMultiBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0)
-
-// === GLOBALS ===
-tMutex glPhysicalPages;
-Uint64 *gaSuperBitmap = (void*)MM_PAGE_SUPBMP; // 1 bit = 64 Pages, 16 MiB per Word
-Uint64 *gaMainBitmap = (void*)MM_PAGE_BITMAP; // 1 bit = 1 Page, 256 KiB per Word
-Uint64 *gaMultiBitmap = (void*)MM_PAGE_DBLBMP; // Each bit means that the page is being used multiple times
-Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
-void **gapPageNodes = (void*)MM_PAGE_NODES; // Reference Counts
-tPAddr giFirstFreePage; // First possibly free page
-Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range
-Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range
-Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range
-Uint64 giMaxPhysPage = 0; // Maximum Physical page
-// Only used in init, allows the init code to provide pages for use by
-// the allocator before the bitmaps exist.
-// 3 entries because the are three calls to MM_AllocPhys in MM_Map
-#define NUM_STATIC_ALLOC 3
-tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0};
-
-// === CODE ===
-/**
- * \brief Initialise the physical memory map using a Multiboot 1 map
- */
-void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
-{
- tMBoot_MMapEnt *mmapStart;
- tMBoot_MMapEnt *ent;
- Uint64 maxAddr = 0;
- int numPages, superPages;
- int i;
- Uint64 base, size;
- tVAddr vaddr;
- tPAddr paddr, firstFreePage;
-
- ENTER("pMBoot=%p", MBoot);
-
- // Scan the physical memory map
- // Looking for the top of physical memory
- mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
- LOG("mmapStart = %p", mmapStart);
- ent = mmapStart;
- while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
- {
- // Adjust for the size of the entry
- ent->Size += 4;
- LOG("ent={Type:%i,Base:0x%x,Length:%x",
- ent->Type, ent->Base, ent->Length);
-
- // If entry is RAM and is above `maxAddr`, change `maxAddr`
- if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
- maxAddr = ent->Base + ent->Length;
-
- // Go to next entry
- ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
- }
-
- // Did we find a valid end?
- if(maxAddr == 0) {
- // No, darn, let's just use the HighMem hack
- giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
- }
- else {
- // Goodie, goodie gumdrops
- giMaxPhysPage = maxAddr >> 12;
- }
- LOG("giMaxPhysPage = 0x%x", giMaxPhysPage);
-
- // Find a contigous section of memory to hold it in
- // - Starting from the end of the kernel
- // - We also need a region for the super bitmap
- superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
- numPages = (giMaxPhysPage + 7) / 8;
- numPages = (numPages + 0xFFF) >> 12;
- LOG("numPages = %i, superPages = %i", numPages, superPages);
- if(maxAddr == 0)
- {
- int todo = numPages*2 + superPages;
- // Ok, naieve allocation, just put it after the kernel
- // - Allocated Bitmap
- vaddr = MM_PAGE_BITMAP;
- paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
- while(todo )
- {
- // Allocate statics
- for( i = 0; i < NUM_STATIC_ALLOC; i++) {
- if(gaiStaticAllocPages[i] != 0) continue;
- gaiStaticAllocPages[i] = paddr;
- paddr += 0x1000;
- }
-
- MM_Map(vaddr, paddr);
- vaddr += 0x1000;
- paddr += 0x1000;
-
- todo --;
-
- if( todo == numPages + superPages )
- vaddr = MM_PAGE_DBLBMP;
- if( todo == superPages )
- vaddr = MM_PAGE_SUPBMP;
- }
- }
- // Scan for a nice range
- else
- {
- int todo = numPages*2 + superPages;
- paddr = 0;
- vaddr = MM_PAGE_BITMAP;
- // Scan!
- for(
- ent = mmapStart;
- (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
- ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
- )
- {
- int avail;
-
- // RAM only please
- if( ent->Type != 1 )
- continue;
-
- // Let's not put it below the kernel, shall we?
- if( ent->Base + ent->Size < (tPAddr)&gKernelBase )
- continue;
-
- LOG("%x <= %x && %x > %x",
- ent->Base, (tPAddr)&gKernelBase,
- ent->Base + ent->Size, (tPAddr)&gKernelEnd - KERNEL_BASE
- );
- // Check if the kernel is in this range
- if( ent->Base <= (tPAddr)&gKernelBase
- && ent->Base + ent->Length > (tPAddr)&gKernelEnd - KERNEL_BASE )
- {
- avail = ent->Length >> 12;
- avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12;
- paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
- }
- // No? then we can use all of the block
- else
- {
- avail = ent->Length >> 12;
- paddr = ent->Base;
- }
-
- Log("MM_InitPhys_Multiboot: paddr=0x%x, avail=0x%x pg", paddr, avail);
-
- // Map
- while( todo && avail --)
- {
- // Static Allocations
- for( i = 0; i < NUM_STATIC_ALLOC && avail; i++) {
- if(gaiStaticAllocPages[i] != 0) continue;
- gaiStaticAllocPages[i] = paddr;
- paddr += 0x1000;
- avail --;
- }
- if(!avail) break;
-
- // Map
- MM_Map(vaddr, paddr);
- todo --;
- vaddr += 0x1000;
- paddr += 0x1000;
-
- // Alter the destination address when needed
- if(todo == superPages+numPages)
- vaddr = MM_PAGE_DBLBMP;
- if(todo == superPages)
- vaddr = MM_PAGE_SUPBMP;
- }
-
- // Fast quit if there's nothing left to allocate
- if( !todo ) break;
- }
- }
- // Save the current value of paddr to simplify the allocation later
- firstFreePage = paddr;
-
- LOG("Clearing multi bitmap");
- // Fill the bitmaps
- memset(gaMultiBitmap, 0, (numPages<<12)/8);
- // - initialise to one, then clear the avaliable areas
- memset(gaMainBitmap, -1, (numPages<<12)/8);
- memset(gaSuperBitmap, -1, (numPages<<12)/(8*64));
- LOG("Setting main bitmap");
- // - Clear all Type=1 areas
- LOG("Clearing valid regions");
- for(
- ent = mmapStart;
- (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
- ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
- )
- {
- // Check if the type is RAM
- if(ent->Type != 1) continue;
-
- // Main bitmap
- base = ent->Base >> 12;
- size = ent->Size >> 12;
-
- if(base & 63) {
- Uint64 val = -1LL << (base & 63);
- gaMainBitmap[base / 64] &= ~val;
- size -= (base & 63);
- base += 64 - (base & 63);
- }
- memset( &gaMainBitmap[base / 64], 0, size/8 );
- if( size & 7 ) {
- Uint64 val = -1LL << (size & 7);
- val <<= (size/8)&7;
- gaMainBitmap[base / 64] &= ~val;
- }
-
- // Super Bitmap
- base = ent->Base >> 12;
- size = ent->Size >> 12;
- size = (size + (base & 63) + 63) >> 6;
- base = base >> 6;
- if(base & 63) {
- Uint64 val = -1LL << (base & 63);
- gaSuperBitmap[base / 64] &= ~val;
-// size -= (base & 63);
-// base += 64 - (base & 63);
- }
- }
-
- // Reference the used pages
- base = (tPAddr)&gKernelBase >> 12;
- size = firstFreePage >> 12;
- memset( &gaMainBitmap[base / 64], -1, size/8 );
- if( size & 7 ) {
- Uint64 val = -1LL << (size & 7);
- val <<= (size/8)&7;
- gaMainBitmap[base / 64] |= val;
- }
-
- // Free the unused static allocs
- for( i = 0; i < NUM_STATIC_ALLOC; i++) {
- if(gaiStaticAllocPages[i] != 0)
- continue;
- gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ]
- &= ~(1LL << ((gaiStaticAllocPages[i]>>12)&63));
- }
-
- // Fill the super bitmap
- LOG("Filling super bitmap");
- memset(gaSuperBitmap, 0, superPages<<12);
- for( base = 0; base < (size+63)/64; base ++)
- {
- if( gaMainBitmap[ base ] + 1 == 0 )
- gaSuperBitmap[ base/64 ] |= 1LL << (base&63);
- }
-
- // Set free page counts
- for( base = 1; base < giMaxPhysPage; base ++ )
- {
- int rangeID;
- // Skip allocated
- if( gaMainBitmap[ base >> 6 ] & (1LL << (base&63)) ) continue;
-
- // Get range ID
- rangeID = MM_int_GetRangeID( base << 12 );
-
- // Increment free page count
- giPhysRangeFree[ rangeID ] ++;
-
- // Check for first free page in range
- if(giPhysRangeFirst[ rangeID ] == 0)
- giPhysRangeFirst[ rangeID ] = base;
- // Set last (when the last free page is reached, this won't be
- // updated anymore, hence will be correct)
- giPhysRangeLast[ rangeID ] = base;
- }
-
- LEAVE('-');
-}
-
-/**
- * \brief Allocate a contiguous range of physical pages with a maximum
- * bit size of \a MaxBits
- * \param Pages Number of pages to allocate
- * \param MaxBits Maximum size of the physical address
- * \note If \a MaxBits is <= 0, any sized address is used (with preference
- * to higher addresses)
- */
-tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
-{
- tPAddr addr, ret;
- int rangeID;
- int nFree = 0, i;
-
- ENTER("iPages iBits", Pages, MaxBits);
-
- if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case
- rangeID = MM_PHYS_MAX;
- else
- rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 );
-
- LOG("rangeID = %i", rangeID);
-
- Mutex_Acquire(&glPhysicalPages);
-
- // Check if the range actually has any free pages
- while(giPhysRangeFree[rangeID] == 0 && rangeID)
- rangeID --;
-
- LOG("rangeID = %i", rangeID);
-
- // What the? Oh, man. No free pages
- if(giPhysRangeFree[rangeID] == 0) {
- Mutex_Release(&glPhysicalPages);
- // TODO: Page out
- // ATM. Just Warning
- Warning(" MM_AllocPhysRange: Out of free pages");
- Log_Warning("Arch",
- "Out of memory (unable to fulfil request for %i pages), zero remaining",
- Pages
- );
- LEAVE('i', 0);
- return 0;
- }
-
- // Check if there is enough in the range
- if(giPhysRangeFree[rangeID] >= Pages)
- {
- LOG("{%i,0x%x -> 0x%x}",
- giPhysRangeFree[rangeID],
- giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID]
- );
- // Do a cheap scan, scanning upwards from the first free page in
- // the range
- nFree = 0;
- addr = giPhysRangeFirst[ rangeID ];
- while( addr <= giPhysRangeLast[ rangeID ] )
- {
- //Log(" MM_AllocPhysRange: addr = 0x%x", addr);
- // Check the super bitmap
- if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) {
- LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
- nFree = 0;
- addr += 1LL << (6+6);
- addr &= ~0xFFF; // (1LL << 6+6) - 1
- continue;
- }
- // Check page block (64 pages)
- if( gaMainBitmap[addr >> 6] + 1 == 0) {
- LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
- nFree = 0;
- addr += 1LL << (6);
- addr &= ~0x3F;
- continue;
- }
- // Check individual page
- if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) {
- LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
- nFree = 0;
- addr ++;
- continue;
- }
- nFree ++;
- addr ++;
- LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr);
- if(nFree == Pages)
- break;
- }
- LOG("nFree = %i", nFree);
- // If we don't find a contiguous block, nFree will not be equal
- // to Num, so we set it to zero and do the expensive lookup.
- if(nFree != Pages) nFree = 0;
- }
-
- if( !nFree )
- {
- // Oops. ok, let's do an expensive check (scan down the list
- // until a free range is found)
-// nFree = 1;
-// addr = giPhysRangeLast[ rangeID ];
- // TODO: Expensive Check
- Mutex_Release(&glPhysicalPages);
- // TODO: Page out
- // ATM. Just Warning
- Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
- Log_Warning("Arch",
- "Out of memory (unable to fulfil request for %i pages)",
- Pages
- );
- LEAVE('i', 0);
- return 0;
- }
- LOG("nFree = %i, addr = 0x%08x", nFree, addr);
-
- // Mark pages as allocated
- addr -= Pages;
- for( i = 0; i < Pages; i++, addr++ )
- {
- gaMainBitmap[addr >> 6] |= 1LL << (addr & 63);
- if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) )
- gaiPageReferences[addr] = 1;
-// Log("page %P refcount = %i", MM_GetRefCount(addr<<12));
- rangeID = MM_int_GetRangeID(addr << 12);
- giPhysRangeFree[ rangeID ] --;
- LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]);
- if(addr == giPhysRangeFirst[ rangeID ])
- giPhysRangeFirst[ rangeID ] += 1;
- }
- addr -= Pages;
- ret = addr; // Save the return address
-
- // Update super bitmap
- Pages += addr & (64-1);
- addr &= ~(64-1);
- Pages = (Pages + (64-1)) & ~(64-1);
- for( i = 0; i < Pages/64; i++ )
- {
- if( gaMainBitmap[ addr >> 6 ] + 1 == 0 )
- gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63);
- }
-
- Mutex_Release(&glPhysicalPages);
- #if TRACE_REF
- Log("MM_AllocPhysRange: ret = %P (Ref %i)", ret << 12, MM_GetRefCount(ret<<12));
- #endif
- LEAVE('x', ret << 12);
- return ret << 12;
-}
-
-/**
- * \brief Allocate a single physical page, with no preference as to address
- * size.
- */
-tPAddr MM_AllocPhys(void)
-{
- int i;
-
- // Hack to allow allocation during setup
- for(i = 0; i < NUM_STATIC_ALLOC; i++) {
- if( gaiStaticAllocPages[i] ) {
- tPAddr ret = gaiStaticAllocPages[i];
- gaiStaticAllocPages[i] = 0;
- Log("MM_AllocPhys: Return %P, static alloc %i", ret, i);
- return ret;
- }
- }
-
- return MM_AllocPhysRange(1, -1);
-}
-
-/**
- * \brief Reference a physical page
- */
-void MM_RefPhys(tPAddr PAddr)
-{
- Uint64 page = PAddr >> 12;
-
- if( page > giMaxPhysPage ) return ;
-
- if( PAGE_ALLOC_TEST(page) )
- {
- tVAddr ref_base = ((tVAddr)&gaiPageReferences[ page ]) & ~0xFFF;
- // Allocate reference page
- if( !MM_GetPhysAddr(ref_base) )
- {
- const int pages_per_refpage = PAGE_SIZE/sizeof(gaiPageReferences[0]);
- int i;
- int page_base = page / pages_per_refpage * pages_per_refpage;
- if( !MM_Allocate( ref_base ) ) {
- Log_Error("Arch", "Out of memory when allocating reference count page");
- return ;
- }
- // Fill block
- Log("Allocated references for %P-%P", page_base << 12, (page_base+pages_per_refpage)<<12);
- for( i = 0; i < pages_per_refpage; i ++ ) {
- int pg = page_base + i;
- gaiPageReferences[pg] = !!PAGE_ALLOC_TEST(pg);
- }
- }
- gaiPageReferences[page] ++;
- }
- else
- {
- // Allocate
- PAGE_ALLOC_SET(page);
- if( gaMainBitmap[page >> 6] + 1 == 0 )
- gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63);
- if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) )
- gaiPageReferences[page] = 1;
- }
-
- #if TRACE_REF
- Log("MM_RefPhys: %P referenced (%i)", page << 12, MM_GetRefCount(page << 12));
- #endif
-}
-
-/**
- * \brief Dereference a physical page
- */
-void MM_DerefPhys(tPAddr PAddr)
-{
- Uint64 page = PAddr >> 12;
-
- if( PAddr >> 12 > giMaxPhysPage ) return ;
-
- if( MM_GetPhysAddr( (tVAddr) &gaiPageReferences[page] ) )
- {
- gaiPageReferences[ page ] --;
- if( gaiPageReferences[ page ] == 0 )
- PAGE_ALLOC_CLEAR(page);
- }
- else
- PAGE_ALLOC_CLEAR(page);
-
- // Update the free counts if the page was freed
- if( !PAGE_ALLOC_TEST(page) )
- {
- int rangeID;
- rangeID = MM_int_GetRangeID( PAddr );
- giPhysRangeFree[ rangeID ] ++;
- if( giPhysRangeFirst[rangeID] > page )
- giPhysRangeFirst[rangeID] = page;
- if( giPhysRangeLast[rangeID] < page )
- giPhysRangeLast[rangeID] = page;
- }
-
- // If the bitmap entry is not -1, unset the bit in the super bitmap
- if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) {
- gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63));
- }
-
- #if TRACE_REF
- Log("Page %P dereferenced (%i)", page << 12, MM_GetRefCount(page << 12));
- #endif
-}
-
-int MM_GetRefCount( tPAddr PAddr )
-{
- PAddr >>= 12;
-
- if( PAddr > giMaxPhysPage ) return 0;
-
- if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) {
- return gaiPageReferences[PAddr];
- }
-
- if( PAGE_ALLOC_TEST(PAddr) )
- {
- return 1;
- }
- return 0;
-}
-
-/**
- * \brief Takes a physical address and returns the ID of its range
- * \param Addr Physical address of page
- * \return Range ID from eMMPhys_Ranges
- */
-int MM_int_GetRangeID( tPAddr Addr )
-{
- if(Addr >> 32)
- return MM_PHYS_MAX;
- else if(Addr >> 24)
- return MM_PHYS_32BIT;
- else if(Addr >> 20)
- return MM_PHYS_24BIT;
- else if(Addr >> 16)
- return MM_PHYS_20BIT;
- else
- return MM_PHYS_16BIT;
-}
-
-int MM_SetPageNode(tPAddr PAddr, void *Node)
-{
- tPAddr page = PAddr >> 12;
- tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1);
-
-// if( !MM_GetRefCount(PAddr) ) return 1;
-
- if( !MM_GetPhysAddr(node_page) ) {
- if( !MM_Allocate(node_page) )
- return -1;
- memset( (void*)node_page, 0, PAGE_SIZE );
- }
-
- gapPageNodes[page] = Node;
- return 0;
-}
-
-int MM_GetPageNode(tPAddr PAddr, void **Node)
-{
-// if( !MM_GetRefCount(PAddr) ) return 1;
- PAddr >>= 12;
-
- if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) {
- *Node = NULL;
- return 0;
- }
-
- *Node = gapPageNodes[PAddr];
- return 0;
-}
-
+++ /dev/null
-/*
- * Acess2 x86_64 Port
- *
- * Virtual Memory Manager
- */
-#define DEBUG 0
-#include <acess.h>
-#include <mm_virt.h>
-#include <threads_int.h>
-#include <proc.h>
-#include <hal_proc.h>
-
-// === DEBUG OPTIONS ===
-#define TRACE_COW 0
-
-// === CONSTANTS ===
-#define PHYS_BITS 52 // TODO: Move out
-#define VIRT_BITS 48
-
-#define PML4_SHIFT 39
-#define PDP_SHIFT 30
-#define PDIR_SHIFT 21
-#define PTAB_SHIFT 12
-
-#define PADDR_MASK 0x7FFFFFFF##FFFFF000
-#define PAGE_MASK ((1LL << 36)-1)
-#define TABLE_MASK ((1LL << 27)-1)
-#define PDP_MASK ((1LL << 18)-1)
-#define PML4_MASK ((1LL << 9)-1)
-
-#define PF_PRESENT 0x001
-#define PF_WRITE 0x002
-#define PF_USER 0x004
-#define PF_LARGE 0x080
-#define PF_GLOBAL 0x100
-#define PF_COW 0x200
-#define PF_PAGED 0x400
-#define PF_NX 0x80000000##00000000
-
-// === MACROS ===
-#define PAGETABLE(idx) (*((Uint64*)MM_FRACTAL_BASE+((idx)&PAGE_MASK)))
-#define PAGEDIR(idx) PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK))
-#define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK))
-#define PAGEMAPLVL4(idx) PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK))
-
-#define TMPCR3() PAGEMAPLVL4(MM_TMPFRAC_BASE>>39)
-#define TMPTABLE(idx) (*((Uint64*)MM_TMPFRAC_BASE+((idx)&PAGE_MASK)))
-#define TMPDIR(idx) PAGETABLE((MM_TMPFRAC_BASE>>12)+((idx)&TABLE_MASK))
-#define TMPDIRPTR(idx) PAGEDIR((MM_TMPFRAC_BASE>>21)+((idx)&PDP_MASK))
-#define TMPMAPLVL4(idx) PAGEDIRPTR((MM_TMPFRAC_BASE>>30)+((idx)&PML4_MASK))
-
-#define INVLPG(__addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr))
-#define INVLPG_ALL() __asm__ __volatile__ ("mov %cr3,%rax;\n\tmov %rax,%cr3;")
-#define INVLPG_GLOBAL() __asm__ __volatile__ ("mov %cr4,%rax;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4")
-
-// === CONSTS ===
-//tPAddr * const gaPageTable = MM_FRACTAL_BASE;
-
-// === IMPORTS ===
-extern void Error_Backtrace(Uint IP, Uint BP);
-extern tPAddr gInitialPML4[512];
-extern void Threads_SegFault(tVAddr Addr);
-extern char _UsertextBase[];
-
-// === PROTOTYPES ===
-void MM_InitVirt(void);
-//void MM_FinishVirtualInit(void);
-void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable );
- int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
-void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected);
-//void MM_DumpTables(tVAddr Start, tVAddr End);
- int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer);
- int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge);
-// int MM_Map(tVAddr VAddr, tPAddr PAddr);
-void MM_Unmap(tVAddr VAddr);
-void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts);
-//void MM_ClearUser(void);
- int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags);
-
-// === GLOBALS ===
-tMutex glMM_TempFractalLock;
-tPAddr gMM_ZeroPage;
-
-// === CODE ===
-void MM_InitVirt(void)
-{
-// Log_Debug("MMVirt", "&PAGEMAPLVL4(0) = %p", &PAGEMAPLVL4(0));
-// MM_DumpTables(0, -1L);
-}
-
-void MM_FinishVirtualInit(void)
-{
- PAGEMAPLVL4(0) = 0;
-}
-
-/**
- * \brief Clone a page from an entry
- * \param Ent Pointer to the entry in the PML4/PDP/PD/PT
- * \param NextLevel Pointer to contents of the entry
- * \param Addr Dest address
- * \note Used in COW
- */
-void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable )
-{
- tPAddr curpage = *Ent & PADDR_MASK;
- int bCopied = 0;
-
- if( MM_GetRefCount( curpage ) <= 0 ) {
- Log_KernelPanic("MMVirt", "Page %P still marked COW, but unreferenced", curpage);
- }
- if( MM_GetRefCount( curpage ) == 1 )
- {
- *Ent &= ~PF_COW;
- *Ent |= PF_PRESENT|PF_WRITE;
- #if TRACE_COW
- Log_Debug("MMVirt", "COW ent at %p (%p) only %P", Ent, NextLevel, curpage);
- #endif
- }
- else
- {
- void *tmp;
- tPAddr paddr;
-
- if( !(paddr = MM_AllocPhys()) ) {
- Threads_SegFault(Addr);
- return ;
- }
-
- ASSERT(paddr != curpage);
-
- tmp = (void*)MM_MapTemp(paddr);
- memcpy( tmp, NextLevel, 0x1000 );
- MM_FreeTemp( (tVAddr)tmp );
-
- #if TRACE_COW
- Log_Debug("MMVirt", "COW ent at %p (%p) from %P to %P", Ent, NextLevel, curpage, paddr);
- #endif
-
- MM_DerefPhys( curpage );
- *Ent &= PF_USER;
- *Ent |= paddr|PF_PRESENT|PF_WRITE;
-
- bCopied = 1;
- }
- INVLPG( (tVAddr)NextLevel );
-
- // Mark COW on contents if it's a PDPT, Dir or Table
- if(bTable)
- {
- Uint64 *dp = NextLevel;
- int i;
- for( i = 0; i < 512; i ++ )
- {
- if( !(dp[i] & PF_PRESENT) )
- continue;
-
- if( bCopied )
- MM_RefPhys( dp[i] & PADDR_MASK );
- if( dp[i] & PF_WRITE ) {
- dp[i] &= ~PF_WRITE;
- dp[i] |= PF_COW;
- }
- }
- }
-}
-
-/*
- * \brief Called on a page fault
- */
-int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
-{
-// Log_Debug("MMVirt", "Addr = %p, ErrorCode = %x", Addr, ErrorCode);
-
- // Catch reserved bits first
- if( ErrorCode & 0x8 )
- {
- Log_Warning("MMVirt", "Reserved bits trashed!");
- Log_Warning("MMVirt", "PML4 Ent = %P", PAGEMAPLVL4(Addr>>39));
- if( !(PAGEMAPLVL4(Addr>>39) & PF_PRESENT) ) goto print_done;
- Log_Warning("MMVirt", "PDP Ent = %P", PAGEDIRPTR(Addr>>30));
- if( !(PAGEDIRPTR(Addr>>30) & PF_PRESENT) ) goto print_done;
- Log_Warning("MMVirt", "PDir Ent = %P", PAGEDIR(Addr>>21));
- if( !(PAGEDIR(Addr>>21) & PF_PRESENT) ) goto print_done;
- Log_Warning("MMVirt", "PTable Ent = %P", PAGETABLE(Addr>>12));
- if( !(PAGETABLE(Addr>>12) & PF_PRESENT) ) goto print_done;
- print_done:
-
- for(;;);
- }
-
- // TODO: Implement Copy-on-Write
- #if 1
- if( PAGEMAPLVL4(Addr>>39) & PF_PRESENT
- && PAGEDIRPTR (Addr>>30) & PF_PRESENT
- && PAGEDIR (Addr>>21) & PF_PRESENT
- && PAGETABLE (Addr>>12) & PF_PRESENT )
- {
- // PML4 Entry
- if( PAGEMAPLVL4(Addr>>39) & PF_COW )
- {
- tPAddr *dp = &PAGEDIRPTR((Addr>>39)*512);
- MM_int_ClonePageEnt( &PAGEMAPLVL4(Addr>>39), dp, Addr, 1 );
-// MM_DumpTables(Addr>>39 << 39, (((Addr>>39) + 1) << 39) - 1);
- }
- // PDP Entry
- if( PAGEDIRPTR(Addr>>30) & PF_COW )
- {
- tPAddr *dp = &PAGEDIR( (Addr>>30)*512 );
- MM_int_ClonePageEnt( &PAGEDIRPTR(Addr>>30), dp, Addr, 1 );
-// MM_DumpTables(Addr>>30 << 30, (((Addr>>30) + 1) << 30) - 1);
- }
- // PD Entry
- if( PAGEDIR(Addr>>21) & PF_COW )
- {
- tPAddr *dp = &PAGETABLE( (Addr>>21)*512 );
- MM_int_ClonePageEnt( &PAGEDIR(Addr>>21), dp, Addr, 1 );
-// MM_DumpTables(Addr>>21 << 21, (((Addr>>21) + 1) << 21) - 1);
- }
- // PT Entry
- if( PAGETABLE(Addr>>12) & PF_COW )
- {
- MM_int_ClonePageEnt( &PAGETABLE(Addr>>12), (void*)(Addr & ~0xFFF), Addr, 0 );
- INVLPG( Addr & ~0xFFF );
- return 0;
- }
- }
- #endif
-
- // If it was a user, tell the thread handler
- if(ErrorCode & 4) {
- Warning("User %s %s memory%s",
- (ErrorCode&2?"write to":"read from"),
- (ErrorCode&1?"bad/locked":"non-present"),
- (ErrorCode&16?" (Instruction Fetch)":"")
- );
- Warning("User Pagefault: Instruction at %04x:%p accessed %p",
- Regs->CS, Regs->RIP, Addr);
- __asm__ __volatile__ ("sti"); // Restart IRQs
- Error_Backtrace(Regs->RIP, Regs->RBP);
- Threads_SegFault(Addr);
- return 0;
- }
-
- // Kernel #PF
- Debug_KernelPanic();
- // -- Check Error Code --
- if(ErrorCode & 8)
- Warning("Reserved Bits Trashed!");
- else
- {
- Warning("Kernel %s %s memory%s",
- (ErrorCode&2?"write to":"read from"),
- (ErrorCode&1?"bad/locked":"non-present"),
- (ErrorCode&16?" (Instruction Fetch)":"")
- );
- }
-
- Log("Thread %i - Code at %p accessed %p", Threads_GetTID(), Regs->RIP, Addr);
- // Print Stack Backtrace
- Error_Backtrace(Regs->RIP, Regs->RBP);
-
- MM_DumpTables(0, -1);
-
- return 1;
-}
-
-void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected)
-{
- #define CANOICAL(addr) ((addr)&0x800000000000?(addr)|0xFFFF000000000000:(addr))
- LogF("%016llx => ", CANOICAL(RangeStart));
-// LogF("%6llx %6llx %6llx %016llx => ",
-// MM_GetPhysAddr( (tVAddr)&PAGEDIRPTR(RangeStart>>30) ),
-// MM_GetPhysAddr( (tVAddr)&PAGEDIR(RangeStart>>21) ),
-// MM_GetPhysAddr( (tVAddr)&PAGETABLE(RangeStart>>12) ),
-// CANOICAL(RangeStart)
-// );
- if( gMM_ZeroPage && (PAGETABLE(RangeStart>>12) & PADDR_MASK) == gMM_ZeroPage )
- LogF("%13s", "zero" );
- else
- LogF("%13llx", PAGETABLE(RangeStart>>12) & PADDR_MASK );
- LogF(" : 0x%6llx (%c%c%c%c%c%c)\r\n",
- Length,
- (Expected & PF_GLOBAL ? 'G' : '-'),
- (Expected & PF_NX ? '-' : 'x'),
- (Expected & PF_PAGED ? 'p' : '-'),
- (Expected & PF_COW ? 'C' : '-'),
- (Expected & PF_USER ? 'U' : '-'),
- (Expected & PF_WRITE ? 'W' : '-')
- );
- #undef CANOICAL
-}
-
-/**
- * \brief Dumps the layout of the page tables
- */
-void MM_DumpTables(tVAddr Start, tVAddr End)
-{
- const tPAddr FIXED_BITS = PF_PRESENT|PF_WRITE|PF_USER|PF_COW|PF_PAGED|PF_NX|PF_GLOBAL;
- const tPAddr CHANGEABLE_BITS = ~FIXED_BITS & 0xFFF;
- const tPAddr MASK = ~CHANGEABLE_BITS; // Physical address and access bits
- tVAddr rangeStart = 0;
- tPAddr expected = CHANGEABLE_BITS; // CHANGEABLE_BITS is used because it's not a vaild value
- tVAddr curPos;
- Uint page;
- tPAddr expected_pml4 = PF_WRITE|PF_USER;
- tPAddr expected_pdp = PF_WRITE|PF_USER;
- tPAddr expected_pd = PF_WRITE|PF_USER;
-
- Log("Table Entries: (%p to %p)", Start, End);
-
- End &= (1L << 48) - 1;
-
- Start >>= 12; End >>= 12;
-
- for(page = Start, curPos = Start<<12;
- page < End;
- curPos += 0x1000, page++)
- {
- //Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27));
- //Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18));
- //Debug("&PAGEDIR(%i page>>9) = %p", page>>9, &PAGEDIR(page>>9));
- //Debug("&PAGETABLE(%i page) = %p", page, &PAGETABLE(page));
-
- // End of a range
- if(!(PAGEMAPLVL4(page>>27) & PF_PRESENT)
- || (PAGEMAPLVL4(page>>27) & FIXED_BITS) != expected_pml4
- || !(PAGEDIRPTR(page>>18) & PF_PRESENT)
- || (PAGEDIRPTR(page>>18) & FIXED_BITS) != expected_pdp
- || !(PAGEDIR(page>>9) & PF_PRESENT)
- || (PAGEDIR(page>>9) & FIXED_BITS) != expected_pd
- || !(PAGETABLE(page) & PF_PRESENT)
- || (PAGETABLE(page) & MASK) != expected)
- {
- if(expected != CHANGEABLE_BITS)
- {
- // Merge
- expected &= expected_pml4 | ~(PF_WRITE|PF_USER);
- expected &= expected_pdp | ~(PF_WRITE|PF_USER);
- expected &= expected_pd | ~(PF_WRITE|PF_USER);
- expected |= expected_pml4 & PF_NX;
- expected |= expected_pdp & PF_NX;
- expected |= expected_pd & PF_NX;
- Log("expected (pml4 = %x, pdp = %x, pd = %x)",
- expected_pml4, expected_pdp, expected_pd);
- // Dump
- MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected );
- expected = CHANGEABLE_BITS;
- }
-
- if( curPos == 0x800000000000L )
- curPos = 0xFFFF800000000000L;
-
- if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) {
- page += (1 << 27) - 1;
- curPos += (1L << 39) - 0x1000;
- continue;
- }
- if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) {
- page += (1 << 18) - 1;
- curPos += (1L << 30) - 0x1000;
- continue;
- }
- if( !(PAGEDIR(page>>9) & PF_PRESENT) ) {
- page += (1 << 9) - 1;
- curPos += (1L << 21) - 0x1000;
- continue;
- }
- if( !(PAGETABLE(page) & PF_PRESENT) ) continue;
-
- expected = (PAGETABLE(page) & MASK);
- expected_pml4 = (PAGEMAPLVL4(page>>27) & FIXED_BITS);
- expected_pdp = (PAGEDIRPTR (page>>18) & FIXED_BITS);
- expected_pd = (PAGEDIR (page>> 9) & FIXED_BITS);
- rangeStart = curPos;
- }
- if(gMM_ZeroPage && (expected & PADDR_MASK) == gMM_ZeroPage )
- expected = expected;
- else if(expected != CHANGEABLE_BITS)
- expected += 0x1000;
- }
-
- if(expected != CHANGEABLE_BITS) {
- // Merge
-
- // Dump
- MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected );
- expected = 0;
- }
-}
-
-/**
- * \brief Get a pointer to a page entry
- * \param Addr Virtual Address
- * \param bTemp Use the Temporary fractal mapping
- * \param bAllocate Allocate entries
- * \param bLargePage Request a large page
- * \param Pointer Location to place the calculated pointer
- * \return Page size, or -ve on error
- */
-int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer)
-{
- tPAddr *pmlevels[4];
- tPAddr tmp;
- int i, size;
-
- #define BITMASK(bits) ( (1LL << (bits))-1 )
-
- if( bTemp )
- {
- pmlevels[3] = &TMPTABLE(0); // Page Table
- pmlevels[2] = &TMPDIR(0); // PDIR
- pmlevels[1] = &TMPDIRPTR(0); // PDPT
- pmlevels[0] = &TMPMAPLVL4(0); // PML4
- }
- else
- {
- pmlevels[3] = (void*)MM_FRACTAL_BASE; // Page Table
- pmlevels[2] = &pmlevels[3][(MM_FRACTAL_BASE>>12)&BITMASK(VIRT_BITS-12)]; // PDIR
- pmlevels[1] = &pmlevels[2][(MM_FRACTAL_BASE>>21)&BITMASK(VIRT_BITS-21)]; // PDPT
- pmlevels[0] = &pmlevels[1][(MM_FRACTAL_BASE>>30)&BITMASK(VIRT_BITS-30)]; // PML4
- }
-
- // Mask address
- Addr &= (1ULL << 48)-1;
-
- for( size = 39, i = 0; size > 12; size -= 9, i ++ )
- {
- Uint64 *ent = &pmlevels[i][Addr >> size];
-// INVLPG( &pmlevels[i][ (Addr >> ADDR_SIZES[i]) &
-
- // Check for a free large page slot
- // TODO: Better support with selectable levels
- if( (Addr & ((1ULL << size)-1)) == 0 && bLargePage )
- {
- if(Pointer) *Pointer = ent;
- return size;
- }
- // Allocate an entry if required
- if( !(*ent & PF_PRESENT) )
- {
- if( !bAllocate ) return -4; // If allocation is not requested, error
- if( !(tmp = MM_AllocPhys()) ) return -2;
- *ent = tmp | 3;
- if( Addr < 0x800000000000 )
- *ent |= PF_USER;
- INVLPG( &pmlevels[i+1][ (Addr>>size)*512 ] );
- memset( &pmlevels[i+1][ (Addr>>size)*512 ], 0, 0x1000 );
- LOG("Init PML%i ent 0x%x %p with %P (*ent = %P)", 4 - i,
- Addr>>size, (Addr>>size) << size, tmp, *ent);
- }
- // Catch large pages
- else if( *ent & PF_LARGE )
- {
- // Alignment
- if( (Addr & ((1ULL << size)-1)) != 0 ) return -3;
- if(Pointer) *Pointer = ent;
- return size; // Large page warning
- }
- }
-
- // And, set the page table entry
- if(Pointer) *Pointer = &pmlevels[i][Addr >> size];
- return size;
-}
-
-/**
- * \brief Map a physical page to a virtual one
- * \param VAddr Target virtual address
- * \param PAddr Physical address of page
- * \param bTemp Use tempoary mappings
- * \param bLarge Treat as a large page
- */
-int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge)
-{
- tPAddr *ent;
- int rv;
-
- ENTER("pVAddr PPAddr", VAddr, PAddr);
-
- // Get page pointer (Allow allocating)
- rv = MM_GetPageEntryPtr(VAddr, bTemp, 1, bLarge, &ent);
- if(rv < 0) LEAVE_RET('i', 0);
-
- if( *ent & 1 ) LEAVE_RET('i', 0);
-
- *ent = PAddr | 3;
-
- if( VAddr < 0x800000000000 )
- *ent |= PF_USER;
-
- INVLPG( VAddr );
-
- LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \brief Map a physical page to a virtual one
- * \param VAddr Target virtual address
- * \param PAddr Physical address of page
- */
-int MM_Map(tVAddr VAddr, tPAddr PAddr)
-{
- return MM_MapEx(VAddr, PAddr, 0, 0);
-}
-
-/**
- * \brief Removed a mapped page
- */
-void MM_Unmap(tVAddr VAddr)
-{
- // Check PML4
- if( !(PAGEMAPLVL4(VAddr >> 39) & 1) ) return ;
- // Check PDP
- if( !(PAGEDIRPTR(VAddr >> 30) & 1) ) return ;
- // Check Page Dir
- if( !(PAGEDIR(VAddr >> 21) & 1) ) return ;
-
- PAGETABLE(VAddr >> PTAB_SHIFT) = 0;
- INVLPG( VAddr );
-}
-
-/**
- * \brief Allocate a block of memory at the specified virtual address
- */
-tPAddr MM_Allocate(tVAddr VAddr)
-{
- tPAddr ret;
-
- ENTER("xVAddr", VAddr);
-
- // Ensure the tables are allocated before the page (keeps things neat)
- MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL);
-
- // Allocate the page
- ret = MM_AllocPhys();
- LOG("ret = %x", ret);
- if(!ret) LEAVE_RET('i', 0);
-
- if( !MM_Map(VAddr, ret) )
- {
- Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier");
- MM_DerefPhys(ret);
- LEAVE('i');
- return 0;
- }
-
- LEAVE('X', ret);
- return ret;
-}
-
-tPAddr MM_AllocateZero(tVAddr VAddr)
-{
- tPAddr ret = gMM_ZeroPage;
-
- MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL);
-
- if(!gMM_ZeroPage) {
- ret = gMM_ZeroPage = MM_AllocPhys();
- MM_RefPhys(ret); // Don't free this please
- MM_Map(VAddr, ret);
- memset((void*)VAddr, 0, 0x1000);
- }
- else {
- MM_Map(VAddr, ret);
- }
- MM_RefPhys(ret); // Refernce for this map
- MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
- return ret;
-}
-
-/**
- * \brief Deallocate a page at a virtual address
- */
-void MM_Deallocate(tVAddr VAddr)
-{
- tPAddr phys;
-
- phys = MM_GetPhysAddr(VAddr);
- if(!phys) return ;
-
- MM_Unmap(VAddr);
-
- MM_DerefPhys(phys);
-}
-
-/**
- * \brief Get the page table entry of a virtual address
- * \param Addr Virtual Address
- * \param Phys Location to put the physical address
- * \param Flags Flags on the entry (set to zero if unmapped)
- * \return Size of the entry (in address bits) - 12 = 4KiB page
- */
-int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags)
-{
- tPAddr *ptr;
- int ret;
-
- if(!Phys || !Flags) return 0;
-
- ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr);
- if( ret < 0 ) return 0;
-
- *Phys = *ptr & PADDR_MASK;
- *Flags = *ptr & 0xFFF;
- return ret;
-}
-
-/**
- * \brief Get the physical address of a virtual location
- */
-tPAddr MM_GetPhysAddr(tVAddr Addr)
-{
- tPAddr *ptr;
- int ret;
-
- ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr);
- if( ret < 0 ) return 0;
-
- if( !(*ptr & 1) ) return 0;
-
- return (*ptr & PADDR_MASK) | (Addr & 0xFFF);
-}
-
-/**
- * \brief Sets the flags on a page
- */
-void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
-{
- tPAddr *ent;
- int rv;
-
- // Get pointer
- rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent);
- if(rv < 0) return ;
-
- // Ensure the entry is valid
- if( !(*ent & 1) ) return ;
-
- // Read-Only
- if( Mask & MM_PFLAG_RO )
- {
- if( Flags & MM_PFLAG_RO ) {
- *ent &= ~PF_WRITE;
- }
- else {
- *ent |= PF_WRITE;
- }
- }
-
- // Kernel
- if( Mask & MM_PFLAG_KERNEL )
- {
- if( Flags & MM_PFLAG_KERNEL ) {
- *ent &= ~PF_USER;
- }
- else {
- *ent |= PF_USER;
- }
- }
-
- // Copy-On-Write
- if( Mask & MM_PFLAG_COW )
- {
- if( Flags & MM_PFLAG_COW ) {
- *ent &= ~PF_WRITE;
- *ent |= PF_COW;
- INVLPG_ALL();
- }
- else {
- *ent &= ~PF_COW;
- *ent |= PF_WRITE;
- }
- }
-
- // Execute
- if( Mask & MM_PFLAG_EXEC )
- {
- if( Flags & MM_PFLAG_EXEC ) {
- *ent &= ~PF_NX;
- }
- else {
- *ent |= PF_NX;
- }
- }
-}
-
-/**
- * \brief Get the flags applied to a page
- */
-Uint MM_GetFlags(tVAddr VAddr)
-{
- tPAddr *ent;
- int rv, ret = 0;
-
- rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent);
- if(rv < 0) return 0;
-
- if( !(*ent & 1) ) return 0;
-
- // Read-Only
- if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO;
- // Kernel
- if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
- // Copy-On-Write
- if( *ent & PF_COW ) ret |= MM_PFLAG_COW;
- // Execute
- if( !(*ent & PF_NX) ) ret |= MM_PFLAG_EXEC;
-
- return ret;
-}
-
-/**
- * \brief Check if the provided buffer is valid
- * \return Boolean valid
- */
-int MM_IsValidBuffer(tVAddr Addr, size_t Size)
-{
- int bIsUser;
- Uint64 pml4, pdp, dir, tab;
-
- Size += Addr & (PAGE_SIZE-1);
- Addr &= ~(PAGE_SIZE-1);
- Addr &= ((1UL << 48)-1); // Clap to address space
-
- pml4 = Addr >> 39;
- pdp = Addr >> 30;
- dir = Addr >> 21;
- tab = Addr >> 12;
-
- if( !(PAGEMAPLVL4(pml4) & 1) ) return 0;
- if( !(PAGEDIRPTR(pdp) & 1) ) return 0;
- if( !(PAGEDIR(dir) & 1) ) return 0;
- if( !(PAGETABLE(tab) & 1) ) return 0;
-
- bIsUser = !!(PAGETABLE(tab) & PF_USER);
-
- while( Size >= PAGE_SIZE )
- {
- if( (tab & 511) == 0 )
- {
- dir ++;
- if( ((dir >> 9) & 511) == 0 )
- {
- pdp ++;
- if( ((pdp >> 18) & 511) == 0 )
- {
- pml4 ++;
- if( !(PAGEMAPLVL4(pml4) & 1) ) return 0;
- }
- if( !(PAGEDIRPTR(pdp) & 1) ) return 0;
- }
- if( !(PAGEDIR(dir) & 1) ) return 0;
- }
-
- if( !(PAGETABLE(tab) & 1) ) return 0;
- if( bIsUser && !(PAGETABLE(tab) & PF_USER) ) return 0;
-
- tab ++;
- Size -= PAGE_SIZE;
- }
- return 1;
-}
-
-// --- Hardware Mappings ---
-/**
- * \brief Map a range of hardware pages
- */
-tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
-{
- tVAddr ret;
- int num;
-
- //TODO: Add speedups (memory of first possible free)
- for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_TOP; ret += 0x1000 )
- {
- for( num = Number; num -- && ret < MM_HWMAP_TOP; ret += 0x1000 )
- {
- if( MM_GetPhysAddr(ret) != 0 ) break;
- }
- if( num >= 0 ) continue;
-
-// Log_Debug("MMVirt", "Mapping %i pages to %p (base %P)", Number, ret-Number*0x1000, PAddr);
-
- PAddr += 0x1000 * Number;
-
- while( Number -- )
- {
- ret -= 0x1000;
- PAddr -= 0x1000;
- MM_Map(ret, PAddr);
- MM_RefPhys(PAddr);
- }
-
- return ret;
- }
-
- Log_Error("MM", "MM_MapHWPages - No space for %i pages", Number);
- return 0;
-}
-
-/**
- * \brief Free a range of hardware pages
- */
-void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
-{
-// Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
- while( Number -- )
- {
- MM_DerefPhys( MM_GetPhysAddr(VAddr) );
- MM_Unmap(VAddr);
- VAddr += 0x1000;
- }
-}
-
-
-/**
- * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
- * \brief Allocates DMA physical memory
- * \param Pages Number of pages required
- * \param MaxBits Maximum number of bits the physical address can have
- * \param PhysAddr Pointer to the location to place the physical address allocated
- * \return Virtual address allocate
- */
-tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
-{
- tPAddr phys;
- tVAddr ret;
-
- // Sanity Check
- if(MaxBits < 12 || !PhysAddr) return 0;
-
- // Fast Allocate
- if(Pages == 1 && MaxBits >= PHYS_BITS)
- {
- phys = MM_AllocPhys();
- *PhysAddr = phys;
- ret = MM_MapHWPages(phys, 1);
- MM_DerefPhys(phys);
- return ret;
- }
-
- // Slow Allocate
- phys = MM_AllocPhysRange(Pages, MaxBits);
- // - Was it allocated?
- if(phys == 0) return 0;
-
- // Allocated successfully, now map
- ret = MM_MapHWPages(phys, Pages);
- // MapHWPages references the pages, so deref them back down to 1
- for(;Pages--;phys+=0x1000)
- MM_DerefPhys(phys);
- if( ret == 0 ) {
- // If it didn't map, free then return 0
- return 0;
- }
-
- *PhysAddr = phys;
- return ret;
-}
-
-// --- Tempory Mappings ---
-tVAddr MM_MapTemp(tPAddr PAddr)
-{
- const int max_slots = (MM_TMPMAP_END - MM_TMPMAP_BASE) / PAGE_SIZE;
- tVAddr ret = MM_TMPMAP_BASE;
- int i;
-
- for( i = 0; i < max_slots; i ++, ret += PAGE_SIZE )
- {
- tPAddr *ent;
- if( MM_GetPageEntryPtr( ret, 0, 1, 0, &ent) < 0 ) {
- continue ;
- }
-
- if( *ent & 1 )
- continue ;
-
- *ent = PAddr | 3;
- MM_RefPhys(PAddr);
- INVLPG(ret);
- return ret;
- }
- return 0;
-}
-
-void MM_FreeTemp(tVAddr VAddr)
-{
- MM_Deallocate(VAddr);
- return ;
-}
-
-
-// --- Address Space Clone --
-tPAddr MM_Clone(void)
-{
- tPAddr ret;
- int i;
- tVAddr kstackbase;
-
- // #1 Create a copy of the PML4
- ret = MM_AllocPhys();
- if(!ret) return 0;
-
- // #2 Alter the fractal pointer
- Mutex_Acquire(&glMM_TempFractalLock);
- TMPCR3() = ret | 3;
- INVLPG_ALL();
-
- // #3 Set Copy-On-Write to all user pages
- if( Threads_GetPID() != 0 )
- {
- for( i = 0; i < 256; i ++)
- {
- if( PAGEMAPLVL4(i) & PF_WRITE ) {
- PAGEMAPLVL4(i) |= PF_COW;
- PAGEMAPLVL4(i) &= ~PF_WRITE;
- }
-
- TMPMAPLVL4(i) = PAGEMAPLVL4(i);
-// Log_Debug("MM", "TMPMAPLVL4(%i) = 0x%016llx", i, TMPMAPLVL4(i));
- if( !(TMPMAPLVL4(i) & PF_PRESENT) ) continue ;
-
- MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK );
- }
- }
- else
- {
- for( i = 0; i < 256; i ++ )
- {
- TMPMAPLVL4(i) = 0;
- }
- }
-
- // #4 Map in kernel pages
- for( i = 256; i < 512; i ++ )
- {
- // Skip addresses:
- // 320 0xFFFFA.... - Kernel Stacks
- if( i == MM_KSTACK_BASE>>39 ) continue;
- // 509 0xFFFFFE0.. - Fractal mapping
- if( i == MM_FRACTAL_BASE>>39 ) continue;
- // 510 0xFFFFFE8.. - Temp fractal mapping
- if( i == MM_TMPFRAC_BASE>>39 ) continue;
-
- TMPMAPLVL4(i) = PAGEMAPLVL4(i);
- if( TMPMAPLVL4(i) & 1 )
- MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK );
- }
-
- // Mark Per-Process data as COW
- TMPMAPLVL4(MM_PPD_BASE>>39) |= PF_COW;
- TMPMAPLVL4(MM_PPD_BASE>>39) &= ~PF_WRITE;
-
- // #5 Set fractal mapping
- TMPMAPLVL4(MM_FRACTAL_BASE>>39) = ret | 3; // Main
- TMPMAPLVL4(MM_TMPFRAC_BASE>>39) = 0; // Temp
-
- // #6 Create kernel stack
- // tThread->KernelStack is the top
- // There is 1 guard page below the stack
- kstackbase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE;
-
- // Clone stack
- TMPMAPLVL4(MM_KSTACK_BASE >> PML4_SHIFT) = 0;
- for( i = 1; i < KERNEL_STACK_SIZE/0x1000; i ++ )
- {
- tPAddr phys = MM_AllocPhys();
- tVAddr tmpmapping;
- MM_MapEx(kstackbase+i*0x1000, phys, 1, 0);
-
- tmpmapping = MM_MapTemp(phys);
- if( MM_GetPhysAddr( kstackbase+i*0x1000 ) )
- memcpy((void*)tmpmapping, (void*)(kstackbase+i*0x1000), 0x1000);
- else
- memset((void*)tmpmapping, 0, 0x1000);
-// if( i == 0xF )
-// Debug_HexDump("MM_Clone: *tmpmapping = ", (void*)tmpmapping, 0x1000);
- MM_FreeTemp(tmpmapping);
- }
-
-// MAGIC_BREAK();
-
- // #7 Return
- TMPCR3() = 0;
- INVLPG_ALL();
- Mutex_Release(&glMM_TempFractalLock);
-// Log("MM_Clone: RETURN %P", ret);
- return ret;
-}
-
-void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts)
-{
- Uint64 * const table_bases[] = {&PAGETABLE(0), &PAGEDIR(0), &PAGEDIRPTR(0), &PAGEMAPLVL4(0)};
- Uint64 *table = table_bases[(LevelBits-12)/9] + (VAddr >> LevelBits);
- int i;
-// Log("MM_int_ClearTableLevel: (VAddr=%p, LevelBits=%i, MaxEnts=%i)", VAddr, LevelBits, MaxEnts);
- for( i = 0; i < MaxEnts; i ++ )
- {
- // Skip non-present tables
- if( !(table[i] & PF_PRESENT) ) {
- table[i] = 0;
- continue ;
- }
-
- if( (table[i] & PF_COW) && MM_GetRefCount(table[i] & PADDR_MASK) > 1 ) {
- MM_DerefPhys(table[i] & PADDR_MASK);
- table[i] = 0;
- continue ;
- }
- // Clear table contents (if it is a table)
- if( LevelBits > 12 )
- MM_int_ClearTableLevel(VAddr + ((tVAddr)i << LevelBits), LevelBits-9, 512);
- MM_DerefPhys(table[i] & PADDR_MASK);
- table[i] = 0;
- }
-}
-
-void MM_ClearUser(void)
-{
- MM_int_ClearTableLevel(0, 39, 256);
-}
-
-tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize)
-{
- tVAddr ret;
- tPAddr phys;
- int i;
-
- // #1 Set temp fractal to PID0
- Mutex_Acquire(&glMM_TempFractalLock);
- TMPCR3() = ((tPAddr)gInitialPML4 - KERNEL_BASE) | 3;
- INVLPG_ALL();
-
- // #2 Scan for a free stack addresss < 2^47
- for(ret = 0x100000; ret < (1ULL << 47); ret += KERNEL_STACK_SIZE)
- {
- tPAddr *ptr;
- if( MM_GetPageEntryPtr(ret, 1, 0, 0, &ptr) <= 0 ) break;
- if( !(*ptr & 1) ) break;
- }
- if( ret >= (1ULL << 47) ) {
- Mutex_Release(&glMM_TempFractalLock);
- return 0;
- }
-
- // #3 Map all save the last page in the range
- // - This acts as as guard page
- MM_GetPageEntryPtr(ret, 1, 1, 0, NULL); // Make sure tree is allocated
- for( i = 0; i < KERNEL_STACK_SIZE/0x1000 - 1; i ++ )
- {
- phys = MM_AllocPhys();
- if(!phys) {
- // TODO: Clean up
- Log_Error("MM", "MM_NewWorkerStack - Unable to allocate page");
- return 0;
- }
- MM_MapEx(ret + i*0x1000, phys, 1, 0);
- MM_SetFlags(ret + i*0x1000, MM_PFLAG_KERNEL|MM_PFLAG_RO, MM_PFLAG_KERNEL);
- }
-
- // Copy data
- if( StackSize > 0x1000 ) {
- Log_Error("MM", "MM_NewWorkerStack: StackSize(0x%x) > 0x1000, cbf handling", StackSize);
- }
- else {
- tVAddr tmp_addr, dest;
- tmp_addr = MM_MapTemp(phys);
- dest = tmp_addr + (0x1000 - StackSize);
- memcpy( (void*)dest, StackData, StackSize );
- Log_Debug("MM", "MM_NewWorkerStack: %p->%p %i bytes (i=%i)", StackData, dest, StackSize, i);
- Log_Debug("MM", "MM_NewWorkerStack: ret = %p", ret);
- MM_FreeTemp(tmp_addr);
- }
-
- TMPCR3() = 0;
- Mutex_Release(&glMM_TempFractalLock);
-
- return ret + i*0x1000;
-}
-
-/**
- * \brief Allocate a new kernel stack
- */
-tVAddr MM_NewKStack(void)
-{
- tVAddr base = MM_KSTACK_BASE;
- Uint i;
- for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE )
- {
- if(MM_GetPhysAddr(base+KERNEL_STACK_SIZE-0x1000) != 0)
- continue;
-
- //Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE);
- for( i = 0x1000; i < KERNEL_STACK_SIZE; i += 0x1000)
- {
- if( !MM_Allocate(base+i) )
- {
- Log_Warning("MM", "MM_NewKStack - Allocation failed");
- for( i -= 0x1000; i; i -= 0x1000)
- MM_Deallocate(base+i);
- return 0;
- }
- }
-
- return base + KERNEL_STACK_SIZE;
- }
- Log_Warning("MM", "MM_NewKStack - No address space left\n");
- return 0;
-}
+++ /dev/null
-../x86/pci.c
\ No newline at end of file
+++ /dev/null
-;
-;
-;
-%include "arch/x86_64/include/common.inc.asm"
-[BITS 64]
-[section .text]
-
-[extern Threads_Exit]
-
-[global GetRIP]
-GetRIP:
- mov rax, [rsp]
- ret
-
-[global NewTaskHeader]
-NewTaskHeader:
- ; [rsp+0x00]: Thread
- ; [rsp+0x08]: Function
- ; [rsp+0x10]: Argument
-
- mov rdi, [rsp+0x10]
- mov rax, [rsp+0x8]
- add rsp, 0x10 ; Reclaim stack space (thread/fcn)
- xchg bx, bx
- call rax
-
- ; Quit thread with RAX as the return code
- xor rdi, rdi
- mov rsi, rax
- call Threads_Exit
-
-.hlt:
- jmp .hlt
-
-[extern MM_Clone]
-[extern MM_DumpTables]
-[global Proc_CloneInt]
-Proc_CloneInt:
- PUSH_GPR
- ; Save RSP
- mov [rdi], rsp
- call MM_Clone
- ; Save CR3
- mov rsi, [rsp+0x30] ; Saved version of RSI
- mov [rsi], rax
- ; Undo the PUSH_GPR
- add rsp, 0x80
- mov rax, .newTask
- ret
-.newTask:
-; mov rdi, 0
-; mov rsi, 0x800000000000
-; call MM_DumpTables
- POP_GPR
- xor eax, eax
- ret
-
-[global SaveState]
-SaveState:
- ; Save regs to RSI
- add rsi, 0x80
- SAVE_GPR rsi
- ; Save return addr
- mov rax, [rsp]
- mov [rsi], rax
- ; Return RSI as the RSP value
- sub rsi, 0x80
- mov [rdi], rsi
- ; Check for
- mov rax, .restore
- ret
-.restore:
- ; RSP = RSI now
- POP_GPR
- mov rax, [rsp]
- mov rsp, [rsp-0x60] ; Restore RSP from the saved value
- mov [rsp], rax ; Restore return address
- xor eax, eax
- ret
-
-[global SwitchTasks]
-; rdi = New RSP
-; rsi = Old RSP save loc
-; rdx = New RIP
-; rcx = Old RIP save loc
-; r8 = CR3
-SwitchTasks:
- PUSH_GPR
-
- ; Save state RIP and RSP
- lea rax, [rel .restore]
- mov [rcx], rax
- mov [rsi], rsp
-
- ; Change CR3 if requested
- test r8, r8
- jz .setState
- mov cr3, r8
-
- ; Make sure the stack is valid before jumping
- invlpg [rdi-0x1000]
- invlpg [rdi]
- invlpg [rdi+0x1000]
-
- ; Go to new state
-.setState:
- mov rsp, rdi
- jmp rdx
-
- ; Restore point for saved state
-.restore:
- POP_GPR
- xor eax, eax ; Return zero
- ret
-
-[global Proc_InitialiseSSE]
-Proc_InitialiseSSE:
- mov rax, cr4
- or ax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT
- mov cr4, rax
- mov rax, cr0
- and ax, ~(1 << 2) ; Clear EM
- or rax, (1 << 1) ; Set MP
- mov rax, cr0
- ret
-[global Proc_DisableSSE]
-Proc_DisableSSE:
- mov rax, cr0
- or ax, 1 << 3 ; Set TS
- mov cr0, rax
- ret
-[global Proc_EnableSSE]
-Proc_EnableSSE:
- mov rax, cr0
- and ax, ~(1 << 3) ; Clear TS
- mov cr0, rax
- ret
-
-[global Proc_SaveSSE]
-Proc_SaveSSE:
- fxsave [rdi]
- ret
-[global Proc_RestoreSSE]
-Proc_RestoreSSE:
- fxrstor [rdi]
- ret
-
-; vim: ft=nasm
+++ /dev/null
-/*
- * Acess2 x86_64 port
- * proc.c
- */
-#include <acess.h>
-#include <proc.h>
-#include <threads.h>
-#include <threads_int.h>
-#include <desctab.h>
-#include <mm_virt.h>
-#include <errno.h>
-#if USE_MP
-# include <mp.h>
-#endif
-#include <arch_config.h>
-#include <hal_proc.h>
-
-// === FLAGS ===
-#define DEBUG_TRACE_SWITCH 0
-#define BREAK_ON_SWITCH 0 // Break into bochs debugger on a task switch
-
-// === CONSTANTS ===
-
-// === TYPES ===
-typedef struct sCPU
-{
- Uint8 APICID;
- Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
- Uint16 Resvd;
- tThread *Current;
- tThread *IdleThread;
-} tCPU;
-
-// === IMPORTS ===
-extern tGDT gGDT[];
-extern void APStartup(void); // 16-bit AP startup code
-
-extern Uint GetRIP(void); // start.asm
-extern Uint SaveState(Uint *RSP, Uint *Regs);
-extern Uint Proc_CloneInt(Uint *RSP, Uint *CR3);
-extern void NewTaskHeader(void); // Actually takes cdecl args
-extern void Proc_InitialiseSSE(void);
-extern void Proc_SaveSSE(Uint DestPtr);
-extern void Proc_DisableSSE(void);
-
-extern Uint64 gInitialPML4[512]; // start.asm
-extern int giNumCPUs;
-extern int giNextTID;
-extern int giTotalTickets;
-extern int giNumActiveThreads;
-extern tThread gThreadZero;
-extern tProcess gProcessZero;
-extern void Threads_Dump(void);
-extern void Proc_ReturnToUser(tVAddr Handler, tVAddr KStackTop, int Argument);
-extern void Time_UpdateTimestamp(void);
-extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3);
-
-// === PROTOTYPES ===
-//void ArchThreads_Init(void);
-#if USE_MP
-void MP_StartAP(int CPU);
-void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
-#endif
-void Proc_IdleTask(void *unused);
-//void Proc_Start(void);
-//tThread *Proc_GetCurThread(void);
-// int Proc_NewKThread(void (*Fcn)(void*), void *Data);
-// int Proc_Clone(Uint *Err, Uint Flags);
-// int Proc_SpawnWorker(void);
-Uint Proc_MakeUserStack(void);
-//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
-void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN;
- int Proc_Demote(Uint *Err, int Dest, tRegs *Regs);
-//void Proc_CallFaultHandler(tThread *Thread);
-//void Proc_DumpThreadCPUState(tThread *Thread);
-//void Proc_Reschedule(void);
-void Proc_Scheduler(int CPU, Uint RSP, Uint RIP);
-
-// === GLOBALS ===
-//!\brief Used by desctab.asm in SyscallStub
-const int ci_offsetof_tThread_KernelStack = offsetof(tThread, KernelStack);
-// --- Multiprocessing ---
-#if USE_MP
-volatile int giNumInitingCPUs = 0;
-tMPInfo *gMPFloatPtr = NULL;
-tAPIC *gpMP_LocalAPIC = NULL;
-Uint8 gaAPIC_to_CPU[256] = {0};
-#endif
-tCPU gaCPUs[MAX_CPUS];
-tTSS *gTSSs = NULL;
-tTSS gTSS0 = {0};
-// --- Error Recovery ---
-Uint32 gaDoubleFaultStack[1024];
-
-// === CODE ===
-/**
- * \fn void ArchThreads_Init(void)
- * \brief Starts the process scheduler
- */
-void ArchThreads_Init(void)
-{
- Uint pos = 0;
-
- #if USE_MP
- tMPTable *mptable;
-
- // Mark BSP as active
- gaCPUs[0].State = 2;
-
- // -- Initialise Multiprocessing
- // Find MP Floating Table
- // - EBDA/Last 1Kib (640KiB)
- for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) {
- if( *(Uint*)(pos) == MPPTR_IDENT ) {
- Log("Possible %p", pos);
- if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
- gMPFloatPtr = (void*)pos;
- break;
- }
- }
- // - Last KiB (512KiB base mem)
- if(!gMPFloatPtr) {
- for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) {
- if( *(Uint*)(pos) == MPPTR_IDENT ) {
- Log("Possible %p", pos);
- if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
- gMPFloatPtr = (void*)pos;
- break;
- }
- }
- }
- // - BIOS ROM
- if(!gMPFloatPtr) {
- for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) {
- if( *(Uint*)(pos) == MPPTR_IDENT ) {
- Log("Possible %p", pos);
- if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
- gMPFloatPtr = (void*)pos;
- break;
- }
- }
- }
-
- // If the MP Table Exists, parse it
- if(gMPFloatPtr)
- {
- int i;
- tMPTable_Ent *ents;
- Log("gMPFloatPtr = %p", gMPFloatPtr);
- Log("*gMPFloatPtr = {");
- Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig);
- Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig);
- Log("\t.Length = 0x%02x", gMPFloatPtr->Length);
- Log("\t.Version = 0x%02x", gMPFloatPtr->Version);
- Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum);
- Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]",
- gMPFloatPtr->Features[0], gMPFloatPtr->Features[1],
- gMPFloatPtr->Features[2], gMPFloatPtr->Features[3],
- gMPFloatPtr->Features[4]
- );
- Log("}");
-
- mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig );
- Log("mptable = %p", mptable);
- Log("*mptable = {");
- Log("\t.Sig = 0x%08x", mptable->Sig);
- Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength);
- Log("\t.SpecRev = 0x%02x", mptable->SpecRev);
- Log("\t.Checksum = 0x%02x", mptable->Checksum);
- Log("\t.OEMID = '%8c'", mptable->OemID);
- Log("\t.ProductID = '%8c'", mptable->ProductID);
- Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr);
- Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize);
- Log("\t.EntryCount = 0x%04x", mptable->EntryCount);
- Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap);
- Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen);
- Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum);
- Log("}");
-
- gpMP_LocalAPIC = (void*)MM_MapHWPage(mptable->LocalAPICMemMap, 1);
-
- ents = mptable->Entries;
- giNumCPUs = 0;
-
- for( i = 0; i < mptable->EntryCount; i ++ )
- {
- int entSize = 0;
- switch( ents->Type )
- {
- case 0: // Processor
- entSize = 20;
- Log("%i: Processor", i);
- Log("\t.APICID = %i", ents->Proc.APICID);
- Log("\t.APICVer = 0x%02x", ents->Proc.APICVer);
- Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags);
- Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature);
- Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags);
-
-
- if( !(ents->Proc.CPUFlags & 1) ) {
- Log("DISABLED");
- break;
- }
-
- // Check if there is too many processors
- if(giNumCPUs >= MAX_CPUS) {
- giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped
- break;
- }
-
- // Initialise CPU Info
- gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs;
- gaCPUs[giNumCPUs].APICID = ents->Proc.APICID;
- gaCPUs[giNumCPUs].State = 0;
- giNumCPUs ++;
-
- // Send IPI
- if( !(ents->Proc.CPUFlags & 2) )
- {
- MP_StartAP( giNumCPUs-1 );
- }
-
- break;
- case 1: // Bus
- entSize = 8;
- Log("%i: Bus", i);
- Log("\t.ID = %i", ents->Bus.ID);
- Log("\t.TypeString = '%6c'", ents->Bus.TypeString);
- break;
- case 2: // I/O APIC
- entSize = 8;
- Log("%i: I/O APIC", i);
- Log("\t.ID = %i", ents->IOAPIC.ID);
- Log("\t.Version = 0x%02x", ents->IOAPIC.Version);
- Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags);
- Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr);
- break;
- case 3: // I/O Interrupt Assignment
- entSize = 8;
- Log("%i: I/O Interrupt Assignment", i);
- Log("\t.IntType = %i", ents->IOInt.IntType);
- Log("\t.Flags = 0x%04x", ents->IOInt.Flags);
- Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID);
- Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ);
- Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID);
- Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ);
- break;
- case 4: // Local Interrupt Assignment
- entSize = 8;
- Log("%i: Local Interrupt Assignment", i);
- Log("\t.IntType = %i", ents->LocalInt.IntType);
- Log("\t.Flags = 0x%04x", ents->LocalInt.Flags);
- Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID);
- Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ);
- Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID);
- Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ);
- break;
- default:
- Log("%i: Unknown (%i)", i, ents->Type);
- break;
- }
- ents = (void*)( (Uint)ents + entSize );
- }
-
- if( giNumCPUs > MAX_CPUS ) {
- Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS);
- giNumCPUs = MAX_CPUS;
- }
-
- while( giNumInitingCPUs )
- MM_FinishVirtualInit();
-
- Panic("Uh oh... MP Table Parsing is unimplemented\n");
- }
- else {
- Log("No MP Table was found, assuming uniprocessor\n");
- giNumCPUs = 1;
- gTSSs = &gTSS0;
- }
- #else
- giNumCPUs = 1;
- gTSSs = &gTSS0;
- MM_FinishVirtualInit();
- #endif
-
- #if USE_MP
- // Initialise Normal TSS(s)
- for(pos=0;pos<giNumCPUs;pos++)
- {
- #else
- pos = 0;
- #endif
- gTSSs[pos].RSP0 = 0; // Set properly by scheduler
- gGDT[7+pos*2].LimitLow = sizeof(tTSS) & 0xFFFF;
- gGDT[7+pos*2].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
- gGDT[7+pos*2].BaseMid = ((Uint)(&gTSSs[pos])) >> 16;
- gGDT[7+pos*2].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
- gGDT[7+pos*2+1].DWord[0] = ((Uint)(&gTSSs[pos])) >> 32;
- #if USE_MP
- }
- for(pos=0;pos<giNumCPUs;pos++) {
- __asm__ __volatile__ ("ltr %%ax"::"a"(0x38+pos*16));
- }
- #else
- __asm__ __volatile__ ("ltr %%ax"::"a"(0x38));
- #endif
-
- // Set Debug registers
- __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero));
- __asm__ __volatile__ ("mov %%rax, %%db1" : : "a"(0));
-
- gaCPUs[0].Current = &gThreadZero;
-
- gProcessZero.MemState.CR3 = (Uint)gInitialPML4 - KERNEL_BASE;
- gThreadZero.CurCPU = 0;
- gThreadZero.KernelStack = 0xFFFFA00000000000 + KERNEL_STACK_SIZE;
-
- // Set timer frequency
- outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
- outb(0x40, PIT_TIMER_DIVISOR&0xFF); // Low Byte of Divisor
- outb(0x40, (PIT_TIMER_DIVISOR>>8)&0xFF); // High Byte
-
- // Create Per-Process Data Block
- if( !MM_Allocate(MM_PPD_CFG) )
- {
- Warning("Oh, hell, Unable to allocate PPD for Thread#0");
- }
-
- Proc_InitialiseSSE();
-
- Log_Log("Proc", "Multithreading initialised");
-}
-
-#if USE_MP
-void MP_StartAP(int CPU)
-{
- Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
- // Set location of AP startup code and mark for a warm restart
- *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0);
- *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
- outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset
- MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);
- giNumInitingCPUs ++;
-}
-
-void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
-{
- Uint32 addr = (Uint)gpMP_LocalAPIC + 0x300;
- Uint32 val;
-
- // High
- val = (Uint)APICID << 24;
- Log("*%p = 0x%08x", addr+0x10, val);
- *(Uint32*)(addr+0x10) = val;
- // Low (and send)
- val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
- Log("*%p = 0x%08x", addr, val);
- *(Uint32*)addr = val;
-}
-#endif
-
-/**
- * \brief Idle task
- */
-void Proc_IdleTask(void *ptr)
-{
- tCPU *cpu = ptr;
- cpu->IdleThread = Proc_GetCurThread();
- cpu->IdleThread->ThreadName = (char*)"Idle Thread";
- Threads_SetPriority( cpu->IdleThread, -1 ); // Never called randomly
- cpu->IdleThread->Quantum = 1; // 1 slice quantum
- for(;;) {
- HALT(); // Just yeilds
- Threads_Yield();
- }
-}
-
-/**
- * \fn void Proc_Start(void)
- * \brief Start process scheduler
- */
-void Proc_Start(void)
-{
- #if USE_MP
- int i;
- #endif
-
- #if USE_MP
- // Start APs
- for( i = 0; i < giNumCPUs; i ++ )
- {
- int tid;
- if(i) gaCPUs[i].Current = NULL;
-
- Proc_NewKThread(Proc_IdleTask, &gaCPUs[i]);
-
- // Create Idle Task
- gaCPUs[i].IdleThread = Threads_GetThread(tid);
-
-
- // Start the AP
- if( i != giProc_BootProcessorID ) {
- MP_StartAP( i );
- }
- }
-
- // BSP still should run the current task
- gaCPUs[0].Current = &gThreadZero;
- __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero));
-
- // Start interrupts and wait for APs to come up
- Log("Waiting for APs to come up\n");
- __asm__ __volatile__ ("sti");
- while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
- #else
- Proc_NewKThread(Proc_IdleTask, &gaCPUs[0]);
-
- // Start Interrupts (and hence scheduler)
- __asm__ __volatile__("sti");
- #endif
- MM_FinishVirtualInit();
- Log_Log("Proc", "Multithreading started");
-}
-
-/**
- * \fn tThread *Proc_GetCurThread(void)
- * \brief Gets the current thread
- */
-tThread *Proc_GetCurThread(void)
-{
- #if USE_MP
- tThread *ret;
- __asm__ __volatile__ ("mov %%db0, %0" : "=r"(thread));
- return ret; // gaCPUs[ GetCPUNum() ].Current;
- #else
- return gaCPUs[ 0 ].Current;
- #endif
-}
-
-void Proc_ClearProcess(tProcess *Process)
-{
- Log_Warning("Proc", "TODO: Nuke address space etc");
-}
-
-/*
- *
- */
-void Proc_ClearThread(tThread *Thread)
-{
-}
-
-/**
- * \brief Create a new kernel thread
- */
-tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
-{
- Uint rsp;
- tThread *newThread, *cur;
-
- cur = Proc_GetCurThread();
- newThread = Threads_CloneTCB(0);
- if(!newThread) return -1;
-
- // Create new KStack
- newThread->KernelStack = MM_NewKStack();
- // Check for errors
- if(newThread->KernelStack == 0) {
- free(newThread);
- return -1;
- }
-
- rsp = newThread->KernelStack;
- *(Uint*)(rsp-=8) = (Uint)Data; // Data (shadowed)
- *(Uint*)(rsp-=8) = (Uint)Fcn; // Function to call
- *(Uint*)(rsp-=8) = (Uint)newThread; // Thread ID
-
- newThread->SavedState.RSP = rsp;
- newThread->SavedState.RIP = (Uint)&NewTaskHeader;
- newThread->SavedState.SSE = NULL;
-// Log("New (KThread) %p, rsp = %p\n", newThread->SavedState.RIP, newThread->SavedState.RSP);
-
-// MAGIC_BREAK();
- Threads_AddActive(newThread);
-
- return newThread->TID;
-}
-
-/**
- * \fn int Proc_Clone(Uint Flags)
- * \brief Clone the current process
- */
-tTID Proc_Clone(Uint Flags)
-{
- tThread *newThread, *cur = Proc_GetCurThread();
- Uint rip;
-
- // Sanity check
- if( !(Flags & CLONE_VM) ) {
- Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead");
- return -1;
- }
-
- // Create new TCB
- newThread = Threads_CloneTCB(Flags);
- if(!newThread) return -1;
-
- // Save core machine state
- rip = Proc_CloneInt(&newThread->SavedState.RSP, &newThread->Process->MemState.CR3);
- if(rip == 0) return 0; // Child
- newThread->KernelStack = cur->KernelStack;
- newThread->SavedState.RIP = rip;
- newThread->SavedState.SSE = NULL;
-
- // DEBUG
- #if 0
- Log("New (Clone) %p, rsp = %p, cr3 = %p", rip, newThread->SavedState.RSP, newThread->MemState.CR3);
- {
- Uint cr3;
- __asm__ __volatile__ ("mov %%cr3, %0" : "=r" (cr3));
- Log("Current CR3 = 0x%x, PADDR(RSP) = 0x%x", cr3, MM_GetPhysAddr(newThread->SavedState.RSP));
- }
- #endif
- // /DEBUG
-
- // Lock list and add to active
- Threads_AddActive(newThread);
-
- return newThread->TID;
-}
-
-/**
- * \fn int Proc_SpawnWorker(void)
- * \brief Spawns a new worker thread
- */
-int Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
-{
- tThread *new, *cur;
- Uint stack_contents[3];
-
- cur = Proc_GetCurThread();
-
- // Create new thread
- new = Threads_CloneThreadZero();
- if(!new) {
- Warning("Proc_SpawnWorker - Out of heap space!\n");
- return -1;
- }
-
- // Create the stack contents
- stack_contents[2] = (Uint)Data;
- stack_contents[1] = (Uint)Fcn;
- stack_contents[0] = (Uint)new;
-
- // Create a new worker stack (in PID0's address space)
- // - The stack is built by this code using stack_contents
- new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents));
-
- new->SavedState.RSP = new->KernelStack - sizeof(stack_contents);
- new->SavedState.RIP = (Uint)&NewTaskHeader;
- new->SavedState.SSE = NULL;
-
-// Log("New (Worker) %p, rsp = %p\n", new->SavedState.RIP, new->SavedState.RSP);
-
- // Mark as active
- new->Status = THREAD_STAT_PREINIT;
- Threads_AddActive( new );
-
- return new->TID;
-}
-
-/**
- * \brief Creates a new user stack
- */
-Uint Proc_MakeUserStack(void)
-{
- int i;
- Uint base = USER_STACK_TOP - USER_STACK_SZ;
-
- // Check Prospective Space
- for( i = USER_STACK_SZ >> 12; i--; )
- {
- if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
- break;
- }
-
- if(i != -1) return 0;
-
- // Allocate Stack - Allocate incrementally to clean up MM_Dump output
- // - Most of the user stack is the zero page
- for( i = 0; i < (USER_STACK_SZ-USER_STACK_PREALLOC)/0x1000; i++ )
- {
- MM_AllocateZero( base + (i<<12) );
- }
- // - but the top USER_STACK_PREALLOC pages are actually allocated
- for( ; i < USER_STACK_SZ/0x1000; i++ )
- {
- tPAddr alloc = MM_Allocate( base + (i<<12) );
- if( !alloc )
- {
- // Error
- Log_Error("Proc", "Unable to allocate user stack (%i pages requested)", USER_STACK_SZ/0x1000);
- while( i -- )
- MM_Deallocate( base + (i<<12) );
- return 0;
- }
- }
-
- return base + USER_STACK_SZ;
-}
-
-void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
-{
- Uint *stack;
- int i;
- const char **envp = NULL;
- Uint16 ss, cs;
-
-
- // Copy Arguments
- stack = (void*)Proc_MakeUserStack();
- if(!stack) {
- Log_Error("Proc", "Unable to create user stack!");
- Threads_Exit(0, -1);
- }
- stack -= (DataSize+7)/8;
- memcpy( stack, ArgV, DataSize );
- free(ArgV);
-
- // Adjust Arguments and environment
- if(DataSize)
- {
- Uint delta = (Uint)stack - (Uint)ArgV;
- ArgV = (const char**)stack;
- for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta;
- envp = &ArgV[i+1];
- for( i = 0; envp[i]; i++ ) envp[i] += delta;
- }
-
- // User Mode Segments
- // 0x2B = 64-bit
- ss = 0x23; cs = 0x2B;
-
- // Arguments
- *--stack = (Uint)envp;
- *--stack = (Uint)ArgV;
- *--stack = (Uint)ArgC;
- *--stack = Base;
-
- Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
-}
-
-void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
-{
- if( !(CS == 0x1B || CS == 0x2B) || SS != 0x23 ) {
- Log_Error("Proc", "Proc_StartProcess: CS / SS are not valid (%x, %x)",
- CS, SS);
- Threads_Exit(0, -1);
- }
-// Log("Proc_StartProcess: (SS=%x, Stack=%p, Flags=%x, CS=%x, IP=%p)", SS, Stack, Flags, CS, IP);
-// MM_DumpTables(0, USER_MAX);
- if(CS == 0x1B)
- {
- // 32-bit return
- __asm__ __volatile__ (
- "mov %0, %%rsp;\n\t" // Set stack pointer
- "mov %2, %%r11;\n\t" // Set RFLAGS
- "sysret;\n\t"
- : : "r" (Stack), "c" (IP), "r" (Flags)
- );
- }
- else
- {
- // 64-bit return
- __asm__ __volatile__ (
- "mov %0, %%rsp;\n\t" // Set stack pointer
- "mov %2, %%r11;\n\t" // Set RFLAGS
- "sysretq;\n\t"
- : : "r" (Stack), "c" (IP), "r" (Flags)
- );
- }
- for(;;);
-}
-
-/**
- * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
- * \brief Demotes a process to a lower permission level
- * \param Err Pointer to user's errno
- * \param Dest New Permission Level
- * \param Regs Pointer to user's register structure
- */
-int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
-{
- int cpl = Regs->CS & 3;
- // Sanity Check
- if(Dest > 3 || Dest < 0) {
- *Err = -EINVAL;
- return -1;
- }
-
- // Permission Check
- if(cpl > Dest) {
- *Err = -EACCES;
- return -1;
- }
-
- // Change the Segment Registers
- Regs->CS = (((Dest+1)<<4) | Dest) - 8;
- Regs->SS = ((Dest+1)<<4) | Dest;
-
- return 0;
-}
-
-/**
- * \brief Calls a signal handler in user mode
- * \note Used for signals
- */
-void Proc_CallFaultHandler(tThread *Thread)
-{
- // Never returns
- Proc_ReturnToUser(Thread->FaultHandler, Thread->KernelStack, Thread->CurFaultNum);
- for(;;);
-}
-
-void Proc_DumpThreadCPUState(tThread *Thread)
-{
- Log(" At %04x:%016llx", Thread->SavedState.UserCS, Thread->SavedState.UserRIP);
-}
-
-void Proc_Reschedule(void)
-{
- tThread *nextthread, *curthread;
- int cpu = GetCPUNum();
-
- // TODO: Wait for it?
- if(IS_LOCKED(&glThreadListLock)) return;
-
- curthread = gaCPUs[cpu].Current;
-
- nextthread = Threads_GetNextToRun(cpu, curthread);
-
- if(nextthread == curthread) return ;
- if(!nextthread)
- nextthread = gaCPUs[cpu].IdleThread;
- if(!nextthread)
- return ;
-
- #if DEBUG_TRACE_SWITCH
- LogF("\nSwitching to task CR3 = 0x%x, RIP = %p, RSP = %p - %i (%s)\n",
- nextthread->Process->MemState.CR3,
- nextthread->SavedState.RIP,
- nextthread->SavedState.RSP,
- nextthread->TID,
- nextthread->ThreadName
- );
- #endif
-
- // Update CPU state
- gaCPUs[cpu].Current = nextthread;
- gTSSs[cpu].RSP0 = nextthread->KernelStack-4;
- __asm__ __volatile__ ("mov %0, %%db0" : : "r" (nextthread));
-
- if( curthread )
- {
- // Save FPU/MMX/XMM/SSE state
- if( curthread->SavedState.SSE )
- {
- Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
- curthread->SavedState.bSSEModified = 0;
- Proc_DisableSSE();
- }
- SwitchTasks(
- nextthread->SavedState.RSP, &curthread->SavedState.RSP,
- nextthread->SavedState.RIP, &curthread->SavedState.RIP,
- nextthread->Process->MemState.CR3
- );
- }
- else
- {
- Uint tmp;
- SwitchTasks(
- nextthread->SavedState.RSP, &tmp,
- nextthread->SavedState.RIP, &tmp,
- nextthread->Process->MemState.CR3
- );
- }
- return ;
-}
-
-/**
- * \fn void Proc_Scheduler(int CPU)
- * \brief Swap current thread and clears dead threads
- */
-void Proc_Scheduler(int CPU, Uint RSP, Uint RIP)
-{
-#if 0
- tThread *thread;
-
- // If the spinlock is set, let it complete
- if(IS_LOCKED(&glThreadListLock)) return;
-
- // Get current thread
- thread = gaCPUs[CPU].Current;
-
- if( thread )
- {
- tRegs *regs;
- // Reduce remaining quantum and continue timeslice if non-zero
- if(thread->Remaining--) return;
- // Reset quantum for next call
- thread->Remaining = thread->Quantum;
-
- // TODO: Make this more stable somehow
- {
- regs = (tRegs*)(RSP+(1)*8); // CurThread
- thread->SavedState.UserCS = regs->CS;
- thread->SavedState.UserRIP = regs->RIP;
- }
- }
-
- // ACK Timer here?
-
- Proc_Reschedule();
-#endif
-}
-
-// === EXPORTS ===
-EXPORT(Proc_SpawnWorker);
+++ /dev/null
-/home/tpg/Projects/RealmodeEmulator/src/rme.c
\ No newline at end of file
+++ /dev/null
-/home/tpg/Projects/RealmodeEmulator/src/rme.h
\ No newline at end of file
+++ /dev/null
-;
-; Acess2 x86_64 port
-;
-
-%include "arch/x86_64/include/common.inc.asm"
-
-[BITS 32]
-
-;KERNEL_BASE equ 0xFFFF800000000000
-KERNEL_BASE equ 0xFFFFFFFF80000000
-
-[section .multiboot]
-mboot:
- ; Multiboot macros to make a few lines later more readable
- MULTIBOOT_PAGE_ALIGN equ 1<<0
- MULTIBOOT_MEMORY_INFO equ 1<<1
- MULTIBOOT_AOUT_KLUDGE equ 1<<16
- MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
- MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO; | MULTIBOOT_AOUT_KLUDGE
- MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
-
- ; This is the GRUB Multiboot header. A boot signature
- dd MULTIBOOT_HEADER_MAGIC
- dd MULTIBOOT_HEADER_FLAGS
- dd MULTIBOOT_CHECKSUM
- [extern __load_addr]
- [extern __bss_start]
- [extern gKernelEnd]
- ; a.out kludge
- dd mboot ; Location of Multiboot Header
- dd __load_addr ; Load address
- dd __bss_start - KERNEL_BASE ; End of .data
- dd gKernelEnd - KERNEL_BASE ; End of .bss (and kernel)
- dd start - KERNEL_BASE ; Entrypoint
-
-[extern start64]
-
-[section .text]
-[global start]
-start:
- mov [gMultibootMagic - KERNEL_BASE], eax
- mov [gMultibootPtr - KERNEL_BASE], ebx
-
- ; Check for Long Mode support
- mov eax, 0x80000000
- cpuid
- cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
- mov eax, 0x80000001
- cpuid
- jb .not64bitCapable
- test edx, 1<<29
- jz .not64bitCapable
-
- ; Enable PGE (Page Global Enable)
- ; + PAE (Physical Address Extension)
- ; + PSE (Page Size Extensions)
- mov eax, cr4
- or eax, 0x80|0x20|0x10
- mov cr4, eax
-
- ; Load PDP4
- mov eax, gInitialPML4 - KERNEL_BASE
- mov cr3, eax
-
- ; Enable IA-32e mode
- ; (Also enables SYSCALL and NX)
- mov ecx, 0xC0000080
- rdmsr
- or eax, (1 << 11)|(1 << 8)|(1 << 0) ; NXE, LME, SCE
- wrmsr
-
- ; Enable paging
- mov eax, cr0
- or eax, 0x80010000 ; PG & WP
- mov cr0, eax
-
- ; Load GDT
- lgdt [gGDTPtr - KERNEL_BASE]
- jmp 0x08:start64 - KERNEL_BASE
-
-.not64bitCapable:
- mov ah, 0x0F
- mov edi, 0xB8000
- mov esi, csNot64BitCapable - KERNEL_BASE
-
-.loop:
- lodsb
- test al, al
- jz .hlt
- stosw
- jmp .loop
-
-.hlt:
- cli
- hlt
- jmp .hlt
-
-[section .data]
-[global gGDT]
-[global gGDTPtr]
-gGDT:
- dd 0,0
- dd 0x00000000, 0x00209A00 ; 0x08: 64-bit Code
- dd 0x00000000, 0x00009200 ; 0x10: 64-bit Data
- dd 0x00000000, 0x0040FA00 ; 0x18: 32-bit User Code
- dd 0x00000000, 0x0040F200 ; 0x20: User Data
- dd 0x00000000, 0x0020FA00 ; 0x28: 64-bit User Code
- dd 0x00000000, 0x0000F200 ; 0x30: User Data (64 version)
- times MAX_CPUS dd 0, 0x00008900, 0, 0 ; 0x38+16*n: TSS 0
-gGDTPtr:
- dw $-gGDT-1
- dd gGDT-KERNEL_BASE
- dd 0
-[global gMultibootPtr]
-[global gMultibootMagic]
-gMultibootMagic:
- dd 0
-gMultibootPtr:
- dd 0
-
-[section .padata]
-[global gInitialPML4]
-gInitialPML4: ; Covers 256 TiB (Full 48-bit Virtual Address Space)
- dd gInitialPDP - KERNEL_BASE + 3, 0 ; Identity Map Low 4Mb
- times 0xA0*2-1 dq 0
- dd gStackPDP - KERNEL_BASE + 3, 0
- times 512-4-($-gInitialPML4)/8 dq 0
- dd gInitialPML4 - KERNEL_BASE + 3, 0 ; Fractal Mapping
- dq 0
- dq 0
- dd gHighPDP - KERNEL_BASE + 3, 0 ; Map Low 4Mb to kernel base
-
-gInitialPDP: ; Covers 512 GiB
- dd gInitialPD - KERNEL_BASE + 3, 0
- times 511 dq 0
-
-gStackPDP:
- dd gStackPD - KERNEL_BASE + 3, 0
- times 511 dq 0
-
-gHighPDP: ; Covers 512 GiB
- times 510 dq 0
- ;dq 0 + 0x143 ; 1 GiB Page from zero
- dd gInitialPD - KERNEL_BASE + 3, 0
- dq 0
-
-gInitialPD: ; Covers 1 GiB
-; dq 0 + 0x143 ; 1 GiB Page from zero
- dd gInitialPT1 - KERNEL_BASE + 3, 0
- dd gInitialPT2 - KERNEL_BASE + 3, 0
- times 510 dq 0
-
-gStackPD:
- dd gKStackPT - KERNEL_BASE + 3, 0
- times 511 dq 0
-
-gKStackPT: ; Covers 2 MiB
- ; Initial stack - 64KiB
- dq 0
- %assign i 0
- %rep INITIAL_KSTACK_SIZE-1
- dd gInitialKernelStack - KERNEL_BASE + i*0x1000 + 0x103, 0
- %assign i i+1
- %endrep
- times 512-INITIAL_KSTACK_SIZE dq 0
-gInitialPT1: ; 2 MiB
- %assign i 0
- %rep 512
- dq i*4096+0x103
- %assign i i+1
- %endrep
-gInitialPT2: ; 2 MiB
- %assign i 512
- %rep 512
- dq i*4096+0x103
- %assign i i+1
- %endrep
-
-[section .padata]
-[global gInitialKernelStack]
-gInitialKernelStack:
- times 0x1000*(INITIAL_KSTACK_SIZE-1) db 0 ; 8 Pages
-
-[section .rodata]
-csNot64BitCapable:
- db "Not 64-bit Capable",0
-
-; vim: ft=nasm
+++ /dev/null
-;
-; Acess2 x86_64 Port
-;
-%include "arch/x86_64/include/common.inc.asm"
-[bits 64]
-;KERNEL_BASE equ 0xFFFF800000000000
-KERNEL_BASE equ 0xFFFFFFFF80000000
-
-[extern kmain]
-
-[extern gMultibootPtr]
-[extern gMultibootMagic]
-
-[section .text]
-[global start64]
-start64:
- ; Load Registers
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
-
- ; Go to high memory
- mov rax, start64.himem
- jmp rax
-.himem:
-
- xor rax, rax
- mov dr0, rax ; Set CPU0
-
- ; Clear the screen
- mov rax, 0x1F201F201F201F20 ; Set the screen to White on blue, space (4 characters)
- mov edi, 0xB8000
- mov ecx, 80*25*2/8
- rep stosq
-
- ; Set kernel stack
- mov rsp, 0xFFFFA00000000000 + INITIAL_KSTACK_SIZE*0x1000
-
- ; Call main
- mov edi, [gMultibootMagic - KERNEL_BASE]
- mov esi, [gMultibootPtr - KERNEL_BASE]
- call kmain
-
- cli
-.hlt:
- hlt
- jmp .hlt
-
-[global GetCPUNum]
-GetCPUNum:
- mov rax, dr1
- ret
-
-KSTACK_USERSTATE_SIZE equ (5+2+16+2)*8 ; IRET, ErrorNum, ErrorCode, GPRs, FS&GS
-[global Proc_ReturnToUser]
-Proc_ReturnToUser:
- ; RDI - Handler
- ; RSI - Kernel Stack
- ; RDX - Signal num
-
- ;
- ; NOTE: This can cause corruption if the signal happens while the user
- ; has called a kernel operation.
- ; Good thing this can only be called on a user fault.
- ;
-
- xchg bx, bx
- ; Get and alter User SP
- mov rcx, [rsi-0x20] ; Get user SP
- xor eax, eax
- mov [rcx-16], rax
- sub rcx, 16
-
- ; Drop down to user mode
- cli
- mov rsp, rcx ; Set SP
- mov rcx, rdi ; SYSRET IP
-
- mov rdi, rdx ; Argument for handler
- mov r11, 0x202 ; RFlags
- db 0x48
- sysret
-
-; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args)
-; Call a function passing the array as arguments
-[global CallWithArgArray]
-CallWithArgArray:
- push rbp
- mov rbp, rsp
- push r10
- push r11
-
- mov [rbp+2*8], rdi ; Save Ptr to stack
-
- mov r11, rsi ; NArgs
- mov r10, rdx ; Args
-
- ; Arg 1: RDI
- mov rdi, [r10]
- add r10, 8
- dec r11
- jz .call
- ; Arg 2: RSI
- mov rsi, [r10]
- add r10, 8
- dec r11
- jz .call
- ; Arg 3: RDX
- mov rdx, [r10]
- add r10, 8
- dec r11
- jz .call
- ; Arg 4: RCX
- mov rcx, [r10]
- add r10, 8
- dec r11
- jz .call
- ; Arg 5: R8
- mov r8, [r10]
- add r10, 8
- dec r11
- jz .call
- ; Arg 6: R9
- mov r9, [r10]
- add r10, 8
- dec r11
- jz .call
- ; No support for more
-
-.call:
- mov rax, [rbp+2*8] ; Ptr
- call rax
-
- pop r11
- pop r10
-
- lea rsp, [rbp]
- pop rbp
- ret
-
-; vim: ft=nasm
+++ /dev/null
-/*
- * Acess2 Kernel
- * Timekeeping
- * arch/x86_64/time.c
- */
-#include <acess.h>
-#include <arch_config.h>
-
-// === MACROS ===
-#define TIMER_QUANTUM 100
-#define TIMER_FREQ PIT_TIMER_BASE_N/(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR)
-#define MS_PER_TICK_WHOLE (1000*(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR)/PIT_TIMER_BASE_N)
-#define MS_PER_TICK_FRACT ((0x80000000ULL*1000ULL*PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR/PIT_TIMER_BASE_N)&0x7FFFFFFF)
-
-// === IMPORTS ===
-extern volatile Sint64 giTimestamp;
-extern volatile Uint64 giTicks;
-extern volatile Uint64 giPartMiliseconds;
-extern void Timer_CallTimers(void);
-
-// === GLOBALS ===
-volatile Uint64 giTime_TSCAtLastTick = 0;
-volatile Uint64 giTime_TSCPerTick = 0;
-
-// === PROTOTYPES ===
-//Sint64 now(void);
- int Time_Setup(void);
-void Time_UpdateTimestamp(void);
-Uint64 Time_ReadTSC(void);
-
-// === CODE ===
-/**
- * \fn Sint64 now()
- * \brief Return the current timestamp
- */
-Sint64 now(void)
-{
- Uint64 tsc = Time_ReadTSC();
- tsc -= giTime_TSCAtLastTick;
- tsc *= MS_PER_TICK_WHOLE;
- if( giTime_TSCPerTick ) {
- tsc /= giTime_TSCPerTick;
- }
- else
- tsc = 0;
- return giTimestamp + tsc;
-}
-
-/**
- * \fn int Time_Setup(void)
- * \brief Sets the system time from the Realtime-Clock
- */
-int Time_Setup(void)
-{
- Log_Log("Timer", "PIT Timer firing at %iHz, %i.0x%08x miliseconds per tick",
- TIMER_FREQ, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT);
-
- // TODO: Read time from RTC
-
- return 0;
-}
-
-/**
- * \brief Called on the timekeeping IRQ
- */
-void Time_UpdateTimestamp(void)
-{
- Uint64 curTSC = Time_ReadTSC();
-
- if( giTime_TSCAtLastTick )
- {
- giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick;
- }
- giTime_TSCAtLastTick = curTSC;
-
- giTicks ++;
- giTimestamp += MS_PER_TICK_WHOLE;
- giPartMiliseconds += MS_PER_TICK_FRACT;
- if(giPartMiliseconds > 0x80000000) {
- giTimestamp ++;
- giPartMiliseconds -= 0x80000000;
- }
-
- Timer_CallTimers();
-}
-
-#if 0
-/**
- * \fn void Time_TimerThread(void)
- */
-void Time_TimerThread(void)
-{
- Sint64 next;
- Threads_SetName("TIMER");
-
- next = giTimestamp + TIMER_QUANTUM;
- for(;;)
- {
- while(giTimestamp < next) Threads_Yield();
- next = giTimestamp + TIMER_QUANTUM;
- Timer_CallTimers();
- }
-}
-#endif
-
-Uint64 Time_ReadTSC(void)
-{
- Uint32 a, d;
- __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d));
- return ((Uint64)d << 32) | a;
-}
+++ /dev/null
-/*
- */
-#include <acess.h>
-#include <vm8086.h>
-#include <modules.h>
-//#include "rme.h"
-
-// === CONSTANTS ===
-#define VM8086_STACK_SEG 0x9F00
-#define VM8086_STACK_OFS 0x0AFE
-#define VM8086_PAGES_PER_INST 4
-
-// === PROTOTYPES ===
- int VM8086_Install(char **Arguments);
-//tVM8086 *VM8086_Init(void);
-//void VM8086_Free(tVM8086 *State);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL);
-tMutex glVM8086_Process;
-//tRME_State *gpVM8086_State;
-tPID gVM8086_WorkerPID;
-tTID gVM8086_CallingThread;
-tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions
-
-// === CODE ===
-int VM8086_Install(char **Arguments)
-{
- //gpVM8086_State = RME_CreateState();
- return MODULE_ERR_OK;
-}
-
-tVM8086 *VM8086_Init(void)
-{
- return NULL;
-}
-
-void VM8086_Free(tVM8086 *State)
-{
-
-}
-
-void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset)
-{
- return NULL;
-}
-
-void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset)
-{
- return NULL;
-}
-
-void VM8086_Int(tVM8086 *State, Uint8 Interrupt)
-{
-
-}
+++ /dev/null
-Acess2 Binary File Loader Specifcation
---------------------------------------
-
-Binary file loaders are defined by creating a \a tBinaryType variable that
-is registered with the kernel.
-
-\a tBinaryType contains seven fields.
--# \a Next
- This field is used internally by the kernel and should be set to NULL
- when the definition is initialise and not changed by the driver.
--# \a Ident
- This field tells the kernel how to recognise this file format. If the
- first 32-bits of the file (ANDed with the \a Mask field) match this
- value, the file will be passed to this loader.
--# \a Mask
- Determines what bits in the first 32-bits of the file matter for the
- identifcation.
--# \a Name
- This is a C string that uniquely identifies this binary format.
--# \a Load
- This field is a pointer to a function that takes a VFS Handle of the
- source exectuable file as an argument and returns a \a tBinary
- pointer that defines the location and attributes of the exectable's
- segments. (See \a tBinary for more information)
+++ /dev/null
-/*\r
- * Acess v0.1\r
- * ELF Executable Loader Code\r
- */\r
-#define DEBUG 0\r
-#include <acess.h>\r
-#include <binary.h>\r
-#include "elf.h"\r
-\r
-#define DEBUG_WARN 1\r
-\r
-// === PROTOTYPES ===\r
-tBinary *Elf_Load(int fp);\r
-tBinary *Elf_Load64(int fp, Elf64_Ehdr *hdr);\r
-tBinary *Elf_Load32(int fp, Elf32_Ehdr *hdr);\r
- int Elf_Relocate(void *Base);\r
- int Elf_Relocate32(void *Base);\r
- int Elf_GetSymbol(void *Base, const char *Name, Uint *ret);\r
- int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base);\r
-Uint Elf_Int_HashString(const char *str);\r
-\r
-// === GLOBALS ===\r
-tBinaryType gELF_Info = {\r
- NULL,\r
- 0x464C457F, 0xFFFFFFFF, // '\x7FELF'\r
- "ELF",\r
- Elf_Load, Elf_Relocate, Elf_GetSymbol\r
- };\r
-\r
-// === CODE ===\r
-tBinary *Elf_Load(int fp)\r
-{\r
- Elf64_Ehdr hdr;\r
- \r
- // Read ELF Header\r
- VFS_Read(fp, sizeof(hdr), &hdr);\r
- \r
- // Check the file type\r
- if(hdr.e_ident[0] != 0x7F || hdr.e_ident[1] != 'E' || hdr.e_ident[2] != 'L' || hdr.e_ident[3] != 'F') {\r
- Log_Warning("ELF", "Non-ELF File was passed to the ELF loader");\r
- return NULL;\r
- }\r
-\r
- switch(hdr.e_ident[4]) // EI_CLASS\r
- {\r
- case ELFCLASS32:\r
- return Elf_Load32(fp, (Elf32_Ehdr*)&hdr);\r
- case ELFCLASS64:\r
- return Elf_Load64(fp, &hdr);\r
- default:\r
- Log_Warning("ELF", "Unknown EI_CLASS value %i", hdr.e_ident[4]);\r
- return NULL;\r
- }\r
-}\r
-\r
-tBinary *Elf_Load64(int FD, Elf64_Ehdr *Header)\r
-{\r
- tBinary *ret;\r
- Elf64_Phdr phtab[Header->e_phnum];\r
- int nLoadSegments;\r
- int i, j;\r
- \r
- // Sanity check\r
- if( Header->e_phoff == 0 )\r
- {\r
- Log_Warning("ELF", "No program header, panic!");\r
- return NULL;\r
- }\r
- if( Header->e_shentsize != sizeof(Elf64_Shdr) ) {\r
- Log_Warning("ELF", "Header gives shentsize as %i, my type is %i",\r
- Header->e_shentsize, sizeof(Elf64_Shdr) );\r
- }\r
- if( Header->e_phentsize != sizeof(Elf64_Phdr) ) {\r
- Log_Warning("ELF", "Header gives phentsize as %i, my type is %i",\r
- Header->e_phentsize, sizeof(Elf64_Phdr) );\r
- }\r
-\r
- LOG("Header = {");\r
- LOG(" e_ident = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",\r
- Header->e_ident[0], Header->e_ident[1], Header->e_ident[2], Header->e_ident[3],\r
- Header->e_ident[4], Header->e_ident[5], Header->e_ident[6], Header->e_ident[7],\r
- Header->e_ident[8], Header->e_ident[9], Header->e_ident[10], Header->e_ident[11],\r
- Header->e_ident[12], Header->e_ident[13], Header->e_ident[14], Header->e_ident[15]\r
- );\r
- LOG(" e_type = %i", Header->e_type);\r
- LOG(" e_machine = %i", Header->e_machine);\r
- LOG(" e_version = %i", Header->e_version);\r
- LOG(" e_entry = 0x%llx", Header->e_entry);\r
- LOG(" e_phoff = 0x%llx", Header->e_phoff);\r
- LOG(" e_shoff = 0x%llx", Header->e_shoff);\r
- LOG(" e_flags = 0x%x", Header->e_flags);\r
- LOG(" e_ehsize = %i", Header->e_ehsize);\r
- LOG(" e_phentsize = %i", Header->e_phentsize);\r
- LOG(" e_phnum = %i", Header->e_phnum);\r
- LOG(" e_shentsize = %i", Header->e_shentsize);\r
- LOG(" e_shnum = %i", Header->e_shnum);\r
- LOG(" e_shstrndx = %i", Header->e_shstrndx);\r
- LOG("}");\r
-\r
- // Load Program Header table\r
- VFS_Seek(FD, Header->e_phoff, SEEK_SET);\r
- VFS_Read(FD, sizeof(Elf64_Phdr)*Header->e_phnum, phtab);\r
-\r
- // Count load segments\r
- nLoadSegments = 0;\r
- for( i = 0; i < Header->e_phnum; i ++ )\r
- {\r
- if( phtab[i].p_type != PT_LOAD ) continue ;\r
- nLoadSegments ++;\r
- }\r
- \r
- // Allocate Information Structure\r
- ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*nLoadSegments );\r
- // Fill Info Struct\r
- ret->Entry = Header->e_entry;\r
- ret->Base = -1; // Set Base to maximum value\r
- ret->NumSections = nLoadSegments;\r
- ret->Interpreter = NULL;\r
-\r
- j = 0; // LoadSections[] index\r
- for( i = 0; i < Header->e_phnum; i ++ )\r
- {\r
- LOG("phtab[%i] = {", i);\r
- LOG(" .p_type = %i", phtab[i].p_type);\r
- LOG(" .p_flags = 0x%x", phtab[i].p_flags);\r
- LOG(" .p_offset = 0x%llx", phtab[i].p_offset);\r
- LOG(" .p_vaddr = 0x%llx", phtab[i].p_vaddr);\r
- LOG(" .p_paddr = 0x%llx", phtab[i].p_paddr);\r
- LOG(" .p_filesz = 0x%llx", phtab[i].p_filesz);\r
- LOG(" .p_memsz = 0x%llx", phtab[i].p_memsz);\r
- LOG(" .p_align = 0x%llx", phtab[i].p_align);\r
- LOG("}");\r
-\r
- // Get Interpreter Name\r
- if( phtab[i].p_type == PT_INTERP )\r
- {\r
- char *tmp;\r
- if(ret->Interpreter) continue;\r
- tmp = malloc(phtab[i].p_filesz);\r
- VFS_Seek(FD, phtab[i].p_offset, 1);\r
- VFS_Read(FD, phtab[i].p_filesz, tmp);\r
- ret->Interpreter = Binary_RegInterp(tmp);\r
- LOG("Interpreter '%s'", tmp);\r
- free(tmp);\r
- continue;\r
- }\r
- \r
- if( phtab[i].p_type != PT_LOAD ) continue ;\r
- \r
- // Find the executable base\r
- if( phtab[i].p_vaddr < ret->Base ) ret->Base = phtab[i].p_vaddr;\r
-\r
- ret->LoadSections[j].Offset = phtab[i].p_offset;\r
- ret->LoadSections[j].Virtual = phtab[i].p_vaddr;\r
- ret->LoadSections[j].FileSize = phtab[i].p_filesz;\r
- ret->LoadSections[j].MemSize = phtab[i].p_memsz;\r
- \r
- ret->LoadSections[j].Flags = 0;\r
- if( !(phtab[i].p_flags & PF_W) )\r
- ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO;\r
- if( phtab[i].p_flags & PF_X )\r
- ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC;\r
- j ++;\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header)\r
-{\r
- tBinary *ret;\r
- Elf32_Phdr *phtab;\r
- int i, j;\r
- int iLoadCount;\r
-\r
- ENTER("xFD", FD);\r
-\r
- // Check architecture with current CPU\r
- // - TODO: Support kernel level emulation\r
- #if ARCH_IS_x86\r
- if( Header->machine != EM_386 )\r
- {\r
- Log_Warning("ELF", "Unknown architecure on ELF-32");\r
- LEAVE_RET('n');\r
- return NULL;\r
- }\r
- #endif\r
-\r
- // Check for a program header\r
- if(Header->phoff == 0) {\r
- #if DEBUG_WARN\r
- Log_Warning("ELF", "File does not contain a program header (phoff == 0)");\r
- #endif\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- // Read Program Header Table\r
- phtab = malloc( sizeof(Elf32_Phdr) * Header->phentcount );\r
- if( !phtab ) {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- LOG("hdr.phoff = 0x%08x", Header->phoff);\r
- VFS_Seek(FD, Header->phoff, SEEK_SET);\r
- VFS_Read(FD, sizeof(Elf32_Phdr)*Header->phentcount, phtab);\r
- \r
- // Count Pages\r
- iLoadCount = 0;\r
- LOG("Header->phentcount = %i", Header->phentcount);\r
- for( i = 0; i < Header->phentcount; i++ )\r
- {\r
- // Ignore Non-LOAD types\r
- if(phtab[i].Type != PT_LOAD)\r
- continue;\r
- iLoadCount ++;\r
- LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize);\r
- }\r
- \r
- LOG("iLoadCount = %i", iLoadCount);\r
- \r
- // Allocate Information Structure\r
- ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*iLoadCount );\r
- // Fill Info Struct\r
- ret->Entry = Header->entrypoint;\r
- ret->Base = -1; // Set Base to maximum value\r
- ret->NumSections = iLoadCount;\r
- ret->Interpreter = NULL;\r
- \r
- // Load Pages\r
- j = 0;\r
- for( i = 0; i < Header->phentcount; i++ )\r
- {\r
- //LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type);\r
- LOG("phtab[%i] = {", i);\r
- LOG(" .Type = 0x%08x", phtab[i].Type);\r
- LOG(" .Offset = 0x%08x", phtab[i].Offset);\r
- LOG(" .VAddr = 0x%08x", phtab[i].VAddr);\r
- LOG(" .PAddr = 0x%08x", phtab[i].PAddr);\r
- LOG(" .FileSize = 0x%08x", phtab[i].FileSize);\r
- LOG(" .MemSize = 0x%08x", phtab[i].MemSize);\r
- LOG(" .Flags = 0x%08x", phtab[i].Flags);\r
- LOG(" .Align = 0x%08x", phtab[i].Align);\r
- LOG(" }");\r
- // Get Interpreter Name\r
- if( phtab[i].Type == PT_INTERP )\r
- {\r
- char *tmp;\r
- if(ret->Interpreter) continue;\r
- tmp = malloc(phtab[i].FileSize);\r
- VFS_Seek(FD, phtab[i].Offset, 1);\r
- VFS_Read(FD, phtab[i].FileSize, tmp);\r
- ret->Interpreter = Binary_RegInterp(tmp);\r
- LOG("Interpreter '%s'", tmp);\r
- free(tmp);\r
- continue;\r
- }\r
- // Ignore non-LOAD types\r
- if(phtab[i].Type != PT_LOAD) continue;\r
- \r
- // Find Base\r
- if(phtab[i].VAddr < ret->Base) ret->Base = phtab[i].VAddr;\r
- \r
- LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}",\r
- i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize);\r
- \r
- ret->LoadSections[j].Offset = phtab[i].Offset;\r
- ret->LoadSections[j].FileSize = phtab[i].FileSize;\r
- ret->LoadSections[j].Virtual = phtab[i].VAddr;\r
- ret->LoadSections[j].MemSize = phtab[i].MemSize;\r
- ret->LoadSections[j].Flags = 0;\r
- if( !(phtab[i].Flags & PF_W) )\r
- ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO;\r
- if( phtab[i].Flags & PF_X )\r
- ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC;\r
- j ++;\r
- }\r
- \r
- // Clean Up\r
- free(phtab);\r
- // Return\r
- LEAVE('p', ret);\r
- return ret;\r
-}\r
-\r
-// --- ELF RELOCATION ---\r
-int Elf_Relocate(void *Base)\r
-{\r
- Elf64_Ehdr *hdr = Base;\r
- \r
- switch( hdr->e_ident[EI_CLASS] )\r
- {\r
- case ELFCLASS32:\r
- return Elf_Relocate32(Base);\r
- case ELFCLASS64:\r
- return 0;\r
- default:\r
- return 1;\r
- }\r
-}\r
-\r
-\r
-/**\r
- * \brief Relocates a loaded ELF Executable\r
- */\r
-int Elf_Relocate32(void *Base)\r
-{\r
- Elf32_Ehdr *hdr = Base;\r
- Elf32_Phdr *phtab;\r
- int i, j; // Counters\r
- char *libPath;\r
- Uint iRealBase = -1;\r
- Uint iBaseDiff;\r
- int iSegmentCount;\r
- int iSymCount = 0;\r
- Elf32_Rel *rel = NULL;\r
- Elf32_Rela *rela = NULL;\r
- Uint32 *pltgot = NULL;\r
- void *plt = NULL;\r
- Uint32 *ptr;\r
- int relSz=0, relEntSz=8;\r
- int relaSz=0, relaEntSz=8;\r
- int pltSz=0, pltType=0;\r
- Elf32_Dyn *dynamicTab = NULL; // Dynamic Table Pointer\r
- char *dynstrtab = NULL; // .dynamic String Table\r
- Elf32_Sym *dynsymtab = NULL;\r
- int bFailed = 0;\r
- \r
- ENTER("pBase", Base);\r
- \r
- // Parse Program Header to get Dynamic Table\r
- phtab = (void *)( (tVAddr)Base + hdr->phoff );\r
- iSegmentCount = hdr->phentcount;\r
- for(i = 0; i < iSegmentCount; i ++ )\r
- {\r
- // Determine linked base address\r
- if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr)\r
- iRealBase = phtab[i].VAddr;\r
- \r
- // Find Dynamic Section\r
- if(phtab[i].Type == PT_DYNAMIC) {\r
- if(dynamicTab) {\r
- Log_Warning("ELF", "Elf_Relocate - Multiple PT_DYNAMIC segments\n");\r
- continue;\r
- }\r
- dynamicTab = (void *) (tVAddr) phtab[i].VAddr;\r
- j = i; // Save Dynamic Table ID\r
- break;\r
- }\r
- }\r
- \r
- // Check if a PT_DYNAMIC segement was found\r
- if(!dynamicTab) {\r
- Log_Warning("ELF", "Elf_Relocate: No PT_DYNAMIC segment in image, returning\n");\r
- LEAVE('x', 0);\r
- return 0;\r
- }\r
- \r
- // Page Align real base\r
- iRealBase &= ~0xFFF;\r
- \r
- // Adjust "Real" Base\r
- iBaseDiff = (Uint)Base - iRealBase;\r
- // Adjust Dynamic Table\r
- dynamicTab = (void *) ((Uint)dynamicTab + iBaseDiff);\r
- \r
- // === Get Symbol table and String Table ===\r
- for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
- {\r
- switch(dynamicTab[j].d_tag)\r
- {\r
- // --- Symbol Table ---\r
- case DT_SYMTAB:\r
- dynamicTab[j].d_val += iBaseDiff;\r
- dynsymtab = (void*) (tVAddr) dynamicTab[j].d_val;\r
- hdr->misc.SymTable = dynamicTab[j].d_val; // Saved in unused bytes of ident\r
- break;\r
- \r
- // --- String Table ---\r
- case DT_STRTAB:\r
- dynamicTab[j].d_val += iBaseDiff;\r
- dynstrtab = (void*) (tVAddr) dynamicTab[j].d_val;\r
- break;\r
- \r
- // --- Hash Table --\r
- case DT_HASH:\r
- dynamicTab[j].d_val += iBaseDiff;\r
- iSymCount = ((Uint*)((tVAddr)dynamicTab[j].d_val))[1];\r
- hdr->misc.HashTable = dynamicTab[j].d_val; // Saved in unused bytes of ident\r
- break;\r
- }\r
- }\r
-\r
- if( !dynsymtab && iSymCount > 0 ) {\r
- Log_Warning("ELF", "Elf_Relocate: No Dynamic symbol table, but count >0");\r
- return 0;\r
- }\r
-\r
- // Alter Symbols to true base\r
- for(i = 0; i < iSymCount; i ++)\r
- {\r
- dynsymtab[i].value += iBaseDiff;\r
- dynsymtab[i].nameOfs += (Uint)dynstrtab;\r
- //LOG("Sym '%s' = 0x%x (relocated)\n", dynsymtab[i].name, dynsymtab[i].value);\r
- }\r
- \r
- // === Add to loaded list (can be imported now) ===\r
- //Binary_AddLoaded( (Uint)Base );\r
-\r
- // === Parse Relocation Data ===\r
- for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
- {\r
- switch(dynamicTab[j].d_tag)\r
- {\r
- // --- Shared Library Name ---\r
- case DT_SONAME:\r
- LOG(".so Name '%s'\n", dynstrtab+dynamicTab[j].d_val);\r
- break;\r
- // --- Needed Library ---\r
- case DT_NEEDED:\r
- libPath = dynstrtab + dynamicTab[j].d_val;\r
- Log_Notice("ELF", "%p - Required Library '%s' (Ignored in kernel mode)\n", Base, libPath);\r
- break;\r
- // --- PLT/GOT ---\r
- case DT_PLTGOT: pltgot = (void*)(iBaseDiff+dynamicTab[j].d_val); break;\r
- case DT_JMPREL: plt = (void*)(iBaseDiff+dynamicTab[j].d_val); break;\r
- case DT_PLTREL: pltType = dynamicTab[j].d_val; break;\r
- case DT_PLTRELSZ: pltSz = dynamicTab[j].d_val; break;\r
- \r
- // --- Relocation ---\r
- case DT_REL: rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
- case DT_RELSZ: relSz = dynamicTab[j].d_val; break;\r
- case DT_RELENT: relEntSz = dynamicTab[j].d_val; break;\r
- \r
- case DT_RELA: rela = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
- case DT_RELASZ: relaSz = dynamicTab[j].d_val; break;\r
- case DT_RELAENT: relaEntSz = dynamicTab[j].d_val; break;\r
- }\r
- }\r
- \r
- // Parse Relocation Entries\r
- if(rel && relSz)\r
- {\r
- j = relSz / relEntSz;\r
- for( i = 0; i < j; i++ )\r
- {\r
- ptr = (void*)(iBaseDiff + rel[i].r_offset);\r
- if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) {\r
- bFailed = 1;\r
- }\r
- }\r
- }\r
- // Parse Relocation Entries\r
- if(rela && relaSz)\r
- {\r
- j = relaSz / relaEntSz;\r
- for( i = 0; i < j; i++ )\r
- {\r
- ptr = (void*)(iBaseDiff + rela[i].r_offset);\r
- if( !Elf_Int_DoRelocate(rela[i].r_info, ptr, rela[i].r_addend, dynsymtab, (Uint)Base) ) {\r
- bFailed = 1;\r
- }\r
- }\r
- }\r
- \r
- // === Process PLT (Procedure Linkage Table) ===\r
- if(plt && pltSz)\r
- {\r
- if(pltType == DT_REL)\r
- {\r
- Elf32_Rel *pltRel = plt;\r
- j = pltSz / sizeof(Elf32_Rel);\r
- LOG("PLT Rel - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j);\r
- for(i = 0; i < j; i++)\r
- {\r
- ptr = (void*)(iBaseDiff + pltRel[i].r_offset);\r
- if( !Elf_Int_DoRelocate(pltRel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) {\r
- bFailed = 1;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- Elf32_Rela *pltRela = plt;\r
- j = pltSz / sizeof(Elf32_Rela);\r
- LOG("PLT RelA - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j);\r
- for(i=0;i<j;i++)\r
- {\r
- ptr = (void*)(iBaseDiff + pltRela[i].r_offset);\r
- if( !Elf_Int_DoRelocate(pltRela[i].r_info, ptr, pltRela[i].r_addend, dynsymtab, (Uint)Base) ) {\r
- bFailed = 1;\r
- }\r
- }\r
- }\r
- }\r
- \r
- if(bFailed) {\r
- LEAVE('i', 0);\r
- return 0;\r
- }\r
- \r
- LEAVE('x', 1);\r
- return 1;\r
-}\r
-\r
-/**\r
- * \fn void Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base)\r
- * \brief Performs a relocation\r
- * \param r_info Field from relocation entry\r
- * \param ptr Pointer to location of relocation\r
- * \param addend Value to add to symbol\r
- * \param symtab Symbol Table\r
- * \param base Base of loaded binary\r
- */\r
-int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base)\r
-{\r
- Uint val;\r
- int type = ELF32_R_TYPE(r_info);\r
- int sym = ELF32_R_SYM(r_info);\r
- char *sSymName = symtab[sym].name;\r
- \r
- //LogF("Elf_Int_DoRelocate: (r_info=0x%x, ptr=0x%x, addend=0x%x, .., base=0x%x)\n",\r
- // r_info, ptr, addend, base);\r
- \r
- switch( type )\r
- {\r
- // Standard 32 Bit Relocation (S+A)\r
- case R_386_32:\r
- if( !Elf_GetSymbol((void*)base, sSymName, &val) ) // Search this binary first\r
- if( !Binary_GetSymbol( sSymName, &val ) )\r
- return 0;\r
- LOG("%08x R_386_32 *0x%x += 0x%x('%s')", r_info, ptr, val, sSymName);\r
- *ptr = val + addend;\r
- break;\r
- \r
- // 32 Bit Relocation wrt. Offset (S+A-P)\r
- case R_386_PC32:\r
- if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
- if( !Binary_GetSymbol( sSymName, &val ) )\r
- return 0;\r
- LOG("%08x R_386_PC32 *0x%x = 0x%x + 0x%x('%s') - 0x%x", r_info, ptr, *ptr, val, sSymName, (Uint)ptr );\r
- // TODO: Check if it needs the true value of ptr or the compiled value\r
- // NOTE: Testing using true value\r
- *ptr = val + addend - (Uint)ptr;\r
- break;\r
-\r
- // Absolute Value of a symbol (S)\r
- case R_386_GLOB_DAT:\r
- if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
- if( !Binary_GetSymbol( sSymName, &val ) )\r
- return 0;\r
- LOG("%08x R_386_GLOB_DAT *0x%x = 0x%x (%s)", r_info, ptr, val, sSymName);\r
- *ptr = val;\r
- break;\r
- \r
- // Absolute Value of a symbol (S)\r
- case R_386_JMP_SLOT:\r
- if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
- if( !Binary_GetSymbol( sSymName, &val ) )\r
- return 0;\r
- LOG("%08x R_386_JMP_SLOT *0x%x = 0x%x (%s)", r_info, ptr, val, sSymName);\r
- *ptr = val;\r
- break;\r
-\r
- // Base Address (B+A)\r
- case R_386_RELATIVE:\r
- LOG("%08x R_386_RELATIVE *0x%x = 0x%x + 0x%x", r_info, ptr, base, addend);\r
- *ptr = base + addend;\r
- break;\r
- \r
- default:\r
- LOG("Rel 0x%x: 0x%x,%i", ptr, sym, type);\r
- break;\r
- }\r
- return 1;\r
-}\r
-\r
-/**\r
- * \fn int Elf_GetSymbol(void *Base, const char *name, Uint *ret)\r
- * \brief Get a symbol from the loaded binary\r
- */\r
-int Elf_GetSymbol(void *Base, const char *Name, Uint *ret)\r
-{\r
- Elf32_Ehdr *hdr = (void*)Base;\r
- Elf32_Sym *symtab;\r
- int nbuckets = 0;\r
- int iSymCount = 0;\r
- int i;\r
- Uint *pBuckets;\r
- Uint *pChains;\r
- Uint iNameHash;\r
-\r
- if(!Base) return 0;\r
-\r
- pBuckets = (void *) hdr->misc.HashTable;\r
- symtab = (void *) hdr->misc.SymTable;\r
- \r
- nbuckets = pBuckets[0];\r
- iSymCount = pBuckets[1];\r
- pBuckets = &pBuckets[2];\r
- pChains = &pBuckets[ nbuckets ];\r
- \r
- // Get hash\r
- iNameHash = Elf_Int_HashString(Name);\r
- iNameHash %= nbuckets;\r
-\r
- // Check Bucket\r
- i = pBuckets[ iNameHash ];\r
- if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[i].name, Name) == 0) {\r
- if(ret) *ret = symtab[ i ].value;\r
- return 1;\r
- }\r
- \r
- // Walk Chain\r
- while(pChains[i] != STN_UNDEF)\r
- {\r
- i = pChains[i];\r
- if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[ i ].name, Name) == 0) {\r
- if(ret) *ret = symtab[ i ].value;\r
- return 1;\r
- }\r
- }\r
- return 0;\r
-}\r
-\r
-/**\r
- * \fn Uint Elf_Int_HashString(char *str)\r
- * \brief Hash a string in the ELF format\r
- * \param str String to hash\r
- * \return Hash value\r
- */\r
-Uint Elf_Int_HashString(const char *str)\r
-{\r
- Uint h = 0, g;\r
- while(*str)\r
- {\r
- h = (h << 4) + *str++;\r
- if( (g = h & 0xf0000000) )\r
- h ^= g >> 24;\r
- h &= ~g;\r
- }\r
- return h;\r
-}\r
+++ /dev/null
-/**\r
- * \file elf.h\r
- * \brief ELF Exeutable Loader\r
- */\r
-#ifndef _BIN_ELF_H\r
-#define _BIN_ELF_H\r
-\r
-typedef Uint16 Elf64_Half;\r
-typedef Uint32 Elf64_Word;\r
-typedef Uint64 Elf64_Addr;\r
-typedef Uint64 Elf64_Off;\r
-typedef Uint64 Elf64_Xword;\r
-\r
-enum e_ident_values\r
-{\r
- EI_MAG0,\r
- EI_MAG1,\r
- EI_MAG2,\r
- EI_MAG3,\r
- EI_CLASS,\r
- EI_DATA,\r
- EI_VERSION,\r
- EI_OSABI,\r
- EI_ABIVERSION,\r
- EI_PAD,\r
- EI_NIDENT = 16,\r
-};\r
-\r
-#define ELFCLASS32 1\r
-#define ELFCLASS64 2\r
-\r
-/**\r
- * \brief ELF File Header\r
- */\r
-struct sElf32_Ehdr\r
-{\r
- union {\r
- char ident[16]; //!< Identifier Bytes\r
- struct {\r
- Uint Ident1;\r
- Uint Ident2;\r
- Uint HashTable;\r
- Uint SymTable;\r
- } misc;\r
- };\r
- Uint16 filetype; //!< File Type\r
- Uint16 machine; //!< Machine / Arch\r
- Uint32 version; //!< Version (File?)\r
- Uint32 entrypoint; //!< Entry Point\r
- Uint32 phoff; //!< Program Header Offset\r
- Uint32 shoff; //!< Section Header Offset\r
- Uint32 flags; //!< Flags\r
- Uint16 headersize; //!< Header Size\r
- Uint16 phentsize; //!< Program Header Entry Size\r
- Uint16 phentcount; //!< Program Header Entry Count\r
- Uint16 shentsize; //!< Section Header Entry Size\r
- Uint16 shentcount; //!< Section Header Entry Count\r
- Uint16 shstrindex; //!< Section Header String Table Index\r
-} __attribute__ ((packed));\r
-\r
-typedef struct\r
-{\r
- unsigned char e_ident[16];\r
- Elf64_Half e_type;\r
- Elf64_Half e_machine;\r
- Elf64_Word e_version;\r
- Elf64_Addr e_entry;\r
- Elf64_Off e_phoff;\r
- Elf64_Off e_shoff;\r
- Elf64_Word e_flags;\r
- Elf64_Half e_ehsize;\r
- Elf64_Half e_phentsize;\r
- Elf64_Half e_phnum;\r
- Elf64_Half e_shentsize;\r
- Elf64_Half e_shnum;\r
- Elf64_Half e_shstrndx;\r
-} Elf64_Ehdr;\r
-\r
-/**\r
- * \brief Executable Types\r
- */\r
-enum eElf32_ExecTypes\r
-{\r
- ET_NONE = 0, //!< NULL Type\r
- ET_REL = 1, //!< Relocatable (Object)\r
- ET_EXEC = 2, //!< Executable\r
- ET_DYN = 3, //!< Dynamic Library\r
- ET_CORE = 4, //!< Core?\r
- ET_LOPROC = 0xFF00, //!< Low Impl Defined\r
- ET_HIPROC = 0xFFFF //!< High Impl Defined\r
-};\r
-\r
-/**\r
- \name Section IDs\r
- \{\r
-*/\r
-#define SHN_UNDEF 0 //!< Undefined Section\r
-#define SHN_LORESERVE 0xFF00 //!< Low Reserved\r
-#define SHN_LOPROC 0xFF00 //!< Low Impl Defined\r
-#define SHN_HIPROC 0xFF1F //!< High Impl Defined\r
-#define SHN_ABS 0xFFF1 //!< Absolute Address (Base: 0, Size: -1)\r
-#define SHN_COMMON 0xFFF2 //!< Common\r
-#define SHN_HIRESERVE 0xFFFF //!< High Reserved\r
-//! \}\r
-\r
-/**\r
- \enum eElfSectionTypes\r
- \brief ELF Section Types\r
-*/\r
-enum eElfSectionTypes {\r
- SHT_NULL, //0\r
- SHT_PROGBITS, //1\r
- SHT_SYMTAB, //2\r
- SHT_STRTAB, //3\r
- SHT_RELA, //4\r
- SHT_HASH, //5\r
- SHT_DYNAMIC, //6\r
- SHT_NOTE, //7\r
- SHT_NOBITS, //8\r
- SHT_REL, //9\r
- SHT_SHLIB, //A\r
- SHT_DYNSYM, //B\r
- SHT_LAST, //C\r
- SHT_LOPROC = 0x70000000,\r
- SHT_HIPROC = 0x7fffffff,\r
- SHT_LOUSER = 0x80000000,\r
- SHT_HIUSER = 0xffffffff\r
-};\r
-\r
-#define SHF_WRITE 0x1\r
-#define SHF_ALLOC 0x2\r
-#define SHF_EXECINSTR 0x4\r
-#define SHF_MASKPROC 0xf0000000\r
-\r
-struct sElf32_Shent {\r
- Uint32 name;\r
- Uint32 type;\r
- Uint32 flags;\r
- Uint32 address;\r
- Uint32 offset;\r
- Uint32 size;\r
- Uint32 link;\r
- Uint32 info;\r
- Uint32 addralign;\r
- Uint32 entsize;\r
-} __attribute__ ((packed)); //sizeof = 40\r
-\r
-typedef struct\r
-{\r
- Elf64_Word sh_name;\r
- Elf64_Word sh_type;\r
- Elf64_Xword sh_flags;\r
- Elf64_Addr sh_addr;\r
- Elf64_Off sh_offset;\r
- Elf64_Xword sh_size;\r
- Elf64_Word sh_link;\r
- Elf64_Word sh_info;\r
- Elf64_Xword sh_addralign;\r
- Elf64_Xword sh_entsize;\r
-} Elf64_Shdr;\r
-\r
-struct elf_sym_s {\r
- union {\r
- Uint32 nameOfs;\r
- char *name;\r
- };\r
- Uint32 value; //Address\r
- Uint32 size;\r
- Uint8 info;\r
- Uint8 other;\r
- Uint16 shndx;\r
-} __attribute__ ((packed));\r
-#define STN_UNDEF 0 // Undefined Symbol\r
-\r
-enum {\r
- PT_NULL, //0\r
- PT_LOAD, //1\r
- PT_DYNAMIC, //2\r
- PT_INTERP, //3\r
- PT_NOTE, //4\r
- PT_SHLIB, //5\r
- PT_PHDR, //6\r
- PT_LOPROC = 0x70000000,\r
- PT_HIPROC = 0x7fffffff\r
-};\r
-\r
-struct sElf32_Phdr {\r
- Uint32 Type;\r
- Uint32 Offset;\r
- Uint32 VAddr;\r
- Uint32 PAddr;\r
- Uint32 FileSize;\r
- Uint32 MemSize;\r
- Uint32 Flags;\r
- Uint32 Align;\r
-} __attribute__ ((packed));\r
-\r
-typedef struct\r
-{\r
- Elf64_Word p_type;\r
- Elf64_Word p_flags;\r
- Elf64_Off p_offset;\r
- Elf64_Addr p_vaddr;\r
- Elf64_Addr p_paddr;\r
- Elf64_Xword p_filesz;\r
- Elf64_Xword p_memsz;\r
- Elf64_Xword p_align;\r
-} Elf64_Phdr;\r
-\r
-#define PF_X 1\r
-#define PF_W 2\r
-#define PF_R 4\r
-\r
-struct elf32_rel_s {\r
- Uint32 r_offset;\r
- Uint32 r_info;\r
-} __attribute__ ((packed));\r
-\r
-struct elf32_rela_s {\r
- Uint32 r_offset;\r
- Uint32 r_info;\r
- Sint32 r_addend;\r
-} __attribute__ ((packed));\r
-\r
-enum {\r
- R_386_NONE = 0, // none\r
- R_386_32, // S+A\r
- R_386_PC32, // S+A-P\r
- R_386_GOT32, // G+A-P\r
- R_386_PLT32, // L+A-P\r
- R_386_COPY, // none\r
- R_386_GLOB_DAT, // S\r
- R_386_JMP_SLOT, // S\r
- R_386_RELATIVE, // B+A\r
- R_386_GOTOFF, // S+A-GOT\r
- R_386_GOTPC, // GOT+A-P\r
- R_386_LAST // none\r
-};\r
-\r
-#define ELF32_R_SYM(i) ((i)>>8) // Takes an info value and returns a symbol index\r
-#define ELF32_R_TYPE(i) ((i)&0xFF) // Takes an info value and returns a type\r
-#define ELF32_R_INFO(s,t) (((s)<<8)+((t)&0xFF)) // Takes a type and symbol index and returns an info value\r
-\r
-struct elf32_dyn_s {\r
- Uint32 d_tag;\r
- Uint32 d_val; //Also d_ptr\r
-} __attribute__ ((packed));\r
-\r
-enum {\r
- DT_NULL, //!< Marks End of list\r
- DT_NEEDED, //!< Offset in strtab to needed library\r
- DT_PLTRELSZ, //!< Size in bytes of PLT\r
- DT_PLTGOT, //!< Address of PLT/GOT\r
- DT_HASH, //!< Address of symbol hash table\r
- DT_STRTAB, //!< String Table address\r
- DT_SYMTAB, //!< Symbol Table address\r
- DT_RELA, //!< Relocation table address\r
- DT_RELASZ, //!< Size of relocation table\r
- DT_RELAENT, //!< Size of entry in relocation table\r
- DT_STRSZ, //!< Size of string table\r
- DT_SYMENT, //!< Size of symbol table entry\r
- DT_INIT, //!< Address of initialisation function\r
- DT_FINI, //!< Address of termination function\r
- DT_SONAME, //!< String table offset of so name\r
- DT_RPATH, //!< String table offset of library path\r
- DT_SYMBOLIC,//!< Reverse order of symbol searching for library, search libs first then executable\r
- DT_REL, //!< Relocation Entries (Elf32_Rel instead of Elf32_Rela)\r
- DT_RELSZ, //!< Size of above table (bytes)\r
- DT_RELENT, //!< Size of entry in above table\r
- DT_PLTREL, //!< Relocation entry of PLT\r
- DT_DEBUG, //!< Debugging Entry - Unknown contents\r
- DT_TEXTREL, //!< Indicates that modifcations to a non-writeable segment may occur\r
- DT_JMPREL, //!< Address of PLT only relocation entries\r
- DT_LOPROC = 0x70000000, //!< Low Definable\r
- DT_HIPROC = 0x7FFFFFFF //!< High Definable\r
-};\r
-\r
-typedef struct sElf32_Ehdr Elf32_Ehdr;\r
-typedef struct sElf32_Phdr Elf32_Phdr;\r
-typedef struct sElf32_Shent Elf32_Shent;\r
-typedef struct elf_sym_s elf_symtab;\r
-typedef struct elf_sym_s Elf32_Sym;\r
-typedef struct elf32_rel_s Elf32_Rel;\r
-typedef struct elf32_rela_s Elf32_Rela;\r
-typedef struct elf32_dyn_s Elf32_Dyn;\r
-\r
-#endif // defined(_EXE_ELF_H)\r
+++ /dev/null
-/*\r
- * Acess v1\r
- * Portable Executable Loader\r
- */\r
-#define DEBUG 1\r
-#include <acess.h>\r
-#include <binary.h>\r
-#include <modules.h>\r
-#include "pe.h"\r
-\r
-// === PROTOTYPES ===\r
- int PE_Install(char **Arguments);\r
-tBinary *PE_Load(int fp);\r
-tBinary *MZ_Open(int fp);\r
- int PE_Relocate(void *Base);\r
- int PE_GetSymbol(void *Base, const char *Name, Uint *Ret);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, 0x0032, BinPE, PE_Install, NULL, NULL);\r
-const char *gsPE_DefaultInterpreter = "/Acess/Libs/ld-acess.so";\r
-tBinaryType gPE_Loader = {\r
- NULL,\r
- ('M'|('Z'<<8)), 0xFFFF, // 'MZ'\r
- "PE/DOS",\r
- PE_Load, PE_Relocate, PE_GetSymbol\r
- };\r
-\r
-// === CODE ===\r
-int PE_Install(char **Arguments)\r
-{\r
- Binary_RegisterType(&gPE_Loader);\r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \brief Loads a PE Binary\r
- */\r
-tBinary *PE_Load(int FP)\r
-{\r
- int count, i, j;\r
- int iSectCount;\r
- tBinary *ret;\r
- tPE_DOS_HEADER dosHdr;\r
- tPE_IMAGE_HEADERS peHeaders;\r
- tPE_SECTION_HEADER *peSections;\r
- char namebuf[9] = {0};\r
- Uint iFlags, iVA;\r
- \r
- ENTER("xFP", FP);\r
- \r
- // Read DOS header and check\r
- VFS_Read(FP, sizeof(tPE_DOS_HEADER), &dosHdr);\r
- if( dosHdr.Ident != ('M'|('Z'<<8)) ) {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- // - Read PE Header\r
- VFS_Seek(FP, dosHdr.PeHdrOffs, SEEK_SET);\r
- if( VFS_Tell(FP) != dosHdr.PeHdrOffs ) {\r
- ret = MZ_Open(FP);\r
- LEAVE('p', ret);\r
- return ret;\r
- }\r
- VFS_Read(FP, sizeof(tPE_IMAGE_HEADERS), &peHeaders);\r
- \r
- // - Check PE Signature and pass on to the MZ Loader if invalid\r
- if( peHeaders.Signature != (('P')|('E'<<8)) ) {\r
- ret = MZ_Open(FP);\r
- LEAVE('p', ret);\r
- return ret;\r
- }\r
- \r
- // Read Sections (Uses `count` as a temp variable)\r
- count = sizeof(tPE_SECTION_HEADER) * peHeaders.FileHeader.SectionCount;\r
- peSections = malloc( count );\r
- if(!peSections)\r
- {\r
- Warning("PE_Load - Unable to allocate `peSections`, 0x%x bytes", count);\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- VFS_Read(FP, count, peSections);\r
- \r
- // Count Pages\r
- iSectCount = 1; // 1st page is headers\r
- for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ )\r
- {\r
- // Check if the section is loadable\r
- // (VA is zero in non-loadable sections)\r
- if(peSections[i].RVA + peHeaders.OptHeader.ImageBase == 0) continue;\r
- \r
- // Moar pages\r
- iSectCount ++;\r
- }\r
- \r
- LOG("%i Sections", iSectCount);\r
- \r
- // Initialise Executable Information\r
- ret = malloc(sizeof(tBinary) + sizeof(tBinarySection)*iSectCount);\r
- \r
- ret->Entry = peHeaders.OptHeader.EntryPoint + peHeaders.OptHeader.ImageBase;\r
- ret->Base = peHeaders.OptHeader.ImageBase;\r
- ret->Interpreter = gsPE_DefaultInterpreter;\r
- ret->NumSections = iSectCount;\r
- \r
- LOG("Entry=%p, BaseAddress=0x%x\n", ret->Entry, ret->Base);\r
- \r
- ret->LoadSections[0].Virtual = peHeaders.OptHeader.ImageBase;\r
- ret->LoadSections[0].Offset = 0;\r
- ret->LoadSections[0].FileSize = 4096;\r
- ret->LoadSections[0].MemSize = 4096;\r
- ret->LoadSections[0].Flags = 0;\r
- \r
- // Parse Sections\r
- j = 1; // Page Index\r
- for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ )\r
- {\r
- tBinarySection *sect = &ret->LoadSections[j];\r
- iVA = peSections[i].RVA + peHeaders.OptHeader.ImageBase;\r
- \r
- // Skip non-loadable sections\r
- if(iVA == 0) continue;\r
- \r
- // Create Name Buffer\r
- memcpy(namebuf, peSections[i].Name, 8);\r
- LOG("Section %i '%s', iVA = %p", i, namebuf, iVA);\r
- \r
- // Create Flags\r
- iFlags = 0;\r
- if(peSections[i].Flags & PE_SECTION_FLAG_MEM_EXECUTE)\r
- iFlags |= BIN_SECTFLAG_EXEC;\r
- if( !(peSections[i].Flags & PE_SECTION_FLAG_MEM_WRITE) )\r
- iFlags |= BIN_SECTFLAG_RO;\r
- \r
- sect->Virtual = iVA;\r
- sect->Offset = peSections[i].RawOffs;\r
- sect->FileSize = peSections[i].RawSize;\r
- sect->MemSize = peSections[i].VirtualSize;\r
- sect->Flags = iFlags;\r
- j ++;\r
- \r
- LOG("%i Name:'%s', RVA: 0x%x, Size: 0x%x (0x%x), Ofs: 0x%x, Flags: 0x%x",\r
- i, namebuf, \r
- iVA,\r
- peSections[i].VirtualSize, peSections[i].RawSize, peSections[i].RawOffs,\r
- peSections[i].Flags\r
- );\r
- \r
- }\r
- // Free Executable Memory\r
- free(peSections);\r
- \r
- LEAVE('p', ret);\r
- return ret;\r
-}\r
-\r
-/**\r
- */\r
-tBinary *MZ_Open(int FP)\r
-{\r
- ENTER("xFP", FP);\r
- UNIMPLEMENTED();\r
- LEAVE('n');\r
- return NULL;\r
-}\r
-\r
-int PE_Relocate(void *Base)\r
-{\r
- tPE_DOS_HEADER *dosHdr;\r
- tPE_IMAGE_HEADERS *peHeaders;\r
- tPE_SECTION_HEADER *peSections;\r
- tPE_DATA_DIR *directory;\r
- tPE_IMPORT_DIR *impDir;\r
- int i;\r
- Uint iBase = (Uint)Base;\r
- #if 0\r
- void *hLibrary;\r
- char *libPath;\r
- #endif\r
- \r
- ENTER("pBase", Base);\r
- dosHdr = Base;\r
- peHeaders = (void*)( iBase + dosHdr->PeHdrOffs );\r
- LOG("Prefered Base %p", peHeaders->OptHeader.ImageBase);\r
- peSections = (void*)( iBase + sizeof(tPE_IMAGE_HEADERS) );\r
- \r
- directory = (void*)(peSections[0].RVA + iBase);\r
- \r
- // === Load Import Tables\r
- impDir = (void*)( directory[PE_DIR_IMPORT].RVA + iBase );\r
- for( i = 0; impDir[i].DLLName != NULL; i++ )\r
- {\r
- impDir[i].DLLName += iBase;\r
- impDir[i].ImportLookupTable += iBase/4;\r
- impDir[i].ImportAddressTable += iBase/4;\r
- LOG("DLL Required '%s'(0x%x)", impDir[i].DLLName, impDir[i].DLLName);\r
- #if 0\r
- libPath = FindLibrary(impDir[i].DLLName);\r
- if(libPath == NULL)\r
- {\r
- Warning("PE_Relocate - Unable to find library '%s'");\r
- LEAVE('i', -1);\r
- return -1;\r
- }\r
- LOG("DLL Path = '%s'", libPath);\r
- hLibrary = DynLib_Load(libPath, 0);\r
- #endif\r
- }\r
- \r
- for(i=0;i<PE_DIR_LAST;i++)\r
- LOG("directory[%i] = {RVA=0x%x,Size=0x%x}", i, directory[i].RVA, directory[i].Size);\r
- \r
- LEAVE('i', 0);\r
- return 0;\r
-}\r
-\r
-int PE_GetSymbol(void *Base, const char *Name, Uint *Ret)\r
-{\r
- return 0;\r
-}\r
+++ /dev/null
-/*\r
-AcessOS/AcessBasic v1\r
-PE Loader\r
-HEADER\r
-*/\r
-#ifndef _EXE_PE_H\r
-#define _EXE_PE_H\r
-\r
-enum ePE_MACHINES {\r
- PE_MACHINE_I386 = 0x14c, // Intel 386+\r
- PE_MACHINE_IA64 = 0x200 // Intel-64\r
-};\r
-\r
-enum ePE_DIR_INDX {\r
- PE_DIR_EXPORT, // 0\r
- PE_DIR_IMPORT, // 1\r
- PE_DIR_RESOURCE, // 2\r
- PE_DIR_EXCEPTION, // 3\r
- PE_DIR_SECRURITY, // 4\r
- PE_DIR_RELOC, // 5\r
- PE_DIR_DEBUG, // 6\r
- PE_DIR_COPYRIGHT, // 7\r
- PE_DIR_ARCHITECTURE,// 8\r
- PE_DIR_GLOBALPTR, // 9\r
- PE_DIR_TLS, // 10\r
- PE_DIR_LOAD_CFG, // 11\r
- PE_DIR_BOUND_IMPORT,// 12\r
- PE_DIR_IAT, // 13\r
- PE_DIR_DELAY_IMPORT,// 14\r
- PE_DIR_COM_DESCRIPTOR, //15\r
- PE_DIR_LAST\r
-};\r
-\r
-typedef struct {\r
- Uint32 RVA;\r
- Uint32 Size;\r
-} tPE_DATA_DIR;\r
-\r
-typedef struct {\r
- Uint32 *ImportLookupTable; //0x80000000 is Ordninal Flag\r
- Uint32 TimeStamp;\r
- Uint32 FowarderChain;\r
- char *DLLName;\r
- Uint32 *ImportAddressTable; // Array of Addresses - To be edited by loader\r
-} tPE_IMPORT_DIR;\r
-\r
-typedef struct {\r
- Uint16 Hint;\r
- char Name[]; // Zero Term String\r
-} tPE_HINT_NAME;\r
-\r
-typedef struct {\r
- char Name[8];\r
- Uint32 VirtualSize;\r
- Uint32 RVA;\r
- Uint32 RawSize;\r
- Uint32 RawOffs;\r
- Uint32 RelocationsPtr; //Set to 0 in executables\r
- Uint32 LineNumberPtr; //Pointer to Line Numbers\r
- Uint16 RelocationCount; // Set to 0 in executables\r
- Uint16 LineNumberCount;\r
- Uint32 Flags;\r
-} tPE_SECTION_HEADER;\r
-\r
-#define PE_SECTION_FLAG_CODE 0x00000020 // Section contains executable code.\r
-#define PE_SECTION_FLAG_IDATA 0x00000040 // Section contains initialized data.\r
-#define PE_SECTION_FLAG_UDATA 0x00000080 // Section contains uninitialized data.\r
-#define PE_SECTION_FLAG_DISCARDABLE 0x02000000 // Section can be discarded as needed.\r
-#define PE_SECTION_FLAG_MEM_NOT_CACHED 0x04000000 // Section cannot be cached.\r
-#define PE_SECTION_FLAG_MEM_NOT_PAGED 0x08000000 // Section is not pageable.\r
-#define PE_SECTION_FLAG_MEM_SHARED 0x10000000 // Section can be shared in memory.\r
-#define PE_SECTION_FLAG_MEM_EXECUTE 0x20000000 // Section can be executed as code.\r
-#define PE_SECTION_FLAG_MEM_READ 0x40000000 // Section can be read.\r
-#define PE_SECTION_FLAG_MEM_WRITE 0x80000000 // Section can be written to.\r
-\r
-typedef struct {\r
- Uint32 page;\r
- Uint32 size;\r
- Uint16 ents[];\r
-} tPE_FIXUP_BLOCK;\r
-\r
-//File Header\r
-typedef struct {\r
- Uint16 Machine;\r
- Uint16 SectionCount;\r
- Uint32 CreationTimestamp;\r
- Uint32 SymbolTableOffs;\r
- Uint32 SymbolCount;\r
- Uint16 OptHeaderSize;\r
- Uint16 Flags;\r
-} tPE_FILE_HEADER;\r
-\r
-typedef struct {\r
- Uint16 Magic; //0x10b: 32Bit, 0x20b: 64Bit\r
- Uint16 LinkerVersion;\r
- Uint32 CodeSize; //Sum of all Code Segment Sizes\r
- Uint32 DataSize; //Sum of all Intialised Data Segments\r
- Uint32 BssSize; //Sum of all Unintialised Data Segments\r
- Uint32 EntryPoint;\r
- Uint32 CodeRVA;\r
- Uint32 DataRVA;\r
- Uint32 ImageBase; //Prefered Base Address\r
- Uint32 SectionAlignment;\r
- Uint32 FileAlignment;\r
- Uint32 WindowsVersion; //Unused/Irrelevent\r
- Uint32 ImageVersion; //Unused/Irrelevent\r
- Uint32 SubsystemVersion; //Unused, Set to 4\r
- Uint32 Win32Version; //Unused\r
- Uint32 ImageSize;\r
- Uint32 HeaderSize;\r
- Uint32 Checksum; //Unknown Method, Can be set to 0\r
- Uint16 Subsystem; //Required Windows Subsystem (None, GUI, Console)\r
- Uint16 DllFlags;\r
- Uint32 MaxStackSize; //Reserved Stack Size\r
- Uint32 InitialStackSize; //Commited Stack Size\r
- Uint32 InitialReservedHeap; // Reserved Heap Size\r
- Uint32 InitialCommitedHeap; // Commited Heap Size\r
- Uint32 LoaderFlags; // Obselete\r
- Uint32 NumberOfDirEntries;\r
- tPE_DATA_DIR Directory[16];\r
-} tPE_OPT_HEADER;\r
-\r
-// Root Header\r
-typedef struct {\r
- Uint32 Signature;\r
- tPE_FILE_HEADER FileHeader;\r
- tPE_OPT_HEADER OptHeader;\r
-} tPE_IMAGE_HEADERS;\r
-\r
-typedef struct {\r
- Uint16 Ident;\r
- Uint16 Resvd[29];\r
- Uint32 PeHdrOffs; // File address of new exe header\r
-} tPE_DOS_HEADER;\r
-\r
-#endif\r
+++ /dev/null
-/*
- * Acess2
- * Common Binary Loader
- */
-#define DEBUG 0
-#include <acess.h>
-#include <binary.h>
-#include <mm_virt.h>
-#include <hal_proc.h>
-#include <vfs_threads.h>
-
-// === CONSTANTS ===
-#define BIN_LOWEST MM_USER_MIN // 1MiB
-#define BIN_GRANUALITY 0x10000 // 64KiB
-#define BIN_HIGHEST (USER_LIB_MAX-BIN_GRANUALITY) // Just below the kernel
-#define KLIB_LOWEST MM_MODULE_MIN
-#define KLIB_GRANUALITY 0x10000 // 32KiB
-#define KLIB_HIGHEST (MM_MODULE_MAX-KLIB_GRANUALITY)
-
-// === TYPES ===
-typedef struct sKernelBin {
- struct sKernelBin *Next;
- void *Base;
- tBinary *Info;
-} tKernelBin;
-
-// === IMPORTS ===
-extern char *Threads_GetName(int ID);
-extern tKernelSymbol gKernelSymbols[];
-extern tKernelSymbol gKernelSymbolsEnd[];
-extern tBinaryType gELF_Info;
-
-// === PROTOTYPES ===
- int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer);
-tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint);
-tBinary *Binary_GetInfo(tMount MountID, tInode InodeID);
-tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax);
-tVAddr Binary_IsMapped(tBinary *Binary);
-tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path);
-void Binary_Dereference(tBinary *Info);
-#if 0
-Uint Binary_Relocate(void *Base);
-#endif
-Uint Binary_GetSymbolEx(const char *Name, Uint *Value);
-#if 0
-Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val);
-#endif
- int Binary_int_CheckMemFree( tVAddr _start, size_t _len );
-
-// === GLOBALS ===
-tShortSpinlock glBinListLock;
-tBinary *glLoadedBinaries = NULL;
-char **gsaRegInterps = NULL;
- int giRegInterps = 0;
-tShortSpinlock glKBinListLock;
-tKernelBin *glLoadedKernelLibs;
-tBinaryType *gRegBinTypes = &gELF_Info;
-
-// === FUNCTIONS ===
-/**
- * \brief Registers a binary type
- */
-int Binary_RegisterType(tBinaryType *Type)
-{
- Type->Next = gRegBinTypes;
- gRegBinTypes = Type;
- return 1;
-}
-
-/**
- * \fn int Proc_Spawn(const char *Path)
- */
-int Proc_Spawn(const char *Path)
-{
- char stackPath[strlen(Path)+1];
- ENTER("sPath", Path);
-
- strcpy(stackPath, Path);
-
- LOG("stackPath = '%s'", stackPath);
-
- if(Proc_Clone(CLONE_VM|CLONE_NOUSER) == 0)
- {
- // CHILD
- const char *args[2] = {stackPath, NULL};
- LOG("stackPath = '%s'", stackPath);
- Proc_Execve(stackPath, args, &args[1], 0);
- for(;;);
- }
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \todo Document
- */
-int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer)
-{
- int size, argc=0, envc=0;
- int i;
- char *strbuf;
- const char **arrays;
-
- // Calculate size
- size = 0;
- if( ArgV && *ArgV )
- {
- const char **argv = *ArgV;
- for( argc = 0; argv[argc]; argc ++ )
- size += strlen( argv[argc] ) + 1;
- }
- if( EnvP && *EnvP )
- {
- const char **envp = *EnvP;
- for( envc = 0; envp[envc]; envc ++ )
- size += strlen( envp[envc] ) + 1;
- }
- size = (size + sizeof(void*)-1) & ~(sizeof(void*)-1); // Word align
- size += (argc+1+envc+1)*sizeof(void*); // Arrays
- if( Path )
- {
- size += strlen( *Path ) + 1;
- }
-
- if( DestBuffer )
- {
- arrays = DestBuffer;
- strbuf = (void*)&arrays[argc+1+envc+1];
-
- // Fill ArgV
- if( ArgV && *ArgV )
- {
- const char **argv = *ArgV;
- for( i = 0; argv[i]; i ++ )
- {
- arrays[i] = strbuf;
- strcpy(strbuf, argv[i]);
- strbuf += strlen( argv[i] ) + 1;
- }
- *ArgV = arrays;
- arrays += i;
- }
- *arrays++ = NULL;
- // Fill EnvP
- if( EnvP && *EnvP )
- {
- const char **envp = *EnvP;
- for( i = 0; envp[i]; i ++ )
- {
- arrays[i] = strbuf;
- strcpy(strbuf, envp[i]);
- strbuf += strlen( envp[i] ) + 1;
- }
- *EnvP = arrays;
- arrays += i;
- }
- *arrays++ = NULL;
- // Fill path
- if( Path )
- {
- strcpy(strbuf, *Path);
- *Path = strbuf;
- }
- }
-
- return size;
-}
-
-/**
- * \brief Create a new process with the specified set of file descriptors
- */
-int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs)
-{
- void *handles;
- void *cachebuf;
- int size;
- tPID ret;
-
- // --- Save File, ArgV and EnvP
- size = Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, NULL );
- cachebuf = malloc( size );
- Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, cachebuf );
-
- // Cache the VFS handles
- handles = VFS_SaveHandles(nFD, FDs);
-
- // Create new process
- ret = Proc_Clone(CLONE_VM|CLONE_NOUSER);
- if( ret == 0 )
- {
- VFS_RestoreHandles(nFD, handles);
- VFS_FreeSavedHandles(nFD, handles);
- // Frees cachebuf
- Proc_Execve(Binary, ArgV, EnvP, size);
- for(;;);
- }
- if( ret < 0 )
- {
- VFS_FreeSavedHandles(nFD, handles);
- }
-
- return ret;
-}
-
-/**
- * \brief Replace the current user image with another
- * \param File File to load as the next image
- * \param ArgV Arguments to pass to user
- * \param EnvP User's environment
- * \note Called Proc_ for historical reasons
- */
-int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize)
-{
- void *cachebuf;
- tVAddr entry;
- Uint base; // Uint because Proc_StartUser wants it
- int argc;
-
- ENTER("sFile pArgV pEnvP", File, ArgV, EnvP);
-
- // --- Save File, ArgV and EnvP
- if( DataSize == 0 )
- {
- DataSize = Binary_int_CacheArgs( &File, &ArgV, &EnvP, NULL );
- cachebuf = malloc( DataSize );
- Binary_int_CacheArgs( &File, &ArgV, &EnvP, cachebuf );
- }
-
- // --- Get argc
- for( argc = 0; ArgV && ArgV[argc]; argc ++ );
-
- // --- Set Process Name
- Threads_SetName(File);
-
- // --- Clear User Address space
- // NOTE: This is a little roundabout, maybe telling ClearUser to not touch the
- // PPD area would be a better idea.
- {
- int nfd = *Threads_GetMaxFD();
- void *handles;
- handles = VFS_SaveHandles(nfd, NULL);
- VFS_CloseAllUserHandles();
- MM_ClearUser();
- VFS_RestoreHandles(nfd, handles);
- VFS_FreeSavedHandles(nfd, handles);
- }
-
- // --- Load new binary
- base = Binary_Load(File, &entry);
- if(base == 0)
- {
- Log_Warning("Binary", "Proc_Execve - Unable to load '%s'", File);
- LEAVE('-');
- Threads_Exit(0, -10);
- for(;;);
- }
-
- LOG("entry = 0x%x, base = 0x%x", entry, base);
- LEAVE('-');
- // --- And... Jump to it
- Proc_StartUser(entry, base, argc, ArgV, DataSize);
- for(;;); // Tell GCC that we never return
-}
-
-/**
- * \brief Load a binary into the current address space
- * \param Path Path to binary to load
- * \param EntryPoint Pointer for exectuable entry point
- * \return Virtual address where the binary has been loaded
- */
-tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint)
-{
- tMount mount_id;
- tInode inode;
- tBinary *pBinary;
- tVAddr base = -1;
-
- ENTER("sPath pEntryPoint", Path, EntryPoint);
-
- // Sanity Check Argument
- if(Path == NULL) {
- LEAVE('x', 0);
- return 0;
- }
-
- // Check if this path has been loaded before.
- #if 0
- // TODO: Implement a list of string/tBinary pairs for loaded bins
- #endif
-
- // Get Inode
- {
- int fd;
- tFInfo info;
- fd = VFS_Open(Path, VFS_OPENFLAG_READ|VFS_OPENFLAG_EXEC);
- if( fd == -1 ) {
- LOG("%s does not exist", Path);
- LEAVE_RET('x', 0);
- }
- VFS_FInfo(fd, &info, 0);
- VFS_Close(fd);
- mount_id = info.mount;
- inode = info.inode;
- LOG("mount_id = %i, inode = %i", mount_id, inode);
- }
-
- // TODO: Also get modifcation time?
-
- // Check if the binary has already been loaded
- if( !(pBinary = Binary_GetInfo(mount_id, inode)) )
- pBinary = Binary_DoLoad(mount_id, inode, Path); // Else load it
-
- // Error Check
- if(pBinary == NULL) {
- LEAVE('x', 0);
- return 0;
- }
-
- // Map into process space
- base = Binary_MapIn(pBinary, Path, BIN_LOWEST, BIN_HIGHEST);
-
- // Check for errors
- if(base == 0) {
- LEAVE('x', 0);
- return 0;
- }
-
- // Interpret
- if(pBinary->Interpreter) {
- tVAddr start;
- if( Binary_Load(pBinary->Interpreter, &start) == 0 ) {
- LEAVE('x', 0);
- return 0;
- }
- *EntryPoint = start;
- }
- else
- *EntryPoint = pBinary->Entry - pBinary->Base + base;
-
- // Return
- LOG("*EntryPoint = 0x%x", *EntryPoint);
- LEAVE('x', base);
- return base; // Pass the base as an argument to the user if there is an interpreter
-}
-
-/**
- * \brief Finds a matching binary entry
- * \param MountID Mountpoint ID of binary file
- * \param InodeID Inode ID of the file
- * \return Pointer to the binary definition (if already loaded)
- */
-tBinary *Binary_GetInfo(tMount MountID, tInode InodeID)
-{
- tBinary *pBinary;
- for(pBinary = glLoadedBinaries; pBinary; pBinary = pBinary->Next)
- {
- if(pBinary->MountID == MountID && pBinary->Inode == InodeID)
- return pBinary;
- }
- return NULL;
-}
-
-/**
- * \brief Maps an already-loaded binary into an address space.
- * \param Binary Pointer to globally stored binary definition
- * \param Path Path to the binary's file (for debug)
- * \param LoadMin Lowest location to map to
- * \param LoadMax Highest location to map to
- * \return Base load address
- */
-tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax)
-{
- tVAddr base;
- int i, fd;
-
- ENTER("pBinary sPath pLoadMin pLoadMax", Binary, Path, LoadMin, LoadMax);
-
- // Reference Executable (Makes sure that it isn't unloaded)
- Binary->ReferenceCount ++;
-
- // Get Binary Base
- base = Binary->Base;
-
- // Check if base is free
- if(base != 0)
- {
- LOG("Checking base %p", base);
- for( i = 0; i < Binary->NumSections; i ++ )
- {
- if( Binary_int_CheckMemFree( Binary->LoadSections[i].Virtual, Binary->LoadSections[i].MemSize ) )
- {
- base = 0;
- LOG("Address 0x%x is taken\n", Binary->LoadSections[i].Virtual);
- break;
- }
- }
- }
-
- // Check if the executable has no base or it is not free
- if(base == 0)
- {
- // If so, give it a base
- base = LoadMax;
- while(base >= LoadMin)
- {
- for( i = 0; i < Binary->NumSections; i ++ )
- {
- tVAddr addr = Binary->LoadSections[i].Virtual - Binary->Base + base;
- if( Binary_int_CheckMemFree( addr, Binary->LoadSections[i].MemSize ) )
- break;
- }
- // If space was found, break
- if(i == Binary->NumSections) break;
- // Else decrement pointer and try again
- base -= BIN_GRANUALITY;
- }
- LOG("Allocated base %p", base);
- }
-
- // Error Check
- if(base < LoadMin) {
- Log_Warning("Binary", "Executable '%s' cannot be loaded, no space", Path);
- LEAVE('i', 0);
- return 0;
- }
-
- // Map Executable In
- fd = VFS_OpenInode(Binary->MountID, Binary->Inode, VFS_OPENFLAG_READ);
- for( i = 0; i < Binary->NumSections; i ++ )
- {
- tBinarySection *sect = &Binary->LoadSections[i];
- Uint protflags, mapflags;
- tVAddr addr = sect->Virtual - Binary->Base + base;
- LOG("%i - %p to offset 0x%llx (%x)", i, addr, sect->Offset, sect->Flags);
-
- protflags = MMAP_PROT_READ;
- mapflags = MMAP_MAP_FIXED;
-
- if( sect->Flags & BIN_SECTFLAG_EXEC )
- protflags |= MMAP_PROT_EXEC;
- // Read only pages are COW
- if( sect->Flags & BIN_SECTFLAG_RO ) {
- VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_SHARED|mapflags, fd, sect->Offset );
- }
- else {
- protflags |= MMAP_PROT_WRITE;
- VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_PRIVATE|mapflags, fd, sect->Offset );
- }
-
- // Apply anonymous memory for BSS
- if( sect->FileSize < sect->MemSize ) {
- mapflags |= MMAP_MAP_ANONYMOUS;
- VFS_MMap(
- (void*)(addr + sect->FileSize), sect->MemSize - sect->FileSize,
- protflags, MMAP_MAP_PRIVATE|mapflags,
- 0, 0
- );
- }
- }
-
- Log_Debug("Binary", "PID %i - Mapped '%s' to %p", Threads_GetPID(), Path, base);
- VFS_Close(fd);
-
- LEAVE('p', base);
- return base;
-}
-
-#if 0
-/**
- * \fn Uint Binary_IsMapped(tBinary *binary)
- * \brief Check if a binary is already mapped into the address space
- * \param binary Binary information to check
- * \return Current Base or 0
- */
-Uint Binary_IsMapped(tBinary *binary)
-{
- Uint iBase;
-
- // Check prefered base
- iBase = binary->Base;
- if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF))
- return iBase;
-
- for(iBase = BIN_HIGHEST;
- iBase >= BIN_LOWEST;
- iBase -= BIN_GRANUALITY)
- {
- if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF))
- return iBase;
- }
-
- return 0;
-}
-#endif
-
-/**
- * \fn tBinary *Binary_DoLoad(char *truePath)
- * \brief Loads a binary file into memory
- * \param truePath Absolute filename of binary
- */
-tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path)
-{
- tBinary *pBinary;
- int fp;
- Uint32 ident;
- tBinaryType *bt = gRegBinTypes;
-
- ENTER("iMountID XInode sPath", MountID, Inode, Path);
-
- // Open File
- fp = VFS_OpenInode(MountID, Inode, VFS_OPENFLAG_READ);
- if(fp == -1) {
- LOG("Unable to load file, access denied");
- LEAVE('n');
- return NULL;
- }
-
- LOG("fp = 0x%x", fp);
-
- // Read File Type
- VFS_Read(fp, 4, &ident);
- VFS_Seek(fp, 0, SEEK_SET);
-
- LOG("ident = 0x%x", ident);
-
- // Determine the type
- for(; bt; bt = bt->Next)
- {
- if( (ident & bt->Mask) != (Uint32)bt->Ident )
- continue;
- LOG("bt = %p (%s)", bt, bt->Name);
- pBinary = bt->Load(fp);
- break;
- }
-
- // Close File
- VFS_Close(fp);
-
- // Catch errors
- if(!bt) {
- Log_Warning("Binary", "'%s' is an unknown file type. (%02x %02x %02x %02x)",
- Path, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF);
- LEAVE('n');
- return NULL;
- }
-
- LOG("pBinary = %p", pBinary);
-
- // Error Check
- if(pBinary == NULL) {
- LEAVE('n');
- return NULL;
- }
-
- // Initialise Structure
- pBinary->ReferenceCount = 0;
- pBinary->MountID = MountID;
- pBinary->Inode = Inode;
-
- // Debug Information
- LOG("Interpreter: '%s'", pBinary->Interpreter);
- LOG("Base: 0x%x, Entry: 0x%x", pBinary->Base, pBinary->Entry);
- LOG("NumSections: %i", pBinary->NumSections);
-
- // Add to the list
- SHORTLOCK(&glBinListLock);
- pBinary->Next = glLoadedBinaries;
- glLoadedBinaries = pBinary;
- SHORTREL(&glBinListLock);
-
- // TODO: Register the path with the binary
-
- // Return
- LEAVE('p', pBinary);
- return pBinary;
-}
-
-/**
- * \fn void Binary_Unload(void *Base)
- * \brief Unload / Unmap a binary
- * \param Base Loaded Base
- * \note Currently used only for kernel libaries
- */
-void Binary_Unload(void *Base)
-{
- tKernelBin *pKBin;
- tKernelBin *prev = NULL;
- int i;
-
- if((Uint)Base < 0xC0000000)
- {
- // TODO: User Binaries
- Log_Warning("BIN", "Unloading user binaries is currently unimplemented");
- return;
- }
-
- // Kernel Libraries
- for(pKBin = glLoadedKernelLibs;
- pKBin;
- prev = pKBin, pKBin = pKBin->Next)
- {
- // Check the base
- if(pKBin->Base != Base) continue;
- // Deallocate Memory
- for(i = 0; i < pKBin->Info->NumSections; i++)
- {
- // TODO: VFS_MUnmap();
- }
- // Dereference Binary
- Binary_Dereference( pKBin->Info );
- // Remove from list
- if(prev) prev->Next = pKBin->Next;
- else glLoadedKernelLibs = pKBin->Next;
- // Free Kernel Lib
- free(pKBin);
- return;
- }
-}
-
-/**
- * \fn void Binary_Dereference(tBinary *Info)
- * \brief Dereferences and if nessasary, deletes a binary
- * \param Info Binary information structure
- */
-void Binary_Dereference(tBinary *Info)
-{
- // Decrement reference count
- Info->ReferenceCount --;
-
- // Check if it is still in use
- if(Info->ReferenceCount) return;
-
- /// \todo Implement binary freeing
-}
-
-/**
- * \fn char *Binary_RegInterp(char *Path)
- * \brief Registers an Interpreter
- * \param Path Path to interpreter provided by executable
- */
-char *Binary_RegInterp(char *Path)
-{
- int i;
- // NULL Check Argument
- if(Path == NULL) return NULL;
- // NULL Check the array
- if(gsaRegInterps == NULL)
- {
- giRegInterps = 1;
- gsaRegInterps = malloc( sizeof(char*) );
- gsaRegInterps[0] = malloc( strlen(Path) );
- strcpy(gsaRegInterps[0], Path);
- return gsaRegInterps[0];
- }
-
- // Scan Array
- for( i = 0; i < giRegInterps; i++ )
- {
- if(strcmp(gsaRegInterps[i], Path) == 0)
- return gsaRegInterps[i];
- }
-
- // Interpreter is not in list
- giRegInterps ++;
- gsaRegInterps = malloc( sizeof(char*)*giRegInterps );
- gsaRegInterps[i] = malloc( strlen(Path) );
- strcpy(gsaRegInterps[i], Path);
- return gsaRegInterps[i];
-}
-
-// ============
-// Kernel Binary Handling
-// ============
-/**
- * \fn void *Binary_LoadKernel(const char *File)
- * \brief Load a binary into kernel space
- * \note This function shares much with #Binary_Load, but does it's own mapping
- * \param File File to load into the kernel
- */
-void *Binary_LoadKernel(const char *File)
-{
- tBinary *pBinary;
- tKernelBin *pKBinary;
- tVAddr base = -1;
- tMount mount_id;
- tInode inode;
-
- ENTER("sFile", File);
-
- // Sanity Check Argument
- if(File == NULL) {
- LEAVE('n');
- return 0;
- }
-
- {
- int fd = VFS_Open(File, VFS_OPENFLAG_READ);
- tFInfo info;
- if(fd == -1) {
- LEAVE('n');
- return NULL;
- }
- VFS_FInfo(fd, &info, 0);
- mount_id = info.mount;
- inode = info.inode;
- VFS_Close(fd);
- }
-
- // Check if the binary has already been loaded
- if( (pBinary = Binary_GetInfo(mount_id, inode)) )
- {
- for(pKBinary = glLoadedKernelLibs;
- pKBinary;
- pKBinary = pKBinary->Next )
- {
- if(pKBinary->Info == pBinary) {
- LEAVE('p', pKBinary->Base);
- return pKBinary->Base;
- }
- }
- }
- else
- pBinary = Binary_DoLoad(mount_id, inode, File); // Else load it
-
- // Error Check
- if(pBinary == NULL) {
- LEAVE('n');
- return NULL;
- }
-
- // --------------
- // Now pBinary is valid (either freshly loaded or only user mapped)
- // So, map it into kernel space
- // --------------
-
- // Reference Executable (Makes sure that it isn't unloaded)
- pBinary->ReferenceCount ++;
-
- Binary_MapIn(pBinary, File, KLIB_LOWEST, KLIB_HIGHEST);
-
- // Relocate Library
- if( !Binary_Relocate( (void*)base ) )
- {
- Log_Warning("Binary", "Relocation of '%s' failed, unloading", File);
- Binary_Unload( (void*)base );
- Binary_Dereference( pBinary );
- LEAVE('n');
- return 0;
- }
-
- // Add to list (relocator must look at itself manually, not via Binary_GetSymbol)
- pKBinary = malloc(sizeof(*pKBinary));
- pKBinary->Base = (void*)base;
- pKBinary->Info = pBinary;
- SHORTLOCK( &glKBinListLock );
- pKBinary->Next = glLoadedKernelLibs;
- glLoadedKernelLibs = pKBinary;
- SHORTREL( &glKBinListLock );
-
- LEAVE('p', base);
- return (void*)base;
-}
-
-/**
- * \fn Uint Binary_Relocate(void *Base)
- * \brief Relocates a loaded binary (used by kernel libraries)
- * \param Base Loaded base address of binary
- * \return Boolean Success
- */
-Uint Binary_Relocate(void *Base)
-{
- Uint32 ident = *(Uint32*) Base;
- tBinaryType *bt = gRegBinTypes;
-
- for(; bt; bt = bt->Next)
- {
- if( (ident & bt->Mask) == (Uint)bt->Ident )
- return bt->Relocate( (void*)Base);
- }
-
- Log_Warning("BIN", "%p is an unknown file type. (%02x %02x %02x %02x)",
- Base, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF);
- return 0;
-}
-
-/**
- * \fn int Binary_GetSymbol(char *Name, Uint *Val)
- * \brief Get a symbol value
- * \return Value of symbol or -1 on error
- *
- * Gets the value of a symbol from either the currently loaded
- * libraries or the kernel's exports.
- */
-int Binary_GetSymbol(const char *Name, Uint *Val)
-{
- if( Binary_GetSymbolEx(Name, Val) ) return 1;
- return 0;
-}
-
-/**
- * \fn Uint Binary_GetSymbolEx(char *Name, Uint *Value)
- * \brief Get a symbol value
- *
- * Gets the value of a symbol from either the currently loaded
- * libraries or the kernel's exports.
- */
-Uint Binary_GetSymbolEx(const char *Name, Uint *Value)
-{
- int i;
- tKernelBin *pKBin;
- int numKSyms = ((Uint)&gKernelSymbolsEnd-(Uint)&gKernelSymbols)/sizeof(tKernelSymbol);
-
- // Scan Kernel
- for( i = 0; i < numKSyms; i++ )
- {
- if(strcmp(Name, gKernelSymbols[i].Name) == 0) {
- *Value = gKernelSymbols[i].Value;
- return 1;
- }
- }
-
- // Scan Loaded Libraries
- for(pKBin = glLoadedKernelLibs;
- pKBin;
- pKBin = pKBin->Next )
- {
- if( Binary_FindSymbol(pKBin->Base, Name, Value) ) {
- return 1;
- }
- }
-
- Log_Warning("BIN", "Unable to find symbol '%s'", Name);
- return 0;
-}
-
-/**
- * \fn Uint Binary_FindSymbol(void *Base, char *Name, Uint *Val)
- * \brief Get a symbol from the specified library
- * \param Base Base address
- * \param Name Name of symbol to find
- * \param Val Pointer to place final value
- */
-Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val)
-{
- Uint32 ident = *(Uint32*) Base;
- tBinaryType *bt = gRegBinTypes;
-
- for(; bt; bt = bt->Next)
- {
- if( (ident & bt->Mask) == (Uint)bt->Ident )
- return bt->GetSymbol(Base, Name, Val);
- }
-
- Log_Warning("BIN", "Binary_FindSymbol - %p is an unknown file type. (%02x %02x %02x %02x)",
- Base, ident&0xFF, ident>>8, ident>>16, ident>>24);
- return 0;
-}
-
-/**
- * \brief Check if a range of memory is fully free
- * \return Inverse boolean free (0 if all pages are unmapped)
- */
-int Binary_int_CheckMemFree( tVAddr _start, size_t _len )
-{
- _len += _start & (PAGE_SIZE-1);
- _len = (_len + PAGE_SIZE - 1) & ~(PAGE_SIZE-1);
- _start &= ~(PAGE_SIZE-1);
- for( ; _len > PAGE_SIZE; _len -= PAGE_SIZE, _start += PAGE_SIZE ) {
- if( MM_GetPhysAddr(_start) != 0 )
- return 1;
- }
- if( _len == PAGE_SIZE && MM_GetPhysAddr(_start) != 0 )
- return 1;
- return 0;
-}
-
-
-// === EXPORTS ===
-EXPORT(Binary_FindSymbol);
-EXPORT(Binary_Unload);
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * debug.c
- *
- * TODO: Move the Debug_putchar methods out to the arch/ tree
- */
-#include <acess.h>
-#include <stdarg.h>
-
-#define DEBUG_MAX_LINE_LEN 256
-
-#define LOCK_DEBUG_OUTPUT 1
-
-#define TRACE_TO_KTERM 0
-
-// === IMPORTS ===
-extern void Threads_Dump(void);
-extern void KernelPanic_SetMode(void);
-extern void KernelPanic_PutChar(char Ch);
-
-// === PROTOTYPES ===
-static void Debug_Putchar(char ch);
-static void Debug_Puts(int bUseKTerm, const char *Str);
-void Debug_DbgOnlyFmt(const char *format, va_list args);
-void Debug_FmtS(int bUseKTerm, const char *format, ...);
-void Debug_Fmt(int bUseKTerm, const char *format, va_list args);
-void Debug_SetKTerminal(const char *File);
-
-// === GLOBALS ===
- int gDebug_Level = 0;
- int giDebug_KTerm = -1;
- int gbDebug_IsKPanic = 0;
-volatile int gbInPutChar = 0;
-#if LOCK_DEBUG_OUTPUT
-tShortSpinlock glDebug_Lock;
-#endif
-
-// === CODE ===
-static void Debug_Putchar(char ch)
-{
- Debug_PutCharDebug(ch);
- if( !gbDebug_IsKPanic )
- {
- if(gbInPutChar) return ;
- gbInPutChar = 1;
- if(giDebug_KTerm != -1)
- VFS_Write(giDebug_KTerm, 1, &ch);
- gbInPutChar = 0;
- }
- else
- KernelPanic_PutChar(ch);
-}
-
-static void Debug_Puts(int UseKTerm, const char *Str)
-{
- int len = 0;
-
- Debug_PutStringDebug(Str);
-
- if( gbDebug_IsKPanic )
- {
- for( len = 0; Str[len]; len ++ )
- KernelPanic_PutChar( Str[len] );
- }
- else
- for( len = 0; Str[len]; len ++ );
-
- // Output to the kernel terminal
- if( UseKTerm && !gbDebug_IsKPanic && giDebug_KTerm != -1)
- {
- if(gbInPutChar) return ;
- gbInPutChar = 1;
- VFS_Write(giDebug_KTerm, len, Str);
- gbInPutChar = 0;
- }
-}
-
-void Debug_DbgOnlyFmt(const char *format, va_list args)
-{
- Debug_Fmt(0, format, args);
-}
-
-void Debug_Fmt(int bUseKTerm, const char *format, va_list args)
-{
- char buf[DEBUG_MAX_LINE_LEN];
- int len;
- buf[DEBUG_MAX_LINE_LEN-1] = 0;
- len = vsnprintf(buf, DEBUG_MAX_LINE_LEN-1, format, args);
- //if( len < DEBUG_MAX_LINE )
- // do something
- Debug_Puts(bUseKTerm, buf);
- return ;
-}
-
-void Debug_FmtS(int bUseKTerm, const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- Debug_Fmt(bUseKTerm, format, args);
- va_end(args);
-}
-
-void Debug_KernelPanic(void)
-{
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
- gbDebug_IsKPanic = 1;
- KernelPanic_SetMode();
-}
-
-/**
- * \fn void LogF(const char *Msg, ...)
- * \brief Raw debug log (no new line, no prefix)
- */
-void LogF(const char *Fmt, ...)
-{
- va_list args;
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- va_start(args, Fmt);
-
- Debug_Fmt(1, Fmt, args);
-
- va_end(args);
-
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-/**
- * \fn void Debug(const char *Msg, ...)
- * \brief Print only to the debug channel (not KTerm)
- */
-void Debug(const char *Fmt, ...)
-{
- va_list args;
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- Debug_Puts(0, "Debug: ");
- va_start(args, Fmt);
- Debug_DbgOnlyFmt(Fmt, args);
- va_end(args);
- Debug_PutCharDebug('\r');
- Debug_PutCharDebug('\n');
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-/**
- * \fn void Log(const char *Msg, ...)
- */
-void Log(const char *Fmt, ...)
-{
- va_list args;
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- Debug_Puts(1, "Log: ");
- va_start(args, Fmt);
- Debug_Fmt(1, Fmt, args);
- va_end(args);
- Debug_Puts(1, "\r\n");
-
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-void Warning(const char *Fmt, ...)
-{
- va_list args;
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- Debug_Puts(1, "Warning: ");
- va_start(args, Fmt);
- Debug_Fmt(1, Fmt, args);
- va_end(args);
- Debug_Putchar('\r');
- Debug_Putchar('\n');
-
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-void Panic(const char *Fmt, ...)
-{
- va_list args;
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
- // And never SHORTREL
-
- Debug_KernelPanic();
-
- Debug_Puts(1, "Panic: ");
- va_start(args, Fmt);
- Debug_Fmt(1, Fmt, args);
- va_end(args);
- Debug_Putchar('\r');
- Debug_Putchar('\n');
-
- Threads_Dump();
-
- for(;;) ;
-}
-
-void Debug_SetKTerminal(const char *File)
-{
- int tmp;
- if(giDebug_KTerm != -1) {
- tmp = giDebug_KTerm;
- giDebug_KTerm = -1;
- VFS_Close(tmp);
- }
- tmp = VFS_Open(File, VFS_OPENFLAG_WRITE);
-// Log_Log("Debug", "Opened '%s' as 0x%x", File, tmp);
- giDebug_KTerm = tmp;
-// Log_Log("Debug", "Returning to %p", __builtin_return_address(0));
-}
-
-void Debug_Enter(const char *FuncName, const char *ArgTypes, ...)
-{
- va_list args;
- int i;
- int pos;
- tTID tid = Threads_GetTID();
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- i = gDebug_Level ++;
-
- va_start(args, ArgTypes);
-
- Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now());
- while(i--) Debug_Puts(TRACE_TO_KTERM, " ");
-
- Debug_Puts(TRACE_TO_KTERM, FuncName);
- Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid);
- Debug_Puts(TRACE_TO_KTERM, ": (");
-
- while(*ArgTypes)
- {
- pos = strpos(ArgTypes, ' ');
- if(pos == -1 || pos > 1) {
- if(pos == -1)
- Debug_Puts(TRACE_TO_KTERM, ArgTypes+1);
- else {
- Debug_FmtS(TRACE_TO_KTERM, "%.*s", pos-1, ArgTypes+1);
- }
- Debug_Puts(TRACE_TO_KTERM, "=");
- }
- switch(*ArgTypes)
- {
- case 'p': Debug_FmtS(TRACE_TO_KTERM, "%p", va_arg(args, void*)); break;
- case 'P': Debug_FmtS(TRACE_TO_KTERM, "%P", va_arg(args, tPAddr)); break;
- case 's': Debug_FmtS(TRACE_TO_KTERM, "'%s'", va_arg(args, char*)); break;
- case 'i': Debug_FmtS(TRACE_TO_KTERM, "%i", va_arg(args, int)); break;
- case 'u': Debug_FmtS(TRACE_TO_KTERM, "%u", va_arg(args, Uint)); break;
- case 'x': Debug_FmtS(TRACE_TO_KTERM, "0x%x", va_arg(args, Uint)); break;
- case 'b': Debug_FmtS(TRACE_TO_KTERM, "0b%b", va_arg(args, Uint)); break;
- case 'X': Debug_FmtS(TRACE_TO_KTERM, "0x%llx", va_arg(args, Uint64)); break; // Extended (64-Bit)
- case 'B': Debug_FmtS(TRACE_TO_KTERM, "0b%llb", va_arg(args, Uint64)); break; // Extended (64-Bit)
- }
- if(pos != -1) {
- Debug_Puts(TRACE_TO_KTERM, ", ");
- }
-
- if(pos == -1) break;
- ArgTypes = &ArgTypes[pos+1];
- }
-
- va_end(args);
- Debug_Puts(TRACE_TO_KTERM, ")\r\n");
-
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-
-void Debug_Log(const char *FuncName, const char *Fmt, ...)
-{
- va_list args;
- int i = gDebug_Level;
- tTID tid = Threads_GetTID();
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now());
- while(i--) Debug_Puts(TRACE_TO_KTERM, " ");
-
- Debug_Puts(TRACE_TO_KTERM, FuncName);
- Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid);
- Debug_Puts(TRACE_TO_KTERM, ": ");
-
- va_start(args, Fmt);
- Debug_Fmt(TRACE_TO_KTERM, Fmt, args);
- va_end(args);
-
- Debug_Puts(TRACE_TO_KTERM, "\r\n");
-
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-
-void Debug_Leave(const char *FuncName, char RetType, ...)
-{
- va_list args;
- int i;
- tTID tid = Threads_GetTID();
-
- #if LOCK_DEBUG_OUTPUT
- SHORTLOCK(&glDebug_Lock);
- #endif
-
- i = --gDebug_Level;
-
- va_start(args, RetType);
-
- if( i == -1 ) {
- gDebug_Level = 0;
- i = 0;
- }
- Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now());
- // Indenting
- while(i--) Debug_Puts(TRACE_TO_KTERM, " ");
-
- Debug_Puts(TRACE_TO_KTERM, FuncName);
- Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid);
- Debug_Puts(TRACE_TO_KTERM, ": RETURN");
-
- // No Return
- if(RetType == '-') {
- Debug_Puts(TRACE_TO_KTERM, "\r\n");
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
- return;
- }
-
- switch(RetType)
- {
- case 'n': Debug_Puts(TRACE_TO_KTERM, " NULL"); break;
- case 'p': Debug_Fmt(TRACE_TO_KTERM, " %p", args); break;
- case 'P': Debug_Fmt(TRACE_TO_KTERM, " %P", args); break; // PAddr
- case 's': Debug_Fmt(TRACE_TO_KTERM, " '%s'", args); break;
- case 'i': Debug_Fmt(TRACE_TO_KTERM, " %i", args); break;
- case 'u': Debug_Fmt(TRACE_TO_KTERM, " %u", args); break;
- case 'x': Debug_Fmt(TRACE_TO_KTERM, " 0x%x", args); break;
- // Extended (64-Bit)
- case 'X': Debug_Fmt(TRACE_TO_KTERM, " 0x%llx", args); break;
- }
- Debug_Puts(TRACE_TO_KTERM, "\r\n");
-
- va_end(args);
-
- #if LOCK_DEBUG_OUTPUT
- SHORTREL(&glDebug_Lock);
- #endif
-}
-
-void Debug_HexDump(const char *Header, const void *Data, Uint Length)
-{
- const Uint8 *cdat = Data;
- Uint pos = 0;
- LogF("%014lli ", now());
- Debug_Puts(1, Header);
- LogF(" (Hexdump of %p)\r\n", Data);
-
- #define CH(n) ((' '<=cdat[(n)]&&cdat[(n)]<0x7F) ? cdat[(n)] : '.')
-
- while(Length >= 16)
- {
- LogF("%014lli Log: %04x:"
- " %02x %02x %02x %02x %02x %02x %02x %02x"
- " %02x %02x %02x %02x %02x %02x %02x %02x"
- " %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n",
- now(),
- pos,
- cdat[ 0], cdat[ 1], cdat[ 2], cdat[ 3], cdat[ 4], cdat[ 5], cdat[ 6], cdat[ 7],
- cdat[ 8], cdat[ 9], cdat[10], cdat[11], cdat[12], cdat[13], cdat[14], cdat[15],
- CH(0), CH(1), CH(2), CH(3), CH(4), CH(5), CH(6), CH(7),
- CH(8), CH(9), CH(10), CH(11), CH(12), CH(13), CH(14), CH(15)
- );
- Length -= 16;
- cdat += 16;
- pos += 16;
- }
-
- {
- int i ;
- LogF("%014lli Log: %04x: ", now(), pos);
- for(i = 0; i < Length; i ++)
- {
- LogF("%02x ", cdat[i]);
- }
- for( ; i < 16; i ++) LogF(" ");
- LogF(" ");
- for(i = 0; i < Length; i ++)
- {
- if( i == 8 ) LogF(" ");
- LogF("%c", CH(i));
- }
-
- Debug_Putchar('\r');
- Debug_Putchar('\n');
- }
-}
-
-// --- EXPORTS ---
-EXPORT(Debug);
-EXPORT(Log);
-EXPORT(Warning);
-EXPORT(Debug_Enter);
-EXPORT(Debug_Log);
-EXPORT(Debug_Leave);
+++ /dev/null
-# Acess2 Module/Driver Templater Makefile
-# Makefile.tpl
-
--include ../../Makefile.cfg
-
-CPPFLAGS = -I../include -I../arch/$(ARCHDIR)/include -DARCH=$(ARCH) -DBUILD_MODULE
-CFLAGS = -Wall -Werror $(CPPFLAGS)
-
-.PHONY: all clean
-
-all: bochsvbe.kmd
-
-%.kmd: %.o
- $(CC) -shared -nostdlib -o $@ $<
-
-%.o: %.c
- $(CC) $(CFLAGS) -o $@ -c $<
-
-#ata_x86.kmd: ata_x86.o
-#bochsvbe.kmd: bochsvbe.o
+++ /dev/null
-/* AcessOS
- * FIFO Pipe Driver
- */
-#define DEBUG 0
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <semaphore.h>
-
-// === CONSTANTS ===
-#define DEFAULT_RING_SIZE 2048
-#define PF_BLOCKING 1
-
-// === TYPES ===
-typedef struct sPipe {
- struct sPipe *Next;
- char *Name;
- tVFS_Node Node;
- Uint Flags;
- int ReadPos;
- int WritePos;
- int BufSize;
- char *Buffer;
-} tPipe;
-
-// === PROTOTYPES ===
- int FIFO_Install(char **Arguments);
- int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data);
-char *FIFO_ReadDir(tVFS_Node *Node, int Id);
-tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename);
- int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
-void FIFO_Reference(tVFS_Node *Node);
-void FIFO_Close(tVFS_Node *Node);
- int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
-Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
-tPipe *FIFO_Int_NewPipe(int Size, const char *Name);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x0032, FIFO, FIFO_Install, NULL, NULL);
-tVFS_NodeType gFIFO_DirNodeType = {
- .TypeName = "FIFO Dir Node",
- .ReadDir = FIFO_ReadDir,
- .FindDir = FIFO_FindDir,
- .MkNod = FIFO_MkNod,
- .Relink = FIFO_Relink,
- .IOCtl = FIFO_IOCtl
-};
-tVFS_NodeType gFIFO_PipeNodeType = {
- .TypeName = "FIFO Pipe Node",
- .Read = FIFO_Read,
- .Write = FIFO_Write,
- .Close = FIFO_Close,
- .Reference = FIFO_Reference
-};
-tDevFS_Driver gFIFO_DriverInfo = {
- NULL, "fifo",
- {
- .Size = 1,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRW,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gFIFO_DirNodeType
- }
-};
-tVFS_Node gFIFO_AnonNode = {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRW,
- };
-tPipe *gFIFO_NamedPipes = NULL;
-
-// === CODE ===
-/**
- * \fn int FIFO_Install(char **Options)
- * \brief Installs the FIFO Driver
- */
-int FIFO_Install(char **Options)
-{
- DevFS_AddDevice( &gFIFO_DriverInfo );
- return MODULE_ERR_OK;
-}
-
-/**
- * \fn int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
- */
-int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
- return 0;
-}
-
-/**
- * \fn char *FIFO_ReadDir(tVFS_Node *Node, int Id)
- * \brief Reads from the FIFO root
- */
-char *FIFO_ReadDir(tVFS_Node *Node, int Id)
-{
- tPipe *tmp = gFIFO_NamedPipes;
-
- // Entry 0 is Anon Pipes
- if(Id == 0) return strdup("anon");
-
- // Find the id'th node
- while(--Id && tmp) tmp = tmp->Next;
- // If node found, return it
- if(tmp) return strdup(tmp->Name);
- // else error return
- return NULL;
-}
-
-/**
- * \fn tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename)
- * \brief Find a file in the FIFO root
- * \note Creates an anon pipe if anon is requested
- */
-tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename)
-{
- tPipe *tmp;
- if(!Filename) return NULL;
-
- // NULL String Check
- if(Filename[0] == '\0') return NULL;
-
- // Anon Pipe
- if(Filename[0] == 'a' && Filename[1] == 'n'
- && Filename[2] == 'o' && Filename[3] == 'n'
- && Filename[4] == '\0') {
- tmp = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon");
- return &tmp->Node;
- }
-
- // Check Named List
- tmp = gFIFO_NamedPipes;
- while(tmp)
- {
- if(strcmp(tmp->Name, Filename) == 0)
- return &tmp->Node;
- tmp = tmp->Next;
- }
- return NULL;
-}
-
-/**
- * \fn int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
- */
-int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
-{
- return 0;
-}
-
-void FIFO_Reference(tVFS_Node *Node)
-{
- if(!Node->ImplPtr) return ;
-
- Node->ReferenceCount ++;
-}
-
-/**
- * \fn void FIFO_Close(tVFS_Node *Node)
- * \brief Close a FIFO end
- */
-void FIFO_Close(tVFS_Node *Node)
-{
- tPipe *pipe;
- if(!Node->ImplPtr) return ;
-
- Node->ReferenceCount --;
- if(Node->ReferenceCount) return ;
-
- pipe = Node->ImplPtr;
-
- if(strcmp(pipe->Name, "anon") == 0) {
- Log_Debug("FIFO", "Pipe %p closed", Node->ImplPtr);
- free(Node->ImplPtr);
- return ;
- }
-
- return ;
-}
-
-/**
- * \fn int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
- * \brief Relink a file (Deletes named pipes)
- */
-int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
-{
- tPipe *pipe, *tmp;
-
- if(Node != &gFIFO_DriverInfo.RootNode) return 0;
-
- // Can't relink anon
- if(strcmp(OldName, "anon")) return 0;
-
- // Find node
- for(pipe = gFIFO_NamedPipes;
- pipe;
- pipe = pipe->Next)
- {
- if(strcmp(pipe->Name, OldName) == 0)
- break;
- }
- if(!pipe) return 0;
-
- // Relink a named pipe
- if(NewName) {
- // Check new name
- for(tmp = gFIFO_NamedPipes;
- tmp;
- tmp = tmp->Next)
- {
- if(strcmp(tmp->Name, NewName) == 0) return 0;
- }
- // Create new name
- free(pipe->Name);
- pipe->Name = malloc(strlen(NewName)+1);
- strcpy(pipe->Name, NewName);
- return 1;
- }
-
- // Unlink the pipe
- if(Node->ImplPtr) {
- free(Node->ImplPtr);
- return 1;
- }
-
- return 0;
-}
-
-/**
- * \fn Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from a fifo pipe
- */
-Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tPipe *pipe = Node->ImplPtr;
- Uint len;
- Uint remaining = Length;
-
- if(!pipe) return 0;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- while(remaining)
- {
- // Wait for buffer to fill
- if(pipe->Flags & PF_BLOCKING)
- {
- if( pipe->ReadPos == pipe->WritePos )
- VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "FIFO_Read");
-
- }
- else
- {
- if(pipe->ReadPos == pipe->WritePos)
- {
- VFS_MarkAvaliable(Node, 0);
- LEAVE('i', 0);
- return 0;
- }
- }
-
- len = remaining;
- if( pipe->ReadPos < pipe->WritePos )
- {
- int avail_bytes = pipe->WritePos - pipe->ReadPos;
- if( avail_bytes < remaining ) len = avail_bytes;
- }
- else
- {
- int avail_bytes = pipe->WritePos + pipe->BufSize - pipe->ReadPos;
- if( avail_bytes < remaining ) len = avail_bytes;
- }
-
- LOG("len = %i, remaining = %i", len, remaining);
-
- // Check if read overflows buffer
- if(len > pipe->BufSize - pipe->ReadPos)
- {
- int ofs = pipe->BufSize - pipe->ReadPos;
- memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], ofs);
- memcpy((Uint8*)Buffer + ofs, &pipe->Buffer, len-ofs);
- }
- else
- {
- memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], len);
- }
-
- // Increment read position
- pipe->ReadPos += len;
- pipe->ReadPos %= pipe->BufSize;
-
- // Mark some flags
- if( pipe->ReadPos == pipe->WritePos ) {
- VFS_MarkAvaliable(Node, 0);
- }
- VFS_MarkFull(Node, 0); // Buffer can't still be full
-
- // Decrement Remaining Bytes
- remaining -= len;
- // Increment Buffer address
- Buffer = (Uint8*)Buffer + len;
-
- // TODO: Option to read differently
- LEAVE('i', len);
- return len;
- }
-
- LEAVE('i', Length);
- return Length;
-
-}
-
-/**
- * \fn Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to a fifo pipe
- */
-Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tPipe *pipe = Node->ImplPtr;
- Uint len;
- Uint remaining = Length;
-
- if(!pipe) return 0;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- while(remaining)
- {
- // Wait for buffer to empty
- if(pipe->Flags & PF_BLOCKING) {
- if( pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize )
- VFS_SelectNode(Node, VFS_SELECT_WRITE, NULL, "FIFO_Write");
-
- len = remaining;
- if( pipe->ReadPos > pipe->WritePos )
- {
- int rem_space = pipe->ReadPos - pipe->WritePos;
- if(rem_space < remaining) len = rem_space;
- }
- else
- {
- int rem_space = pipe->ReadPos + pipe->BufSize - pipe->WritePos;
- if(rem_space < remaining) len = rem_space;
- }
- }
- else
- {
- if(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize)
- {
- LEAVE('i', 0);
- return 0;
- }
- // Write buffer
- if(pipe->ReadPos - pipe->WritePos < remaining)
- len = pipe->ReadPos - pipe->WritePos;
- else
- len = remaining;
- }
-
- // Check if write overflows buffer
- if(len > pipe->BufSize - pipe->WritePos)
- {
- int ofs = pipe->BufSize - pipe->WritePos;
- memcpy(&pipe->Buffer[pipe->WritePos], Buffer, ofs);
- memcpy(&pipe->Buffer, (Uint8*)Buffer + ofs, len-ofs);
- }
- else
- {
- memcpy(&pipe->Buffer[pipe->WritePos], Buffer, len);
- }
-
- // Increment read position
- pipe->WritePos += len;
- pipe->WritePos %= pipe->BufSize;
-
- // Mark some flags
- if( pipe->ReadPos == pipe->WritePos ) {
- VFS_MarkFull(Node, 1); // Buffer full
- }
- VFS_MarkAvaliable(Node, 1);
-
- // Decrement Remaining Bytes
- remaining -= len;
- // Increment Buffer address
- Buffer = (Uint8*)Buffer + len;
- }
-
- LEAVE('i', Length);
- return Length;
-}
-
-// --- HELPERS ---
-/**
- * \fn tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
- * \brief Create a new pipe
- */
-tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
-{
- tPipe *ret;
- int namelen = strlen(Name) + 1;
- int allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size + namelen;
-
- ret = calloc(1, allocsize);
- if(!ret) return NULL;
-
- // Clear Return
- ret->Flags = PF_BLOCKING;
-
- // Allocate Buffer
- ret->BufSize = Size;
- ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) );
-
- // Set name (and FIFO name)
- ret->Name = ret->Buffer + Size;
- strcpy(ret->Name, Name);
- // - Start empty, max of `Size`
- //Semaphore_Init( &ret->Semaphore, 0, Size, "FIFO", ret->Name );
-
- // Set Node
- ret->Node.ReferenceCount = 1;
- ret->Node.Size = 0;
- ret->Node.ImplPtr = ret;
- ret->Node.UID = Threads_GetUID();
- ret->Node.GID = Threads_GetGID();
- ret->Node.NumACLs = 1;
- ret->Node.ACLs = (void*)( (Uint)ret + sizeof(tPipe) );
- ret->Node.ACLs->Group = 0;
- ret->Node.ACLs->ID = ret->Node.UID;
- ret->Node.ACLs->Inv = 0;
- ret->Node.ACLs->Perms = -1;
- ret->Node.CTime
- = ret->Node.MTime
- = ret->Node.ATime = now();
- ret->Node.Type = &gFIFO_PipeNodeType;
-
- return ret;
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - IO Cache
- *
- * By thePowersGang (John Hodge)
- *
- * TODO: Convert to use spare physical pages instead
- */
-#define DEBUG 0
-#include <acess.h>
-#include <iocache.h>
-
-// === TYPES ===
-typedef struct sIOCache_Ent tIOCache_Ent;
-typedef struct sIOCache_PageInfo tIOCache_PageInfo;
-
-// === STRUCTURES ===
-struct sIOCache_Ent
-{
- tIOCache_Ent *Next;
- Uint64 Num;
- Sint64 LastAccess;
- Sint64 LastWrite;
- Uint8 Data[];
-};
-
-struct sIOCache_PageInfo
-{
- tIOCache_PageInfo *GlobalNext;
- tIOCache_PageInfo *CacheNext;
- tIOCache *Owner;
- tPAddr BasePhys;
- Uint64 BaseOffset;
-};
-
-struct sIOCache
-{
- tIOCache *Next;
- int SectorSize;
- tMutex Lock;
- int Mode;
- Uint32 ID;
- tIOCache_WriteCallback Write;
- int CacheSize;
- int CacheUsed;
- tIOCache_Ent *Entries;
-};
-
-// === GLOBALS ===
-tShortSpinlock glIOCache_Caches;
-tIOCache *gIOCache_Caches = NULL;
- int giIOCache_NumCaches = 0;
-tIOCache_PageInfo *gIOCache_GlobalPages;
-
-// === CODE ===
-/**
- * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
- * \brief Creates a new IO Cache
- */
-tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
-{
- tIOCache *ret = calloc( 1, sizeof(tIOCache) );
-
- // Sanity Check
- if(!ret) return NULL;
-
- // Fill Structure
- ret->SectorSize = SectorSize;
- ret->Mode = IOCACHE_WRITEBACK;
- ret->ID = ID;
- ret->Write = Write;
- ret->CacheSize = CacheSize;
-
- // Append to list
- SHORTLOCK( &glIOCache_Caches );
- ret->Next = gIOCache_Caches;
- gIOCache_Caches = ret;
- SHORTREL( &glIOCache_Caches );
-
- // Return
- return ret;
-}
-
-/**
- * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
- * \brief Read from a cached sector
- */
-int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
-{
-
- ENTER("pCache XSector pBuffer", Cache, Sector, Buffer);
-
- // Sanity Check!
- if(!Cache || !Buffer) {
- LEAVE('i', -1);
- return -1;
- }
-
- // Lock
- Mutex_Acquire( &Cache->Lock );
- if(Cache->CacheSize == 0) {
- Mutex_Release( &Cache->Lock );
- LEAVE('i', -1);
- return -1;
- }
-
- #if IOCACHE_USE_PAGES
- tIOCache_PageInfo *page;
- size_t offset = (Sector*Cache->SectorSize) % PAGE_SIZE;
- Uint64 wanted_base = (Sector*Cache->SectorSize) & ~(PAGE_SIZE-1);
- for( page = Cache->Pages; page; page = page->CacheNext )
- {
- void *tmp;
- if(page->BaseOffset < WantedBase) continue;
- if(page->BaseOffset > WantedBase) break;
- tmp = MM_MapTemp( page->BasePhys );
- memcpy( Buffer, tmp + offset, Cache->SectorSize );
- MM_FreeTemp( tmp );
- }
- #else
- tIOCache_Ent *ent;
- // Search the list
- for( ent = Cache->Entries; ent; ent = ent->Next )
- {
- // Have we found what we are looking for?
- if( ent->Num == Sector ) {
- memcpy(Buffer, ent->Data, Cache->SectorSize);
- ent->LastAccess = now();
- Mutex_Release( &Cache->Lock );
- LEAVE('i', 1);
- return 1;
- }
- // It's a sorted list, so as soon as we go past `Sector` we know
- // it's not there
- if(ent->Num > Sector) break;
- }
- #endif
-
- Mutex_Release( &Cache->Lock );
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
- * \brief Cache a sector
- */
-int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
-{
- tIOCache_Ent *ent, *prev;
- tIOCache_Ent *new;
- tIOCache_Ent *oldest = NULL, *oldestPrev;
-
- // Sanity Check!
- if(!Cache || !Buffer)
- return -1;
-
- // Lock
- Mutex_Acquire( &Cache->Lock );
- if(Cache->CacheSize == 0) {
- Mutex_Release( &Cache->Lock );
- return -1;
- }
-
- // Search the list
- prev = (tIOCache_Ent*)&Cache->Entries;
- for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
- {
- // Is it already here?
- if( ent->Num == Sector ) {
- Mutex_Release( &Cache->Lock );
- return 0;
- }
-
- // Check if we have found the oldest entry
- if( !oldest || oldest->LastAccess > ent->LastAccess ) {
- oldest = ent;
- oldestPrev = prev;
- }
-
- // Here we go!
- if(ent->Num > Sector)
- break;
- }
-
- // Create the new entry
- new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
- new->Next = ent;
- new->Num = Sector;
- new->LastAccess = now();
- new->LastWrite = 0; // Zero is special, it means unmodified
- memcpy(new->Data, Buffer, Cache->SectorSize);
-
- // Have we reached the maximum cached entries?
- if( Cache->CacheUsed == Cache->CacheSize )
- {
- tIOCache_Ent *savedPrev = prev;
- oldestPrev = (tIOCache_Ent*)&Cache->Entries;
- // If so, search for the least recently accessed entry
- for( ; ent; prev = ent, ent = ent->Next )
- {
- // Check if we have found the oldest entry
- if( !oldest || oldest->LastAccess > ent->LastAccess ) {
- oldest = ent;
- oldestPrev = prev;
- }
- }
- if( !oldest ) {
- Log_Error("IOCache", "Cache full, but also empty");
- return -1;
- }
- // Remove from list, write back and free
- oldestPrev->Next = oldest->Next;
- if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
- Cache->Write(Cache->ID, oldest->Num, oldest->Data);
- free(oldest);
-
- // Decrement the used count
- Cache->CacheUsed --;
-
- // Restore `prev`
- prev = savedPrev;
- }
-
- // Append to list
- prev->Next = new;
- Cache->CacheUsed ++;
-
- // Release Spinlock
- Mutex_Release( &Cache->Lock );
-
- // Return success
- return 1;
-}
-
-/**
- * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
- * \brief Read from a cached sector
- */
-int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
-{
- tIOCache_Ent *ent;
-
- // Sanity Check!
- if(!Cache || !Buffer)
- return -1;
- // Lock
- Mutex_Acquire( &Cache->Lock );
- if(Cache->CacheSize == 0) {
- Mutex_Release( &Cache->Lock );
- return -1;
- }
-
- // Search the list
- for( ent = Cache->Entries; ent; ent = ent->Next )
- {
- // Have we found what we are looking for?
- if( ent->Num == Sector ) {
- memcpy(ent->Data, Buffer, Cache->SectorSize);
- ent->LastAccess = ent->LastWrite = now();
-
- if(Cache->Mode == IOCACHE_WRITEBACK) {
- Cache->Write(Cache->ID, Sector, Buffer);
- ent->LastWrite = 0;
- }
-
- Mutex_Release( &Cache->Lock );
- return 1;
- }
- // It's a sorted list, so as soon as we go past `Sector` we know
- // it's not there
- if(ent->Num > Sector) break;
- }
-
- Mutex_Release( &Cache->Lock );
- return 0;
-}
-
-/**
- * \fn void IOCache_Flush( tIOCache *Cache )
- * \brief Flush a cache
- */
-void IOCache_Flush( tIOCache *Cache )
-{
- tIOCache_Ent *ent;
-
- if( Cache->Mode == IOCACHE_VIRTUAL ) return;
-
- // Lock
- Mutex_Acquire( &Cache->Lock );
- if(Cache->CacheSize == 0) {
- Mutex_Release( &Cache->Lock );
- return;
- }
-
- // Write All
- for( ent = Cache->Entries; ent; ent = ent->Next )
- {
- Cache->Write(Cache->ID, ent->Num, ent->Data);
- ent->LastWrite = 0;
- }
-
- Mutex_Release( &Cache->Lock );
-}
-
-/**
- * \fn void IOCache_Destroy( tIOCache *Cache )
- * \brief Destroy a cache
- */
-void IOCache_Destroy( tIOCache *Cache )
-{
- tIOCache_Ent *ent, *prev = NULL;
-
- // Lock
- Mutex_Acquire( &Cache->Lock );
- if(Cache->CacheSize == 0) {
- Mutex_Release( &Cache->Lock );
- return;
- }
-
- // Free All
- for(ent = Cache->Entries;
- ent;
- prev = ent, ent = ent->Next, free(prev) )
- {
- if( Cache->Mode != IOCACHE_VIRTUAL )
- {
- Cache->Write(Cache->ID, ent->Num, ent->Data);
- ent->LastWrite = 0;
- }
- }
-
- Cache->CacheSize = 0;
-
- Mutex_Release( &Cache->Lock );
-
- // Remove from list
- SHORTLOCK( &glIOCache_Caches );
- {
- tIOCache *cache;
- tIOCache *prev_cache = (tIOCache*)&gIOCache_Caches;
- for(cache = gIOCache_Caches;
- cache;
- prev_cache = cache, cache = cache->Next )
- {
- if(cache == Cache) {
- prev_cache->Next = cache->Next;
- break;
- }
- }
- }
- SHORTREL( &glIOCache_Caches );
-
- free(Cache);
-}
+++ /dev/null
-/*\r
- * AcessOS/AcessBasic v0.1\r
- * PCI Bus Driver\r
- */\r
-#define DEBUG 0\r
-#include <acess.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include <fs_devfs.h>\r
-#include <drv_pci.h>\r
-#include <drv_pci_int.h>\r
-\r
-#define LIST_DEVICES 1\r
-\r
-// === STRUCTURES ===\r
-typedef struct sPCIDevice\r
-{\r
- Uint16 bus, slot, fcn;\r
- Uint16 vendor, device;\r
- Uint32 class; // Class:Subclass:ProgIf\r
- Uint8 revision;\r
- Uint32 ConfigCache[256/4];\r
- char Name[8];\r
- tVFS_Node Node;\r
-} tPCIDevice;\r
-\r
-// === CONSTANTS ===\r
-#define SPACE_STEP 5\r
-#define MAX_RESERVED_PORT 0xD00\r
-\r
-// === PROTOTYPES ===\r
- int PCI_Install(char **Arguments);\r
- int PCI_ScanBus(int ID, int bFill);\r
- \r
-char *PCI_int_ReadDirRoot(tVFS_Node *node, int pos);\r
-tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename);\r
-Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset);\r
-Uint64 PCI_int_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer);\r
- int PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL);\r
- int giPCI_BusCount = 1;\r
- int giPCI_InodeHandle = -1;\r
- int giPCI_DeviceCount = 0;\r
-tPCIDevice *gPCI_Devices = NULL;\r
-tVFS_NodeType gPCI_RootNodeType = {\r
- .TypeName = "PCI Root Node",\r
- .ReadDir = PCI_int_ReadDirRoot,\r
- .FindDir = PCI_int_FindDirRoot\r
-};\r
-tVFS_NodeType gPCI_DevNodeType = {\r
- .TypeName = "PCI Dev Node",\r
- .Read = PCI_int_ReadDevice\r
-};\r
-tDevFS_Driver gPCI_DriverStruct = {\r
- NULL, "pci",\r
- {\r
- .Flags = VFS_FFLAG_DIRECTORY,\r
- .Size = -1,\r
- .NumACLs = 1,\r
- .ACLs = &gVFS_ACL_EveryoneRX,\r
- .Type = &gPCI_RootNodeType\r
- }\r
-};\r
-Uint32 *gaPCI_PortBitmap = NULL;\r
-Uint32 gaPCI_BusBitmap[256/32];\r
- \r
-// === CODE ===\r
-/**\r
- * \brief Scan the PCI Bus for devices\r
- * \param Arguments Boot-time parameters\r
- */\r
-int PCI_Install(char **Arguments)\r
-{\r
- int i;\r
- void *tmpPtr;\r
- \r
- // Build Portmap\r
- gaPCI_PortBitmap = malloc( 1 << 13 );\r
- if( !gaPCI_PortBitmap ) {\r
- Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13);\r
- return MODULE_ERR_MALLOC;\r
- }\r
- memset( gaPCI_PortBitmap, 0, 1 << 13 );\r
- for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )\r
- gaPCI_PortBitmap[i] = -1;\r
- for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )\r
- gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;\r
- \r
- // Scan Bus (Bus 0, Don't fill gPCI_Devices)\r
- i = PCI_ScanBus(0, 0);\r
- if(i != MODULE_ERR_OK) return i;\r
- \r
- if(giPCI_DeviceCount == 0) {\r
- Log_Notice("PCI", "No devices were found");\r
- return MODULE_ERR_NOTNEEDED;\r
- }\r
- \r
- // Allocate device buffer\r
- tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice));\r
- if(tmpPtr == NULL) {\r
- Log_Warning("PCI", "Malloc ERROR");\r
- return MODULE_ERR_MALLOC;\r
- }\r
- gPCI_Devices = tmpPtr;\r
- \r
- Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount);\r
- \r
- // Reset counts\r
- giPCI_DeviceCount = 0;\r
- giPCI_BusCount = 0;\r
- memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap));\r
- // Rescan, filling the PCI device array\r
- PCI_ScanBus(0, 1);\r
- \r
- // Complete Driver Structure\r
- gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
- \r
- // And add to DevFS\r
- DevFS_AddDevice(&gPCI_DriverStruct);\r
- \r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \brief Scans a specific PCI Bus\r
- * \param BusID PCI Bus ID to scan\r
- * \param bFill Fill the \a gPCI_Devices array?\r
- */\r
-int PCI_ScanBus(int BusID, int bFill)\r
-{\r
- int dev, fcn;\r
- tPCIDevice devInfo;\r
- \r
- if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) )\r
- return MODULE_ERR_OK;\r
- \r
- gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32));\r
- \r
- for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus\r
- {\r
- for( fcn = 0; fcn < 8; fcn++ ) // Max 8 functions per device\r
- {\r
- // Check if the device/function exists\r
- if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))\r
- continue;\r
- \r
- if(devInfo.class == PCI_OC_PCIBRIDGE)\r
- {\r
- #if LIST_DEVICES\r
- if( !bFill )\r
- Log_Log("PCI", "Bridge @ %i,%i:%i (0x%x:0x%x)",\r
- BusID, dev, fcn, devInfo.vendor, devInfo.device);\r
- #endif\r
- //TODO: Handle PCI-PCI Bridges\r
- //PCI_ScanBus(devInfo.???, bFill);\r
- giPCI_BusCount ++;\r
- }\r
- else\r
- {\r
- #if LIST_DEVICES\r
- if( !bFill )\r
- Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x",\r
- BusID, dev, fcn, devInfo.class, devInfo.vendor, devInfo.device);\r
- #endif\r
- }\r
- \r
- if( bFill ) {\r
- devInfo.Node.Inode = giPCI_DeviceCount;\r
- memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice));\r
- }\r
- giPCI_DeviceCount ++;\r
- \r
- // If bit 23 of (soemthing) is set, there are sub-functions\r
- if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )\r
- break;\r
- }\r
- }\r
- \r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \brief Read from Root of PCI Driver\r
-*/\r
-char *PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos)\r
-{\r
- ENTER("pNode iPos", Node, Pos);\r
- if(Pos < 0 || Pos >= giPCI_DeviceCount) {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- LEAVE('s', gPCI_Devices[Pos].Name);\r
- return strdup( gPCI_Devices[Pos].Name );\r
-}\r
-/**\r
- */\r
-tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename)\r
-{\r
- int bus,slot,fcn;\r
- int i;\r
- // Validate Filename (Pointer and length)\r
- if(!filename || strlen(filename) != 7)\r
- return NULL;\r
- // Check for spacers\r
- if(filename[2] != '.' || filename[5] != ':')\r
- return NULL;\r
- \r
- // Get Information\r
- if(filename[0] < '0' || filename[0] > '9') return NULL;\r
- bus = (filename[0] - '0')*10;\r
- if(filename[1] < '0' || filename[1] > '9') return NULL;\r
- bus += filename[1] - '0';\r
- if(filename[3] < '0' || filename[3] > '9') return NULL;\r
- slot = (filename[3] - '0')*10;\r
- if(filename[4] < '0' || filename[4] > '9') return NULL;\r
- slot += filename[4] - '0';\r
- if(filename[6] < '0' || filename[6] > '9') return NULL;\r
- fcn = filename[6] - '0';\r
- \r
- // Find Match\r
- for(i=0;i<giPCI_DeviceCount;i++)\r
- {\r
- if(gPCI_Devices[i].bus != bus) continue;\r
- if(gPCI_Devices[i].slot != slot) continue;\r
- if(gPCI_Devices[i].fcn != fcn) continue;\r
- \r
- return &gPCI_Devices[i].Node;\r
- }\r
- \r
- // Error Return\r
- return NULL;\r
-}\r
-\r
-/**\r
- */\r
-Uint64 PCI_int_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
-{ \r
- if( pos + length > 256 ) return 0;\r
- \r
- memcpy(\r
- buffer,\r
- (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
- length);\r
- \r
- return length;\r
-}\r
-\r
-// --- Kernel Code Interface ---\r
-/**\r
- * \brief Counts the devices with the specified codes\r
- * \param vendor Vendor ID\r
- * \param device Device ID\r
- */\r
-int PCI_CountDevices(Uint16 vendor, Uint16 device)\r
-{\r
- int i, ret=0;\r
- for(i=0;i<giPCI_DeviceCount;i++)\r
- {\r
- if(gPCI_Devices[i].vendor != vendor) continue;\r
- if(gPCI_Devices[i].device != device) continue;\r
- ret ++;\r
- }\r
- return ret;\r
-}\r
-\r
-/**\r
- * \brief Gets the ID of the specified PCI device\r
- * \param vendor Vendor ID\r
- * \param device Device ID\r
- * \param idx Number of matching entry wanted\r
- */\r
-tPCIDev PCI_GetDevice(Uint16 vendor, Uint16 device, int idx)\r
-{\r
- int i, j=0;\r
- for( i = 0; i < giPCI_DeviceCount; i ++ )\r
- {\r
- if(gPCI_Devices[i].vendor != vendor) continue;\r
- if(gPCI_Devices[i].device != device) continue;\r
- if(j == idx) return i;\r
- j ++;\r
- }\r
- return -1;\r
-}\r
-\r
-/**\r
- * \brief Gets the ID of a device by it's class code\r
- * \param class Class Code\r
- * \param mask Mask for class comparison\r
- * \param prev ID of previous device (-1 for no previous)\r
- */\r
-tPCIDev PCI_GetDeviceByClass(Uint32 class, Uint32 mask, tPCIDev prev)\r
-{\r
- int i;\r
- // Check if prev is negative (meaning get first)\r
- if(prev < 0) i = 0;\r
- else i = prev+1;\r
- \r
- for( ; i < giPCI_DeviceCount; i++ )\r
- {\r
- if((gPCI_Devices[i].class & mask) == class)\r
- return i;\r
- }\r
- return -1;\r
-}\r
-\r
-int PCI_GetDeviceInfo(tPCIDev ID, Uint16 *Vendor, Uint16 *Device, Uint32 *Class)\r
-{\r
- tPCIDevice *dev = &gPCI_Devices[ID];\r
- if(ID < 0 || ID >= giPCI_DeviceCount) return 1;\r
- \r
- if(Vendor) *Vendor = dev->vendor;\r
- if(Device) *Device = dev->device;\r
- if(Class) *Class = dev->class;\r
- return 0;\r
-}\r
-\r
-int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision)\r
-{\r
- tPCIDevice *dev = &gPCI_Devices[ID];\r
- if(ID < 0 || ID >= giPCI_DeviceCount) return 1;\r
- \r
- if(Revision) *Revision = dev->revision;\r
- return 0;\r
-}\r
-\r
-int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID)\r
-{\r
- tPCIDevice *dev = &gPCI_Devices[ID];\r
- if(ID < 0 || ID >= giPCI_DeviceCount) return 1;\r
- \r
- if(SubsystemVendor) *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF;\r
- if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16;\r
-\r
- return 0;\r
-}\r
-\r
-Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset)\r
-{\r
- Bus &= 0xFF;\r
- Slot &= 0x1F;\r
- Fcn &= 7;\r
- Offset &= 0xFC;\r
- return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC);\r
-}\r
-\r
-Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size)\r
-{\r
- tPCIDevice *dev;\r
- Uint32 dword, addr;\r
- \r
- if( ID < 0 || ID >= giPCI_DeviceCount ) return 0;\r
- if( Offset < 0 || Offset > 256 ) return 0;\r
-\r
- // TODO: Should I support non-aligned reads?\r
- if( Offset & (Size - 1) ) return 0;\r
-\r
- dev = &gPCI_Devices[ID];\r
- addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
-\r
- dword = PCI_CfgReadDWord(addr);\r
- gPCI_Devices[ID].ConfigCache[Offset/4] = dword;\r
- switch( Size )\r
- {\r
- case 1: return (dword >> (8 * (Offset&3))) & 0xFF;\r
- case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF;\r
- case 4: return dword;\r
- default:\r
- return 0;\r
- }\r
-}\r
-\r
-void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value)\r
-{\r
- tPCIDevice *dev;\r
- Uint32 dword, addr;\r
- int shift;\r
- if( ID < 0 || ID >= giPCI_DeviceCount ) return ;\r
- if( Offset < 0 || Offset > 256 ) return ;\r
- \r
- dev = &gPCI_Devices[ID];\r
- addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
-\r
- if(Size != 4)\r
- dword = PCI_CfgReadDWord(addr);\r
- switch(Size)\r
- {\r
- case 1:\r
- shift = (Offset&3)*8;\r
- dword &= ~(0xFF << shift);\r
- dword |= Value << shift;\r
- break;\r
- case 2:\r
- shift = (Offset&2)*8;\r
- dword &= ~(0xFFFF << shift);\r
- dword |= Value << shift;\r
- break;\r
- case 4:\r
- dword = Value;\r
- break;\r
- default:\r
- return;\r
- }\r
- PCI_CfgWriteDWord(addr, dword);\r
-}\r
-\r
-/**\r
- * \brief Get the IRQ assigned to a device\r
- */\r
-Uint8 PCI_GetIRQ(tPCIDev id)\r
-{\r
- if(id < 0 || id >= giPCI_DeviceCount)\r
- return 0;\r
- return gPCI_Devices[id].ConfigCache[15] & 0xFF;\r
- //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
-}\r
-\r
-/**\r
- * \brief Read the a BAR (base address register) from the PCI config space\r
- */\r
-Uint32 PCI_GetBAR(tPCIDev id, int BARNum)\r
-{\r
- if(id < 0 || id >= giPCI_DeviceCount)\r
- return 0;\r
- if(BARNum < 0 || BARNum >= 6)\r
- return 0;\r
- return gPCI_Devices[id].ConfigCache[4+BARNum];\r
-}\r
-\r
-/**\r
- * \brief Get device information for a slot/function\r
- */\r
-int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info)\r
-{\r
- Uint32 vendor_dev, tmp;\r
- int i;\r
- Uint32 addr;\r
- addr = PCI_int_GetBusAddr(bus, slot, fcn, 0); \r
-\r
- vendor_dev = PCI_CfgReadDWord( addr );\r
- if((vendor_dev & 0xFFFF) == 0xFFFF) // Invalid Device\r
- return 0;\r
-\r
- info->ConfigCache[0] = vendor_dev;\r
- for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )\r
- {\r
- info->ConfigCache[i] = PCI_CfgReadDWord(addr);\r
- } \r
-\r
- info->bus = bus;\r
- info->slot = slot;\r
- info->fcn = fcn;\r
- info->vendor = vendor_dev & 0xFFFF;\r
- info->device = vendor_dev >> 16;\r
- tmp = info->ConfigCache[2];\r
- info->revision = tmp & 0xFF;\r
- info->class = tmp >> 8;\r
- \r
-// #if LIST_DEVICES\r
-// Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
-// Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
-// Log("Class: 0x%06x", info->class);\r
-// #endif\r
- \r
- // Make node name\r
- info->Name[0] = '0' + bus/10;\r
- info->Name[1] = '0' + bus%10;\r
- info->Name[2] = '.';\r
- info->Name[3] = '0' + slot/10;\r
- info->Name[4] = '0' + slot%10;\r
- info->Name[5] = ':';\r
- info->Name[6] = '0' + fcn;\r
- info->Name[7] = '\0';\r
- \r
- // Create VFS Node\r
- memset( &info->Node, 0, sizeof(tVFS_Node) );\r
- info->Node.Size = 256;\r
- \r
- info->Node.NumACLs = 1;\r
- info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
- \r
- info->Node.Type = &gPCI_RootNodeType;\r
- \r
- return 1;\r
-}\r
-\r
-// === EXPORTS ===\r
-//*\r
-EXPORT(PCI_CountDevices);\r
-EXPORT(PCI_GetDevice);\r
-EXPORT(PCI_GetDeviceByClass);\r
-EXPORT(PCI_GetDeviceInfo);\r
-EXPORT(PCI_GetDeviceVersion);\r
-EXPORT(PCI_GetDeviceSubsys);\r
-//EXPORT(PCI_AssignPort);\r
-EXPORT(PCI_GetBAR);\r
-EXPORT(PCI_GetIRQ);\r
-//*/\r
+++ /dev/null
-/*
- * Acess2
- * - Kernel Status Driver
- */
-#define DEBUG 1
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <fs_sysfs.h>
-
-// === CONSTANTS ===
-#define VERSION ((0 << 8) | (1)) // 0.01
-
-// === TYPES ===
-typedef struct sSysFS_Ent
-{
- struct sSysFS_Ent *Next;
- struct sSysFS_Ent *ListNext;
- struct sSysFS_Ent *Parent;
- tVFS_Node Node;
- char Name[];
-} tSysFS_Ent;
-
-// === PROTOTYPES ===
- int SysFS_Install(char **Arguments);
- int SysFS_IOCtl(tVFS_Node *Node, int Id, void *Data);
-
-#if 0
- int SysFS_RegisterFile(const char *Path, const char *Data, int Length);
- int SysFS_UpdateFile(int ID, const char *Data, int Length);
- int SysFS_RemoveFile(int ID);
-#endif
-
-char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Id);
-tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename);
-Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-void SysFS_Comm_CloseFile(tVFS_Node *Node);
-
-// === GLOBALS ===
-extern tSysFS_Ent gSysFS_Version; // Defined Later
-extern tSysFS_Ent gSysFS_Root; // Defined Later
-MODULE_DEFINE(0, VERSION, SysFS, SysFS_Install, NULL, NULL);
-tVFS_NodeType gSysFS_FileNodeType = {
- .TypeName = "SysFS File",
- .Read = SysFS_Comm_ReadFile
- };
-tVFS_NodeType gSysFS_DirNodeType = {
- .TypeName = "SysFS Dir",
- .ReadDir = SysFS_Comm_ReadDir,
- .FindDir = SysFS_Comm_FindDir
- };
-tSysFS_Ent gSysFS_Version_Kernel = {
- NULL, NULL, // Nexts
- &gSysFS_Version, // Parent
- {
- .Inode = 1, // File #1
- .ImplPtr = NULL,
- .ImplInt = (Uint)&gSysFS_Version_Kernel, // Self-Link
- .Size = 0,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRO,
- .Type = &gSysFS_FileNodeType
- },
- "Kernel"
-};
-tSysFS_Ent gSysFS_Version = {
- NULL, NULL,
- &gSysFS_Root,
- {
- .Size = 1,
- .ImplPtr = &gSysFS_Version_Kernel,
- .ImplInt = (Uint)&gSysFS_Version, // Self-Link
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gSysFS_DirNodeType
- },
- "Version"
-};
-// Root of the SysFS tree (just used to keep the code clean)
-tSysFS_Ent gSysFS_Root = {
- NULL, NULL,
- NULL,
- {
- .Size = 1,
- .ImplPtr = &gSysFS_Version,
- .ImplInt = (Uint)&gSysFS_Root // Self-Link
- },
- "/"
-};
-tDevFS_Driver gSysFS_DriverInfo = {
- NULL, "system",
- {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .ImplPtr = &gSysFS_Version,
- .Size = sizeof(gSysFS_Root)/sizeof(tSysFS_Ent),
- .Type = &gSysFS_DirNodeType
- }
-};
- int giSysFS_NextFileID = 2;
-tSysFS_Ent *gSysFS_FileList;
-
-// === CODE ===
-/**
- * \fn int SysFS_Install(char **Options)
- * \brief Installs the SysFS Driver
- */
-int SysFS_Install(char **Options)
-{
- {
- const char *fmt = "Acess2 "EXPAND_STR(KERNEL_VERSION)" "EXPAND_STR(ARCHDIR)" build %i, hash %s";
- gSysFS_Version_Kernel.Node.Size = sprintf(NULL, fmt, BUILD_NUM, gsGitHash);
- gSysFS_Version_Kernel.Node.ImplPtr = malloc( gSysFS_Version_Kernel.Node.Size + 1 );
- sprintf(gSysFS_Version_Kernel.Node.ImplPtr, fmt, BUILD_NUM, gsGitHash);
- }
-
- DevFS_AddDevice( &gSysFS_DriverInfo );
- return MODULE_ERR_OK;
-}
-
-/**
- * \fn int SysFS_RegisterFile(char *Path, char *Data, int Length)
- * \brief Registers a file (buffer) for the user to be able to read from
- * \param Path Path for the file to be accessable from (relative to SysFS root)
- * \param Data Pointer to the data buffer (must be non-volatile)
- * \param Length Length of the data buffer
- * \return The file's identifier
- */
-int SysFS_RegisterFile(const char *Path, const char *Data, int Length)
-{
- int start = 0;
- int tmp;
- tSysFS_Ent *ent = NULL;
- tSysFS_Ent *child, *prev;
-
- // Find parent directory
- while( (tmp = strpos(&Path[start], '/')) != -1 )
- {
- prev = NULL;
-
- if(ent)
- child = ent->Node.ImplPtr;
- else
- child = gSysFS_DriverInfo.RootNode.ImplPtr;
- for( ; child; prev = child, child = child->Next )
- {
- if( strncmp( &Path[start], child->Name, tmp+1 ) == '/' )
- break;
- }
-
- // Need a new directory?
- if( !child )
- {
- child = calloc( 1, sizeof(tSysFS_Ent)+tmp+1 );
- child->Next = NULL;
- memcpy(child->Name, &Path[start], tmp);
- child->Name[tmp] = '\0';
- child->Parent = ent;
- child->Node.Inode = 0;
- child->Node.ImplPtr = NULL;
- child->Node.ImplInt = (Uint)child; // Uplink
- child->Node.NumACLs = 1;
- child->Node.ACLs = &gVFS_ACL_EveryoneRX;
- child->Node.Flags = VFS_FFLAG_DIRECTORY;
- child->Node.Type = &gSysFS_DirNodeType;
- if( !prev ) {
- if(ent)
- ent->Node.ImplPtr = child;
- else
- gSysFS_DriverInfo.RootNode.ImplPtr = child;
- // ^^^ Impossible (There is already /Version)
- }
- else
- prev->Next = child;
- if(ent)
- ent->Node.Size ++;
- else
- gSysFS_DriverInfo.RootNode.Size ++;
- Log_Log("SysFS", "Added directory '%s'", child->Name);
- }
-
- ent = child;
-
- start = tmp+1;
- }
-
- // ent: Parent tSysFS_Ent or NULL
- // start: beginning of last path element
-
- // Check if the name is taken
- prev = NULL;
- if(ent)
- child = ent->Node.ImplPtr;
- else
- child = gSysFS_DriverInfo.RootNode.ImplPtr;
- for( ; child; child = child->Next )
- {
- if( strcmp( &Path[start], child->Name ) == 0 )
- break;
- }
- if( child ) {
- Log_Warning("SysFS", "'%s' is taken (in '%s')\n", &Path[start], Path);
- return 0;
- }
-
- // Create new node
- child = calloc( 1, sizeof(tSysFS_Ent)+strlen(&Path[start])+1 );
- child->Next = NULL;
- strcpy(child->Name, &Path[start]);
- child->Parent = ent;
-
- child->Node.Inode = giSysFS_NextFileID++;
- child->Node.ImplPtr = (void*)Data;
- child->Node.ImplInt = (Uint)child; // Uplink
- child->Node.Size = Length;
- child->Node.NumACLs = 1;
- child->Node.ACLs = &gVFS_ACL_EveryoneRO;
- child->Node.Type = &gSysFS_FileNodeType;
-
- // Add to parent's child list
- if(ent) {
- ent->Node.Size ++;
- child->Next = ent->Node.ImplPtr;
- ent->Node.ImplPtr = child;
- }
- else {
- gSysFS_DriverInfo.RootNode.Size ++;
- child->Next = gSysFS_DriverInfo.RootNode.ImplPtr;
- gSysFS_DriverInfo.RootNode.ImplPtr = child;
- }
- // Add to global file list
- child->ListNext = gSysFS_FileList;
- gSysFS_FileList = child;
-
- Log_Log("SysFS", "Added '%s' (%p)", Path, Data);
-
- return child->Node.Inode;
-}
-
-/**
- * \fn int SysFS_UpdateFile(int ID, char *Data, int Length)
- * \brief Updates a file
- * \param ID Identifier returned by ::SysFS_RegisterFile
- * \param Data Pointer to the data buffer
- * \param Length Length of the data buffer
- * \return Boolean Success
- */
-int SysFS_UpdateFile(int ID, const char *Data, int Length)
-{
- tSysFS_Ent *ent;
-
- for( ent = gSysFS_FileList; ent; ent = ent->Next )
- {
- // It's a reverse sorted list
- if(ent->Node.Inode < ID) return 0;
- if(ent->Node.Inode == ID)
- {
- ent->Node.ImplPtr = (void*)Data;
- ent->Node.Size = Length;
- return 1;
- }
- }
-
- return 0;
-}
-
-/**
- * \fn int SysFS_RemoveFile(int ID)
- * \brief Removes a file from user access
- * \param ID Identifier returned by ::SysFS_RegisterFile
- * \return Boolean Success
- * \note If a handle is still open to the file, it will be invalidated
- */
-int SysFS_RemoveFile(int ID)
-{
- tSysFS_Ent *file;
- tSysFS_Ent *ent, *parent, *prev;
-
- prev = NULL;
- for( ent = gSysFS_FileList; ent; prev = ent, ent = ent->Next )
- {
- // It's a reverse sorted list
- if(ent->Node.Inode < ID) return 0;
- if(ent->Node.Inode == ID) break;
- }
-
- if(!ent) return 0;
-
- // Set up for next part
- file = ent;
- parent = file->Parent;
-
- // Remove from file list
- if(prev)
- prev->ListNext = file->ListNext;
- else
- gSysFS_FileList = file->ListNext;
- file->Node.Size = 0;
- file->Node.ImplPtr = NULL;
-
- // Search parent directory
- for( ent = parent->Node.ImplPtr; ent; prev = ent, ent = ent->Next )
- {
- if( ent == file ) break;
- }
- if(!ent) {
- Log_Warning("SysFS", "Bookkeeping Error: File in list, but not in directory");
- return 0;
- }
-
- // Remove from parent directory
- if(prev)
- prev->Next = ent->Next;
- else
- parent->Node.ImplPtr = ent->Next;
-
- // Free if not in use
- if(file->Node.ReferenceCount == 0)
- free(file);
-
- return 1;
-}
-
-/**
- * \fn char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos)
- * \brief Reads from a SysFS directory
- */
-char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos)
-{
- tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr;
- if(Pos < 0 || Pos >= Node->Size) return NULL;
-
- for( ; child; child = child->Next, Pos-- )
- {
- if( Pos == 0 ) return strdup(child->Name);
- }
- return NULL;
-}
-
-/**
- * \fn tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename)
- * \brief Find a file in a SysFS directory
- */
-tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename)
-{
- tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr;
-
- for( ; child; child = child->Next )
- {
- if( strcmp(child->Name, Filename) == 0 )
- return &child->Node;
- }
-
- return NULL;
-}
-
-/**
- * \fn Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from an exposed buffer
- */
-Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- if( Offset > Node->Size ) return -1;
- if( Length > Node->Size ) Length = Node->Size;
- if( Offset + Length > Node->Size) Length = Node->Size - Offset;
-
- if( Node->ImplPtr )
- memcpy(Buffer, (void*)((Uint)Node->ImplPtr+(Uint)Offset), Length);
- else
- return -1;
-
- return Length;
-}
-
-/**
- * \fn void SysFS_Comm_CloseFile(tVFS_Node *Node)
- * \brief Closes an open file
- * \param Node Node to close
- */
-void SysFS_Comm_CloseFile(tVFS_Node *Node)
-{
- // Dereference
- Node->ReferenceCount --;
- if( Node->ReferenceCount > 0 ) return;
-
- // Check if it is still valid
- if( Node->ImplPtr ) return;
-
- // Delete
- free( (void*)Node->ImplInt );
-}
+++ /dev/null
-/*
- * Acess2 Virtual Terminal Driver
- */
-#define DEBUG 0
-#include "vterm.h"
-#include <fs_devfs.h>
-#include <modules.h>
-#include <api_drv_keyboard.h>
-#include <api_drv_video.h>
-#include <errno.h>
-#include <semaphore.h>
-
-// === CONSTANTS ===
-#define VERSION ((0<<8)|(50))
-
-#define NUM_VTS 8
-//#define DEFAULT_OUTPUT "BochsGA"
-#define DEFAULT_OUTPUT "Vesa"
-#define FALLBACK_OUTPUT "x86_VGAText"
-#define DEFAULT_INPUT "PS2Keyboard"
-#define DEFAULT_WIDTH 640
-#define DEFAULT_HEIGHT 480
-#define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen
-//#define DEFAULT_SCROLLBACK 0
-
-// === TYPES ===
-
-// === IMPORTS ===
-extern void Debug_SetKTerminal(const char *File);
-
-// === PROTOTYPES ===
- int VT_Install(char **Arguments);
-char *VT_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name);
- int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
-Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
- int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data);
-
-// === CONSTANTS ===
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL);
-tVFS_NodeType gVT_RootNodeType = {
- .TypeName = "VTerm Root",
- .ReadDir = VT_ReadDir,
- .FindDir = VT_FindDir,
- .IOCtl = VT_Root_IOCtl
- };
-tVFS_NodeType gVT_TermNodeType = {
- .TypeName = "VTerm",
- .Read = VT_Read,
- .Write = VT_Write,
- .IOCtl = VT_Terminal_IOCtl
- };
-tDevFS_Driver gVT_DrvInfo = {
- NULL, "VTerm",
- {
- .Flags = VFS_FFLAG_DIRECTORY,
- .Size = NUM_VTS,
- .Inode = -1,
- .NumACLs = 0,
- .Type = &gVT_RootNodeType
- }
-};
-// --- Terminals ---
-tVTerm gVT_Terminals[NUM_VTS];
- int giVT_CurrentTerminal = 0;
-tVTerm *gpVT_CurTerm = &gVT_Terminals[0];
-// --- Video State ---
-short giVT_RealWidth = DEFAULT_WIDTH; //!< Screen Width
-short giVT_RealHeight = DEFAULT_HEIGHT; //!< Screen Height
- int giVT_Scrollback = DEFAULT_SCROLLBACK;
-// --- Driver Handles ---
-char *gsVT_OutputDevice = NULL;
-char *gsVT_InputDevice = NULL;
- int giVT_OutputDevHandle = -2;
- int giVT_InputDevHandle = -2;
-
-// === CODE ===
-/**
- * \fn int VT_Install(char **Arguments)
- * \brief Installs the Virtual Terminal Driver
- */
-int VT_Install(char **Arguments)
-{
- int i;
-
- // Scan Arguments
- if(Arguments)
- {
- char **args;
- const char *arg;
- for(args = Arguments; (arg = *args); args++ )
- {
- char data[strlen(arg)+1];
- char *opt = data;
- char *val;
-
- val = strchr(arg, '=');
- strcpy(data, arg);
- if( val ) {
- data[ val - arg ] = '\0';
- val ++;
- }
- Log_Debug("VTerm", "Argument '%s'", arg);
-
- if( strcmp(opt, "Video") == 0 ) {
- if( !gsVT_OutputDevice )
- gsVT_OutputDevice = strdup(val);
- }
- else if( strcmp(opt, "Input") == 0 ) {
- if( !gsVT_InputDevice )
- gsVT_InputDevice = strdup(val);
- }
- else if( strcmp(opt, "Width") == 0 ) {
- giVT_RealWidth = atoi( val );
- }
- else if( strcmp(opt, "Height") == 0 ) {
- giVT_RealHeight = atoi( val );
- }
- else if( strcmp(opt, "Scrollback") == 0 ) {
- giVT_Scrollback = atoi( val );
- }
- }
- }
-
- // Apply Defaults
- if(!gsVT_OutputDevice) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
- else if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
- if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)FALLBACK_OUTPUT;
- if( Module_EnsureLoaded( gsVT_OutputDevice ) ) {
- Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT);
- return MODULE_ERR_MISC;
- }
-
- if(!gsVT_InputDevice) gsVT_InputDevice = (char*)DEFAULT_INPUT;
- else if( Module_EnsureLoaded( gsVT_InputDevice ) ) gsVT_InputDevice = (char*)DEFAULT_INPUT;
-
- // Create device paths
- {
- char *tmp;
- tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 );
- strcpy(tmp, "/Devices/");
- strcpy(&tmp[9], gsVT_OutputDevice);
- gsVT_OutputDevice = tmp;
-
- tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 );
- strcpy(tmp, "/Devices/");
- strcpy(&tmp[9], gsVT_InputDevice);
- gsVT_InputDevice = tmp;
- }
-
- Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice);
- Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice);
-
- VT_InitOutput();
- VT_InitInput();
-
- // Create Nodes
- for( i = 0; i < NUM_VTS; i++ )
- {
- gVT_Terminals[i].Mode = TERM_MODE_TEXT;
- gVT_Terminals[i].Flags = 0;
-// gVT_Terminals[i].Flags = VT_FLAG_HIDECSR; //HACK - Stop all those memcpy calls
- gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
- gVT_Terminals[i].WritePos = 0;
- gVT_Terminals[i].AltWritePos = 0;
- gVT_Terminals[i].ViewPos = 0;
- gVT_Terminals[i].ReadingThread = -1;
- gVT_Terminals[i].ScrollHeight = 0;
-
- // Initialise
- VT_int_ChangeMode( &gVT_Terminals[i],
- TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight );
-
- gVT_Terminals[i].Name[0] = '0'+i;
- gVT_Terminals[i].Name[1] = '\0';
- gVT_Terminals[i].Node.Inode = i;
- gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i];
- gVT_Terminals[i].Node.NumACLs = 0; // Only root can open virtual terminals
-
- gVT_Terminals[i].Node.Type = &gVT_TermNodeType;
-// Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name);
- }
-
- // Add to DevFS
- DevFS_AddDevice( &gVT_DrvInfo );
-
- // Set kernel output to VT0
- Debug_SetKTerminal("/Devices/VTerm/0");
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Set the video resolution
- * \param Width New screen width
- * \param Height New screen height
- */
-void VT_SetResolution(int Width, int Height)
-{
- tVideo_IOCtl_Mode mode = {0};
- int tmp;
- int i;
-
- // Create the video mode
- mode.width = Width;
- mode.height = Height;
- mode.bpp = 32;
- mode.flags = 0;
-
- // Set video mode
- VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode );
- tmp = mode.id;
- if( Width != mode.width || Height != mode.height )
- {
- Log_Warning("VTerm",
- "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)",
- giVT_RealWidth, giVT_RealHeight,
- mode.width, mode.height
- );
- giVT_RealWidth = mode.width;
- giVT_RealHeight = mode.height;
- }
- VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp );
-
- // Resize text terminals if needed
- if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) )
- {
- int newBufSize = (giVT_RealWidth/giVT_CharWidth)
- *(giVT_RealHeight/giVT_CharHeight)
- *(giVT_Scrollback+1);
- //tVT_Char *tmp;
- // Resize the text terminals
- Log_Debug("VTerm", "Resizing terminals to %ix%i",
- giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight);
- for( i = 0; i < NUM_VTS; i ++ )
- {
- if( gVT_Terminals[i].Mode != TERM_MODE_TEXT ) continue;
-
- gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth;
- gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight;
- gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight;
-
- gVT_Terminals[i].Text = realloc(
- gVT_Terminals[i].Text,
- newBufSize*sizeof(tVT_Char)
- );
- }
- }
-}
-
-/**
- * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
- * \brief Read from the VTerm Directory
- */
-char *VT_ReadDir(tVFS_Node *Node, int Pos)
-{
- if(Pos < 0) return NULL;
- if(Pos >= NUM_VTS) return NULL;
- return strdup( gVT_Terminals[Pos].Name );
-}
-
-/**
- * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
- * \brief Find an item in the VTerm directory
- * \param Node Root node
- * \param Name Name (number) of the terminal
- */
-tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
-{
- int num;
-
- ENTER("pNode sName", Node, Name);
-
- // Open the input and output files if needed
- if(giVT_OutputDevHandle == -2) VT_InitOutput();
- if(giVT_InputDevHandle == -2) VT_InitInput();
-
- // Sanity check name
- if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
- LEAVE('n');
- return NULL;
- }
- // Get index
- num = Name[0] - '0';
- if(num >= NUM_VTS) {
- LEAVE('n');
- return NULL;
- }
- // Return node
- LEAVE('p', &gVT_Terminals[num].Node);
- return &gVT_Terminals[num].Node;
-}
-
-/**
- * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief Control the VTerm Driver
- */
-int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
- int len;
- switch(Id)
- {
- case DRV_IOCTL_TYPE: return DRV_TYPE_MISC;
- case DRV_IOCTL_IDENT: memcpy(Data, "VT\0\0", 4); return 0;
- case DRV_IOCTL_VERSION: return VERSION;
- case DRV_IOCTL_LOOKUP: return 0;
-
- case 4: // Get Video Driver
- if(Data) strcpy(Data, gsVT_OutputDevice);
- return strlen(gsVT_OutputDevice);
-
- case 5: // Set Video Driver
- if(!Data) return -EINVAL;
- if(Threads_GetUID() != 0) return -EACCES;
-
- len = strlen(Data);
-
- // TODO: Check if the string used is a heap string
-
- free(gsVT_OutputDevice);
-
- gsVT_OutputDevice = malloc(len+1);
- strcpy(gsVT_OutputDevice, Data);
-
- VFS_Close(giVT_OutputDevHandle);
- giVT_OutputDevHandle = -1;
-
- VT_InitOutput();
- return 1;
- }
- return 0;
-}
-
-/**
- * \brief Read from a virtual terminal
- */
-Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- int pos = 0;
- int avail;
- tVTerm *term = &gVT_Terminals[ Node->Inode ];
- Uint32 *codepoint_buf = Buffer;
- Uint32 *codepoint_in;
-
- Mutex_Acquire( &term->ReadingLock );
-
- // Check current mode
- switch(term->Mode)
- {
- // Text Mode (UTF-8)
- case TERM_MODE_TEXT:
- VT_int_UpdateCursor(term, 1);
-
- VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)");
-
- avail = term->InputWrite - term->InputRead;
- if(avail < 0)
- avail += MAX_INPUT_CHARS8;
- if(avail > Length - pos)
- avail = Length - pos;
-
- while( avail -- )
- {
- ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead];
- pos ++;
- term->InputRead ++;
- while(term->InputRead >= MAX_INPUT_CHARS8)
- term->InputRead -= MAX_INPUT_CHARS8;
- }
- break;
-
- //case TERM_MODE_FB:
- // Other - UCS-4
- default:
- VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)");
-
- avail = term->InputWrite - term->InputRead;
- if(avail < 0)
- avail += MAX_INPUT_CHARS32;
- Length /= 4;
- if(avail > Length - pos)
- avail = Length - pos;
-
- codepoint_in = (void*)term->InputBuffer;
- codepoint_buf = Buffer;
-
- while( avail -- )
- {
- codepoint_buf[pos] = codepoint_in[term->InputRead];
- pos ++;
- term->InputRead ++;
- while(term->InputRead >= MAX_INPUT_CHARS32)
- term->InputRead -= MAX_INPUT_CHARS32;
- }
- pos *= 4;
- break;
- }
-
- // Mark none avaliable if buffer empty
- if( term->InputRead == term->InputWrite )
- VFS_MarkAvaliable(&term->Node, 0);
-
- term->ReadingThread = -1;
-
-// VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT);
-
- Mutex_Release( &term->ReadingLock );
-
- return pos;
-}
-
-/**
- * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
- * \brief Write to a virtual terminal
- */
-Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tVTerm *term = &gVT_Terminals[ Node->Inode ];
- int size;
-
- // Write
- switch( term->Mode )
- {
- // Print Text
- case TERM_MODE_TEXT:
- VT_int_PutString(term, Buffer, Length);
- break;
-
- // Framebuffer :)
- case TERM_MODE_FB:
- // - Sanity Checking
- size = term->Width*term->Height*4;
- if( Offset > size ) {
- Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)",
- Offset, size);
- return 0;
- }
- if( Offset + Length > size ) {
- Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)",
- Offset+Length, size);
- Length = size - Offset;
- }
-
- // Update screen if needed
- if( Node->Inode == giVT_CurrentTerminal )
- {
- if( giVT_RealHeight > term->Height )
- Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4;
- // Handle undersized virtual terminals
- if( giVT_RealWidth > term->Width )
- {
- // No? :( Well, just center it
- int x, y, w, h;
- Uint dst_ofs;
- // TODO: Fix to handle the final line correctly?
- x = Offset/4; y = x / term->Width; x %= term->Width;
- w = Length/4+x; h = w / term->Width; w %= term->Width;
-
- // Center
- x += (giVT_RealWidth - term->Width) / 2;
- dst_ofs = (x + y * giVT_RealWidth) * 4;
- while(h--)
- {
- VFS_WriteAt( giVT_OutputDevHandle,
- dst_ofs,
- term->Width * 4,
- Buffer
- );
- Buffer = (void*)( (Uint)Buffer + term->Width*4 );
- dst_ofs += giVT_RealWidth * 4;
- }
- return 0;
- }
- else
- {
- return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer );
- }
- }
- else
- {
- if( !term->Buffer )
- term->Buffer = malloc( term->Width * term->Height * 4 );
- // Copy to the local cache
- memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length );
- }
- break;
- // Just pass on (for now)
- // TODO: Handle locally too to ensure no information is lost on
- // VT Switch (and to isolate terminals from each other)
- case TERM_MODE_2DACCEL:
- //case TERM_MODE_3DACCEL:
- if( Node->Inode == giVT_CurrentTerminal )
- {
- VFS_Write( giVT_OutputDevHandle, Length, Buffer );
- }
- break;
- }
-
- return 0;
-}
-
-/**
- * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief Call an IO Control on a virtual terminal
- */
-int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
- int *iData = Data;
- int ret;
- tVTerm *term = Node->ImplPtr;
- ENTER("pNode iId pData", Node, Id, Data);
-
- if(Id >= DRV_IOCTL_LOOKUP) {
- // Only root can fiddle with graphics modes
- // TODO: Remove this and replace with user ownership
- if( Threads_GetUID() != 0 ) return -1;
- }
-
- switch(Id)
- {
- // --- Core Defined
- case DRV_IOCTL_TYPE:
- LEAVE('i', DRV_TYPE_TERMINAL);
- return DRV_TYPE_TERMINAL;
- case DRV_IOCTL_IDENT:
- memcpy(Data, "VT\0\0", 4);
- LEAVE('i', 0);
- return 0;
- case DRV_IOCTL_VERSION:
- LEAVE('x', VERSION);
- return VERSION;
- case DRV_IOCTL_LOOKUP:
- LEAVE('i', 0);
- return 0;
-
- // Get/Set the mode (and apply any changes)
- case TERM_IOCTL_MODETYPE:
- if(Data != NULL)
- {
- if( CheckMem(Data, sizeof(int)) == 0 ) {
- LEAVE('i', -1);
- return -1;
- }
- Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData);
-
- // Update mode if needed
- if( term->Mode != *iData || term->NewWidth || term->NewHeight)
- {
- // Adjust for text mode
- if( *iData == TERM_MODE_TEXT ) {
- term->NewHeight *= giVT_CharHeight;
- term->NewWidth *= giVT_CharWidth;
- }
- // Fill unchanged dimensions
- if(term->NewHeight == 0) term->NewHeight = term->Height;
- if(term->NewWidth == 0) term->NewWidth = term->Width;
- // Set new mode
- VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight);
- // Clear unapplied dimensions
- term->NewWidth = 0;
- term->NewHeight = 0;
- }
-
- // Update the screen dimensions
- if(Node->Inode == giVT_CurrentTerminal)
- VT_SetTerminal( giVT_CurrentTerminal );
- }
- LEAVE('i', term->Mode);
- return term->Mode;
-
- // Get/set the terminal width
- case TERM_IOCTL_WIDTH:
- if(Data != NULL) {
- if( CheckMem(Data, sizeof(int)) == 0 ) {
- LEAVE('i', -1);
- return -1;
- }
- term->NewWidth = *iData;
- }
- if( term->NewWidth )
- ret = term->NewWidth;
- else if( term->Mode == TERM_MODE_TEXT )
- ret = term->TextWidth;
- else
- ret = term->Width;
- LEAVE('i', ret);
- return ret;
-
- // Get/set the terminal height
- case TERM_IOCTL_HEIGHT:
- if(Data != NULL) {
- if( CheckMem(Data, sizeof(int)) == 0 ) {
- LEAVE('i', -1);
- return -1;
- }
- term->NewHeight = *iData;
- }
- if( term->NewHeight )
- ret = term->NewHeight;
- else if( term->Mode == TERM_MODE_TEXT )
- ret = term->TextHeight;
- else
- ret = term->Height;
- LEAVE('i', ret);
- return ret;
-
- case TERM_IOCTL_FORCESHOW:
- Log_Log("VTerm", "Thread %i forced VTerm %i to be shown",
- Threads_GetTID(), (int)Node->Inode);
- VT_SetTerminal( Node->Inode );
- LEAVE('i', 1);
- return 1;
-
- case TERM_IOCTL_GETSETCURSOR:
- if(Data != NULL)
- {
- tVideo_IOCtl_Pos *pos = Data;
- if( !CheckMem(Data, sizeof(*pos)) ) {
- errno = -EINVAL;
- LEAVE('i', -1);
- return -1;
- }
-
- if( term->Mode == TERM_MODE_TEXT )
- {
- if(term->Flags & VT_FLAG_ALTBUF)
- term->AltWritePos = pos->x + pos->y * term->TextWidth;
- else
- term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos;
- VT_int_UpdateCursor(term, 0);
- }
- else
- {
- term->VideoCursorX = pos->x;
- term->VideoCursorY = pos->y;
- VT_int_UpdateCursor(term, 1);
- }
- }
- ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos;
- LEAVE('i', ret);
- return ret;
-
- case TERM_IOCTL_SETCURSORBITMAP: {
- tVideo_IOCtl_Bitmap *bmp = Data;
- if( Data == NULL )
- {
- free( term->VideoCursor );
- term->VideoCursor = NULL;
- LEAVE('i', 0);
- return 0;
- }
-
- // Sanity check bitmap
- if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) {
- Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
- errno = -EINVAL;
- LEAVE_RET('i', -1);
- }
- if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) {
- Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
- errno = -EINVAL;
- LEAVE_RET('i', -1);
- }
-
- // Reallocate if needed
- if(term->VideoCursor)
- {
- if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) {
- free(term->VideoCursor);
- term->VideoCursor = NULL;
- }
- }
- if(!term->VideoCursor) {
- term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
- if(!term->VideoCursor) {
- Log_Error("VTerm", "Unable to allocate memory for cursor");
- errno = -ENOMEM;
- LEAVE_RET('i', -1);
- }
- }
-
- memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
-
- Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i",
- (int)term->Node.Inode, bmp, bmp->W, bmp->H);
-
- if(gpVT_CurTerm == term)
- VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor);
-
- LEAVE('i', 0);
- return 0; }
- }
- LEAVE('i', -1);
- return -1;
-}
-
-/**
- * \fn void VT_SetTerminal(int ID)
- * \brief Set the current terminal
- */
-void VT_SetTerminal(int ID)
-{
- // Copy the screen state
- if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT )
- {
- if( !gpVT_CurTerm->Buffer )
- gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 );
- if( gpVT_CurTerm->Width < giVT_RealWidth )
- {
- int line;
- Uint ofs = 0;
- Uint32 *dest = gpVT_CurTerm->Buffer;
- // Slower scanline copy
- for( line = 0; line < gpVT_CurTerm->Height; line ++ )
- {
- VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest);
- ofs += giVT_RealWidth * 4;
- dest += gpVT_CurTerm->Width;
- }
- }
- else
- {
- VFS_ReadAt(giVT_OutputDevHandle,
- 0, gpVT_CurTerm->Height*giVT_RealWidth*4,
- gpVT_CurTerm->Buffer
- );
- }
- }
-
- // Update current terminal ID
- Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID);
- giVT_CurrentTerminal = ID;
- gpVT_CurTerm = &gVT_Terminals[ID];
-
- if( gpVT_CurTerm->Mode == TERM_MODE_TEXT )
- {
- VT_SetMode( VIDEO_BUFFMT_TEXT );
- }
- else
- {
- // Update the cursor image
- if(gpVT_CurTerm->VideoCursor)
- VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor);
- VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER );
- }
-
- if(gpVT_CurTerm->Buffer)
- {
- // TODO: Handle non equal sized
- VFS_WriteAt(
- giVT_OutputDevHandle,
- 0,
- gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32),
- gpVT_CurTerm->Buffer
- );
- }
-
- VT_int_UpdateCursor(gpVT_CurTerm, 1);
- // Update the screen
- VT_int_UpdateScreen(gpVT_CurTerm, 1);
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv/vterm.h
- * - Virtual Terminal - Common
- */
-#ifndef _VTERM_H_
-#define _VTERM_H_
-
-#include <acess.h>
-#include <api_drv_video.h> // tVT_Char
-#include <api_drv_terminal.h>
-#include <vfs.h>
-
-// === CONSTANTS ===
-#define MAX_INPUT_CHARS32 64
-#define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4)
-#define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16))
-
-/**
- * \{
- */
-#define VT_FLAG_HIDECSR 0x01 //!< Hide the cursor
-#define VT_FLAG_ALTBUF 0x02 //!< Alternate screen buffer
-#define VT_FLAG_RAWIN 0x04 //!< Don't handle ^Z/^C/^V
-#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer
-#define VT_FLAG_SHOWCSR 0x20 //!< Always show the text cursor
-/**
- * \}
- */
-
-enum eVT_InModes {
- VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100/xterm Emulation)
- VT_INMODE_TEXT32, // UTF-32 Text Mode (Acess Native)
- NUM_VT_INMODES
-};
-
-
-// === TYPES ==
-typedef struct sVTerm tVTerm;
-
-// === STRUCTURES ===
-struct sVTerm
-{
- int Mode; //!< Current Mode (see ::eTplTerminal_Modes)
- int Flags; //!< Flags (see VT_FLAG_*)
-
- short NewWidth; //!< Un-applied dimensions (Width)
- short NewHeight; //!< Un-applied dimensions (Height)
- short Width; //!< Virtual Width
- short Height; //!< Virtual Height
- short TextWidth; //!< Text Virtual Width
- short TextHeight; //!< Text Virtual Height
-
- Uint32 CurColour; //!< Current Text Colour
-
- int ViewPos; //!< View Buffer Offset (Text Only)
- int WritePos; //!< Write Buffer Offset (Text Only)
- tVT_Char *Text;
-
- tVT_Char *AltBuf; //!< Alternate Screen Buffer
- int AltWritePos; //!< Alternate write position
- short ScrollTop; //!< Top of scrolling region (smallest)
- short ScrollHeight; //!< Length of scrolling region
-
- int VideoCursorX;
- int VideoCursorY;
-
- tMutex ReadingLock; //!< Lock the VTerm when a process is reading from it
- tTID ReadingThread; //!< Owner of the lock
- int InputRead; //!< Input buffer read position
- int InputWrite; //!< Input buffer write position
- char InputBuffer[MAX_INPUT_CHARS8];
-// tSemaphore InputSemaphore;
-
- Uint32 *Buffer;
-
- // TODO: Do I need to keep this about?
- // When should it be deallocated? on move to text mode, or some other time
- // Call set again, it's freed, and if NULL it doesn't get reallocated.
- tVideo_IOCtl_Bitmap *VideoCursor;
-
- char Name[2]; //!< Name of the terminal
- tVFS_Node Node;
-};
-
-// === GOBALS ===
-extern tVTerm *gpVT_CurTerm;
-extern int giVT_Scrollback;
-extern short giVT_RealWidth; //!< Screen Width
-extern short giVT_RealHeight; //!< Screen Width
-extern char *gsVT_OutputDevice;
-extern char *gsVT_InputDevice;
-extern int giVT_OutputDevHandle;
-extern int giVT_InputDevHandle;
-
-// === FUNCTIONS ===
-extern void VT_SetResolution(int Width, int Height);
-extern void VT_SetTerminal(int ID);
-// --- Output ---
-extern void VT_InitOutput(void);
-extern void VT_SetMode(int Mode);
-extern void VT_int_ScrollFramebuffer( tVTerm *Term, int Count );
-extern void VT_int_UpdateCursor( tVTerm *Term, int bShow );
-extern void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll );
-// --- Input ---
-extern void VT_InitInput(void);
-extern void VT_KBCallBack(Uint32 Codepoint);
-// --- VT100 Emulation ---
-extern void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args);
-extern int VT_int_ParseEscape(tVTerm *Term, const char *Buffer);
-// --- Terminal Buffer ---
-extern void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count);
-extern void VT_int_PutChar(tVTerm *Term, Uint32 Ch);
-extern void VT_int_ScrollText(tVTerm *Term, int Count);
-extern void VT_int_ClearLine(tVTerm *Term, int Num);
-extern void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight);
-extern void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv/vterm_font.c
- * - Virtual Terminal - Font rendering code
- */
-#include "vterm.h"
-#include <api_drv_terminal.h>
-
-// ---
-// Font Render
-// ---
-#define MONOSPACE_FONT 10816
-
-#if MONOSPACE_FONT == 10808 // 8x8
-# include "vterm_font_8x8.h"
-#elif MONOSPACE_FONT == 10816 // 8x16
-# include "vterm_font_8x16.h"
-#endif
-
-// === PROTOTYPES ===
-Uint8 *VT_Font_GetChar(Uint32 Codepoint);
-
-// === GLOBALS ===
-int giVT_CharWidth = FONT_WIDTH;
-int giVT_CharHeight = FONT_HEIGHT;
-
-// === CODE ===
-/**
- * \brief Render a font character
- */
-void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC)
-{
- Uint8 *font;
- int x, y;
-
- // 8-bpp and below
- if( Depth <= 8 )
- {
- Uint8 *buf = Buffer;
-
- font = VT_Font_GetChar(Codepoint);
-
- for(y = 0; y < FONT_HEIGHT; y ++)
- {
- for(x = 0; x < FONT_WIDTH; x ++)
- {
- if(*font & (1 << (FONT_WIDTH-x-1)))
- buf[x] = FGC;
- else
- buf[x] = BGC;
- }
- buf = (void*)( (tVAddr)buf + Pitch );
- font ++;
- }
- }
- // 16-bpp and below
- else if( Depth <= 16 )
- {
- Uint16 *buf = Buffer;
-
- font = VT_Font_GetChar(Codepoint);
-
- for(y = 0; y < FONT_HEIGHT; y ++)
- {
- for(x = 0; x < FONT_WIDTH; x ++)
- {
- if(*font & (1 << (FONT_WIDTH-x-1)))
- buf[x] = FGC;
- else
- buf[x] = BGC;
- }
- buf = (void*)( (tVAddr)buf + Pitch );
- font ++;
- }
- }
- // 24-bpp colour
- // - Special handling to not overwrite the next pixel
- //TODO: Endian issues here
- else if( Depth == 24 )
- {
- Uint8 *buf = Buffer;
- Uint8 bg_r = (BGC >> 16) & 0xFF;
- Uint8 bg_g = (BGC >> 8) & 0xFF;
- Uint8 bg_b = (BGC >> 0) & 0xFF;
- Uint8 fg_r = (FGC >> 16) & 0xFF;
- Uint8 fg_g = (FGC >> 8) & 0xFF;
- Uint8 fg_b = (FGC >> 0) & 0xFF;
-
- font = VT_Font_GetChar(Codepoint);
-
- for(y = 0; y < FONT_HEIGHT; y ++)
- {
- for(x = 0; x < FONT_WIDTH; x ++)
- {
- Uint8 r, g, b;
-
- if(*font & (1 << (FONT_WIDTH-x-1))) {
- r = fg_r; g = fg_g; b = fg_b;
- }
- else {
- r = bg_r; g = bg_g; b = bg_b;
- }
- buf[x*3+0] = b;
- buf[x*3+1] = g;
- buf[x*3+2] = r;
- }
- buf = (void*)( (tVAddr)buf + Pitch );
- font ++;
- }
- }
- // 32-bpp colour (nice and easy)
- else if( Depth == 32 )
- {
- Uint32 *buf = Buffer;
-
- font = VT_Font_GetChar(Codepoint);
-
- for(y = 0; y < FONT_HEIGHT; y ++)
- {
- for(x = 0; x < FONT_WIDTH; x ++)
- {
- if(*font & (1 << (FONT_WIDTH-x-1)))
- buf[x] = FGC;
- else
- buf[x] = BGC;
- }
- buf = (Uint32*)( (tVAddr)buf + Pitch );
- font ++;
- }
- }
-}
-
-/**
- * \fn Uint32 VT_Colour12to24(Uint16 Col12)
- * \brief Converts a 12-bit colour into 24 bits
- */
-Uint32 VT_Colour12to24(Uint16 Col12)
-{
- Uint32 ret;
- int tmp;
- tmp = Col12 & 0xF;
- ret = (tmp << 0) | (tmp << 4);
- tmp = (Col12 & 0xF0) >> 4;
- ret |= (tmp << 8) | (tmp << 12);
- tmp = (Col12 & 0xF00) >> 8;
- ret |= (tmp << 16) | (tmp << 20);
- return ret;
-}
-/**
- * \brief Converts a 12-bit colour into 15 bits
- */
-Uint16 VT_Colour12to15(Uint16 Col12)
-{
- Uint32 ret;
- int tmp;
- tmp = Col12 & 0xF;
- ret = (tmp << 1) | (tmp & 1);
- tmp = (Col12 & 0xF0) >> 4;
- ret |= ( (tmp << 1) | (tmp & 1) ) << 5;
- tmp = (Col12 & 0xF00) >> 8;
- ret |= ( (tmp << 1) | (tmp & 1) ) << 10;
- return ret;
-}
-
-/**
- * \brief Converts a 12-bit colour into any other depth
- * \param Col12 12-bit source colour
- * \param Depth Desired bit deptj
- * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5)
- */
-Uint32 VT_Colour12toN(Uint16 Col12, int Depth)
-{
- Uint32 ret;
- Uint32 r, g, b;
- int rSize, gSize, bSize;
-
- // Fast returns
- if( Depth == 24 ) return VT_Colour12to24(Col12);
- if( Depth == 15 ) return VT_Colour12to15(Col12);
- // - 32 is a special case, it's usually 24-bit colour with an unused byte
- if( Depth == 32 ) return VT_Colour12to24(Col12);
-
- // Bounds checks
- if( Depth < 8 ) return 0;
- if( Depth > 32 ) return 0;
-
- r = Col12 & 0xF;
- g = (Col12 & 0xF0) >> 4;
- b = (Col12 & 0xF00) >> 8;
-
- rSize = gSize = bSize = Depth / 3;
- if( rSize + gSize + bSize < Depth ) // Depth % 3 == 1
- gSize ++;
- if( rSize + gSize + bSize < Depth ) // Depth % 3 == 2
- bSize ++;
-
- // Expand
- r <<= rSize - 4; g <<= gSize - 4; b <<= bSize - 4;
- // Fill with the lowest bit
- if( Col12 & 0x001 ) r |= (1 << (rSize - 4)) - 1;
- if( Col12 & 0x010 ) r |= (1 << (gSize - 4)) - 1;
- if( Col12 & 0x100 ) r |= (1 << (bSize - 4)) - 1;
-
- // Create output
- ret = r;
- ret |= g << rSize;
- ret |= b << (rSize + gSize);
-
- return ret;
-}
-
-/**
- * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint)
- * \brief Gets an index into the font array given a Unicode Codepoint
- * \note See http://en.wikipedia.org/wiki/CP437
- */
-Uint8 *VT_Font_GetChar(Uint32 Codepoint)
-{
- int index = 0;
- if(Codepoint < 128)
- return &VTermFont[Codepoint*FONT_HEIGHT];
- switch(Codepoint)
- {
- case 0xC7: index = 128; break; // Ç
- case 0xFC: index = 129; break; // ü
- case 0xE9: index = 130; break; // é
- case 0xE2: index = 131; break; // â
- case 0xE4: index = 132; break; // ä
- case 0xE0: index = 133; break; // Ã
- case 0xE5: index = 134; break; // å
- case 0xE7: index = 135; break; // ç
- case 0xEA: index = 136; break; // ê
- case 0xEB: index = 137; break; // ë
- case 0xE8: index = 138; break; // è
- case 0xEF: index = 139; break; // ï
- case 0xEE: index = 140; break; // î
- case 0xEC: index = 141; break; // ì
- case 0xC4: index = 142; break; // Ä
- case 0xC5: index = 143; break; // Ã…
- }
-
- return &VTermFont[index*FONT_HEIGHT];
-}
-
-EXPORTAS(&giVT_CharWidth, giVT_CharWidth);
-EXPORTAS(&giVT_CharHeight, giVT_CharHeight);
-EXPORT(VT_Font_Render);
-EXPORT(VT_Colour12to24);
+++ /dev/null
-/*
- * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup
- * Altered for Acess2
- */
-#define FONT_WIDTH 8
-#define FONT_HEIGHT 16
-static Uint8 VTermFont[256*16]=
-{
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
- 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
- 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
- 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
- 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
- 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
- 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
- 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
- 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
- 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
- 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
- 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
- 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
+++ /dev/null
-/*
- * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup
- * Altered for Acess2
- */
-#define FONT_WIDTH 8
-#define FONT_HEIGHT 8
-static Uint8 VTermFont[256*8]=
-{
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
- 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
- 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
- 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
- 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
- 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
- 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
- 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
- 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
- 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
- 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
- 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
- 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
- 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
- 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
- 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
- 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
- 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
- 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
- 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
- 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
- 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
- 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
- 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
- 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
- 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
- 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
- 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
- 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
- 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
- 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
- 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
- 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
- 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
- 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
- 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
- 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
- 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
- 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
- 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
- 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
- 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
- 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
- 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
- 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
- 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
- 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
- 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
- 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
- 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
- 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
- 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
- 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
- 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
- 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
- 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
- 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
- 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
- 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
- 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
- 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
- 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
- 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
- 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
- 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
- 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
- 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
- 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
- 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
- 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
- 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
- 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
- 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
- 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
- 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
- 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
- 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
- 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
- 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
- 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
- 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
- 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
- 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
- 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
- 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
- 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
- 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
- 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
- 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
- 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
- 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
- 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
- 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
- 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
- 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
- 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
- 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
- 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
- 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
- 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
- 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
- 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
- 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
- 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
- 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
- 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
- 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
- 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
- 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
- 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
- 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
- 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78,
- 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
- 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
- 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00,
- 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
- 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
- 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
- 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38,
- 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
- 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
- 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
- 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
- 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
- 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00,
- 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00,
- 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00,
- 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00,
- 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
- 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
- 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
- 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
- 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
- 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
- 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00,
- 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
- 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18,
- 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00,
- 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30,
- 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7,
- 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70,
- 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
- 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
- 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
- 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
- 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00,
- 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00,
- 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00,
- 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00,
- 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00,
- 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00,
- 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f,
- 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03,
- 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
- 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00,
- 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00,
- 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
- 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
- 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
- 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
- 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00,
- 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
- 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36,
- 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
- 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36,
- 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
- 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00,
- 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
- 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36,
- 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00,
- 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0,
- 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00,
- 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
- 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00,
- 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00,
- 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0,
- 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00,
- 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
- 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00,
- 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00,
- 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00,
- 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00,
- 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0,
- 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00,
- 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
- 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00,
- 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00,
- 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00,
- 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00,
- 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70,
- 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
- 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00,
- 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c,
- 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
- 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv/vterm_input.c
- * - Virtual Terminal - Input code
- */
-#include "vterm.h"
-#include <api_drv_keyboard.h>
-
-// === GLOBALS ===
-// --- Key States --- (Used for VT Switching/Magic Combos)
- int gbVT_CtrlDown = 0;
- int gbVT_AltDown = 0;
- int gbVT_SysrqDown = 0;
-
-// === CODE ===
-/**
- * \fn void VT_InitInput()
- * \brief Initialises the input
- */
-void VT_InitInput()
-{
- giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ);
- if(giVT_InputDevHandle == -1) {
- Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice);
- return ;
- }
- VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack);
-}
-
-/**
- * \fn void VT_KBCallBack(Uint32 Codepoint)
- * \brief Called on keyboard interrupt
- * \param Codepoint Pseudo-UTF32 character
- *
- * Handles a key press and sends the key code to the user's buffer.
- * If the code creates a kernel-magic sequence, it is not passed to the
- * user and is handled in-kernel.
- */
-void VT_KBCallBack(Uint32 Codepoint)
-{
- tVTerm *term = gpVT_CurTerm;
-
- // Catch VT binds
- switch( Codepoint & KEY_ACTION_MASK )
- {
- case KEY_ACTION_RELEASE:
- switch(Codepoint & KEY_CODEPOINT_MASK)
- {
- case KEY_LALT: gbVT_AltDown &= ~1; break;
- case KEY_RALT: gbVT_AltDown &= ~2; break;
- case KEY_LCTRL: gbVT_CtrlDown &= ~1; break;
- case KEY_RCTRL: gbVT_CtrlDown &= ~2; break;
- }
- break;
-
- case KEY_ACTION_PRESS:
- switch(Codepoint & KEY_CODEPOINT_MASK)
- {
- case KEY_LALT: gbVT_AltDown |= 1; break;
- case KEY_RALT: gbVT_AltDown |= 2; break;
- case KEY_LCTRL: gbVT_CtrlDown |= 1; break;
- case KEY_RCTRL: gbVT_CtrlDown |= 2; break;
- }
-
- if(!gbVT_AltDown || !gbVT_CtrlDown)
- break;
- switch(Codepoint & KEY_CODEPOINT_MASK)
- {
- case KEY_F1: VT_SetTerminal(0); return;
- case KEY_F2: VT_SetTerminal(1); return;
- case KEY_F3: VT_SetTerminal(2); return;
- case KEY_F4: VT_SetTerminal(3); return;
- case KEY_F5: VT_SetTerminal(4); return;
- case KEY_F6: VT_SetTerminal(5); return;
- case KEY_F7: VT_SetTerminal(6); return;
- case KEY_F8: VT_SetTerminal(7); return;
- case KEY_F9: VT_SetTerminal(8); return;
- case KEY_F10: VT_SetTerminal(9); return;
- case KEY_F11: VT_SetTerminal(10); return;
- case KEY_F12: VT_SetTerminal(11); return;
- }
-
- // Scrolling is only valid in text mode
- if(gpVT_CurTerm->Mode != TERM_MODE_TEXT)
- break;
-
- switch(Codepoint & KEY_CODEPOINT_MASK)
- {
- // Scrolling
- case KEY_PGUP:
- if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF )
- return ;
- gpVT_CurTerm->ViewPos = MAX(
- 0,
- gpVT_CurTerm->ViewPos - gpVT_CurTerm->Width
- );
- return;
- case KEY_PGDOWN:
- if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF )
- return ;
- gpVT_CurTerm->ViewPos = MIN(
- gpVT_CurTerm->ViewPos + gpVT_CurTerm->Width,
- gpVT_CurTerm->Width * gpVT_CurTerm->Height*giVT_Scrollback
- );
- return;
- }
- break;
- }
-
- // Encode key
- if(term->Mode == TERM_MODE_TEXT)
- {
- Uint8 buf[6] = {0};
- int len = 0;
-
- // Ignore anything that isn't a press or refire
- if( (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_PRESS
- && (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_REFIRE
- )
- {
- return ;
- }
-
- Codepoint &= KEY_CODEPOINT_MASK;
-
- // Ignore Modifer Keys
- if(Codepoint > KEY_MODIFIERS) return;
-
- // Get UTF-8/ANSI Encoding
- switch(Codepoint)
- {
- // 0: No translation, don't send to user
- case 0: break;
- case KEY_LEFT:
- buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D';
- len = 3;
- break;
- case KEY_RIGHT:
- buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'C';
- len = 3;
- break;
- case KEY_UP:
- buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'A';
- len = 3;
- break;
- case KEY_DOWN:
- buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'B';
- len = 3;
- break;
-
- case KEY_PGUP:
- buf[0] = '\x1B'; buf[1] = '['; buf[2] = '5'; buf[3] = '~';
- len = 4;
- break;
- case KEY_PGDOWN:
- buf[0] = '\x1B'; buf[1] = '['; buf[2] = '6'; buf[3] = '~';
- len = 4;
- break;
-
- // Attempt to encode in UTF-8
- default:
- len = WriteUTF8( buf, Codepoint );
- if(len == 0) {
- Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint);
- }
- break;
- }
-
- if(len == 0) {
- // Unprintable / Don't Pass
- return;
- }
-
-#if 0
- // Handle meta characters
- if( !(term->Flags & VT_FLAG_RAWIN) )
- {
- switch(buf[0])
- {
- case '\3': // ^C
-
- break;
- }
- }
-#endif
-
- // Write
- if( MAX_INPUT_CHARS8 - term->InputWrite >= len )
- memcpy( &term->InputBuffer[term->InputWrite], buf, len );
- else {
- memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite );
- memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) );
- }
- // Roll the buffer over
- term->InputWrite += len;
- term->InputWrite %= MAX_INPUT_CHARS8;
- if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) {
- term->InputRead = term->InputWrite + 1;
- term->InputRead %= MAX_INPUT_CHARS8;
- }
- }
- else
- {
- // Encode the raw key event
- Uint32 *raw_in = (void*)term->InputBuffer;
-
- #if 0
- // Drop new keys
- if( term->InputWrite == term->InputRead )
- return ;
- #endif
-
- raw_in[ term->InputWrite ] = Codepoint;
- term->InputWrite ++;
- if(term->InputWrite >= MAX_INPUT_CHARS32)
- term->InputWrite -= MAX_INPUT_CHARS32;
-
- #if 1
- // TODO: Should old or new be dropped?
- if(term->InputRead == term->InputWrite) {
- term->InputRead ++;
- if( term->InputRead >= MAX_INPUT_CHARS32 )
- term->InputRead -= MAX_INPUT_CHARS32;
- }
- #endif
- }
-
- VFS_MarkAvaliable(&term->Node, 1);
-}
-
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv/vterm_input.c
- * - Virtual Terminal - Input code
- */
-#include "vterm.h"
-#include <api_drv_video.h>
-
-// === CODE ===
-/**
- * \fn void VT_InitOutput()
- * \brief Initialise Video Output
- */
-void VT_InitOutput()
-{
- giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE);
- if(giVT_OutputDevHandle == -1) {
- Log_Warning("VTerm", "Oh F**k, I can't open the video device '%s'", gsVT_OutputDevice);
- return ;
- }
- VT_SetResolution( giVT_RealWidth, giVT_RealHeight );
- VT_SetTerminal( 0 );
- VT_SetMode( VIDEO_BUFFMT_TEXT );
-}
-
-/**
- * \brief Set video output buffer mode
- */
-void VT_SetMode(int Mode)
-{
- VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode );
-}
-
-/**
- * \fn void VT_int_ScrollFramebuffer( tVTerm *Term, int Count )
- * \note Scrolls the framebuffer down by \a Count text lines
- */
-void VT_int_ScrollFramebuffer( tVTerm *Term, int Count )
-{
- int tmp;
- struct {
- Uint8 Op;
- Uint16 DstX, DstY;
- Uint16 SrcX, SrcY;
- Uint16 W, H;
- } PACKED buf;
-
- // Only update if this is the current terminal
- if( Term != gpVT_CurTerm ) return;
-
- if( Count > Term->ScrollHeight ) Count = Term->ScrollHeight;
- if( Count < -Term->ScrollHeight ) Count = -Term->ScrollHeight;
-
- // Switch to 2D Command Stream
- tmp = VIDEO_BUFFMT_2DSTREAM;
- VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
-
- // BLIT to 0,0 from 0,giVT_CharHeight
- buf.Op = VIDEO_2DOP_BLIT;
- buf.SrcX = 0; buf.DstX = 0;
- // TODO: Don't assume character dimensions
- buf.W = Term->TextWidth * giVT_CharWidth;
- if( Count > 0 )
- {
- buf.SrcY = (Term->ScrollTop+Count) * giVT_CharHeight;
- buf.DstY = Term->ScrollTop * giVT_CharHeight;
- }
- else // Scroll up, move text down
- {
- Count = -Count;
- buf.SrcY = Term->ScrollTop * giVT_CharHeight;
- buf.DstY = (Term->ScrollTop+Count) * giVT_CharHeight;
- }
- buf.H = (Term->ScrollHeight-Count) * giVT_CharHeight;
- VFS_WriteAt(giVT_OutputDevHandle, 0, sizeof(buf), &buf);
-
- // Restore old mode (this function is only called during text mode)
- tmp = VIDEO_BUFFMT_TEXT;
- VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
-}
-
-void VT_int_UpdateCursor( tVTerm *Term, int bShow )
-{
- tVideo_IOCtl_Pos csr_pos;
-
- if( Term != gpVT_CurTerm ) return ;
-
- if( !bShow )
- {
- csr_pos.x = -1;
- csr_pos.y = -1;
- }
- else if( Term->Mode == TERM_MODE_TEXT )
- {
- int offset;
-
-// if( !(Term->Flags & VT_FLAG_SHOWCSR)
-// && ( (Term->Flags & VT_FLAG_HIDECSR) || !Term->Node.ReadThreads)
-// )
- if( !Term->Text || Term->Flags & VT_FLAG_HIDECSR )
- {
- csr_pos.x = -1;
- csr_pos.y = -1;
- }
- else
- {
- if(Term->Flags & VT_FLAG_ALTBUF)
- offset = Term->AltWritePos;
- else
- offset = Term->WritePos - Term->ViewPos;
-
- csr_pos.x = offset % Term->TextWidth;
- csr_pos.y = offset / Term->TextWidth;
- if( 0 > csr_pos.y || csr_pos.y >= Term->TextHeight )
- csr_pos.y = -1, csr_pos.x = -1;
- }
- }
- else
- {
- csr_pos.x = Term->VideoCursorX;
- csr_pos.y = Term->VideoCursorY;
- }
- VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos);
-}
-
-/**
- * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
- * \brief Updates the video framebuffer
- */
-void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
-{
- tVT_Char *buffer;
- int view_pos, write_pos;
- // Only update if this is the current terminal
- if( Term != gpVT_CurTerm ) return;
-
- switch( Term->Mode )
- {
- case TERM_MODE_TEXT:
- view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewPos;
- write_pos = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltWritePos : Term->WritePos;
- buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text;
- // Re copy the entire screen?
- if(UpdateAll) {
- VFS_WriteAt(
- giVT_OutputDevHandle,
- 0,
- Term->TextWidth*Term->TextHeight*sizeof(tVT_Char),
- &buffer[view_pos]
- );
- }
- // Only copy the current line
- else {
- int ofs = write_pos - write_pos % Term->TextWidth;
- VFS_WriteAt(
- giVT_OutputDevHandle,
- (ofs - view_pos)*sizeof(tVT_Char),
- Term->TextWidth*sizeof(tVT_Char),
- &buffer[ofs]
- );
- }
- break;
- case TERM_MODE_FB:
- break;
- }
-
- VT_int_UpdateCursor(Term, 1);
-}
-
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv/vterm_termbuf.c
- * - Virtual Terminal - Terminal buffer manipulation
- */
-#include "vterm.h"
-
-// === CODE ===
-
-/**
- * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
- * \brief Print a string to the Virtual Terminal
- */
-void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
-{
- Uint32 val;
- int i;
-
- // Iterate
- for( i = 0; i < Count; i++ )
- {
- // Handle escape sequences
- if( Buffer[i] == 0x1B )
- {
- i ++;
- i += VT_int_ParseEscape(Term, (const char*)&Buffer[i]) - 1;
- continue;
- }
-
- // Fast check for non UTF-8
- if( Buffer[i] < 128 ) // Plain ASCII
- VT_int_PutChar(Term, Buffer[i]);
- else { // UTF-8
- i += ReadUTF8(&Buffer[i], &val) - 1;
- VT_int_PutChar(Term, val);
- }
- }
- // Update Screen
- VT_int_UpdateScreen( Term, 0 );
-}
-
-/**
- * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
- * \brief Write a single character to a VTerm
- */
-void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
-{
- int i;
- tVT_Char *buffer;
- int write_pos;
-
- if(Term->Flags & VT_FLAG_ALTBUF) {
- buffer = Term->AltBuf;
- write_pos = Term->AltWritePos;
- }
- else {
- buffer = Term->Text;
- write_pos = Term->WritePos;
- }
-
- switch(Ch)
- {
- case '\0': return; // Ignore NULL byte
- case '\n':
- VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
- write_pos += Term->TextWidth;
- case '\r':
- write_pos -= write_pos % Term->TextWidth;
- break;
-
- case '\t': { int tmp = write_pos / Term->TextWidth;
- write_pos %= Term->TextWidth;
- do {
- buffer[ write_pos ].Ch = '\0';
- buffer[ write_pos ].Colour = Term->CurColour;
- write_pos ++;
- } while(write_pos & 7);
- write_pos += tmp * Term->TextWidth;
- break; }
-
- case '\b':
- // Backspace is invalid at Offset 0
- if(write_pos == 0) break;
-
- write_pos --;
- // Singe Character
- if(buffer[ write_pos ].Ch != '\0') {
- buffer[ write_pos ].Ch = 0;
- buffer[ write_pos ].Colour = Term->CurColour;
- break;
- }
- // Tab
- i = 7; // Limit it to 8
- do {
- buffer[ write_pos ].Ch = 0;
- buffer[ write_pos ].Colour = Term->CurColour;
- write_pos --;
- } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
- if(buffer[ write_pos ].Ch != '\0')
- write_pos ++;
- break;
-
- default:
- buffer[ write_pos ].Ch = Ch;
- buffer[ write_pos ].Colour = Term->CurColour;
- // Update the line before wrapping
- if( (write_pos + 1) % Term->TextWidth == 0 )
- VT_int_UpdateScreen( Term, 0 );
- write_pos ++;
- break;
- }
-
- if(Term->Flags & VT_FLAG_ALTBUF)
- {
- Term->AltBuf = buffer;
- Term->AltWritePos = write_pos;
-
- if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight)
- {
- Term->AltWritePos -= Term->TextWidth;
- VT_int_ScrollText(Term, 1);
- }
-
- }
- else
- {
- Term->Text = buffer;
- Term->WritePos = write_pos;
- // Move Screen
- // - Check if we need to scroll the entire scrollback buffer
- if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1))
- {
- int base;
-
- // Update previous line
- Term->WritePos -= Term->TextWidth;
- VT_int_UpdateScreen( Term, 0 );
-
- // Update view position
- base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback);
- if(Term->ViewPos < base)
- Term->ViewPos += Term->Width;
- if(Term->ViewPos > base)
- Term->ViewPos = base;
-
- VT_int_ScrollText(Term, 1);
- }
- // Ok, so we only need to scroll the screen
- else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
- {
- // Update the last line
- Term->WritePos -= Term->TextWidth;
- VT_int_UpdateScreen( Term, 0 );
- Term->WritePos += Term->TextWidth;
-
- VT_int_ScrollText(Term, 1);
-
- Term->ViewPos += Term->TextWidth;
- }
- }
-
- //LEAVE('-');
-}
-
-void VT_int_ScrollText(tVTerm *Term, int Count)
-{
- tVT_Char *buf;
- int height, init_write_pos;
- int len, i;
- int scroll_top, scroll_height;
-
- // Get buffer pointer and attributes
- if( Term->Flags & VT_FLAG_ALTBUF )
- {
- buf = Term->AltBuf;
- height = Term->TextHeight;
- init_write_pos = Term->AltWritePos;
- scroll_top = Term->ScrollTop;
- scroll_height = Term->ScrollHeight;
- }
- else
- {
- buf = Term->Text;
- height = Term->TextHeight*(giVT_Scrollback+1);
- init_write_pos = Term->WritePos;
- scroll_top = 0;
- scroll_height = height;
- }
-
- // Scroll text downwards
- if( Count > 0 )
- {
- int base;
-
- // Set up
- if(Count > scroll_height) Count = scroll_height;
- base = Term->TextWidth*(scroll_top + scroll_height - Count);
- len = Term->TextWidth*(scroll_height - Count);
-
- // Scroll terminal cache
- memmove(
- &buf[Term->TextWidth*scroll_top],
- &buf[Term->TextWidth*(scroll_top+Count)],
- len*sizeof(tVT_Char)
- );
- // Clear last rows
- for( i = 0; i < Term->TextWidth*Count; i ++ )
- {
- buf[ base + i ].Ch = 0;
- buf[ base + i ].Colour = Term->CurColour;
- }
-
- // Update Screen
- VT_int_ScrollFramebuffer( Term, Count );
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos = base;
- else
- Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
- for( i = 0; i < Count; i ++ )
- {
- VT_int_UpdateScreen( Term, 0 );
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos += Term->TextWidth;
- else
- Term->WritePos += Term->TextWidth;
- }
- }
- else
- {
- Count = -Count;
- if(Count > scroll_height) Count = scroll_height;
-
- len = Term->TextWidth*(scroll_height - Count);
-
- // Scroll terminal cache
- memmove(
- &buf[Term->TextWidth*(scroll_top+Count)],
- &buf[Term->TextWidth*scroll_top],
- len*sizeof(tVT_Char)
- );
- // Clear preceding rows
- for( i = 0; i < Term->TextWidth*Count; i ++ )
- {
- buf[ i ].Ch = 0;
- buf[ i ].Colour = Term->CurColour;
- }
-
- VT_int_ScrollFramebuffer( Term, -Count );
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos = Term->TextWidth*scroll_top;
- else
- Term->WritePos = Term->ViewPos;
- for( i = 0; i < Count; i ++ )
- {
- VT_int_UpdateScreen( Term, 0 );
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos += Term->TextWidth;
- else
- Term->WritePos += Term->TextWidth;
- }
- }
-
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos = init_write_pos;
- else
- Term->WritePos = init_write_pos;
-}
-
-/**
- * \brief Clears a line in a virtual terminal
- * \param Term Terminal to modify
- * \param Num Line number to clear
- */
-void VT_int_ClearLine(tVTerm *Term, int Num)
-{
- int i;
- tVT_Char *cell;
-
- if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ;
-
- cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text;
- cell = &cell[ Num*Term->TextWidth ];
-
- for( i = Term->TextWidth; i--; )
- {
- cell[ i ].Ch = 0;
- cell[ i ].Colour = Term->CurColour;
- }
-}
-
-/**
- * \brief Update the screen mode
- * \param Term Terminal to update
- * \param NewMode New mode to set
- * \param NewWidth New framebuffer width
- * \param NewHeight New framebuffer height
- */
-void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight)
-{
- int oldW = Term->Width;
- int oldTW = Term->TextWidth;
- int oldH = Term->Height;
- int oldTH = Term->TextHeight;
- tVT_Char *oldTBuf = Term->Text;
- Uint32 *oldFB = Term->Buffer;
- int w, h, i;
-
- // TODO: Increase RealWidth/RealHeight when this happens
- if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth;
- if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
-
- Term->Mode = NewMode;
-
- // Fast exit if no resolution change
- if(NewWidth == Term->Width && NewHeight == Term->Height)
- return ;
-
- // Calculate new dimensions
- Term->Width = NewWidth;
- Term->Height = NewHeight;
- Term->TextWidth = NewWidth / giVT_CharWidth;
- Term->TextHeight = NewHeight / giVT_CharHeight;
- Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
-
- // Allocate new buffers
- // - Text
- Term->Text = calloc(
- Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
- sizeof(tVT_Char)
- );
- if(oldTBuf) {
- // Copy old buffer
- w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
- h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
- h *= giVT_Scrollback + 1;
- for( i = 0; i < h; i ++ )
- {
- memcpy(
- &Term->Text[i*Term->TextWidth],
- &oldTBuf[i*oldTW],
- w*sizeof(tVT_Char)
- );
- }
- free(oldTBuf);
- }
-
- // - Alternate Text
- Term->AltBuf = realloc(
- Term->AltBuf,
- Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
- );
-
- // - Framebuffer
- if(oldFB) {
- Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
- // Copy old buffer
- w = (oldW > Term->Width) ? Term->Width : oldW;
- h = (oldH > Term->Height) ? Term->Height : oldH;
- for( i = 0; i < h; i ++ )
- {
- memcpy(
- &Term->Buffer[i*Term->Width],
- &oldFB[i*oldW],
- w*sizeof(Uint32)
- );
- }
- free(oldFB);
- }
-
- // Debug
- switch(NewMode)
- {
- case TERM_MODE_TEXT:
- Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
- Term, Term->TextWidth, Term->TextHeight);
- break;
- case TERM_MODE_FB:
- Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
- Term, Term->Width, Term->Height);
- break;
- //case TERM_MODE_2DACCEL:
- //case TERM_MODE_3DACCEL:
- // return;
- }
-}
-
-
-void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
-{
- if(Enabled)
- Term->Flags |= VT_FLAG_ALTBUF;
- else
- Term->Flags &= ~VT_FLAG_ALTBUF;
- VT_int_UpdateScreen(Term, 1);
-}
-
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv/vterm_vt100.c
- * - Virtual Terminal - VT100 (Kinda) Emulation
- */
-#include "vterm.h"
-
-// === CONSTANTS ===
-const Uint16 caVT100Colours[] = {
- // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray
- // Same again, but bright
- VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA,
- VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE
- };
-
-// === CODE ===
-/**
- * \brief Handle a standard large escape code
- *
- * Handles any escape code of the form \x1B[n,...A where n is an integer
- * and A is any letter.
- */
-void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args)
-{
- int tmp = 1;
- switch(CmdChar)
- {
- // Left
- case 'D':
- tmp = -1;
- // Right
- case 'C':
- if(argc == 1) tmp *= args[0];
- if( Term->Flags & VT_FLAG_ALTBUF )
- {
- if( (Term->AltWritePos + tmp) % Term->TextWidth == 0 ) {
- Term->AltWritePos -= Term->AltWritePos % Term->TextWidth;
- Term->AltWritePos += Term->TextWidth - 1;
- }
- else
- Term->AltWritePos += tmp;
- }
- else
- {
- if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) {
- Term->WritePos -= Term->WritePos % Term->TextWidth;
- Term->WritePos += Term->TextWidth - 1;
- }
- else
- Term->WritePos += tmp;
- }
- break;
-
- // Erase
- case 'J':
- switch(args[0])
- {
- case 0: // Erase below
- break;
- case 1: // Erase above
- break;
- case 2: // Erase all
- if( Term->Flags & VT_FLAG_ALTBUF )
- {
- int i = Term->TextHeight;
- while( i-- ) VT_int_ClearLine(Term, i);
- Term->AltWritePos = 0;
- VT_int_UpdateScreen(Term, 1);
- }
- else
- {
- int i = Term->TextHeight * (giVT_Scrollback + 1);
- while( i-- ) VT_int_ClearLine(Term, i);
- Term->WritePos = 0;
- Term->ViewPos = 0;
- VT_int_UpdateScreen(Term, 1);
- }
- break;
- }
- break;
-
- // Erase in line
- case 'K':
- switch(args[0])
- {
- case 0: // Erase to right
- if( Term->Flags & VT_FLAG_ALTBUF )
- {
- int i, max;
- max = Term->Width - Term->AltWritePos % Term->Width;
- for( i = 0; i < max; i ++ )
- Term->AltBuf[Term->AltWritePos+i].Ch = 0;
- }
- else
- {
- int i, max;
- max = Term->Width - Term->WritePos % Term->Width;
- for( i = 0; i < max; i ++ )
- Term->Text[Term->WritePos+i].Ch = 0;
- }
- VT_int_UpdateScreen(Term, 0);
- break;
- case 1: // Erase to left
- if( Term->Flags & VT_FLAG_ALTBUF )
- {
- int i = Term->AltWritePos % Term->Width;
- while( i -- )
- Term->AltBuf[Term->AltWritePos++].Ch = 0;
- }
- else
- {
- int i = Term->WritePos % Term->Width;
- while( i -- )
- Term->Text[Term->WritePos++].Ch = 0;
- }
- VT_int_UpdateScreen(Term, 0);
- break;
- case 2: // Erase all
- if( Term->Flags & VT_FLAG_ALTBUF )
- {
- VT_int_ClearLine(Term, Term->AltWritePos / Term->Width);
- }
- else
- {
- VT_int_ClearLine(Term, Term->WritePos / Term->Width);
- }
- VT_int_UpdateScreen(Term, 0);
- break;
- }
- break;
-
- // Set cursor position
- case 'H':
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos = args[0] + args[1]*Term->TextWidth;
- else
- Term->WritePos = args[0] + args[1]*Term->TextWidth;
- //Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]);
- break;
-
- // Scroll up `n` lines
- case 'S':
- tmp = -1;
- // Scroll down `n` lines
- case 'T':
- if(argc == 1) tmp *= args[0];
- if( Term->Flags & VT_FLAG_ALTBUF )
- VT_int_ScrollText(Term, tmp);
- else
- {
- if(Term->ViewPos/Term->TextWidth + tmp < 0)
- break;
- if(Term->ViewPos/Term->TextWidth + tmp > Term->TextHeight * (giVT_Scrollback + 1))
- break;
-
- Term->ViewPos += Term->TextWidth*tmp;
- }
- break;
-
- // Set Font flags
- case 'm':
- for( ; argc--; )
- {
- int colour_idx;
- // Flags
- if( 0 <= args[argc] && args[argc] <= 8)
- {
- switch(args[argc])
- {
- case 0: Term->CurColour = DEFAULT_COLOUR; break; // Reset
- case 1: Term->CurColour |= 0x80000000; break; // Bright
- case 2: Term->CurColour &= ~0x80000000; break; // Dim
- }
- }
- // Foreground Colour
- else if(30 <= args[argc] && args[argc] <= 37) {
- // Get colour index, accounting for bright bit
- colour_idx = args[argc]-30 + ((Term->CurColour>>28) & 8);
- Term->CurColour &= 0x8000FFFF;
- Term->CurColour |= (Uint32)caVT100Colours[ colour_idx ] << 16;
- }
- // Background Colour
- else if(40 <= args[argc] && args[argc] <= 47) {
- // Get colour index, accounting for bright bit
- colour_idx = args[argc]-40 + ((Term->CurColour>>12) & 8);
- Term->CurColour &= 0xFFFF8000;
- Term->CurColour |= caVT100Colours[ colour_idx ];
- }
- else {
- Log_Warning("VTerm", "Unknown font flag %i", args[argc]);
- }
- }
- break;
-
- // Set scrolling region
- case 'r':
- if( argc != 2 ) break;
- Term->ScrollTop = args[0];
- Term->ScrollHeight = args[1] - args[0];
- break;
-
- default:
- Log_Warning("VTerm", "Unknown control sequence '\\x1B[%c'", CmdChar);
- break;
- }
-}
-
-/**
- * \fn int VT_int_ParseEscape(tVTerm *Term, const char *Buffer)
- * \brief Parses a VT100 Escape code
- */
-int VT_int_ParseEscape(tVTerm *Term, const char *Buffer)
-{
- char c;
- int argc = 0, j = 1;
- int args[6] = {0,0,0,0};
- int bQuestionMark = 0;
-
- switch(Buffer[0])
- {
- //Large Code
- case '[':
- // Get Arguments
- c = Buffer[j++];
- if(c == '?') {
- bQuestionMark = 1;
- c = Buffer[j++];
- }
- if( '0' <= c && c <= '9' )
- {
- do {
- if(c == ';') c = Buffer[j++];
- while('0' <= c && c <= '9') {
- args[argc] *= 10;
- args[argc] += c-'0';
- c = Buffer[j++];
- }
- argc ++;
- } while(c == ';');
- }
-
- // Get Command
- if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
- {
- if( bQuestionMark )
- {
- switch(c)
- {
- // DEC Private Mode Set
- case 'h':
- if(argc != 1) break;
- switch(args[0])
- {
- case 25:
- Term->Flags &= ~VT_FLAG_HIDECSR;
- break;
- case 1047:
- VT_int_ToggleAltBuffer(Term, 1);
- break;
- }
- break;
- case 'l':
- if(argc != 1) break;
- switch(args[0])
- {
- case 25:
- Term->Flags |= VT_FLAG_HIDECSR;
- break;
- case 1047:
- VT_int_ToggleAltBuffer(Term, 0);
- break;
- }
- break;
- default:
- Log_Warning("VTerm", "Unknown control sequence '\\x1B[?%c'", c);
- break;
- }
- }
- else
- {
- VT_int_ParseEscape_StandardLarge(Term, c, argc, args);
- }
- }
- break;
-
- default:
- Log_Notice("VTerm", "TODO: Handle short escape codes");
- break;
- }
-
- //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer);
- return j;
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge
- *
- * drvutil.c
- * - Common Driver/Filesystem Helper Functions
- */
-#define DEBUG 0
-#include <acess.h>
-#include <api_drv_disk.h>
-#include <api_drv_video.h>
-
-// === TYPES ===
-
-// === PROTOTYPES ===
-//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);
-//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src);
-//void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);
-//void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);
-void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf);
-//void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);
-void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
-void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
-
-// === GLOBALS ===
-tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = {
- NULL,
- DrvUtil_Video_2D_Fill,
- DrvUtil_Video_2D_Blit
-};
-tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor = {
- 8, 16,
- 0, 0,
- {
- 0, 0, 0 , 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0, 0, 0 , 0, 0, 0, 0, 0,
- 0, 0, 0 , 0, 0, 0, 0, 0
- }
-};
-
-// === CODE ===
-// --- Video Driver Helpers ---
-int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length,
- tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers)
-{
- const Uint8 *stream = Buffer;
- int rem = Length;
- int op;
- while( rem )
- {
- rem --;
- op = *stream;
- stream ++;
-
- if(op > NUM_VIDEO_2DOPS) {
- Log_Warning("DrvUtil",
- "DrvUtil_Video_2DStream: Unknown operation %i",
- op);
- return Length-rem;
- }
-
- if(op*sizeof(void*) > SizeofHandlers) {
- Log_Warning("DrvUtil",
- "DrvUtil_Video_2DStream: Driver does not support op %i",
- op);
- return Length-rem;
- }
-
- switch(op)
- {
- case VIDEO_2DOP_NOP: break;
-
- case VIDEO_2DOP_FILL:
- if(rem < 10) return Length-rem;
-
- if(!Handlers->Fill) {
- Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
- " does not support VIDEO_2DOP_FILL");
- return Length-rem;
- }
-
- Handlers->Fill(
- Ent,
- ((const Uint16*)stream)[0], ((const Uint16*)stream)[1],
- ((const Uint16*)stream)[2], ((const Uint16*)stream)[3],
- ((const Uint32*)stream)[4]
- );
-
- rem -= 10;
- stream += 10;
- break;
-
- case VIDEO_2DOP_BLIT:
- if(rem < 12) return Length-rem;
-
- if(!Handlers->Blit) {
- Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
- " does not support VIDEO_2DOP_BLIT");
- return Length-rem;
- }
-
- Handlers->Blit(
- Ent,
- ((const Uint16*)stream)[0], ((const Uint16*)stream)[1],
- ((const Uint16*)stream)[2], ((const Uint16*)stream)[3],
- ((const Uint16*)stream)[4], ((const Uint16*)stream)[5]
- );
-
- rem -= 12;
- stream += 12;
- break;
-
- }
- }
- return 0;
-}
-
-int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Buffer)
-{
- Uint8 *dest;
- const Uint32 *src = Buffer;
- int csr_x, csr_y;
- int x, y;
- int bytes_per_px = (FBInfo->Depth + 7) / 8;
- ENTER("pFBInfo xOffset xLength pBuffer",
- Mode, FBInfo, Offset, Length, Buffer);
-
- csr_x = FBInfo->CursorX;
- csr_y = FBInfo->CursorY;
-
- DrvUtil_Video_RemoveCursor(FBInfo);
-
- switch( FBInfo->BufferFormat )
- {
- case VIDEO_BUFFMT_TEXT:
- {
- const tVT_Char *chars = Buffer;
- int widthInChars = FBInfo->Width/giVT_CharWidth;
- int heightInChars = FBInfo->Height/giVT_CharHeight;
- int i;
-
- LOG("bytes_per_px = %i", bytes_per_px);
- LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars);
-
- Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char);
-
- x = Offset % widthInChars; y = Offset / widthInChars;
- LOG("x = %i, y = %i", x, y);
-
- // Sanity Check
- if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0);
- if(y >= heightInChars) LEAVE_RET('i', 0);
-
- if( Offset + Length > heightInChars*widthInChars )
- {
- Length = heightInChars*widthInChars - Offset;
- }
-
- dest = FBInfo->Framebuffer;
- LOG("dest = %p", dest);
- dest += y * giVT_CharHeight * FBInfo->Pitch;
- LOG("dest = %p", dest);
-
- for( i = 0; i < Length; i++ )
- {
- if( y >= heightInChars )
- {
- Log_Notice("DrvUtil", "Stopped at %i", i);
- break;
- }
-
- VT_Font_Render(
- chars->Ch,
- dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch,
- VT_Colour12toN(chars->BGCol, FBInfo->Depth),
- VT_Colour12toN(chars->FGCol, FBInfo->Depth)
- );
-
- chars ++;
- x ++;
- if( x >= widthInChars )
- {
- x = 0;
- y ++;
- dest += FBInfo->Pitch*giVT_CharHeight;
- LOG("dest = %p", dest);
- }
- }
- Length = i * sizeof(tVT_Char);
- }
- break;
-
- case VIDEO_BUFFMT_FRAMEBUFFER:
- if(FBInfo->Width*FBInfo->Height*4 < Offset+Length)
- {
- Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow");
- return 0;
- }
-
- switch(FBInfo->Depth)
- {
- case 15:
- case 16:
- Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in LFB write");
- break;
- case 24:
- x = Offset % FBInfo->Width;
- y = Offset / FBInfo->Width;
- dest = (Uint8*)FBInfo->Framebuffer + y*FBInfo->Pitch;
- for( ; Length >= 4; Length -= 4 )
- {
- dest[x*3+0] = *src & 0xFF;
- dest[x*3+1] = (*src >> 8) & 0xFF;
- dest[x*3+2] = (*src >> 16) & 0xFF;
- x ++;
- if(x == FBInfo->Width) {
- dest += FBInfo->Pitch;
- x = 0;
- }
- }
- break;
- case 32:
- // Copy to Frambuffer
- if( FBInfo->Pitch != FBInfo->Width*4 )
- {
- Uint32 *px;
- // Pitch isn't 4*Width
- x = Offset % FBInfo->Width;
- y = Offset / FBInfo->Height;
-
- px = (Uint32*)FBInfo->Framebuffer + y*FBInfo->Pitch/4;
-
- for( ; Length >= 4; Length -= 4, x )
- {
- px[x++] = *src ++;
- if( x == FBInfo->Width ) {
- x = 0;
- px += FBInfo->Pitch;
- }
- }
- }
- else
- {
- dest = (Uint8 *)FBInfo->Framebuffer + Offset;
- memcpy(dest, Buffer, Length);
- }
- break;
- default:
- Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Unknown bit depthn %i", FBInfo->Depth);
- break;
- }
- break;
-
- case VIDEO_BUFFMT_2DSTREAM:
- Length = DrvUtil_Video_2DStream(
- FBInfo, Buffer, Length,
- &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions)
- );
- break;
-
- default:
- LEAVE('i', -1);
- return -1;
- }
-
- DrvUtil_Video_DrawCursor(FBInfo, csr_x, csr_y);
-
- LEAVE('x', Length);
- return Length;
-}
-
-int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap)
-{
- int csrX = Buf->CursorX, csrY = Buf->CursorY;
- size_t size;
-
- // Clear old bitmap
- if( Buf->CursorBitmap )
- {
- DrvUtil_Video_RemoveCursor(Buf);
- if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H )
- {
- free( Buf->CursorSaveBuf );
- Buf->CursorSaveBuf = NULL;
- }
- if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor)
- free(Buf->CursorBitmap);
- Buf->CursorBitmap = NULL;
- }
-
- // If the new bitmap is null, disable drawing
- if( !Bitmap )
- {
- Buf->CursorX = -1;
- Buf->CursorY = -1;
- return 0;
- }
-
- // Sanity check the bitmap
- if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) )
- {
- Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap);
- errno = -EINVAL;
- return -1;
- }
-
- // Don't take a copy of the DrvUtil provided cursor
- if( Bitmap == &gDrvUtil_TextModeCursor )
- {
- Buf->CursorBitmap = Bitmap;
- }
- else
- {
- size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4;
-
- // Take a copy
- Buf->CursorBitmap = malloc( size );
- memcpy(Buf->CursorBitmap, Bitmap, size);
- }
-
- // Restore cursor position
- DrvUtil_Video_DrawCursor(Buf, csrX, csrY);
- return 0;
-}
-
-void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y)
-{
- int render_ox=0, render_oy=0, render_w, render_h;
-
- DrvUtil_Video_RemoveCursor(Buf);
-
- // X < 0 disables the cursor
- if( X < 0 ) {
- Buf->CursorX = -1;
- return ;
- }
-
- // Sanity checking
- if( X < 0 || Y < 0 ) return;
- if( X >= Buf->Width || Y >= Buf->Height ) return;
-
- // Ensure the cursor is enabled
- if( !Buf->CursorBitmap ) return ;
-
- // Save cursor position (for changing the bitmap)
- Buf->CursorX = X; Buf->CursorY = Y;
-
- // Apply cursor's center offset
- X -= Buf->CursorBitmap->XOfs;
- Y -= Buf->CursorBitmap->YOfs;
-
- // Get the width of the cursor on screen (clipping to right/bottom edges)
- render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W;
- render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H;
-
- // Clipp to left/top edges
- if(X < 0) { render_ox = -X; X = 0; }
- if(Y < 0) { render_oy = -Y; Y = 0; }
-
- // Save values
- Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h;
- Buf->CursorDestX = X; Buf->CursorDestY = Y;
- Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy;
-
- // Call render routine
- DrvUtil_Video_RenderCursor(Buf);
-}
-
-void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
-{
- int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY;
- int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH;
- int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY;
- int bytes_per_px = (Buf->Depth + 7) / 8;
- int save_pitch = Buf->CursorBitmap->W * bytes_per_px;
- void *dest;
- Uint32 *src;
- int x, y;
-
- dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px;
- src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x;
-
- // Allocate save buffer if not already
- if( !Buf->CursorSaveBuf )
- Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px );
-
- // Save behind the cursor
- for( y = 0; y < render_h; y ++ )
- memcpy(
- (Uint8*)Buf->CursorSaveBuf + y*save_pitch,
- (Uint8*)dest + y*Buf->Pitch,
- render_w*bytes_per_px
- );
-
- // Draw the cursor
- switch(Buf->Depth)
- {
- case 15:
- case 16:
- Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw");
- break;
- case 24:
- for( y = 0; y < render_h; y ++ )
- {
- Uint8 *px;
- px = dest;
- for(x = 0; x < render_w; x ++, px += 3)
- {
- Uint32 value = src[x];
- // TODO: Should I implement alpha blending?
- if(value & 0xFF000000)
- {
- px[0] = value & 0xFF;
- px[1] = (value >> 8) & 0xFF;
- px[2] = (value >> 16) & 0xFF;
- }
- else
- ;
- }
- src += Buf->CursorBitmap->W;
- dest = (Uint8*)dest + Buf->Pitch;
- }
- break;
- case 32:
- for( y = 0; y < render_h; y ++ )
- {
- Uint32 *px;
- px = dest;
- for(x = 0; x < render_w; x ++, px ++)
- {
- Uint32 value = src[x];
- // TODO: Should I implement alpha blending?
- if(value & 0xFF000000)
- *px = value;
- else
- ; // NOP, completely transparent
- }
- src += Buf->CursorBitmap->W;
- dest = (Uint8*)dest + Buf->Pitch;
- }
- break;
- default:
- Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth);
- Buf->CursorX = -1;
- break;
- }
-}
-
-void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
-{
- int bytes_per_px = (Buf->Depth + 7) / 8;
- int y, save_pitch;
- Uint8 *dest, *src;
-
- // Just a little sanity
- if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ;
- if( !Buf->CursorSaveBuf ) return ;
-
-// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
-
- // Set up
- save_pitch = Buf->CursorBitmap->W * bytes_per_px;
- dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
- src = Buf->CursorSaveBuf;
-
- // Copy each line back
- for( y = 0; y < Buf->CursorRenderH; y ++ )
- {
- memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
- src += save_pitch;
- dest += Buf->Pitch;
- }
-
- // Set the cursor as removed
- Buf->CursorX = -1;
-}
-
-void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
-{
- tDrvUtil_Video_BufInfo *FBInfo = Ent;
-
- // TODO: Handle non-32bit modes
- if( FBInfo->Depth != 32 ) return;
-
- // TODO: Be less hacky
- int pitch = FBInfo->Pitch/4;
- Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X;
- while( H -- ) {
- Uint32 *tmp;
- int i;
- tmp = buf;
- for(i=W;i--;tmp++) *tmp = Colour;
- buf += pitch;
- }
-}
-
-void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
-{
- tDrvUtil_Video_BufInfo *FBInfo = Ent;
- int scrnpitch = FBInfo->Pitch;
- int bytes_per_px = (FBInfo->Depth + 7) / 8;
- int dst = DstY*scrnpitch + DstX;
- int src = SrcY*scrnpitch + SrcX;
- int tmp;
-
- //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)",
- // Ent, DstX, DstY, SrcX, SrcY, W, H);
-
- if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX;
- if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX;
- if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY;
- if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY;
-
- //Debug("W = %i, H = %i", W, H);
-
- if( dst > src ) {
- // Reverse copy
- dst += H*scrnpitch;
- src += H*scrnpitch;
- while( H -- ) {
- dst -= scrnpitch;
- src -= scrnpitch;
- tmp = W*bytes_per_px;
- for( tmp = W; tmp --; ) {
- *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp);
- }
- }
- }
- else if(W == FBInfo->Width && FBInfo->Pitch == FBInfo->Width*bytes_per_px) {
- memmove((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, H*FBInfo->Pitch);
- }
- else {
- // Normal copy is OK
- while( H -- ) {
- memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px);
- dst += scrnpitch;
- src += scrnpitch;
- }
- }
- //Log("Vesa_2D_Blit: RETURN");
-}
-
-
-// --- Disk Driver Helpers ---
-Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,
- tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument)
-{
- Uint8 tmp[BlockSize]; // C99
- Uint64 block = Start / BlockSize;
- int offset = Start - block * BlockSize;
- int leading = BlockSize - offset;
- Uint64 num;
- int tailings;
- Uint64 ret;
-
- ENTER("XStart XLength pBuffer pReadBlocks XBlockSize xArgument",
- Start, Length, Buffer, ReadBlocks, BlockSize, Argument);
-
- // Non aligned start, let's fix that!
- if(offset != 0)
- {
- if(leading > Length) leading = Length;
- LOG("Reading %i bytes from Block1+%i", leading, offset);
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('i', 0);
- return 0;
- }
- memcpy( Buffer, &tmp[offset], leading );
-
- if(leading == Length) {
- LEAVE('i', leading);
- return leading;
- }
-
- Buffer = (Uint8*)Buffer + leading;
- block ++;
- num = ( Length - leading ) / BlockSize;
- tailings = Length - num * BlockSize - leading;
- }
- else {
- num = Length / BlockSize;
- tailings = Length % BlockSize;
- }
-
- // Read central blocks
- if(num)
- {
- LOG("Reading %i blocks", num);
- ret = ReadBlocks(block, num, Buffer, Argument);
- if(ret != num ) {
- LEAVE('X', leading + ret * BlockSize);
- return leading + ret * BlockSize;
- }
- }
-
- // Read last tailing block
- if(tailings != 0)
- {
- LOG("Reading %i bytes from last block", tailings);
- block += num;
- Buffer = (Uint8*)Buffer + num * BlockSize;
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('X', leading + num * BlockSize);
- return leading + num * BlockSize;
- }
- memcpy( Buffer, tmp, tailings );
- }
-
- LEAVE('X', Length);
- return Length;
-}
-
-Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer,
- tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks,
- Uint64 BlockSize, Uint Argument)
-{
- Uint8 tmp[BlockSize]; // C99
- Uint64 block = Start / BlockSize;
- int offset = Start - block * BlockSize;
- int leading = BlockSize - offset;
- Uint64 num;
- int tailings;
- Uint64 ret;
-
- ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize xArgument",
- Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument);
-
- // Non aligned start, let's fix that!
- if(offset != 0)
- {
- if(leading > Length) leading = Length;
- LOG("Writing %i bytes to Block1+%i", leading, offset);
- // Read a copy of the block
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('i', 0);
- return 0;
- }
- // Modify
- memcpy( &tmp[offset], Buffer, leading );
- // Write Back
- ret = WriteBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('i', 0);
- return 0;
- }
-
- if(leading == Length) {
- LEAVE('i', leading);
- return leading;
- }
-
- Buffer = (Uint8*)Buffer + leading;
- block ++;
- num = ( Length - leading ) / BlockSize;
- tailings = Length - num * BlockSize - leading;
- }
- else {
- num = Length / BlockSize;
- tailings = Length % BlockSize;
- }
-
- // Read central blocks
- if(num)
- {
- LOG("Writing %i blocks", num);
- ret = WriteBlocks(block, num, Buffer, Argument);
- if(ret != num ) {
- LEAVE('X', leading + ret * BlockSize);
- return leading + ret * BlockSize;
- }
- }
-
- // Read last tailing block
- if(tailings != 0)
- {
- LOG("Writing %i bytes to last block", tailings);
- block += num;
- Buffer = (Uint8*)Buffer + num * BlockSize;
- // Read
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('X', leading + num * BlockSize);
- return leading + num * BlockSize;
- }
- // Modify
- memcpy( tmp, Buffer, tailings );
- // Write
- ret = WriteBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('X', leading + num * BlockSize);
- return leading + num * BlockSize;
- }
-
- }
-
- LEAVE('X', Length);
- return Length;
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * events.c
- * - Thread level event handling
- */
-#define DEBUG 0
-#include <acess.h>
-#include <threads_int.h>
-#include <events.h>
-
-// === CODE ===
-void Threads_PostEvent(tThread *Thread, Uint32 EventMask)
-{
- // Sanity checking
- if( !Thread ) return ;
- if( EventMask == 0 ) return ;
- // TODO: Check that only one bit is set?
-
- ENTER("pThread xEventMask", Thread, EventMask);
-
- SHORTLOCK( &Thread->IsLocked );
-
- Thread->EventState |= EventMask;
- LOG("Thread->EventState = 0x%x", Thread->EventState);
-
- // Currently sleeping on an event?
- if( Thread->Status == THREAD_STAT_EVENTSLEEP )
- {
- // Waiting on this event?
- if( (Uint32)Thread->RetStatus & EventMask )
- {
- // Wake up
- LOG("Waking thread %p(%i %s)", Thread, Thread->TID, Thread->ThreadName);
- Threads_AddActive(Thread);
- }
- }
-
- SHORTREL( &Thread->IsLocked );
- LEAVE('-');
-}
-
-/**
- * \brief Wait for an event to occur
- */
-Uint32 Threads_WaitEvents(Uint32 EventMask)
-{
- Uint32 rv;
- tThread *us = Proc_GetCurThread();
-
- ENTER("xEventMask", EventMask);
-
- // Early return check
- if( EventMask == 0 )
- {
- LEAVE('i', 0);
- return 0;
- }
-
- LOG("us = %p(%i %s)", us, us->TID, us->ThreadName);
-
- // Check if a wait is needed
- SHORTLOCK( &us->IsLocked );
- while( !(us->EventState & EventMask) )
- {
- LOG("Locked and preparing for wait");
- // Wait
- us->RetStatus = EventMask; // HACK: Store EventMask in RetStatus
- SHORTLOCK( &glThreadListLock );
- us = Threads_RemActive();
- us->Status = THREAD_STAT_EVENTSLEEP;
- // Note stored anywhere because we're woken using other means
- SHORTREL( &glThreadListLock );
- SHORTREL( &us->IsLocked );
- while(us->Status == THREAD_STAT_EVENTSLEEP) Threads_Yield();
- // Woken when lock is acquired
- SHORTLOCK( &us->IsLocked );
- }
-
- // Get return value and clear changed event bits
- rv = us->EventState & EventMask;
- us->EventState &= ~EventMask;
-
- SHORTREL( &us->IsLocked );
-
- LEAVE('x', rv);
- return rv;
-}
-
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * heap.c
- */
-#include <acess.h>
-#include <mm_virt.h>
-#include <heap_int.h>
-
-#define WARNINGS 1
-#define DEBUG_TRACE 0
-#define VERBOSE_DUMP 0
-
-// === CONSTANTS ===
-#define HEAP_INIT_SIZE 0x8000 // 32 KiB
-#define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words
-#define POW2_SIZES 0
-#define COMPACT_HEAP 0 // Use 4 byte header?
-#define FIRST_FIT 0
-
-//#define MAGIC_FOOT 0x2ACE5505
-//#define MAGIC_FREE 0xACE55000
-//#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE
-#define MAGIC_FOOT 0x544F4F46 // 'FOOT'
-#define MAGIC_FREE 0x45455246 // 'FREE'
-#define MAGIC_USED 0x44455355 // 'USED'
-
-// === PROTOTYPES ===
-void Heap_Install(void);
-void *Heap_Extend(int Bytes);
-void *Heap_Merge(tHeapHead *Head);
-//void *Heap_Allocate(const char *File, int Line, size_t Bytes);
-//void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
-//void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
-//void Heap_Deallocate(void *Ptr);
-void Heap_Dump(void);
-void Heap_Stats(void);
-
-// === GLOBALS ===
-tMutex glHeap;
-void *gHeapStart;
-void *gHeapEnd;
-
-// === CODE ===
-void Heap_Install(void)
-{
- gHeapStart = (void*)MM_KHEAP_BASE;
- gHeapEnd = (void*)MM_KHEAP_BASE;
- Heap_Extend(HEAP_INIT_SIZE);
-}
-
-/**
- * \brief Extend the size of the heap
- */
-void *Heap_Extend(int Bytes)
-{
- Uint i;
- tHeapHead *head = gHeapEnd;
- tHeapFoot *foot;
-
- // Bounds Check
- if( (tVAddr)gHeapEnd == MM_KHEAP_MAX )
- return NULL;
-
- if( Bytes == 0 ) {
- Log_Warning("Heap", "Heap_Extend called with Bytes=%i", Bytes);
- return NULL;
- }
-
- // Bounds Check
- if( (tVAddr)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) {
-// Bytes = MM_KHEAP_MAX - (tVAddr)gHeapEnd;
- return NULL;
- }
-
- // Heap expands in pages
- for( i = 0; i < (Bytes+0xFFF) >> 12; i ++ )
- {
- if( !MM_Allocate( (tVAddr)gHeapEnd+(i<<12) ) )
- {
- Warning("OOM - Heap_Extend");
- return NULL;
- }
- }
-
- // Increas heap end
- gHeapEnd = (Uint8*)gHeapEnd + (i << 12);
-
- // Create Block
- head->Size = (Bytes+0xFFF)&~0xFFF;
- head->Magic = MAGIC_FREE;
- foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) );
- foot->Head = head;
- foot->Magic = MAGIC_FOOT;
-
- return Heap_Merge(head); // Merge with previous block
-}
-
-/**
- * \brief Merges two ajacent heap blocks
- */
-void *Heap_Merge(tHeapHead *Head)
-{
- tHeapFoot *foot;
- tHeapFoot *thisFoot;
- tHeapHead *head;
-
- //Log("Heap_Merge: (Head=%p)", Head);
-
- thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
- if((Uint)thisFoot > (Uint)gHeapEnd) return NULL;
-
- // Merge Left (Down)
- foot = (void*)( (Uint)Head - sizeof(tHeapFoot) );
- if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart)
- && foot->Head->Magic == MAGIC_FREE) {
- foot->Head->Size += Head->Size; // Increase size
- thisFoot->Head = foot->Head; // Change backlink
- Head->Magic = 0; // Clear old head
- Head->Size = 0;
- Head = foot->Head; // Save new head address
- foot->Head = NULL; // Clear central footer
- foot->Magic = 0;
- }
-
- // Merge Right (Upwards)
- head = (void*)( (Uint)Head + Head->Size );
- if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE)
- {
- Head->Size += head->Size;
- foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
- foot->Head = Head; // Update Backlink
- thisFoot->Head = NULL; // Clear old footer
- thisFoot->Magic = 0;
- head->Size = 0; // Clear old header
- head->Magic = 0;
- }
-
- // Return new address
- return Head;
-}
-
-/**
- * \param File Allocating source file
- * \param Line Source line
- * \param __Bytes Size of region to allocate
- */
-void *Heap_Allocate(const char *File, int Line, size_t __Bytes)
-{
- tHeapHead *head, *newhead;
- tHeapFoot *foot, *newfoot;
- tHeapHead *best = NULL;
- Uint bestSize = 0; // Speed hack
- size_t Bytes;
-
- if( __Bytes == 0 ) {
- //return NULL; // TODO: Return a known un-mapped range.
- return INVLPTR;
- }
-
- // Get required size
- #if POW2_SIZES
- Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot);
- Bytes = 1UUL << LOG2(__Bytes);
- #else
- Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
- #endif
-
- // Lock Heap
- Mutex_Acquire(&glHeap);
-
- // Traverse Heap
- for( head = gHeapStart;
- (Uint)head < (Uint)gHeapEnd;
- head = (void*)((Uint)head + head->Size)
- )
- {
- // Alignment Check
- #if POW2_SIZES
- if( head->Size != 1UUL << LOG2(head->Size) ) {
- #else
- if( head->Size & (MIN_SIZE-1) ) {
- #endif
- Mutex_Release(&glHeap); // Release spinlock
- #if WARNINGS
- Log_Warning("Heap", "Size of heap address %p is invalid not aligned (0x%x)", head, head->Size);
- Heap_Dump();
- #endif
- return NULL;
- }
-
- // Check if allocated
- if(head->Magic == MAGIC_USED) continue;
- // Error check
- if(head->Magic != MAGIC_FREE) {
- Mutex_Release(&glHeap); // Release spinlock
- #if WARNINGS
- Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)",
- head, &head->Magic, head->Magic);
- Heap_Dump();
- #endif
- return NULL;
- }
-
- // Size check
- if(head->Size < Bytes) continue;
-
- // Perfect fit
- if(head->Size == Bytes) {
- head->Magic = MAGIC_USED;
- head->File = File;
- head->Line = Line;
- head->ValidSize = __Bytes;
- head->AllocateTime = now();
- Mutex_Release(&glHeap); // Release spinlock
- #if DEBUG_TRACE
- Debug("[Heap ] Malloc'd %p (%i bytes), returning to %p",
- head->Data, head->Size, __builtin_return_address(0));
- #endif
- return head->Data;
- }
-
- // Break out of loop
- #if FIRST_FIT
- best = head;
- bestSize = head->Size;
- break;
- #else
- // or check if the block is the best size
- if(bestSize > head->Size) {
- best = head;
- bestSize = head->Size;
- }
- #endif
- }
-
- // If no block large enough is found, create one
- if(!best)
- {
- best = Heap_Extend( Bytes );
- // Check for errors
- if(!best) {
- Mutex_Release(&glHeap); // Release spinlock
- return NULL;
- }
- // Check size
- if(best->Size == Bytes) {
- best->Magic = MAGIC_USED; // Mark block as used
- best->File = File;
- best->Line = Line;
- best->ValidSize = __Bytes;
- best->AllocateTime = now();
- Mutex_Release(&glHeap); // Release spinlock
- #if DEBUG_TRACE
- Debug("[Heap ] Malloc'd %p (%i bytes), returning to %s:%i", best->Data, best->Size, File, Line);
- #endif
- return best->Data;
- }
- }
-
- // Split Block
- newhead = (void*)( (Uint)best + Bytes );
- newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) );
- foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) );
-
- newfoot->Head = best; // Create new footer
- newfoot->Magic = MAGIC_FOOT;
- newhead->Size = best->Size - Bytes; // Create new header
- newhead->Magic = MAGIC_FREE;
- foot->Head = newhead; // Update backlink in old footer
- best->Size = Bytes; // Update size in old header
- best->ValidSize = __Bytes;
- best->Magic = MAGIC_USED; // Mark block as used
- best->File = File;
- best->Line = Line;
- best->AllocateTime = now();
-
- Mutex_Release(&glHeap); // Release spinlock
- #if DEBUG_TRACE
- Debug("[Heap ] Malloc'd %p (0x%x bytes), returning to %s:%i",
- best->Data, best->Size, File, Line);
- #endif
- return best->Data;
-}
-
-/**
- * \brief Free an allocated memory block
- */
-void Heap_Deallocate(void *Ptr)
-{
- tHeapHead *head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
- tHeapFoot *foot;
-
- // INVLPTR is returned from Heap_Allocate when the allocation
- // size is zero.
- if( Ptr == INVLPTR ) return;
-
- #if DEBUG_TRACE
- Debug("[Heap ] free: %p freed by %p (%i old)", Ptr, __builtin_return_address(0), now()-head->AllocateTime);
- #endif
-
- // Alignment Check
- if( (Uint)Ptr & (sizeof(Uint)-1) ) {
- Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr);
- return;
- }
-
- // Sanity check
- if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd)
- {
- Log_Warning("Heap", "free - Passed a non-heap address by %p (%p < %p < %p)\n",
- __builtin_return_address(0), gHeapStart, Ptr, gHeapEnd);
- return;
- }
-
- // Check memory block - Header
- head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
- if(head->Magic == MAGIC_FREE) {
- Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0));
- return;
- }
- if(head->Magic != MAGIC_USED) {
- Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic);
- Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
- return;
- }
-
- // Check memory block - Footer
- foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
- if(foot->Head != head) {
- Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head);
- Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
- return;
- }
- if(foot->Magic != MAGIC_FOOT) {
- Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)", head, &foot->Magic, foot->Magic);
- Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
- return;
- }
-
- // Lock
- Mutex_Acquire( &glHeap );
-
- // Mark as free
- head->Magic = MAGIC_FREE;
- //head->File = NULL;
- //head->Line = 0;
- head->ValidSize = 0;
- // Merge blocks
- Heap_Merge( head );
-
- // Release
- Mutex_Release( &glHeap );
-}
-
-/**
- * \brief Increase/Decrease the size of an allocation
- * \param File Calling File
- * \param Line Calling Line
- * \param __ptr Old memory
- * \param __size New Size
- */
-void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
-{
- tHeapHead *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) );
- tHeapHead *nextHead;
- tHeapFoot *foot;
- Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1);
-
- // Check for reallocating NULL
- if(__ptr == NULL) return Heap_Allocate(File, Line, __size);
-
- // Check if resize is needed
- if(newSize <= head->Size) return __ptr;
-
- // Check if next block is free
- nextHead = (void*)( (Uint)head + head->Size );
-
- // Extend into next block
- if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
- {
- Uint size = nextHead->Size + head->Size;
- foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) );
- // Exact Fit
- if(size == newSize) {
- head->Size = newSize;
- head->ValidSize = __size;
- head->File = File;
- head->Line = Line;
- foot->Head = head;
- nextHead->Magic = 0;
- nextHead->Size = 0;
- return __ptr;
- }
- // Create a new heap block
- nextHead = (void*)( (Uint)head + newSize );
- nextHead->Size = size - newSize;
- nextHead->Magic = MAGIC_FREE;
- foot->Head = nextHead; // Edit 2nd footer
- head->Size = newSize; // Edit first header
- head->File = File;
- head->Line = Line;
- head->ValidSize = __size;
- // Create new footer
- foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) );
- foot->Head = head;
- foot->Magic = MAGIC_FOOT;
- return __ptr;
- }
-
- // Extend downwards?
- foot = (void*)( (Uint)head - sizeof(tHeapFoot) );
- nextHead = foot->Head;
- if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
- {
- Uint size = nextHead->Size + head->Size;
- // Inexact fit, split things up
- if(size > newSize)
- {
- // TODO
- Warning("[Heap ] TODO: Space efficient realloc when new size is smaller");
- }
-
- // Exact fit
- if(size >= newSize)
- {
- Uint oldDataSize;
- // Set 1st (new/lower) header
- nextHead->Magic = MAGIC_USED;
- nextHead->Size = newSize;
- nextHead->File = File;
- nextHead->Line = Line;
- nextHead->ValidSize = __size;
- // Get 2nd (old) footer
- foot = (void*)( (Uint)nextHead + newSize );
- foot->Head = nextHead;
- // Save old data size
- oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead);
- // Clear old header
- head->Size = 0;
- head->Magic = 0;
- // Copy data
- memcpy(nextHead->Data, __ptr, oldDataSize);
- // Return
- return nextHead->Data;
- }
- // On to the expensive then
- }
-
- // Well, darn
- nextHead = Heap_Allocate( File, Line, __size );
- nextHead -= 1;
- nextHead->File = File;
- nextHead->Line = Line;
- nextHead->ValidSize = __size;
-
- memcpy(
- nextHead->Data,
- __ptr,
- head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead)
- );
-
- free(__ptr);
-
- return nextHead->Data;
-}
-
-/**
- * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
- * \brief Allocate and Zero a buffer in memory
- * \param File Allocating file
- * \param Line Line of allocation
- * \param Bytes Size of the allocation
- */
-void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
-{
- void *ret = Heap_Allocate(File, Line, Bytes);
- if(ret == NULL) return NULL;
-
- memset( ret, 0, Bytes );
-
- return ret;
-}
-
-/**
- * \fn int Heap_IsHeapAddr(void *Ptr)
- * \brief Checks if an address is a heap pointer
- */
-int Heap_IsHeapAddr(void *Ptr)
-{
- tHeapHead *head;
- if((Uint)Ptr < (Uint)gHeapStart) return 0;
- if((Uint)Ptr > (Uint)gHeapEnd) return 0;
- if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
-
- head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
- if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE)
- return 0;
-
- return 1;
-}
-
-/**
- */
-void Heap_Validate(void)
-{
- Heap_Dump();
-}
-
-#if WARNINGS
-void Heap_Dump(void)
-{
- tHeapHead *head, *badHead;
- tHeapFoot *foot = NULL;
-
- head = gHeapStart;
- while( (Uint)head < (Uint)gHeapEnd )
- {
- foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
- #if VERBOSE_DUMP
- Log_Log("Heap", "%p (0x%P): 0x%08x (%i) %4C",
- head, MM_GetPhysAddr((tVAddr)head), head->Size, head->ValidSize, &head->Magic);
- Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
- if(head->File) {
- Log_Log("Heap", "%sowned by %s:%i",
- (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
- }
- #endif
-
- // Sanity Check Header
- if(head->Size == 0) {
- Log_Warning("Heap", "HALTED - Size is zero");
- break;
- }
- if(head->Size & (MIN_SIZE-1)) {
- Log_Warning("Heap", "HALTED - Size is malaligned");
- break;
- }
- if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
- Log_Warning("Heap", "HALTED - Head Magic is Bad");
- break;
- }
-
- // Check footer
- if(foot->Magic != MAGIC_FOOT) {
- Log_Warning("Heap", "HALTED - Foot Magic is Bad");
- break;
- }
- if(head != foot->Head) {
- Log_Warning("Heap", "HALTED - Footer backlink is invalid");
- break;
- }
-
- #if VERBOSE_DUMP
- Log_Log("Heap", "");
- #endif
-
- // All OK? Go to next
- head = foot->NextHead;
- }
-
- // If the heap is valid, ok!
- if( (tVAddr)head == (tVAddr)gHeapEnd )
- return ;
-
- // Check for a bad return
- if( (tVAddr)head >= (tVAddr)gHeapEnd )
- return ;
-
- #if !VERBOSE_DUMP
- Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
- head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
- if(foot)
- Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic);
- if(head->File) {
- Log_Log("Heap", "%sowned by %s:%i",
- (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
- }
- Log_Log("Heap", "");
- #endif
-
-
- badHead = head;
-
- // Work backwards
- foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) );
- Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot);
- head = foot->Head;
- while( (tVAddr)head >= (tVAddr)badHead )
- {
- Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
- head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
- Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
- if(head->File)
- Log_Log("Heap", "%sowned by %s:%i",
- (head->Magic!=MAGIC_USED?"was ":""),
- head->File, head->Line);
- Log_Log("Heap", "");
-
- // Sanity Check Header
- if(head->Size == 0) {
- Log_Warning("Heap", "HALTED - Size is zero");
- break;
- }
- if(head->Size & (MIN_SIZE-1)) {
- Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
- break ;
- }
- if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
- Log_Warning("Heap", "HALTED - Head Magic is Bad");
- break;
- }
-
- // Check footer
- if(foot->Magic != MAGIC_FOOT) {
- Log_Warning("Heap", "HALTED - Foot Magic is Bad");
- break;
- }
- if(head != foot->Head) {
- Log_Warning("Heap", "HALTED - Footer backlink is invalid");
- break;
- }
-
- if(head == badHead) break;
-
- foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) );
- head = foot->Head;
- Log_Debug("Heap", "head=%p", head);
- }
-
- Panic("Heap_Dump - Heap is corrupted, kernel panic!");
-}
-#endif
-
-#if 1
-void Heap_Stats(void)
-{
- tHeapHead *head;
- int nBlocks = 0;
- int nFree = 0;
- int totalBytes = 0;
- int freeBytes = 0;
- int maxAlloc=0, minAlloc=-1;
- int avgAlloc, frag, overhead;
-
- for(head = gHeapStart;
- (Uint)head < (Uint)gHeapEnd;
- head = (void*)( (Uint)head + head->Size )
- )
- {
- nBlocks ++;
- totalBytes += head->Size;
- if( head->Magic == MAGIC_FREE )
- {
- nFree ++;
- freeBytes += head->Size;
- }
- else if( head->Magic == MAGIC_USED) {
- if(maxAlloc < head->Size) maxAlloc = head->Size;
- if(minAlloc == -1 || minAlloc > head->Size)
- minAlloc = head->Size;
- }
- else {
- Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
- break;
- }
-
- // Print the block info?
- #if 1
- if( head->Magic == MAGIC_FREE )
- Log_Debug("Heap", "%p (%P) - 0x%x free",
- head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size);
- else
- Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)",
- head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size, head->ValidSize, head->File, head->Line,
- now() - head->AllocateTime
- );
- #endif
- }
-
- Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
- Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
- if(nBlocks != 0)
- frag = (nFree-1)*10000/nBlocks;
- else
- frag = 0;
- Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
- if(nBlocks <= nFree)
- avgAlloc = 0;
- else
- avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
- if(avgAlloc != 0)
- overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
- else
- overhead = 0;
- Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
- avgAlloc, overhead/100, overhead%100
- );
- Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes",
- minAlloc, maxAlloc);
-
- // Scan and get distribution
- #if 1
- if(nBlocks > 0)
- {
- struct {
- Uint Size;
- Uint Count;
- } sizeCounts[nBlocks];
- int i;
-
- memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
-
- for(head = gHeapStart;
- (Uint)head < (Uint)gHeapEnd;
- head = (void*)( (Uint)head + head->Size )
- )
- {
- for( i = 0; i < nBlocks; i ++ ) {
- if( sizeCounts[i].Size == 0 )
- break;
- if( sizeCounts[i].Size == head->Size )
- break;
- }
- // Should never reach this part (in a non-concurrent case)
- if( i == nBlocks ) continue;
- sizeCounts[i].Size = head->Size;
- sizeCounts[i].Count ++;
- #if 1
- //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
- // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
- // );
- //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
- // sizeCounts[i].Size, sizeCounts[i].Count);
- #endif
- }
-
- for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
- {
- Log("Heap_Stats: 0x%x - %i blocks",
- sizeCounts[i].Size, sizeCounts[i].Count
- );
- }
- }
- #endif
-}
-#endif
-
-// === EXPORTS ===
-EXPORT(Heap_Allocate);
-EXPORT(Heap_AllocateZero);
-EXPORT(Heap_Reallocate);
-EXPORT(Heap_Deallocate);
-EXPORT(Heap_IsHeapAddr);
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * acess.h
- */
-#ifndef _ACESS_H
-#define _ACESS_H
-/**
- * \file acess.h
- * \brief Acess2 Kernel API Core
- */
-
-//! NULL Pointer
-#define NULL ((void*)0)
-//! Pack a structure
-#define PACKED __attribute__((packed))
-//! Mark a function as not returning
-#define NORETURN __attribute__((noreturn))
-//! Mark a parameter as unused
-#define UNUSED(x) UNUSED_##x __attribute__((unused))
-//! Get the offset of a member in a structure
-#define offsetof(st, m) ((Uint)((char *)&((st *)(0))->m - (char *)0 ))
-
-/**
- * \name Boolean constants
- * \{
- */
-#define TRUE 1
-#define FALSE 0
-/**
- * \}
- */
-
-#include <arch.h>
-#include <stdarg.h>
-#include "errno.h"
-
-// --- Types ---
-typedef Uint32 tPID; //!< Process ID type
-typedef Uint32 tTID; //!< Thread ID Type
-typedef Uint32 tUID; //!< User ID Type
-typedef Uint32 tGID; //!< Group ID Type
-typedef Sint64 tTimestamp; //!< Timestamp (miliseconds since 00:00 1 Jan 1970)
-typedef Sint64 tTime; //!< Same again
-typedef struct sShortSpinlock tShortSpinlock; //!< Opaque (kinda) spinlock
-typedef int bool; //!< Boolean type
-
-// --- Helper Macros ---
-/**
- * \name Helper Macros
- * \{
- */
-#define CONCAT(x,y) x ## y
-#define EXPAND_CONCAT(x,y) CONCAT(x,y)
-#define STR(x) #x
-#define EXPAND_STR(x) STR(x)
-
-extern char __buildnum[];
-#define BUILD_NUM ((int)(Uint)&__buildnum)
-extern const char gsGitHash[];
-
-#define VER2(major,minor) ((((major)&0xFF)<<8)|((minor)&0xFF))
-/**
- * \}
- */
-
-//! \brief Error number
-#define errno (*Threads_GetErrno())
-
-// === CONSTANTS ===
-// --- Memory Flags --
-/**
- * \name Memory Flags
- * \{
- * \todo Move to mm_virt.h
- */
-#define MM_PFLAG_RO 0x01 // Writes disallowed
-#define MM_PFLAG_EXEC 0x02 // Allow execution
-#define MM_PFLAG_NOPAGE 0x04 // Prevent from being paged out
-#define MM_PFLAG_COW 0x08 // Copy-On-Write
-#define MM_PFLAG_KERNEL 0x10 // Kernel-Only (Ring0)
-/**
- * \}
- */
-// --- Interface Flags & Macros
-/**
- * \name Flags for Proc_Clone
- * \{
- */
-//! Clone the entire process
-#define CLONE_VM 0x10
-//! Don't copy user pages
-#define CLONE_NOUSER 0x20
-/**
- * \}
- */
-
-// === Types ===
-/**
- * \brief Thread root function
- *
- * Function pointer prototype of a thread entrypoint. When it
- * returns, Threads_Exit is called
- */
-typedef void (*tThreadFunction)(void*);
-
-// === Kernel Export Macros ===
-/**
- * \name Kernel exports
- * \{
- */
-//! Kernel symbol definition
-typedef struct sKernelSymbol {
- const char *Name; //!< Symbolic name
- tVAddr Value; //!< Value of the symbol
-} tKernelSymbol;
-//! Export a pointer symbol (function/array)
-#define EXPORT(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_name}
-//! Export a variable
-#define EXPORTV(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)&_name}
-//! Export a symbol under another name
-#define EXPORTAS(_sym,_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_sym}
-/**
- * \}
- */
-
-// === FUNCTIONS ===
-// --- IRQs ---
-/**
- * \name IRQ hander registration
- * \{
- */
-extern int IRQ_AddHandler(int Num, void (*Callback)(int, void*), void *Ptr);
-extern void IRQ_RemHandler(int Handle);
-/**
- * \}
- */
-
-// --- Logging ---
-/**
- * \name Logging to kernel ring buffer
- * \{
- */
-extern void Log_KernelPanic(const char *Ident, const char *Message, ...);
-extern void Log_Panic(const char *Ident, const char *Message, ...);
-extern void Log_Error(const char *Ident, const char *Message, ...);
-extern void Log_Warning(const char *Ident, const char *Message, ...);
-extern void Log_Notice(const char *Ident, const char *Message, ...);
-extern void Log_Log(const char *Ident, const char *Message, ...);
-extern void Log_Debug(const char *Ident, const char *Message, ...);
-/**
- * \}
- */
-
-// --- Debug ---
-/**
- * \name Debugging and Errors
- * \{
- */
-extern void Debug_KernelPanic(void); //!< Initiate a kernel panic
-extern void Panic(const char *Msg, ...); //!< Print a panic message (initiates a kernel panic)
-extern void Warning(const char *Msg, ...); //!< Print a warning message
-extern void LogF(const char *Fmt, ...); //!< Print a log message without a trailing newline
-extern void Log(const char *Fmt, ...); //!< Print a log message
-extern void Debug(const char *Fmt, ...); //!< Print a debug message (doesn't go to KTerm)
-extern void LogV(const char *Fmt, va_list Args); //!< va_list Log message
-extern void Debug_Enter(const char *FuncName, const char *ArgTypes, ...);
-extern void Debug_Log(const char *FuncName, const char *Fmt, ...);
-extern void Debug_Leave(const char *FuncName, char RetType, ...);
-extern void Debug_HexDump(const char *Header, const void *Data, Uint Length);
-#define UNIMPLEMENTED() Warning("'%s' unimplemented", __func__)
-#if DEBUG
-# define ENTER(_types...) Debug_Enter((char*)__func__, _types)
-# define LOG(_fmt...) Debug_Log((char*)__func__, _fmt)
-# define LEAVE(_t...) Debug_Leave((char*)__func__, _t)
-# define LEAVE_RET(_t,_v...) do{LEAVE(_t,_v);return _v;}while(0)
-# define LEAVE_RET0() do{LEAVE('-');return;}while(0)
-#else
-# define ENTER(...)
-# define LOG(...)
-# define LEAVE(...)
-# define LEAVE_RET(_t,_v...) return (_v)
-# define LEAVE_RET0() return
-#endif
-#if SANITY
-# define ASSERT(expr) do{if(!(expr))Panic("%s: Assertion '"#expr"' failed",(char*)__func__);}while(0)
-#else
-# define ASSERT(expr)
-#endif
-/**
- * \}
- */
-
-// --- IO ---
-#if NO_IO_BUS
-#define inb(a) (Log_Panic("Arch", STR(ARCHDIR)" does not support in*/out* (%s:%i)", __FILE__, __LINE__),0)
-#define inw(a) inb(a)
-#define ind(a) inb(a)
-#define inq(a) inb(a)
-#define outb(a,b) inb(a)
-#define outw(a,b) inb(a)
-#define outd(a,b) inb(a)
-#define outq(a,b) inb(a)
-#else
-/**
- * \name I/O Memory Access
- * \{
- */
-extern void outb(Uint16 Port, Uint8 Data);
-extern void outw(Uint16 Port, Uint16 Data);
-extern void outd(Uint16 Port, Uint32 Data);
-extern void outq(Uint16 Port, Uint64 Data);
-extern Uint8 inb(Uint16 Port);
-extern Uint16 inw(Uint16 Port);
-extern Uint32 ind(Uint16 Port);
-extern Uint64 inq(Uint16 Port);
-/**
- * \}
- */
-#endif
-// --- Memory Management ---
-/**
- * \name Memory Management
- * \{
- * \todo Move to mm_virt.h
- */
-/**
- * \brief Allocate a physical page at \a VAddr
- * \param VAddr Virtual Address to allocate at
- * \return Physical address allocated
- */
-extern tPAddr MM_Allocate(tVAddr VAddr) __attribute__ ((warn_unused_result));
-/**
- * \brief Deallocate a page
- * \param VAddr Virtual address to unmap
- */
-extern void MM_Deallocate(tVAddr VAddr);
-/**
- * \brief Map a physical page at \a PAddr to \a VAddr
- * \param VAddr Target virtual address
- * \param PAddr Physical address to map
- * \return Boolean Success
- */
-extern int MM_Map(tVAddr VAddr, tPAddr PAddr);
-/**
- * \brief Get the physical address of \a Addr
- * \param Addr Address of the page to get the physical address of
- * \return Physical page mapped at \a Addr
- */
-extern tPAddr MM_GetPhysAddr(tVAddr Addr);
-/**
- * \brief Set the access flags on a page
- * \param VAddr Virtual address of the page
- * \param Flags New flags value
- * \param Mask Flags to set
- */
-extern void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask);
-/**
- * \brief Get the flags on a flag
- * \param VAddr Virtual address of page
- * \return Flags value of the page
- */
-extern Uint MM_GetFlags(tVAddr VAddr);
-/**
- * \brief Checks is a memory range is user accessable
- * \param VAddr Base address to check
- * \return 1 if the memory is all user-accessable, 0 otherwise
- */
-#define MM_IsUser(VAddr) (!(MM_GetFlags((VAddr))&MM_PFLAG_KERNEL))
-/**
- * \brief Temporarily map a page into the address space
- * \param PAddr Physical addres to map
- * \return Virtual address of page in memory
- * \note There is only a limited ammount of slots avaliable
- */
-extern tVAddr MM_MapTemp(tPAddr PAddr);
-/**
- * \brief Free a temporarily mapped page
- * \param VAddr Allocate virtual addres of page
- */
-extern void MM_FreeTemp(tVAddr VAddr);
-/**
- * \brief Map a physcal address range into the virtual address space
- * \param PAddr Physical address to map in
- * \param Number Number of pages to map
- */
-extern tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number);
-/**
- * \brief Allocates DMA physical memory
- * \param Pages Number of pages required
- * \param MaxBits Maximum number of bits the physical address can have
- * \param PhysAddr Pointer to the location to place the physical address allocated
- * \return Virtual address allocate
- */
-extern tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr);
-/**
- * \brief Unmaps an allocated hardware range
- * \param VAddr Virtual address allocate by ::MM_MapHWPages or ::MM_AllocDMA
- * \param Number Number of pages to free
- */
-extern void MM_UnmapHWPages(tVAddr VAddr, Uint Number);
-/**
- * \brief Allocate a single physical page
- * \return Physical address allocated
- */
-extern tPAddr MM_AllocPhys(void);
-/**
- * \brief Allocate a contiguous range of physical pages
- * \param Pages Number of pages to allocate
- * \param MaxBits Maximum number of address bits allowed
- * \return First physical address allocated
- */
-extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
-/**
- * \brief Reference a physical page
- * \param PAddr Page to mark as referenced
- */
-extern void MM_RefPhys(tPAddr PAddr);
-/**
- * \brief Dereference a physical page
- * \param PAddr Page to dereference
- */
-extern void MM_DerefPhys(tPAddr PAddr);
-/**
- * \brief Get the number of times a page has been referenced
- * \param PAddr Address to check
- * \return Reference count for the page
- */
-extern int MM_GetRefCount(tPAddr PAddr);
-/**
- * \brief Set the node associated with a page
- * \param PAddr Physical address of page
- * \param Node Node pointer (tVFS_Node)
- * \return Boolean failure
- * \retval 0 Success
- * \retval 1 Page not allocated
- */
-extern int MM_SetPageNode(tPAddr PAddr, void *Node);
-/**
- * \brief Get the node associated with a page
- * \param PAddr Physical address of page
- * \param Node Node pointer (tVFS_Node) destination
- * \return Boolean failure
- * \retval 0 Success
- * \retval 1 Page not allocated
- */
-extern int MM_GetPageNode(tPAddr PAddr, void **Node);
-/**
- * \}
- */
-
-// --- Memory Manipulation ---
-/**
- * \name Memory Manipulation
- * \{
- */
-extern int memcmp(const void *m1, const void *m2, size_t count);
-extern void *memcpy(void *dest, const void *src, size_t count);
-extern void *memcpyd(void *dest, const void *src, size_t count);
-extern void *memmove(void *dest, const void *src, size_t len);
-extern void *memset(void *dest, int val, size_t count);
-extern void *memsetd(void *dest, Uint32 val, size_t count);
-/**
- * \}
- */
-/**
- * \name Memory Validation
- * \{
- */
-extern int CheckString(const char *String);
-extern int CheckMem(const void *Mem, int Num);
-/**
- * \}
- */
-
-// --- Endianness ---
-/**
- * \name Endianness Swapping
- * \{
- */
-#ifdef __BIG_ENDIAN__
-#define LittleEndian16(_val) SwapEndian16(_val)
-#define LittleEndian32(_val) SwapEndian32(_val)
-#define BigEndian16(_val) (_val)
-#define BigEndian32(_val) (_val)
-#else
-#define LittleEndian16(_val) (_val)
-#define LittleEndian32(_val) (_val)
-#define BigEndian16(_val) SwapEndian16(_val)
-#define BigEndian32(_val) SwapEndian32(_val)
-#endif
-extern Uint16 SwapEndian16(Uint16 Val);
-extern Uint32 SwapEndian32(Uint32 Val);
-/**
- * \}
- */
-
-// --- Strings ---
-/**
- * \name Strings
- * \{
- */
-extern int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
-extern int sprintf(char *__s, const char *__format, ...);
-extern size_t strlen(const char *Str);
-extern char *strcpy(char *__dest, const char *__src);
-extern char *strncpy(char *__dest, const char *__src, size_t max);
-extern char *strcat(char *__dest, const char *__src);
-extern char *strncat(char *__dest, const char *__src, size_t n);
-extern int strcmp(const char *__str1, const char *__str2);
-extern int strncmp(const char *Str1, const char *Str2, size_t num);
-extern int strucmp(const char *Str1, const char *Str2);
-// strdup macro is defined in heap.h
-extern char *_strdup(const char *File, int Line, const char *Str);
-extern char **str_split(const char *__str, char __ch);
-extern char *strchr(const char *__s, int __c);
-extern int strpos(const char *Str, char Ch);
-extern int strpos8(const char *str, Uint32 search);
-extern void itoa(char *buf, Uint64 num, int base, int minLength, char pad);
-extern int atoi(const char *string);
-extern int ParseInt(const char *string, int *Val);
-extern int ReadUTF8(const Uint8 *str, Uint32 *Val);
-extern int WriteUTF8(Uint8 *str, Uint32 Val);
-extern int ModUtil_SetIdent(char *Dest, const char *Value);
-extern int ModUtil_LookupString(const char **Array, const char *Needle);
-
-extern Uint8 ByteSum(const void *Ptr, int Size);
-extern int Hex(char *Dest, size_t Size, const Uint8 *SourceData);
-extern int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString);
-/**
- * \}
- */
-
-/**
- * \brief Get a random number
- * \return Random number
- * \note Current implementation is a linear congruency
- */
-extern int rand(void);
-/**
- * \brief Call a function with a variable number of arguments
- * \param Function Function address
- * \param NArgs Number of entries in \a Args
- * \param Args Array of arguments
- * \return Integer from called Function
- */
-extern int CallWithArgArray(void *Function, int NArgs, Uint *Args);
-
-// --- Heap ---
-#include <heap.h>
-/**
- * \brief Magic heap allocation function
- */
-extern void *alloca(size_t Size);
-
-// --- Modules ---
-/**
- * \name Modules
- * \{
- */
-extern int Module_LoadMem(void *Buffer, Uint Length, const char *ArgStr);
-extern int Module_LoadFile(const char *Path, const char *ArgStr);
-/**
- * \}
- */
-
-// --- Timing ---
-/**
- * \name Time and Timing
- * \{
- */
-/**
- * \brief Create a timestamp from a time
- */
-extern tTime timestamp(int sec, int mins, int hrs, int day, int month, int year);
-/**
- * \brief Extract the date/time from a timestamp
- */
-extern void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
-/**
- * \brief Gets the current timestamp (miliseconds since Midnight 1st January 1970)
- */
-extern Sint64 now(void);
-/**
- * \}
- */
-
-// --- Threads ---
-/**
- * \name Threads and Processes
- * \{
- */
-extern int Proc_SpawnWorker(void (*Fcn)(void*), void *Data);
-extern int Proc_Spawn(const char *Path);
-extern int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs);
-extern int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize);
-extern void Threads_Exit(int TID, int Status);
-extern void Threads_Yield(void);
-extern void Threads_Sleep(void);
-extern int Threads_WakeTID(tTID Thread);
-extern tPID Threads_GetPID(void);
-extern tTID Threads_GetTID(void);
-extern tUID Threads_GetUID(void);
-extern tGID Threads_GetGID(void);
-extern int SpawnTask(tThreadFunction Function, void *Arg);
-extern int *Threads_GetErrno(void);
-extern int Threads_SetName(const char *NewName);
-/**
- * \}
- */
-
-// --- Simple Math ---
-//! Divide and round up
-extern int DivUp(int num, int dem);
-//! Divide and Modulo 64-bit unsigned integer
-extern Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem);
-
-static inline int MIN(int a, int b) { return a < b ? a : b; }
-static inline int MAX(int a, int b) { return a > b ? a : b; }
-
-#include <binary_ext.h>
-#include <vfs_ext.h>
-#include <mutex.h>
-
-#endif
+++ /dev/null
-/*
- * Acess2
- * - Abstract Data Types
- */
-#ifndef _ADT_H_
-#define _ADT_H_
-
-/**
- * \name Ring Buffers
- * \{
- */
-typedef struct sRingBuffer
-{
- size_t Start; //!< Start of data in ring buffer
- size_t Length; //!< Number of data bytes in buffer
- size_t Space; //!< Allocated space in buffer
- tShortSpinlock Lock; //!< Lock to prevent collisions
- char Data[]; //!< Buffer
-} tRingBuffer;
-
-/**
- * \brief Create a ring buffer \a Space bytes large
- * \param Space Ammount of space to allocate within the buffer
- * \return Pointer to the buffer structure
- */
-extern tRingBuffer *RingBuffer_Create(size_t Space);
-/**
- * \brief Read at most \a Length bytes from the buffer
- * \param Dest Destinaton buffer
- * \param Buffer Source ring buffer
- * \param Length Requested number of bytes
- * \return Number of bytes read
- */
-extern size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length);
-/**
- * \brief Write at most \a Length bytes to the buffer
- * \param Buffer Destination ring buffer
- * \param Source Source buffer
- * \param Length Provided number of bytes
- * \return Number of bytes written
- */
-extern size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length);
-/**
- * \}
- */
-
-
-#endif
+++ /dev/null
-/**
- * \file api_drv_common.h
- * \brief Common Driver Interface Definitions
- * \author John Hodge (thePowersGang)
- *
- * \section Introduction
- * There are two ways Acess drivers can communicate with userspace
- * applications, both are through the VFS. The first is by exposing a
- * device as a file buffer, the second is by sending commands via the
- * ioctl() system call.
- * All drivers in Acess must at least conform to the specifcation in this
- * file (even if it is just implementing eTplDrv_IOCtl.DRV_IOCTL_TYPE and
- * returning eTplDrv_Type.DRV_TYPE_NULL, however, doing so is discouraged)
- *
- * \section ioctls Core IOCtl calls
- * As said, the core Acess driver specifcation defines specific IOCtl calls
- * that all drivers should implement. The four core IOCtls (defined in
- * ::eTplDrv_IOCtl) allow another binary (wether it be a user-mode application
- * or another driver) to tell what type of device a driver provides, the
- * basic identifcation of the driver (4 character ID and BCD version number)
- * and be able to use externally standardised calls that may not have
- * standardised call numbers.
- * NOTE: All ioctl calls WILL return -1 if the driver ran into an error
- * of its own fault while executing the call. If the user was at fault
- * (e.g. by passing a bad buffer) the call will return -2.
- *
- * \section types Driver Types
- * When the eTplDrv_IOCtl.DRV_IOCTL_TYPE call is made, the driver should
- * return the relevant entry in the ::eTplDrv_Type enumeration that describes
- * what sub-specifcation (and hence, what device type) it implements.
- * These device types are described in their own files, which are liked
- * from their entries in ::eTplDrv_Type.
- */
-#ifndef _API_DRV_COMMON_H
-#define _API_DRV_COMMON_H
-
-/**
- * \enum eTplDrv_IOCtl
- * \brief Common IOCtl Calls
- */
-enum eTplDrv_IOCtl {
- /**
- * ioctl(...)
- * \brief Get driver type
- * \return The relevant entry from ::eTplDrv_Type
- */
- DRV_IOCTL_TYPE,
-
- /**
- * ioctl(..., char *dest[32])
- * \brief Get driver identifier string
- * \return 0 on no error
- *
- * This call sets the 32-byte array \a dest to the drivers 31 charater
- * identifier. This identifier must be unique to the driver series.
- */
- DRV_IOCTL_IDENT,
-
- /**
- * ioctl(...)
- * \brief Get driver version number
- * \return 24-bit BCD version number (2.2.2)
- *
- * This call returns the 6-digit (2 major, 2 minor, 2 patch) version
- * number of the driver.
- */
- DRV_IOCTL_VERSION,
-
- /**
- * ioctl(..., char *name)
- * \brief Get a IOCtl call ID from a symbolic name
- * \return ID number of the call, or 0 if not found
- *
- * This call allows user applications to not need to know the ID numbers
- * of this driver's IOCtl calls by taking a string and returning the
- * IOCtl call number associated with that method name.
- */
- DRV_IOCTL_LOOKUP,
-
- /**
- * \brief First non-reserved IOCtl number for driver extension
- */
- DRV_IOCTL_USERMIN = 0x1000,
-};
-
-/**
- * \brief eTplDrv_IOCtl.DRV_IOCTL_LOOKUP names for the core IOCtls
- * These are the official lookup names of the core calls
- */
-#define DRV_IOCTLNAMES "type", "ident", "version", "lookup"
-
-/**
- * \brief Helper macro for the base IOCtl calls
- * \param _type Type number from eTplDrv_Type to return
- * \param _ident String of max 32-characters that identifies this driver
- * \param _version Driver's 8.8.8 BCD version number
- * \param _ioctls Pointer to the IOCtls string array
- * \warning If you have DEBUG enabled in the calling file, this function
- * will do LEAVE()s before returning, so make sure that the
- * IOCtl function is ENTER()ed when using debug with this macro
- *
- * Usage: (Id is the IOCtl call ID)
- * \code
- * switch(Id)
- * {
- * BASE_IOCTLS(DRV_TYPE_MISC, "Ident", 0x100, csaIOCtls)
- * // Your IOCtls go here, starting at index 4
- * }
- * \endcode
- */
-#define BASE_IOCTLS(_type, _ident, _version, _ioctls) \
- case DRV_IOCTL_TYPE: LEAVE('i', (_type)); return (_type);\
- case DRV_IOCTL_IDENT: {\
- int tmp = ModUtil_SetIdent(Data, (_ident));\
- LEAVE('i', tmp); return tmp;\
- }\
- case DRV_IOCTL_VERSION: LEAVE('x', (_version)); return (_version);\
- case DRV_IOCTL_LOOKUP:{\
- int tmp = ModUtil_LookupString( _ioctls, (const char*)Data );\
- LEAVE('i', tmp);\
- return tmp;\
- }
-
-/**
- * \enum eTplDrv_Type
- * \brief Driver Types returned by DRV_IOCTL_TYPE
- */
-enum eTplDrv_Type {
- DRV_TYPE_NULL, //!< NULL Type - Custom Interface
- DRV_TYPE_MISC, //!< Miscelanious Compilant - Supports the core calls
- DRV_TYPE_TERMINAL, //!< Terminal - see api_drv_terminal.h
- DRV_TYPE_VIDEO, //!< Video - see api_drv_video.h
- DRV_TYPE_SOUND, //!< Audio
- DRV_TYPE_DISK, //!< Disk - see api_drv_disk.h
- DRV_TYPE_KEYBOARD, //!< Keyboard - see api_drv_keyboard.h
- DRV_TYPE_MOUSE, //!< Mouse
- DRV_TYPE_JOYSTICK, //!< Joystick / Gamepad
- DRV_TYPE_NETWORK //!< Network Device - see api_drv_network.h
-};
-
-#endif
+++ /dev/null
-/**\r
- * \file api_drv_disk.h\r
- * \brief Disk Driver Interface Definitions\r
- * \author John Hodge (thePowersGang)\r
- * \r
- * \section Nomeclature\r
- * All addreses are 64-bit counts of bytes from the logical beginning of\r
- * the disk unless explicitly stated.\r
- * \r
- * \section dirs VFS Layout\r
- * Disk drivers have a flexible directory layout. The root directory can\r
- * contain subdirectories, with the only conditions being that all nodes\r
- * must support ::eTplDrv_IOCtl with DRV_IOCTL_TYPE returning DRV_TYPE_DISK.\r
- * And all file nodes representing disk devices (or partitions) and implemeting\r
- * ::eTplDisk_IOCtl fully\r
- * \r
- * \section files Files\r
- * When a read or write occurs on a normal file in the disk driver it will\r
- * read/write the represented device. The accesses need not be aligned to\r
- * the block size, however aligned reads/writes should be handled specially\r
- * to improve speed (this can be aided by using ::DrvUtil_ReadBlock and\r
- * ::DrvUtil_WriteBlock)\r
- */\r
-#ifndef _API_DRV_DISK_H\r
-#define _API_DRV_DISK_H\r
-\r
-#include <api_drv_common.h>\r
-\r
-/**\r
- * \enum eTplDisk_IOCtl\r
- * \brief Common Disk IOCtl Calls\r
- * \extends eTplDrv_IOCtl\r
- */\r
-enum eTplDisk_IOCtl {\r
- /**\r
- * ioctl(..., void)\r
- * \brief Get the block size\r
- * \return Size of a hardware block for this device\r
- */\r
- DISK_IOCTL_GETBLOCKSIZE = 4,\r
- \r
- /**\r
- * ioctl(..., tTplDisk_CacheRegion *RegionInfo)\r
- * \brief Sets the cache importantce and protocol for a section of\r
- * memory.\r
- * \param RegionInfo Pointer to a region information structure\r
- * \return Boolean failure\r
- */\r
- DISK_IOCTL_SETCACHEREGION,\r
- \r
- /**\r
- * ioctl(..., Uint64 *Info[2])\r
- * \brief Asks the driver to precache a region of disk.\r
- * \param Region 64-bit Address and Size pair describing the area to cache\r
- * \return Number of blocks cached\r
- */\r
- DISK_IOCTL_PRECACHE,\r
- \r
- /**\r
- * ioclt(..., Uint64 *Region[2])\r
- * \brief Asks to driver to flush the region back to disk\r
- * \param Region 64-bit Address and Size pair describing the area to flush\r
- * \note If Region[0] == -1 then the entire disk's cache is flushed\r
- * \return Number of blocks flushed (or 0 for entire disk)\r
- */\r
- DISK_IOCTL_FLUSH\r
-};\r
-\r
-/**\r
- * \brief Describes the cache parameters of a region on the disk\r
- */\r
-typedef struct sTplDisk_CacheRegion\r
-{\r
- Uint64 Base; //!< Base of cache region\r
- Uint64 Length; //!< Size of cache region\r
- /**\r
- * \brief Cache Protocol & Flags\r
- * \r
- * The low 4 bits denot the cache protocol to be used by the\r
- * region (see ::eTplDisk_CacheProtocols for a list).\r
- * The high 4 bits denote flags to apply to the cache (see\r
- * ::eTplDisk_CacheFlags)\r
- */\r
- Uint8 Flags;\r
- Uint8 Priority; //!< Lower is a higher proritory\r
- /**\r
- * \brief Maximum size of cache, in blocks\r
- * \note If CacheSize is zero, the implemenation defined limit is used\r
- */\r
- Uint16 CacheSize;\r
-} tTplDisk_CacheRegion;\r
-\r
-/**\r
- * \brief Cache protocols to use\r
- */\r
-enum eTplDisk_CacheProtocols\r
-{\r
- /**\r
- * \brief Don't cache the region\r
- */\r
- \r
- DISK_CACHEPROTO_DONTCACHE,\r
- /**\r
- * \brief Most recently used blocks cached\r
- * \note This is the default action for undefined regions\r
- */\r
- DISK_CACHEPROTO_RECENTLYUSED,\r
- /**\r
- * \brief Cache the entire region in memory\r
- * \r
- * This is a faster version of setting Length to CacheSize*BlockSize\r
- */\r
- DISK_CACHEPROTO_FULLCACHE,\r
- \r
- /**\r
- * \brief Cache only on demand\r
- * \r
- * Only cache when the ::DISK_IOCTL_PRECACHE IOCtl is used\r
- */\r
- DISK_CACHEPROTO_EXPLICIT\r
-};\r
-\r
-/**\r
- * \brief Flags for the cache\r
- */\r
-enum eTplDisk_CacheFlags\r
-{\r
- /**\r
- * \brief Write all changes to the region straight back to media\r
- */\r
- DISK_CACHEFLAG_WRITETHROUGH = 0x10\r
-};\r
-\r
-/**\r
- * \brief IOCtl name strings\r
- */\r
-#define DRV_DISK_IOCTLNAMES "get_block_size","set_cache_region","set_precache"\r
-\r
-/**\r
- * \name Disk Driver Utilities\r
- * \{\r
- */\r
-\r
-/**\r
- * \brief Callback function type used by DrvUtil_ReadBlock and DrvUtil_WriteBlock\r
- * \param Address Zero based block number to read\r
- * \param Count Number of blocks to read\r
- * \param Buffer Destination for read blocks\r
- * \param Argument Argument provided in ::DrvUtil_ReadBlock and ::DrvUtil_WriteBlock\r
- */\r
-typedef Uint (*tDrvUtil_Read_Callback)(Uint64 Address, Uint Count, void *Buffer, Uint Argument);\r
-typedef Uint (*tDrvUtil_Write_Callback)(Uint64 Address, Uint Count, const void *Buffer, Uint Argument);\r
-\r
-/**\r
- * \brief Reads a range from a block device using aligned reads\r
- * \param Start Base byte offset\r
- * \param Length Number of bytes to read\r
- * \param Buffer Destination for read data\r
- * \param ReadBlocks Callback function to read a sequence of blocks\r
- * \param BlockSize Size of an individual block\r
- * \param Argument An argument to pass to \a ReadBlocks\r
- * \return Number of bytes read\r
- */\r
-extern Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,\r
- tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument);\r
-/**\r
- * \brief Writes a range to a block device using aligned writes\r
- * \param Start Base byte offset\r
- * \param Length Number of bytes to write\r
- * \param Buffer Destination for read data\r
- * \param ReadBlocks Callback function to read a sequence of blocks\r
- * \param WriteBlocks Callback function to write a sequence of blocks\r
- * \param BlockSize Size of an individual block\r
- * \param Argument An argument to pass to \a ReadBlocks and \a WriteBlocks\r
- * \return Number of bytes written\r
- */\r
-extern Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer,\r
- tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks,\r
- Uint64 BlockSize, Uint Argument);\r
-\r
-/**\r
- * \}\r
- */\r
-\r
-#endif\r
+++ /dev/null
-/**\r
- * \file api_drv_joystick.h\r
- * \brief Joystick Driver Interface Definitions\r
- * \author John Hodge (thePowersGang)\r
- * \r
- * \section dirs VFS Layout\r
- * Joystick drivers define a single VFS node, that acts as a fixed size file.\r
- * Reads from this file return the current state, writes are ignored.\r
- *\r
- * \section File Structure\r
- * The device file must begin with a valid sJoystick_FileHeader structure.\r
- * The file header is followed by \a NAxies instances of sJoystick_Axis, one\r
- * for each axis.\r
- * This is followed by \a NButtons boolean values (represented using \a Uint8),\r
- * each representing the state of a single button (where 0 is unpressed,\r
- * 0xFF is fully depressed - intermediate values are valid in the case of\r
- * variable-pressure buttons)\r
- */\r
-#ifndef _API_DRV_JOYSTICK_H\r
-#define _API_DRV_JOYSTICK_H\r
-\r
-#include <api_drv_common.h>\r
-\r
-/**\r
- * \enum eTplJoystick_IOCtl\r
- * \brief Common Joystick IOCtl Calls\r
- * \extends eTplDrv_IOCtl\r
- */\r
-enum eTplJoystick_IOCtl {\r
- /**\r
- * ioctl(..., tJoystick_Callback *Callback)\r
- * \brief Sets the callback\r
- * \note Can be called from kernel mode only\r
- *\r
- * Sets the callback that is called when a event occurs (button or axis\r
- * change). This function pointer must be in kernel mode (although,\r
- * kernel->user or kernel->ring3driver abstraction functions can be used)\r
- * \r
- * Axis events depend on the axis limit, if non-zero, the callback fires\r
- * if the cursor position changes. Otherwise it fires when the axis value\r
- * (cursor accelleration) changes.\r
- */\r
- JOY_IOCTL_SETCALLBACK = 4,\r
-\r
- /**\r
- * ioctl(..., int *Argument)\r
- * \brief Set the argument passed as the first parameter to the callback\r
- * \note Kernel mode only\r
- */\r
- JOY_IOCTL_SETCALLBACKARG,\r
-\r
- /**\r
- * ioctl(..., tJoystickNumValue *)\r
- * \brief Set maximum value for sJoystick_Axis.CursorPos\r
- * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
- */\r
- JOY_IOCTL_GETSETAXISLIMIT,\r
-\r
- /**\r
- * ioctl(..., tJoystickNumValue *)\r
- * \brief Set the value of sJoystick_Axis.CursorPos\r
- * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
- */\r
- JOY_IOCTL_GETSETAXISPOSITION,\r
- \r
- /**\r
- * ioctl(..., tJoystickNumValue *)\r
- * \brief Set axis flags\r
- * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
- * \todo Define flag values\r
- */\r
- JOY_IOCTL_GETSETAXISFLAGS,\r
-\r
- /**\r
- * ioctl(..., tJoystickNumValue *)\r
- * \brief Set Button Flags\r
- * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
- * \todo Define flag values\r
- */\r
- JOY_IOCTL_GETSETBUTTONFLAGS,\r
-};\r
-\r
-/**\r
- * \brief Symbolic names for Joystick IOCtls\r
- */\r
-#define DRV_JOY_IOCTLNAMES "set_callback", "set_callback_arg", "getset_axis_limit", "getset_axis_position", \\r
- "getset_axis_flags", "getset_button_flags"\r
-\r
-// === TYPES ===\r
-typedef struct sJoystick_NumValue tJoystick_NumValue;\r
-typedef struct sJoystick_FileHeader tJoystick_FileHeader;\r
-typedef struct sJoystick_Axis tJoystick_Axis;\r
-\r
-/**\r
- * \brief Number/Value pair for joystick IOCtls\r
- */\r
-struct sJoystick_NumValue\r
-{\r
- int Num; //!< Axis/Button number\r
- int Value; //!< Value (see IOCtl defs for meaning)\r
-};\r
-\r
-/**\r
- * \brief Callback type for JOY_IOCTL_SETCALLBACK\r
- * \param Ident Ident value passed to JOY_IOCTL_SETCALLBACK\r
- * \r
- */\r
-typedef void (*tJoystick_Callback)(int Ident, int bIsAxis, int Num, int Delta);\r
-\r
-/**\r
- * \struct sJoystick_FileHeader\r
- * \brief Format of the joystick VFS node's first bytes\r
- */\r
-struct sJoystick_FileHeader\r
-{\r
- Uint16 NAxies; //!< Number of Axies\r
- Uint16 NButtons; //!< Number of buttons\r
-};\r
-\r
-/**\r
- * \brief Axis Definition in file data\r
- *\r
- * Describes the current state of an axis on the joystick.\r
- * \a CursorPos is between zero and the current limit set by the\r
- * JOY_IOCTL_GETSETAXISLIMIT IOCtl, while \a CurValue indicates the\r
- * current position of the joystick axis. This is defined to be between\r
- * \a MinValue and \a MaxValue.\r
- */\r
-struct sJoystick_Axis\r
-{\r
- Sint16 MinValue; //!< Minumum value for \a CurValue\r
- Sint16 MaxValue; //!< Maximum value for \a CurValue\r
- Sint16 CurValue; //!< Current value (joystick position)\r
- Uint16 CursorPos; //!< Current state (cursor position)\r
-};\r
-\r
-/**\r
- * \brief Macro to define a structure for a joystick's node\r
- * \param _naxies Number of axies\r
- * \param _nbuttons Number of buttons\r
- * \note This just defines the structure, it's up to the driver to set the\r
- * sJoystick_FileHeader.NAxies and sJoystick_FileHeader.NButtons fields.\r
- */\r
-#define JOY_INFOSTRUCT(_naxies, _nbuttons) struct { \\r
- Uint16 NAxies, NButtons;\\r
- tJoystick_Axis Axies[_naxies];\\r
- Uint16 Buttons[_nbuttons];\\r
- }\r
-\r
-#endif\r
+++ /dev/null
-/**\r
- * \file api_drv_keyboard.h\r
- * \brief Keyboard Driver Interface Definitions\r
- * \author John Hodge (thePowersGang)\r
- * \r
- * \section dirs VFS Layout\r
- * Keyboard drivers consist of only a single node, which is a normal file\r
- * node with a size of zero. All reads and writes to this node are ignored\r
- * (tVFS_Node.Read and tVFS_Node.Write are NULL)\r
- */\r
-#ifndef _API_DRV_KEYBOARD_H\r
-#define _API_DRV_KEYBOARD_H\r
-\r
-#include <api_drv_common.h>\r
-\r
-/**\r
- * \enum eTplKeyboard_IOCtl\r
- * \brief Common Keyboard IOCtl Calls\r
- * \extends eTplDrv_IOCtl\r
- */\r
-enum eTplKeyboard_IOCtl {\r
- /**\r
- * ioctl(..., int *Rate)\r
- * \brief Get/Set Repeat Rate\r
- * \param Rate New repeat rate (pointer)\r
- * \return Current/New Repeat rate\r
- * \r
- * Gets/Set the repeat rate (actually the time in miliseconds between\r
- * repeats) of a held down key.\r
- * If the rate is set to zero, repeating will be disabled.\r
- */\r
- KB_IOCTL_REPEATRATE = 4,\r
- \r
- /**\r
- * ioctl(..., int *Delay)\r
- * \brief Get/Set Repeat Delay\r
- * \param Delay New repeat delay (pointer)\r
- * \return Current/New repeat delay\r
- * \r
- * Gets/Set the time in miliseconds before a key starts repeating\r
- * after a key is pressed.\r
- * Setting the delay to a negative number will cause the function to\r
- * return -1\r
- */\r
- KB_IOCTL_REPEATDELAY,\r
- \r
- \r
- /**\r
- * ioctl(..., tKeybardCallback *Callback)\r
- * \brief Sets the callback\r
- * \note Can be called from kernel mode only\r
- * \r
- * Sets the function to be called when a key event occurs (press, release\r
- * or repeat). This function pointer must be in kernel mode (although,\r
- * kernel->user or kernel->ring3driver abstraction functions can be used)\r
- *\r
- * This function is called when a key is pressed, repeated or released.\r
- * If the raw scancode is to be included with the key event, it should precede\r
- * the event.\r
- */\r
- KB_IOCTL_SETCALLBACK\r
-};\r
-\r
-/**\r
- * \brief Symbolic names for Keyboard IOCtls\r
- */\r
-#define DRV_KEYBAORD_IOCTLNAMES "getset_repeat_rate", "getset_repeat_delay", "set_callback"\r
-\r
-/**\r
- * \brief Callback type for KB_IOCTL_SETCALLBACK\r
- * \param Key Key symbol (Unicode or eTplKeyboard_KeyCodes)\r
- */\r
-typedef void (*tKeybardCallback)(Uint32 Key);\r
-\r
-/**\r
- * \name Callback key flags\r
- * \brief Flags for values passed to the callback\r
- * \{\r
- */\r
-#define KEY_CODEPOINT_MASK 0x3FFFFFFF\r
-#define KEY_ACTION_MASK 0xC0000000\r
-#define KEY_ACTION_PRESS 0x00000000 //!< Key pressed\r
-#define KEY_ACTION_RELEASE 0x40000000 //!< Key released\r
-#define KEY_ACTION_REFIRE 0x80000000 //!< Repeated key\r
-#define KEY_ACTION_RAWSYM 0xC0000000 //!< Raw key symbol (comes before a press/repeat/release)\r
-/**\r
- * \}\r
- */\r
-\r
-/**\r
- * \brief Symbolic key codes\r
- * \r
- * These key codes represent non-pritable characters and are placed above\r
- * the Unicode character space.\r
- * If the using driver recieves a key code with the 31st bit set, it means\r
- * that that key has been released.\r
- */\r
-enum eTplKeyboard_KeyCodes {\r
- KEY_ESC = 0x1B, //!< Escape Character\r
- \r
- KEY_NP_MASK = 0x20000000, //! Mask for non-printable characters\r
- \r
- /**\r
- * \name Special Keys\r
- * \brief These keys are usually used on their own\r
- * \{\r
- */\r
- KEY_CAPSLOCK,\r
- KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,\r
- KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, \r
- KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,\r
- KEY_NUMLOCK, KEY_SCROLLLOCK,\r
- KEY_HOME, KEY_END, KEY_INS, KEY_DEL,\r
- KEY_PAUSE, KEY_BREAK,\r
- KEY_PGUP, KEY_PGDOWN,\r
- KEY_KPENTER, KEY_KPSLASH, KEY_KPMINUS, KEY_KPPLUS, KEY_KPSTAR,\r
- KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT,\r
- KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL,\r
- KEY_LWIN, KEY_RWIN,\r
- KEY_MENU,\r
- /**\r
- * \}\r
- */\r
- \r
- // Modifiers\r
- /**\r
- * \name Modifiers\r
- * \brief These keye usually alter the character stream sent to the user\r
- * \{\r
- */\r
- KEY_MODIFIERS = 0x30000000,\r
- KEY_LCTRL, KEY_RCTRL,\r
- KEY_LALT, KEY_RALT,\r
- KEY_LSHIFT, KEY_RSHIFT,\r
- /**\r
- * \}\r
- */\r
-};\r
-\r
-\r
-#endif\r
+++ /dev/null
-/**\r
- * \file api_drv_network.h\r
- * \brief Network Interface Driver Interface Definitions\r
- * \r
- * \section dirs VFS Layout\r
- * All network drivers must have the following basic VFS structure\r
- * The root of the driver will only contain files that are named from zero\r
- * upwards that represent the present network adapters that this driver\r
- * controls. All VFS nodes must implement ::eTplDrv_IOCtl with\r
- * DRV_IOCTL_TYPE returning DRV_TYPE_NETWORK.\r
- * The adapter nodes must also implement ::eTplNetwork_IOCtl fully\r
- * (unless it is noted in the ::eTplNetwork_IOCtl documentation that a\r
- * call is optional)\r
- * \r
- * \section files Adapter Files\r
- * \subsection Reading\r
- * When an adapter file is read from, the driver will block the reading\r
- * thread until a packet arrives (if there is not already an unhandled\r
- * packet in the queue) this will then be read into the destination buffer.\r
- * If the packet does not fit in the buffer, the end of it is discarded.\r
- * Likewise, if the packet does not completely fill the buffer, the call\r
- * will still read to the buffer and then return the size of the packet.\r
- * \subsection Writing\r
- * When an adapter is written to, the data written is encoded as a packet\r
- * and sent, if the data is not the correct size to be sent (if the packet\r
- * is too small, or if it is too large) -1 should be returned and the packet\r
- * will not be sent.\r
- */\r
-#ifndef _API_DRV_NETWORK_H\r
-#define _API_DRV_NETWORK_H\r
-\r
-#include <api_drv_common.h>\r
-\r
-/**\r
- * \enum eTplNetwork_IOCtl\r
- * \brief Common Network IOCtl Calls\r
- * \extends eTplDrv_IOCtl\r
- */\r
-enum eTplNetwork_IOCtl {\r
- /**\r
- * ioctl(..., Uint8 *MAC[6])\r
- * \brief Get the MAC address of the interface\r
- * \return 1 on success, 0 if the file is the root, -1 on error\r
- * \r
- * Copies the six byte Media Access Control (MAC) address of the\r
- * adapter to the \a MAC array.\r
- */\r
- NET_IOCTL_GETMAC = 4\r
-};\r
-\r
-/**\r
- * \brief IOCtl name strings for use with eTplDrv_IOCtl.DRV_IOCTL_LOOKUP\r
- */\r
-#define DRV_NETWORK_IOCTLNAMES "get_mac_addr"\r
-\r
-#endif\r
+++ /dev/null
-/**\r
- * \file api_drv_terminal.h\r
- * \brief Terminal Driver Interface Definitions\r
-*/\r
-#ifndef _API_DRV_TERMINAL_H\r
-#define _API_DRV_TERMINAL_H\r
-\r
-#include <api_drv_common.h>\r
-\r
-/**\r
- * \brief Common Terminal IOCtl Calls\r
- * \extends eTplDrv_IOCtl\r
- */\r
-enum eTplTerminal_IOCtl {\r
- /**\r
- * ioctl(..., int *mode)\r
- * \brief Get/Set the current video mode type\r
- * \param mode Pointer to an integer with the new mode number (or NULL)\r
- * If \a mode is non-NULL the current terminal mode is changed/updated\r
- * to the mode indicated by \a *mode\r
- * \note See ::eTplTerminal_Modes\r
- * \return Current/new terminal mode\r
- */\r
- TERM_IOCTL_MODETYPE = 4,\r
- \r
- /**\r
- * ioctl(..., int *width)\r
- * \brief Get/set the display width\r
- * \param width Pointer to an integer containing the new width (or NULL)\r
- * \return Current/new width\r
- * \r
- * If \a width is non-NULL the current width is updated (but is not\r
- * applied until ::TERM_IOCTL_MODETYPE is called with \a mode non-NULL.\r
- */\r
- TERM_IOCTL_WIDTH,\r
- \r
- /**\r
- * ioctl(..., int *height)\r
- * \brief Get/set the display height\r
- * \param height Pointer to an integer containing the new height\r
- * \return Current height\r
- * \r
- * If \a height is non-NULL the current height is updated (but is not\r
- * applied until ::TERM_IOCTL_MODETYPE is called with a non-NULL \a mode.\r
- */\r
- TERM_IOCTL_HEIGHT,\r
- \r
- /**\r
- * ioctl(..., tTerm_IOCtl_Mode *info)\r
- * \brief Queries the current driver about it's native modes\r
- * \param info A pointer to a ::tTerm_IOCtl_Mode with \a ID set to\r
- * the mode index (or NULL)\r
- * \return Number of modes\r
- * \r
- * If \a info is NULL, the number of avaliable vative display modes\r
- * is returned. These display modes will have sequential ID numbers\r
- * from zero up to this value.\r
- * \r
- * \note The id field of \a info is not for use with ::TERM_IOCTL_MODETYPE\r
- * This field is just for indexing the mode to get its information.\r
- */\r
- TERM_IOCTL_QUERYMODE,\r
- \r
- /**\r
- * ioctl(...)\r
- * \brief Forces the current terminal to be shown\r
- */\r
- TERM_IOCTL_FORCESHOW,\r
- \r
- /**\r
- * ioctl(..., tVideo_IOCtl_Pos *pos)\r
- * \brief Returns the current text cursor position\r
- * \param pos New cursor position. If NULL, the position is not changed\r
- * \return Cursor position (as X+Y*Width)\r
- */\r
- TERM_IOCTL_GETSETCURSOR,\r
- \r
- /**\r
- * ioctl(..., tVideo_IOCtl_Bitmap *Bmp)\r
- * \brief Set the video cursor bitmap\r
- * \param Bmp New bitmap (if NULL, the current bitmap is removed)\r
- * \return Boolean failure\r
- */\r
- TERM_IOCTL_SETCURSORBITMAP,\r
-};\r
-\r
-/**\r
- * \brief Virtual Terminal Mode\r
- * Describes a VTerm mode to the caller of ::TERM_IOCTL_QUERYMODE\r
- */\r
-typedef struct sTerm_IOCtl_Mode\r
-{\r
- short ID; //!< Zero Based index of mode\r
- short DriverID; //!< Driver's ID number (from ::tVideo_IOCtl_Mode)\r
- Uint16 Height; //!< Height\r
- Uint16 Width; //!< Width\r
- Uint8 Depth; //!< Bits per cell\r
- struct {\r
- unsigned bText: 1; //!< Text Mode marker\r
- unsigned unused: 7;\r
- };\r
-} tTerm_IOCtl_Mode;\r
-\r
-/**\r
- * \brief Terminal Modes\r
- */\r
-enum eTplTerminal_Modes {\r
- /**\r
- * \brief UTF-8 Text Mode\r
- * Any writes to the terminal file are treated as UTF-8 encoded\r
- * strings and reads will also return UTF-8 strings.\r
- */\r
- TERM_MODE_TEXT,\r
- \r
- /**\r
- * \brief 32bpp Framebuffer\r
- * Writes to the terminal file will write to the framebuffer.\r
- * Reads will return UTF-32 characters\r
- */\r
- TERM_MODE_FB,\r
- \r
- /**\r
- * \brief 32bpp 2D Accellerated mode\r
- * Writes to the terminal file will be read as a command stream\r
- * defined in ::eTplTerminal_2D_Commands\r
- */\r
- TERM_MODE_2DACCEL,\r
- \r
- /**\r
- * \brief OpenGL 2D/3D\r
- * Writes to the terminal file will send 3D commands\r
- * Reads will return UTF-32 characters\r
- * \note May or may not stay in the spec\r
- */\r
- TERM_MODE_3D,\r
- \r
- /**\r
- * \brief Number of terminal modes\r
- */\r
- NUM_TERM_MODES\r
-};\r
-\r
-/**\r
- * \brief 2D Command IDs\r
- * \todo Complete this structure\r
- * \r
- * Command IDs for when the terminal type is eTplTerminal_Modes.TERM_MODE_2DACCEL\r
- */\r
-enum eTplTerminal_2D_Commands\r
-{\r
- /**\r
- * \brief No Operation - Used for padding\r
- */\r
- TERM_2DCMD_NOP,\r
- \r
- /**\r
- * (Uint16 X, Y, W, H, Uint32 Data[])\r
- * \brief Blits a bitmap to the display\r
- * \param X,Y Coordinates of Top-Left corner\r
- * \param W,H Dimensions\r
- * \param Data 32-bpp pixel data\r
- */\r
- TERM_2DCMD_PUSH\r
-};\r
-\r
-#endif\r
+++ /dev/null
-/**\r
- * \file api_drv_video.h\r
- * \brief Video Driver Interface Definitions\r
- * \note For AcessOS Version 1\r
- * \r
- * Video drivers extend the common driver interface api_drv_common.h\r
- * and must support the IOCtl numbers defined in this file to be\r
- * compatable with Acess (drivers may implement more IOCtls above\r
- * DRV_IOCTL_USERMIN).\r
- * \r
- * \section IOCtls\r
- * As said, a compatable driver must implement these calls correctly,\r
- * but they may choose not to allow direct user access to the framebuffer.\r
- * \r
- * \section Screen Contents\r
- * Writes to the driver's file while in component colour modes\r
- * must correspond to a change of the contents of the screen. The framebuffer\r
- * must start at offset 0 in the file.\r
- * Reading from the screen must either return zero, or read from the\r
- * framebuffer.\r
- * \r
- * \section Mode Support\r
- * All video drivers must support text output for every resolution (hardware\r
- * accelerated or software), and at least the _BLIT and _FILL 2D operations\r
- * (these may be implemented specifically for text mode).\r
- */\r
-#ifndef _API_DRV_VIDEO_H\r
-#define _API_DRV_VIDEO_H\r
-\r
-#include <api_drv_common.h>\r
-\r
-/**\r
- * \enum eTplVideo_IOCtl\r
- * \brief Common Video IOCtl Calls\r
- * \extends eTplDrv_IOCtl\r
- */\r
-enum eTplVideo_IOCtl {\r
- /**\r
- * ioctl(..., int *mode)\r
- * \brief Get/Set Mode\r
- * \return Current mode ID or -1 on error\r
- * \r
- * If \a mode is non-NULL, the current video mode is set to \a *mode.\r
- * This updated ID is then returned to the user.\r
- */\r
- VIDEO_IOCTL_GETSETMODE = 4,\r
- \r
- /**\r
- * ioctl(..., tVideo_IOCtl_Mode *info)\r
- * \brief Find a matching mode\r
- * \return 1 if a mode was found, 0 otherwise\r
- * \r
- * Using avaliable modes matching the \a bpp and \a flags fields\r
- * set the \a id, \a width and \a heights fields to the closest\r
- * matching mode.\r
- */\r
- VIDEO_IOCTL_FINDMODE,\r
- \r
- /**\r
- * ioctl(..., tVideo_IOCtl_Mode *info)\r
- * \brief Get mode info\r
- * \return 1 if the mode exists, 0 otherwise\r
- * \r
- * Set \a info's fields to the mode specified by the \a id field.\r
- */\r
- VIDEO_IOCTL_MODEINFO,\r
- \r
- /**\r
- * ioctl(..., int *NewFormat)\r
- * \brief Switches between Text, Framebuffer and 3D modes\r
- * \param NewFormat Pointer to the new format code (see eTplVideo_BufFormats)\r
- * \return Original format\r
- * \r
- * Enabes and disables the video text mode, changing the behavior of\r
- * writes to the device file.\r
- */\r
- VIDEO_IOCTL_SETBUFFORMAT,\r
- \r
- /**\r
- * ioctl(..., tVideo_IOCtl_Pos *pos)\r
- * \brief Sets the cursor position\r
- * \return Boolean success\r
- * \r
- * Set the text mode cursor position (if it is supported)\r
- * If the \a pos is set to (-1,-1) the cursor is hidden, otherwise\r
- * \a pos MUST be within the current screen size (as given by the\r
- * current mode's tVideo_IOCtl_Mode.width and tVideo_IOCtl_Mode.height\r
- * fields).\r
- */\r
- VIDEO_IOCTL_SETCURSOR,\r
- \r
- /**\r
- * ioctl(..., tVideo_IOCtl_Bitmap *Image)\r
- * \brief Sets the cursor image\r
- * \return Boolean success\r
- *\r
- * Sets the graphics mode cursor image\r
- */\r
- VIDEO_IOCTL_SETCURSORBITMAP\r
-};\r
-\r
-/**\r
- * \brief Symbolic names for Video IOCtls (#4 onwards)\r
- */\r
-#define DRV_VIDEO_IOCTLNAMES "getset_mode", "find_mode", "mode_info", "set_buf_format", "set_cursor", "set_cursor_bitmap"\r
-\r
-/**\r
- * \brief Mode Structure used in IOCtl Calls\r
- * \r
- * Defines a video mode supported by (or requested of) this driver (depending\r
- * on what ioctl call is used)\r
- */\r
-typedef struct sVideo_IOCtl_Mode\r
-{\r
- short id; //!< Mode ID\r
- Uint16 width; //!< Width\r
- Uint16 height; //!< Height\r
- Uint8 bpp; //!< Bits per pixel\r
- Uint8 flags; //!< Mode Flags (none defined, should be zero)\r
-} tVideo_IOCtl_Mode;\r
-\r
-/**\r
- * \brief Buffer Format Codes\r
- */\r
-enum eTplVideo_BufFormats\r
-{\r
- /**\r
- * \brief Text Mode\r
- * \r
- * The device file presents itself as an array of ::tVT_Char\r
- * each describing a character cell on the screen.\r
- * These cells are each \a giVT_CharWidth pixels wide and\r
- * \a giVT_CharHeight high.\r
- */\r
- VIDEO_BUFFMT_TEXT,\r
- /**\r
- * \brief Framebuffer Mode\r
- * \r
- * The device file presents as an array of 32-bpp pixels describing\r
- * the entire screen. The format of a single pixel is in xRGB format\r
- * (top 8 bits ignored, next 8 bits red, next 8 bits green and\r
- * the bottom 8 bits blue)\r
- */\r
- VIDEO_BUFFMT_FRAMEBUFFER,\r
- /**\r
- * \brief 2D Accelerated Mode\r
- * \r
- * The device file acts as a character device, accepting a stream of\r
- * commands described in eTplVideo_2DCommands when written to.\r
- */\r
- VIDEO_BUFFMT_2DSTREAM,\r
- /**\r
- * \brief 3D Accelerated Mode\r
- * \r
- * The device file acts as a character device, accepting a stream of\r
- * commands described in eTplVideo_3DCommands when written to.\r
- */\r
- VIDEO_BUFFMT_3DSTREAM\r
-};\r
-\r
-/**\r
- * \brief 2D Accellerated Video Commands\r
- * \r
- * Commands passed in the command stream for ::VIDEO_BUFFMT_2DSTREAM\r
- */\r
-enum eTplVideo_2DCommands\r
-{\r
- /**\r
- * \brief No Operation\r
- */\r
- VIDEO_2DOP_NOP,\r
- /**\r
- * \brief Fill a region\r
- * \param X Uint16 - Leftmost pixels of the region\r
- * \param Y Uint16 - Topmost pixels of the region\r
- * \param W Uint16 - Width of the region\r
- * \param H Uint16 - Height of the region\r
- * \param Colour Uint32 - Value to fill with\r
- */\r
- VIDEO_2DOP_FILL,\r
- /**\r
- * \brief Copy a region from one part of the framebuffer to another\r
- * \param DestX Uint16 - Leftmost pixels of the destination\r
- * \param DestY Uint16 - Topmost pixels of the destination\r
- * \param SrcX Uint16 - Leftmost pixels of the source\r
- * \param SrcY Uint16 - Topmost pixels of the source\r
- * \param Width Uint16 - Width of the region\r
- * \param Height Uint16 - Height of the region\r
- */\r
- VIDEO_2DOP_BLIT,\r
-\r
-\r
- /**\r
- * \brief Copy a region from video memory to the framebuffer\r
- */\r
- VIDEO_2DOP_BLITBUF,\r
-\r
- /**\r
- * \brief Copy and scale a region from video memory to the framebuffer\r
- */\r
- VIDEO_2DOP_BLITSCALEBUF,\r
-\r
- NUM_VIDEO_2DOPS\r
-};\r
-\r
-/**\r
- * \brief Describes a position in the video framebuffer\r
- */\r
-typedef struct sVideo_IOCtl_Pos\r
-{\r
- Sint16 x; //!< X Coordinate\r
- Sint16 y; //!< Y Coordinate\r
-} tVideo_IOCtl_Pos;\r
-\r
-/**\r
- * \brief Bitmap object (out of band image)\r
- */\r
-typedef struct sVideo_IOCtl_Bitmap\r
-{\r
- Sint16 W; //!< Width of image\r
- Sint16 H; //!< Height of image\r
- Sint16 XOfs; //!< X Offset of center\r
- Sint16 YOfs; //!< Y Offset of center\r
- Uint32 Data[]; //!< Image data (ARGB array)\r
-} tVideo_IOCtl_Bitmap;\r
-\r
-/**\r
- * \brief Virtual Terminal Representation of a character\r
- */\r
-typedef struct sVT_Char\r
-{\r
- Uint32 Ch; //!< UTF-32 Character\r
- union {\r
- struct {\r
- Uint16 BGCol; //!< 12-bit Foreground Colour\r
- Uint16 FGCol; //!< 12-bit Background Colour\r
- };\r
- Uint32 Colour; //!< Compound colour for ease of access\r
- };\r
-} tVT_Char;\r
-\r
-/**\r
- * \name Basic builtin colour definitions\r
- * \{\r
- */\r
-#define VT_COL_BLACK 0x0000\r
-#define VT_COL_GREY 0x0888\r
-#define VT_COL_LTGREY 0x0CCC\r
-#define VT_COL_WHITE 0x0FFF\r
-/**\r
- * \}\r
- */\r
-\r
-//! \brief Defines the width of a rendered character\r
-extern int giVT_CharWidth;\r
-//! \brief Defines the height of a rendered character\r
-extern int giVT_CharHeight;\r
-/**\r
- * \name Font rendering\r
- * \{\r
- */\r
-/**\r
- * \brief Driver helper that renders a character to a buffer\r
- * \param Codepoint Unicode character to render\r
- * \param Buffer Buffer to render to\r
- * \param Depth Bit depth of the destination buffer\r
- * \param Pitch Number of bytes per line\r
- * \param BGC 32-bit Background Colour\r
- * \param FGC 32-bit Foreground Colour\r
- * \r
- * This function is provided to help video drivers to support a simple\r
- * text mode by keeping the character rendering abstracted from the driver,\r
- * easing the driver development and reducing code duplication.\r
- */\r
-extern void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC);\r
-/**\r
- * \fn Uint32 VT_Colour12to24(Uint16 Col12)\r
- * \brief Converts a colour from 12bpp to 24bpp\r
- * \param Col12 12-bpp input colour\r
- * \return Expanded 32-bpp (24-bit colour) version of \a Col12\r
- */\r
-extern Uint32 VT_Colour12to24(Uint16 Col12);\r
-/**\r
- * \brief Converts a colour from 12bpp to 14bpp\r
- * \param Col12 12-bpp input colour\r
- * \return 15 bits per pixel value\r
- */\r
-extern Uint16 VT_Colour12to15(Uint16 Col12);\r
-/**\r
- * \brief Converts a colour from 12bpp to 32bpp\r
- * \param Col12 12-bpp input colour\r
- * \param Depth Desired bit depth\r
- * \return \a Depth bit number, denoting Col12\r
- * \r
- * Expands the source colour into a \a Depth bits per pixel representation.\r
- * The colours are expanded with preference to Green, Blue and Red in that order\r
- * (so, green gets the first spare pixel, blue gets the next, and red never gets\r
- * the spare). \n\r
- * The final bit of each component is used to fill the lower bits of the output.\r
- */\r
-extern Uint32 VT_Colour12toN(Uint16 Col12, int Depth);\r
-/**\r
- * \}\r
- */\r
-\r
-/**\r
- * \brief Maximum cursor width for using the DrvUtil software cursor\r
- */\r
-#define DRVUTIL_MAX_CURSOR_W 32\r
-/**\r
- * \brief Maximum cursor width for using the DrvUtil software cursor\r
- */\r
-#define DRVUTIL_MAX_CURSOR_H 32\r
-\r
-/**\r
- * \brief Framebuffer information used by all DrvUtil_Video functions\r
- */\r
-typedef struct sDrvUtil_Video_BufInfo\r
-{\r
- /**\r
- * \name Framebuffer state\r
- * \{\r
- */\r
- /**\r
- * \brief Framebuffer virtual address\r
- */\r
- void *Framebuffer;\r
- /**\r
- * \brief Bytes between the start of each line\r
- */\r
- int Pitch;\r
- /**\r
- * \brief Number of pixels in each line\r
- */\r
- short Width;\r
- /**\r
- * \brief Total number of lines\r
- */\r
- short Height;\r
- /**\r
- * \brief Bit depth of the framebuffer\r
- */\r
- short Depth;\r
- /*\r
- * \}\r
- */\r
- \r
- /**\r
- * \brief Buffer write format\r
- */\r
- short BufferFormat;\r
- \r
- /**\r
- * \name Software cursor controls\r
- * \{\r
- */\r
- /**\r
- * \brief X coordinate of the cursor\r
- */\r
- short CursorX;\r
- /**\r
- * \brief Y coordinate of the cursor\r
- */\r
- short CursorY;\r
-\r
- /**\r
- * \brief Cursor bitmap\r
- */\r
- tVideo_IOCtl_Bitmap *CursorBitmap;\r
- /*\r
- * \}\r
- */\r
-\r
- /*\r
- * \name Internal fields\r
- * \{\r
- */\r
-\r
- /**\r
- * \brief Buffer to store the area under the cursor\r
- */\r
- void *CursorSaveBuf;\r
- \r
- int CursorReadX; //!< X offset in cursor bitmap corresponding to \a CursorDestX\r
- int CursorReadY; //!< Same as \a CursorReadX but for Y\r
- int CursorRenderW; //!< Width of rendered cursor\r
- int CursorRenderH; //!< Height of rendered cursor\r
- int CursorDestX; //!< X coordinate Destination for rendered cursor\r
- int CursorDestY; //!< Y coordinate destination for rendered cursor\r
-\r
- /*\r
- * \}\r
- */\r
-} tDrvUtil_Video_BufInfo;\r
-\r
-/**\r
- * \brief Handlers for eTplVideo_2DCommands\r
- */\r
-typedef struct sDrvUtil_Video_2DHandlers\r
-{\r
- /**\r
- * \brief No Operation, Ignored\r
- * \see VIDEO_2DOP_NOP\r
- */\r
- void *Nop;\r
- /**\r
- * \brief Fill a buffer region\r
- * \param X Lefthand edge\r
- * \param Y Top edge\r
- * \param W Width\r
- * \param H Height\r
- * \param Colour Colour to fill with\r
- * \see VIDEO_2DOP_FILL\r
- */\r
- void (*Fill)(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);\r
- /**\r
- * \brief Fill a buffer region\r
- * \param DestX Lefthand edge of destination\r
- * \param DestY Top edge of destination\r
- * \param SrcX Lefthand edge of source\r
- * \param SrcY Top edge of source\r
- * \param W Width\r
- * \param H Height\r
- * \see VIDEO_2DOP_BLIT\r
- */\r
- void (*Blit)(void *Ent, Uint16 DestX, Uint16 DestY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);\r
-} tDrvUtil_Video_2DHandlers;\r
-\r
-/**\r
- * \brief Handle a 2D operation stream for a driver\r
- * \param Ent Value to pass to handlers\r
- * \param Buffer Stream buffer\r
- * \param Length Length of stream\r
- * \param Handlers Handlers to use for the stream\r
- * \param SizeofHandlers Size of \a tDrvUtil_Video_2DHandlers according\r
- * to the driver. Used as version control and error avoidence.\r
- */\r
-extern int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length,\r
- tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);\r
-\r
-/**\r
- * \brief Perform write operations to a LFB\r
- * \param FBInfo Framebuffer descriptor, see type for details\r
- * \param Offset Offset provided by VFS call\r
- * \param Length Length provided by VFS call\r
- * \param Src Data from VFS call\r
- * \return Number of bytes written\r
- *\r
- * Handles all write modes in software, using the VT font calls for rendering.\r
- * \note Calls the cursor clear and redraw if the cursor area is touched\r
- */\r
-extern int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Src);\r
-\r
-/**\r
- * \name Software cursor rendering\r
- * \{\r
- */\r
-/**\r
- * \brief Set the cursor bitmap for a buffer\r
- * \param Buf Framebuffer descriptor\r
- * \param Bitmap New cursor bitmap\r
- */\r
-extern int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);\r
-/**\r
- * \brief Render the cursor at (\a X, \a Y)\r
- * \param Buf Framebuffer descriptor, see type for details\r
- * \param X X coord of the cursor\r
- * \param Y Y coord of the cursor\r
- */\r
-extern void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);\r
-/**\r
- * \brief Removes the rendered cursor from the screen\r
- * \param Buf Framebuffer descriptor, see type for details\r
- */\r
-extern void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);\r
-\r
-/**\r
- * \brief Text mode cursor image\r
- */\r
-extern tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor;\r
-/**\r
- * \}\r
- */\r
-#endif\r
+++ /dev/null
-/**
- * \file apidoc/arch_x86.h
- * \brief x86(-64) Specific Functions
- * \author John Hodge (thePowersGang)
- *
- * \section toc Table of Contents
- * - \ref portio "Port IO"
- * - \ref dma "DMA - Direct Memory Access"
- *
- * \section portio Port IO
- * The x86 architecture has two memory spaces, the first is the system
- * memory accessable using standard loads and stores. The second is the
- * 16-bit IO Bus. This bus is accessed using the \a in and \a out opcodes
- * and is used to configure devices attached to the system.
- * A driver should not use \a in and \a out directly, but instead use
- * the provided \a in* and \a out* functions to access the IO Bus.
- * This allows the kernel to run a driver in userspace if requested without
- * the binary needing to be altered.
- *
- * \section dma DMA - Direct Memory Access
- */
-
-/**
- * \name IO Bus Access
- * \{
- */
-extern Uint8 inb(Uint16 Port); //!< Read 1 byte from the IO Bus
-extern Uint16 inw(Uint16 Port); //!< Read 2 bytes from the IO Bus
-extern Uint32 inl(Uint16 Port); //!< Read 4 bytes from the IO Bus
-extern Uint64 inq(Uint16 Port); //!< Read 8 bytes from the IO Bus\
-
-extern void outb(Uint16 Port, Uint8 Value); //!< Write 1 byte to the IO Bus
-extern void outw(Uint16 Port, Uint16 Value); //!< Write 2 bytes to the IO Bus
-extern void outl(Uint16 Port, Uint32 Value); //!< Write 4 bytes to the IO Bus
-extern void outq(Uint16 Port, Uint64 Value); //!< Write 8 bytes to the IO Bus
-/**
- * \}
- */
+++ /dev/null
-/**
- * \file apidoc_mainpage.h
- * \brief API Documentation Home Page
- * \author John Hodge (thePowersGang)
- *
- * \mainpage Acess 2 Kernel API Documentation
- *
- * \section intro Introduction
- * These documents attempt to describe the standard Acess 2 (and hopefully
- * future versions) Kernel mode API.
- * The documentation covers filesystem drivers, binary formats and the
- * various device driver interface standards.
- *
- * \section index "Sections"
- * - \ref modules.h "Module Definitions"
- * - Describes how a module is defined in Acess
- * - \ref binary.h "Binary Formats"
- * - Explains how to register a new binary format with the kernel
- * - \ref vfs.h "VFS - The Virtual File System"
- * - The VFS is the core of Acess's driver architecture
- * - \ref drivers "Device Drivers"
- * - Describes how drivers should use the VFS to expose themselves to the
- * user.
- * - Drivers for specific types of hardware must behave in the specific
- * way described here.
- *
- * \page drivers Device Drivers
- *
- * \section toc Contents
- * - \ref drvintro "Introduction"
- * - \ref drv_misc "Miscelanious Devices"
- * - \ref drv_video "Video Drivers"
- *
- * \section drvintro Introduction
- * All Acess2 device drivers communicate with user-level (and other parts
- * of the greater kernel) via the VFS. They register themselves in a similar
- * way to how filesystem drivers do, however instead of registering with
- * the VFS core, they register with a special filesystem driver called the
- * \ref fs_devfs.h "Device Filesystem" (devfs). The DevFS provides the
- * ::DevFS_AddDevice function that takes a ::tDevFS_Driver structure as
- * an agument. This structure specifies the driver's name and its root
- * VFS node. This node is used to provide the user access to the
- * driver's functions via IOCtl calls and Reading or Writing to the driver
- * file. Drivers are also able to expose a readonly buffer by using
- * \ref fs_sysfs.h "ProcDev", usually to provide state information or device
- * capabilities for the the user.
- *
- * The device driver interfaces are all based on the core specifcation
- * in api_drv_common.h (Common Device Driver definitions).
- * The following subsections define the various specific types of driver
- * interfaces. These definitions only define the bare minimum of what the
- * driver must implement, if the driver author so wants to, they can add
- * IOCtl calls and/or files (where allowed by the type specifcation) to
- * their device's VFS layout.
- *
- * \subsection drv_misc Miscelanious Devices
- * If a device type does not have a specifcation for it, the driver can
- * identify itself as a miscelanious device by returning DRV_TYPE_MISC
- * from \ref DRV_IOCTL_TYPE.
- * A misc device must at least implement the IOCtl calls defined in the
- * \ref api_drv_common.h "Common Device Driver definitions", allowing it
- * to be identified easily by the user and for interfacing programs to
- * utilise the DRV_IOCTL_LOOKUP call.
- *
- * \subsection drv_video Video Devices
- * Video drivers are based on a framebuffer model (unless in 3D mode,
- * which is not yet fully standardised, so should be ignored).
- * The driver will contain only one VFS node, that exposes the video
- * framebuffer (this may not be the true framebuffer, to allow for double-buffering)
- * to the user. See the full documentation in api_drv_video.h for the
- * complete specifcation.
- *
- * \subsection drv_disk Disk/Storage Devices
- * Storage devices present themselves as a linear collection of bytes.
- * Reads and writes to the device need not be aligned to the stated block
- * size, but it is suggested that users of a storage device file align
- * their accesses to block boundaries.
- * The functions DrvUtil_ReadBlock and DrvUtil_WriteBlock are provided
- * to storage drivers to assist in handling non-alinged reads and writes.
- *
- * \see api_drv_common.h Common Spec.
- * \see api_drv_video.h Video Device Spec.
- * \see api_drv_keyboard.h Keyboard Device Spec.
- * \see api_drv_disk.h Disk/Storage Device Spec.
- * \see api_drv_network.h Network Device Spec.
- * \see api_drv_terminal.h Virtual Terminal Spec.
- */
+++ /dev/null
-/**
- * \file binary.h
- * \brief Binary Loader Definitions
- * \author John Hodge (thePowersGang)
- */
-#ifndef _BINARY_H
-#define _BINARY_H
-
-// === TYPES ===
-/**
- * \brief Representation of a section in a binary file
- *
- * Tells the binary loader where the page data resides on disk and where
- * to load it to (relative to the binary base). Once the data is read,
- * the \a Physical field contains the physical address of the page.
- */
-typedef struct sBinarySection
-{
- Uint64 Offset; //!< File offset of the section
- tVAddr Virtual; //!< Virtual load address
- size_t FileSize; //!< Number of bytes to load from the file
- size_t MemSize; //!< Number of bytes in memory
- Uint Flags; //!< Load Flags
-} tBinarySection;
-
-/**
- * \brief Flags for ::tBinarySection.Flags
- * \name Binary Section Flags
- * \{
- */
-//! \brief Read-only
-#define BIN_SECTFLAG_RO 0x0001
-//! \brief Executable
-#define BIN_SECTFLAG_EXEC 0x0002
-/**
- * \}
- */
-
-/**
- * \brief Defines a binary file
- *
- * This structure defines and maintains the state of a binary during and
- * after loading.
- * Before the binary is loaded into memory (when it has just been returned
- * from tBinaryType.Load) the \a Pages array will contain the file offsets
- * to the page data in the \a Physical fields (or -1 for uninitialised
- * data) and the \a Size fields define how much data is stored in-file
- * for the page (to allow partial pages to be loaded from disk)
- * Once the binary is loaded (NOTE: Drivers do not need to know about this,
- * it is here for information only) the \a Physical fields now contain the
- * physical addresses of the pages filled with the data. The \a Virtual
- * fields contain the preferred virtual address of the pages (a given
- * process may have these pages mapped to a different location).
- */
-typedef struct sBinary
-{
- struct sBinary *Next; //!< Pointer used by the kernel
-
- tMount MountID; //!< Mount ID
- tInode Inode; //!< Inode (Used for fast reopen)
-
- /**
- * \brief Interpreter used to load the file
- * \note This can be either requested by the individual file, or a per-driver option
- */
- const char *Interpreter;
- /**
- * \brief Entrypoint of the binary (at requested base);
- */
- tVAddr Entry;
- /**
- * \brief File's requested load base
- */
- tVAddr Base;
- /**
- * \brief Number of times this binary has been mapped
- */
- int ReferenceCount;
- /**
- * \brief Number of sections defined in the file
- */
- int NumSections;
- /**
- * \brief Array of sections defined by this binary
- * \note Contains \a NumSections entries
- */
- tBinarySection LoadSections[];
-} tBinary;
-
-/**
- * \brief Binary type definition
- *
- * This structure is used to define a loader for a specific binary type
- * so that the kernel's core binary loader can understand that type.
- * The tBinaryType.Relocate and tBinaryType.GetSymbol need only be non-NULL
- * if the binary type is to be used for kernel modules, otherwise it will
- * only be able to load binaries for user space.
- */
-typedef struct sBinaryType
-{
- /**
- * \brief Pointer used by the kernel
- * \note Should not be touched by the driver (initialise to NULL)
- */
- struct sBinaryType *Next;
- /**
- * \brief Identifying DWord
- *
- * If he first 32-bits of the file match this value (when ANDed with
- * tBinaryType.Mask), this binary loader will be used to load the file.
- */
- Uint32 Ident;
- Uint32 Mask; //!< Mask value for tBinaryType.Ident
- const char *Name; //!< Name of this executable type (for debug purpouses)
- /**
- * \brief Read a binary from a file
- * \param FD VFS File handle to file to load
- * \return Pointer to a ::tBinary that describes how to load the file
- *
- * This function reads a binary file and returns a ::tBinary pointer
- * that tells the core binary loader how to read the data from the file
- * and where to map it to.
- */
- tBinary *(*Load)(int FD);
-
- /**
- * \brief Prepares a mapped binary for execution at this address
- * \param Base Binary loaded in memory
- * \return Boolean Success
- * \note This pointer can be NULL, but then the binary cannot be used
- * to load a kernel module.
- *
- * tBinaryType.Relocate takes a binary that was loaded according to
- * tBinaryType.Load and prepares it to run at the address it is
- * loaded to, attempting to satisfy any external unresolved symbols
- * required, if a symbol cannot be located, the function will return
- * zero.
- */
- int (*Relocate)(void *Base);
-
- /**
- * \brief Gets a symbol's address from a loaded binary
- * \note The binary pointed to by \a Base may not have been through
- * tBinaryType.Relocate at this time, so the driver should
- * accomodate this.
- */
- int (*GetSymbol)(void *Base, const char *Name, Uint *Dest);
-} tBinaryType;
-
-/**
- * \brief Registers an interpreter path with the binary loader
- * \param Path Path to the requested interpreter (need not be a "true" path)
- * \return Pointer to the cached string
- *
- * Speeds up checking if the intepreter is loaded in the kernel by allowing
- * the search to use pointer comparisons instead of string comparisons.
- */
-extern char *Binary_RegInterp(char *Path);
-
-/**
- * \brief Registers a binary type with the kernel's loader
- * \param Type Pointer to the loader's type structure
- * \return Boolean success
- * \note The structure \a Type must be persistant (usually it will be a
- * constant global variable)
- *
- * This function tells the binary loader about a new file type, and gives
- * it the functions to read the type into a ::tBinary structure, relocate
- * it and to find the value of symbols defined within the binary.
- */
-extern int Binary_RegisterType(tBinaryType *Type);
-
-#endif
+++ /dev/null
-/*
- * Acess 2
- * binary_ext.h
- * - Exported Symbols from the binary loader
- */
-#ifndef _BINARY_EXT_H
-#define _BINARY_EXT_H
-
-// === FUNCTIONS ===
-extern void *Binary_LoadFile(const char *Path);
-extern void *Binary_LoadKernel(const char *Path);
-extern Uint Binary_Relocate(void *Mem);
-extern void Binary_Unload(void *Base);
-extern int Binary_GetSymbol(const char *Name, Uint *Dest);
-extern Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val);
-
-#endif
+++ /dev/null
-/**\r
- * \file drv_pci.h\r
- * \brief PCI Bus Driver\r
- * \author John Hodge (thePowersGang)\r
- */\r
-#ifndef _DRV_PCI_H\r
-#define _DRV_PCI_H\r
-\r
-/**\r
- * \brief PCI Class Codes\r
- */\r
-enum ePCIClasses\r
-{\r
- PCI_CLASS_PRE20 = 0x00,\r
- PCI_CLASS_STORAGE,\r
- PCI_CLASS_NETWORK,\r
- PCI_CLASS_DISPLAY,\r
- PCI_CLASS_MULTIMEDIA,\r
- PCI_CLASS_MEMORY,\r
- PCI_CLASS_BRIDGE,\r
- PCI_CLASS_COMM,\r
- PCI_CLASS_PREPH,\r
- PCI_CLASS_INPUT,\r
- PCI_CLASS_DOCKING,\r
- PCI_CLASS_PROCESSORS,\r
- PCI_CLASS_SERIALBUS,\r
- PCI_CLASS_MISC = 0xFF\r
-};\r
-\r
-enum ePCIOverClasses\r
-{\r
- PCI_OC_PCIBRIDGE = 0x060400,\r
- PCI_OC_SCSI = 0x010000\r
-};\r
-\r
-typedef int tPCIDev;\r
-\r
-/**\r
- * \brief Count PCI Devices\r
- * \r
- * Counts the number of devices with specified Vendor and Device IDs\r
- */\r
-extern int PCI_CountDevices(Uint16 VendorID, Uint16 DeviceID);\r
-extern tPCIDev PCI_GetDevice(Uint16 VendorID, Uint16 DeviceID, int index);\r
-/**\r
- * \param ClassCode (Class:SubClass:PI)\r
- */\r
-extern tPCIDev PCI_GetDeviceByClass(Uint32 ClassCode, Uint32 Mask, tPCIDev prev);\r
-\r
-extern int PCI_GetDeviceInfo(tPCIDev id, Uint16 *Vendor, Uint16 *Device, Uint32 *Class);\r
-extern int PCI_GetDeviceVersion(tPCIDev id, Uint8 *Revision);\r
-extern int PCI_GetDeviceSubsys(tPCIDev id, Uint16 *SubsystemVendor, Uint16 *SubsystemID);\r
-\r
-extern Uint32 PCI_ConfigRead(tPCIDev id, int Offset, int Size);\r
-extern void PCI_ConfigWrite(tPCIDev id, int Offset, int Size, Uint32 Value);\r
-\r
-extern Uint8 PCI_GetIRQ(tPCIDev id);\r
-extern Uint32 PCI_GetBAR(tPCIDev id, int BAR);\r
-//extern Uint16 PCI_AssignPort(tPCIDev id, int bar, int count);\r
-\r
-#endif\r
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * drv_pci_int.h
- * - PCI internal definitions
- */
-#ifndef _DRV_PCI_INT_H
-#define _DRV_PCI_INT_H
-
-#include <acess.h>
-
-extern Uint32 PCI_CfgReadDWord(Uint32 Addr);
-extern void PCI_CfgWriteDWord(Uint32 Addr, Uint32 data);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2
- * errno.h
- */
-#ifndef _ERRNO_H
-#define _ERRNO_H
-
-enum eErrorNums
-{
- EOK,
-
- ENOSYS, // Invalid Instruction
- EINVAL, // Invalid Paramater
- ENOMEM, // No free memory
- EACCES, // Not permitted
- ENOTFOUND, // Item not found
- EREADONLY, // Read only
- ENOTIMPL, // Not implemented
- ENOENT, // No entry?
- EEXIST, // Already exists
- ENFILE, // Too many open files
- ENOTDIR, // Not a directory
-
- EALREADY, // Operation was a NOP
- EINTERNAL, // Internal Error
-
- NUM_ERRS
-};
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * events.h
- * - Thread Events
- */
-#ifndef _EVENTS_H_
-#define _EVENTS_H_
-
-#include <threads.h>
-
-#define THREAD_EVENT_VFS 0x00000001
-#define THREAD_EVENT_IPCMSG 0x00000002
-#define THREAD_EVENT_SIGNAL 0x00000004
-#define THREAD_EVENT_TIMER 0x00000008
-
-// === FUNCTIONS ===
-extern void Threads_PostEvent(tThread *Thread, Uint32 EventMask);
-extern Uint32 Threads_WaitEvents(Uint32 EventMask);
-
-#endif
-
+++ /dev/null
-/**
- * \file fs_devfs.h
- * \brief Acess Device Filesystem interface
- * \author John Hodge (thePowersGang)
- */
-#ifndef _FS_DEVFS_H
-#define _FS_DEVFS_H
-#include <vfs.h>
-
-// === TYPES ===
-/**
- * \brief DevFS driver definition
- */
-typedef struct sDevFS_Driver
-{
- struct sDevFS_Driver *Next; //!< Set to NULL by drivers (used internally)
- const char *Name; //!< Name of the driver file/folder (must be unique)
- tVFS_Node RootNode; //!< Root node of driver
-} tDevFS_Driver;
-
-// === FUNCTIONS ===
-/**
- * \fn int DevFS_AddDevice(tDevFS_Driver *Device)
- * \brief Registers a device in the Device Filesystem
- * \param Device Pointer to a persistant structure that represents the driver
- * \return Boolean success
- */
-extern int DevFS_AddDevice(tDevFS_Driver *Device);
-
-/**
- * \brief Unregisters a device with the Device Filesystem
- */
-extern void DevFS_DelDevice(tDevFS_Driver *Device);
-
-#endif
+++ /dev/null
-/*
- * Acess2
- * - SysFS Export Header
- */
-/**
- * \file fs_sysfs.h
- * \brief ProcDev/SysFS Interface
- * \author John Hodge (thePowersGang)
- *
- *
- */
-#ifndef _FS_SYSFS_H_
-#define _FS_SYSFS_H_
-
-/**
- * \brief Registers a file in the SysFS tree
- * \param Path Path relative to the SysFS root (no .. or .)
- * \param Data File buffer address
- * \param Length Length of the file buffer
- * \return An ID number to refer to the file, or -1 on error
- * \note \a Data must be maintained until ::SysFS_UpdateFile is called
- * with a different buffer, or ::SysFS_RemoveFile is called.
- */
-extern int SysFS_RegisterFile(const char *Path, const char *Data, int Length);
-
-/**
- * \brief Updates the size/pointer associated with a SysFD file
- * \param ID Number returned by ::SysFS_RegisterFile
- * \param Data New buffer address
- * \param Length New length of the file
- * \return Boolean Success
- */
-extern int SysFS_UpdateFile(int ID, const char *Data, int Length);
-
-/**
- * \brief Removes a file from the SysFS tree
- * \param ID Number returned by ::SysFS_RegisterFile
- */
-extern int SysFS_RemoveFile(int ID);
-
-#endif
+++ /dev/null
-/**
- * Acess2
- * - By John Hodge (thePowersGang)
- *
- * include/hal_proc.h
- * - HAL Process management functions
- *
- */
-#ifndef _HAL_PROC_H_
-#define _HAL_PROC_H_
-/**
- * \file hal_proc.h
- * \brief Achitecture defined thread/process management functions
- */
-
-#include <threads_int.h>
-
-/**
- * \brief Initialise the architecture dependent side of threading
- */
-extern void ArchThreads_Init(void);
-/**
- * \brief Start preemptive multithreading (if needed)
- */
-extern void Proc_Start(void);
-/**
- * \brief Called just before a thread is freed
- */
-extern void Proc_ClearThread(tThread *Thread);
-/**
- * \brief Called just before a process is freed
- */
-extern void Proc_ClearProcess(tProcess *Process);
-/**
- * \brief Get the ID of this CPU
- * \return Zero based CPU ID
- */
-extern int GetCPUNum(void);
-/**
- * \brief Create a copy of the current process
- * \param Flags Options for the clone
- * \return ID of the new thread/process
- */
-extern tTID Proc_Clone(Uint Flags);
-/**
- * \brief Create a new kernel thread for the process
- * \param Fnc Thread root function
- * \param Ptr Argument to pass the root function
- * \return ID of new thread
- */
-extern tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr );
-/**
- * \brief Start a user task
- * \param Entrypoint User entrypoint
- * \param Base Base of executable (argument for ld-acess)
- * \param ArgC Number of arguments when the program was invoked
- * \param ArgV Heap allocated arguments and environment (two NULL terminated lists)
- * \param DataSize Size of the \a ArgV buffer in bytes
- * \note This function should free \a ArgV
- */
-extern void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) NORETURN;
-/**
- * \brief Call the fault handler for a thread
- * \param Thread Thread that is at fault :)
- */
-extern void Proc_CallFaultHandler(tThread *Thread);
-/**
- * \brief Dump the CPU state for a thread
- */
-extern void Proc_DumpThreadCPUState(tThread *Thread);
-/**
- * \brief Select a new task and run it, suspending this
- */
-extern void Proc_Reschedule(void);
-
-/**
- * \brief Clear the user's memory space back to the minimum required to run
- */
-extern void MM_ClearUser(void);
-/**
- * \brief Dump the address space to the debug channel
- * \param Start First address
- * \param End Last address
- */
-extern void MM_DumpTables(tVAddr Start, tVAddr End);
-
-/**
- * \brief Check if a buffer is valid (and all user if originally user)
- * \param Addr Base address
- * \param Size Size of the buffer in bytes
- * \return Boolean valid (0: invalid, non-0: Valid)
- */
-extern int MM_IsValidBuffer(tVAddr Addr, size_t Size);
-#endif
+++ /dev/null
-/**
- * Acess2 Kernel
- * heap.h
- * - Dynamic Memory Allocation exports
- */
-
-#ifndef _HEAP_H_
-#define _HEAP_H_
-
-extern void *Heap_Allocate(const char *File, int Line, size_t Bytes);
-extern void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
-extern void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
-extern void Heap_Deallocate(void *Ptr);
-extern int Heap_IsHeapAddr(void *Ptr);
-extern void Heap_Validate(void);
-
-#define malloc(size) Heap_Allocate(_MODULE_NAME_"/"__FILE__, __LINE__, (size))
-#define calloc(num,size) Heap_AllocateZero(_MODULE_NAME_"/"__FILE__, __LINE__, (num)*(size))
-#define realloc(ptr,size) Heap_Reallocate(_MODULE_NAME_"/"__FILE__, __LINE__, (ptr), (size))
-#define free(ptr) Heap_Deallocate((ptr))
-#define IsHeap(ptr) Heap_IsHeapAddr((ptr))
-
-#define strdup(Str) _strdup(_MODULE_NAME_"/"__FILE__, __LINE__, (Str))
-
-#endif
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * heap_int.h
- * - Internal Heap Header
- */
-#ifndef _HEAP_INT_H
-#define _HEAP_INT_H
-
-typedef struct {
- Uint Size;
- int ValidSize;
- const char *File;
- int Line;
- Uint Magic;
- tTime AllocateTime;
- char Data[];
-} tHeapHead;
-
-typedef struct {
- Uint Magic;
- tHeapHead *Head;
- tHeapHead NextHead[]; // Array to make it act like an element, but have no size and refer to the next block
-} tHeapFoot;
-
-#endif
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * init.h
- */
-#ifndef _INIT_H
-#define _INIT_H
-
-extern void Arch_LoadBootModules(void);
-extern void StartupPrint(const char *String);
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * - IO Cache
- *
- * By thePowersGang (John Hodge)
- */
-/**
- * \file iocache.h
- * \brief I/O Caching Helper Subsystem
- *
- * The IO Cache abstracts caching of disk sectors away from the device
- * driver to reduce code duplication and allow a central location for
- * disk cache management that can be flushed simply by the kernel, without
- * having to ask each driver to do it indivitually.
- */
-#ifndef _IOCHACHE_H_
-#define _IOCHACHE_H_
-
-// === TYPES ===
-/**
- * \brief IO Cache Handle
- */
-typedef struct sIOCache tIOCache;
-/**
- * \brief Write Callback
- *
- * Called to write a sector back to the device
- */
-typedef int (*tIOCache_WriteCallback)(Uint32 ID, Uint64 Sector, void *Buffer);
-
-// === CONSTANTS ===
-/**
- * \brief I/O Cache handling modes
- */
-enum eIOCache_Modess {
- /**
- * \brief Writeback
- *
- * Transparently writes data straight to the device when the cache
- * is written to.
- */
- IOCACHE_WRITEBACK,
- /**
- * \brief Delay Write
- *
- * Only writes when instructed to (by ::IOCache_Flush) or when a
- * cached sector is being reallocated.
- */
- IOCACHE_DELAYWRITE,
- /**
- * \brief Virtual - No Writes
- *
- * Changes to the cache contents are only reflected in memory,
- * any calls to ::IOCache_Flush will silently return without doing
- * anything and if a sector is reallocated, all changes will be lost
- */
- IOCACHE_VIRTUAL
-};
-
-// === FUNCTIONS ===
-/**
- * \brief Creates a new IO Cache
- * \param Write Function to call to write a sector to the device
- * \param ID ID to pass to \a Write
- * \param SectorSize Size of a cached sector
- * \param CacheSize Maximum number of objects that can be in the cache at one time
- */
-tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize );
-
-/**
- * \brief Reads from a cached sector
- * \param Cache Cache handle returned by ::IOCache_Create
- * \param Sector Sector's ID number
- * \param Buffer Destination for the data read
- * \return 1 if the data was read, 0 if the sector is not cached, -1 on error
- */
- int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer );
-
-/**
- * \brief Adds a sector to the cache
- * \param Cache Cache handle returned by ::IOCache_Create
- * \param Sector Sector's ID number
- * \param Buffer Data to cache
- * \return 1 on success, 0 if the sector is already cached, -1 on error
- */
- int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer );
-
-/**
- * \brief Writes to a cached sector
- * \param Cache Cache handle returned by ::IOCache_Create
- * \param Sector Sector's ID number
- * \param Buffer Data to write to the cache
- * \return 1 if the data was read, 0 if the sector is not cached, -1 on error
- *
- * If the sector is in the cache, it is updated.
- * Wether the Write callback is called depends on the selected caching
- * behaviour.
- */
- int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer );
-
-/**
- * \brief Flush altered sectors out to the device
- * \param Cache Cache handle returned by ::IOCache_Create
- *
- * This will call the cache's write callback on each altered sector
- * to flush the write cache.
- */
-void IOCache_Flush( tIOCache *Cache );
-
-/**
- * \brief Flushes the cache and then removes it
- * \param Cache Cache handle returned by ::IOCache_Create
- * \note After this is called \a Cache is no longer valid
- *
- * IOCache_Destroy writes all changed sectors to the device and then
- * deallocates the cache for other systems to use.
- */
-void IOCache_Destroy( tIOCache *Cache );
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge
- *
- * include/keyvalue.h
- * - Key/Value pair parsing
- */
-#ifndef _ACESS_KEYVALUE_H_
-#define _ACESS_KEYVALUE_H_
-
-typedef struct sKeyVal_ParseRules tKeyVal_ParseRules;
-typedef struct sKeyVal_int_Rule tKeyVal_int_Rule;
-typedef void (*tKeyVal_UnkCb)(char *String);
-typedef void (*tKeyVal_KeyCb)(const char *Key, char *Value);
-
-/**
- * \brief Handling rule for a key
- */
-struct sKeyVal_int_Rule
-{
- const char *Key;
- const char *Type; // Acess printf format, with 'F' being a tKeyVal_KeyCb
- void *Data;
-};
-
-struct sKeyVal_ParseRules
-{
- /**
- * \brief Function to call when no match is found
- */
- tKeyVal_UnkCb Unknown;
- tKeyVal_int_Rule Rules[];
-};
-
-/**
- * \brief Parse a NULL terminated list of strings as Key/Value pairs
- * \param Rules Parsing rules
- * \param Strings Input string list
- */
-extern int KeyVal_ParseNull(tKeyVal_ParseRules *Rules, char **Strings);
-
-#endif
-
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * mboot.h
- */
-#ifndef _MBOOT_H
-#define _MBOOT_H
-
-#define MULTIBOOT_MAGIC 0x2BADB002
-
-// === TYPES ===
-typedef struct {
- Uint32 Flags;
- Uint32 LowMem;
- Uint32 HighMem;
- Uint32 BootDevice;
- Uint32 CommandLine;
- Uint32 ModuleCount;
- Uint32 Modules;
- Uint32 SymbolInfo[4]; // #32 UNUSED
- Uint32 MMapLength;
- Uint32 MMapAddr; // #40
-} tMBoot_Info;
-
-typedef struct {
- Uint32 Start;
- Uint32 End;
- Uint32 String;
- Uint32 Resvd;
-} tMBoot_Module;
-
-typedef struct {
- Uint32 Size; // (May be at offset -4)
- Uint64 Base;
- Uint64 Length;
- Uint32 Type; //1:RAM,Else Reserved
-} __attribute__ ((packed)) tMBoot_MMapEnt;
-
-#endif
+++ /dev/null
-/*
- * AcessOS 2
- * - Module Loader
- */
-/**
- * \file modules.h
- * \brief Module Handling and Loader Definitions
- * \author John Hodge (thePowersGang)
- *
- * This file serves two pourposes. First it defines the format for native
- * Acess2 modules and the functions to create them.
- * Second, it defines the structure and register function for new module
- * loaders, allowing Acess to understand many different module / driver
- * formats.
- *
- * Modules are defined by including this file in the module's main source
- * file and using the ::MODULE_DEFINE macro to create the module header.
- *
- * To register a new module loader with the kernel, the loader module must
- * create and populate an instance of ::tModuleLoader then pass it to
- * ::Module_RegisterLoader
- */
-#ifndef _MODULE_H
-#define _MODULE_H
-
-/**
- * \brief Module header magic value
- */
-#define MODULE_MAGIC ('A'|('M'<<8)|('D'<<16)|('\2'<<24))
-
-/**
- * \def MODULE_ARCH_ID
- * \brief Architecture ID
- */
-// IA32 - Architecture 1
-#if ARCHDIR == x86
-# define MODULE_ARCH_ID 1
-// IA64 - Architecture 2
-#elif ARCHDIR == x86_64
-# define MODULE_ARCH_ID 2
-#else
-# error "Unknown architecture when determining MODULE_ARCH_ID ('" #ARCHDIR "')"
-#endif
-
-/**
- * \brief Define a module
- * \param _flags Module Flags
- * \param _ver Module Version
- * \param _ident Unique Module Name
- * \param _entry Module initialiser / entrypoint
- * \param _deinit Module cleanup / unloader
- * \param _deps NULL terminated list of this's module's dependencies
- * Contains the identifiers of the required modules.
- */
-#define MODULE_DEFINE(_flags,_ver,_ident,_entry,_deinit,_deps...) \
- const char *EXPAND_CONCAT(_DriverDeps_,_ident)[]={_deps};\
- tModule __attribute__ ((section ("KMODULES"),unused))\
- EXPAND_CONCAT(_DriverInfo_,_ident)=\
- {MODULE_MAGIC,MODULE_ARCH_ID,_flags,_ver,NULL,EXPAND_STR(_ident),\
- _entry,_deinit,EXPAND_CONCAT(_DriverDeps_,_ident)}
-
-/**
- * \brief Module header
- * \note There is no reason for a module to touch this structure beyond
- * using ::MODULE_DEFINE to create it.
- */
-typedef struct sModule
-{
- Uint32 Magic; //!< Identifying magic value (See ::MODULE_MAGIC)
- Uint8 Arch; //!< Achitecture ID (See ::MODULE_ARCH_ID)
- Uint8 Flags; //!< Module Flags
- Uint16 Version; //!< Module Version in Major.Minor 8.8 form
- struct sModule *Next; //!< Next module in list (not to be touched by the driver)
- const char *Name; //!< Module Name/Identifier
- int (*Init)(char **Arguments); //!< Module initialiser / entrypoint
- void (*Deinit)(void); //!< Cleanup Function
- const char **Dependencies; //!< NULL terminated list of dependencies
-} PACKED tModule;
-
-/**
- * \brief Return values for tModule.Init
- */
-enum eModuleErrors
-{
- MODULE_ERR_OK, //!< No Error
- MODULE_ERR_MISC, //!< Misc Error
- MODULE_ERR_NOTNEEDED, //!< Module not needed
- MODULE_ERR_MALLOC, //!< Error with malloc/realloc/calloc
-
- MODULE_ERR_BADMODULE, //!< Bad module (only used by loader)
- MODULE_ERR_MAX //!< Maximum defined error code
-};
-
-/**
- * \brief Module Loader definition
- *
- * Allows a module to extend the loader to recognise other module types
- * E.g. EDI, UDI, Windows, Linux, ...
- */
-typedef struct sModuleLoader
-{
- struct sModuleLoader *Next; //!< Kernel Only - Next loader in list
- char *Name; //!< Friendly name for the loader
- int (*Detector)(void *Base); //!< Simple detector function
- int (*Loader)(void *Base); //!< Initialises the module
- int (*Unloader)(void *Base); //!< Calls module's cleanup
-} PACKED tModuleLoader;
-
-/**
- * \brief Registers a tModuleLoader with the kernel
- * \param Loader Pointer to loader structure (must be persistent)
- * \return Boolean Success
- */
-extern int Module_RegisterLoader(tModuleLoader *Loader);
-
-/**
- * \brief Initialises (if needed) a named module
- * \param Name Module name to initialise
- * \return -1 on not existing, 0 if the module initialised (or if it was already initialised)
- */
-extern int Module_EnsureLoaded(const char *Name);
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * mutex.h
- * - Mutual Exclusion syncronisation primitive
- */
-#ifndef _MUTEX_H
-#define _MUTEX_H
-
-#include <acess.h>
-
-typedef struct sMutex tMutex;
-
-struct sMutex
-{
- tShortSpinlock Protector; //!< Protector for the lock strucure
- const char *Name; //!< Human-readable name
- struct sThread *volatile Owner; //!< Owner of the lock (set upon getting the lock)
- struct sThread *Waiting; //!< Waiting threads
- struct sThread *LastWaiting; //!< Waiting threads
-};
-
-/**
- * \brief Acquire a heavy mutex
- * \param Mutex Mutex to acquire
- * \return zero on success, -1 if terminated
- *
- * This type of mutex checks if the mutex is avaliable, and acquires it
- * if it is. Otherwise, the current thread is added to the mutex's wait
- * queue and the thread suspends. When the holder of the mutex completes,
- * the oldest thread (top thread) on the queue is given the lock and
- * restarted.
- */
-extern int Mutex_Acquire(tMutex *Mutex);
-
-/**
- * \brief Release a held mutex
- * \param Mutex Mutex to release
- * \note Releasing a non-held mutex has no effect
- */
-extern void Mutex_Release(tMutex *Mutex);
-
-/**
- * \brief Is this mutex locked?
- * \param Mutex Mutex pointer
- */
-extern int Mutex_IsLocked(tMutex *Mutex);
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * semaphore.h
- * - Semaphore syncronisation primitive
- */
-#ifndef _SEMAPHORE_H
-#define _SEMAPHORE_H
-
-#include <acess.h>
-
-/**
- * \brief Semaphore typedef
- */
-typedef struct sSemaphore tSemaphore;
-
-/**
- * \brief Semaphore structure
- */
-struct sSemaphore {
- tShortSpinlock Protector; //!< Protector for the lock strucure
- const char *ModName; //!< Human-readable module name
- const char *Name; //!< Human-readable name
- volatile int Value; //!< Current value
- volatile int MaxValue; //!< Maximum value (signal will wait if it will go over this)
-
- struct sThread *Waiting; //!< Waiting threads
- struct sThread *LastWaiting; //!< Waiting threads
- struct sThread *Signaling; //!< Waiting threads (from Semaphore_Signal)
- struct sThread *LastSignaling; //!< Last waiting thread (from Semaphore_Signal)
-};
-
-/**
- * \brief Initialise the semaphore
- * \param Sem Semaphore structure to initialsie
- * \param InitValue Initial value of the semaphore
- * \param MaxValue Maximum value for the semaphore
- * \param Module Module name
- * \param Name Symbolic name
- * \note Not always needed, as initialising to 0 is valid, but it is preferred
- * if all semaphores have \a Name set
- *
- * \note \a Module and \a Name must be avaliable for as long as the semaphore is used
- */
-extern void Semaphore_Init(tSemaphore *Sem, int InitValue, int MaxValue, const char *Module, const char *Name);
-/**
- * \brief Acquire items from the semaphore
- * \param Sem Semaphore structure to use
- * \param MaxToTake Maximum number of items to take off the list (if zero, as much as possible is taken)
- * \return Number of items fetched
- * \retval 0 Semaphore interrupted (signal/message)
- * \retval -1 Unspecified error
- */
-extern int Semaphore_Wait(tSemaphore *Sem, int MaxToTake);
-/**
- * \brief Add an "item" to the semaphore
- * \param Sem Semaphore to use
- * \param AmmountToAdd Number of items to add
- * \return Actual number of items added
- * \retval 0 Semaphore interrupted
- * \retval -1 Unspecified error
- */
-extern int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd);
-/**
- * \brief Get the number of items waiting in a semaphore
- * \param Sem Semaphore to use
- * \return Number of waiting items
- */
-extern int Semaphore_GetValue(tSemaphore *Sem);
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * Signal List
- */
-#ifndef _SIGNAL_H_
-#define _SIGNAL_H_
-
-enum eSignals {
- SIGKILL,
- SIGSTOP,
- SIGCONT,
- SIGCHLD,
- NSIG
-};
-
-#endif
+++ /dev/null
-/*
- * Acess2
- * syscalls.h
- * - System Call List
- *
- * NOTE: Generated from Kernel/syscalls.lst
- */
-#ifndef _SYSCALLS_H
-#define _SYSCALLS_H
-
-#define SYS_EXIT 0 // Kill this thread
-#define SYS_CLONE 1 // Create a new thread
-#define SYS_KILL 2 // Send a signal
-#define SYS_SETFAULTHANDLER 3 // Set signal Handler
-#define SYS_YIELD 4 // Yield remainder of timestamp
-#define SYS_SLEEP 5 // Sleep until messaged or signaled
-#define SYS_WAITEVENT 6 // Wait for an event
-#define SYS_WAITTID 7 // Wait for a thread to do something
-#define SYS_SETNAME 8 // Sets the name of the current thread
-#define SYS_GETNAME 9 // Gets the name of a thread
-#define SYS_GETTID 10 // Get current thread ID
-#define SYS_GETPID 11 // Get current thread group ID
-#define SYS_SETPRI 12 // Set process priority
-#define SYS_SENDMSG 13 // Send an IPC message
-#define SYS_GETMSG 14 // Recieve an IPC message
-#define SYS_GETTIME 15 // Get the current timestamp
-#define SYS_SPAWN 16 // Spawn a new process
-#define SYS_EXECVE 17 // Replace the current process
-#define SYS_LOADBIN 18 // Load a binary into the current address space
-#define SYS_UNLOADBIN 19 // Unload a loaded binary
-#define SYS_LOADMOD 20 // Load a module into the kernel
-#define SYS_GETPHYS 32 // Get the physical address of a page
-#define SYS_MAP 33 // Map a physical address
-#define SYS_ALLOCATE 34 // Allocate a page
-#define SYS_UNMAP 35 // Unmap a page
-#define SYS_PREALLOC 36 // Preallocate a page
-#define SYS_SETFLAGS 37 // Set a page's flags
-#define SYS_SHAREWITH 38 // Share a page with another thread
-#define SYS_GETUID 39 // Get current User ID
-#define SYS_GETGID 40 // Get current Group ID
-#define SYS_SETUID 41 // Set current user ID
-#define SYS_SETGID 42 // Set current Group ID
-#define SYS_OPEN 64 // Open a file
-#define SYS_REOPEN 65 // Close a file and reuse its handle
-#define SYS_CLOSE 66 // Close a file
-#define SYS_READ 67 // Read from an open file
-#define SYS_WRITE 68 // Write to an open file
-#define SYS_IOCTL 69 // Perform an IOCtl Call
-#define SYS_SEEK 70 // Seek to a new position in the file
-#define SYS_READDIR 71 // Read from an open directory
-#define SYS_OPENCHILD 72 // Open a child entry in a directory
-#define SYS_GETACL 73 // Get an ACL Value
-#define SYS_SETACL 74 // Set an ACL Value
-#define SYS_FINFO 75 // Get file information
-#define SYS_MKDIR 76 // Create a new directory
-#define SYS_LINK 77 // Create a new link to a file
-#define SYS_SYMLINK 78 // Create a symbolic link
-#define SYS_UNLINK 79 // Delete a file
-#define SYS_TELL 80 // Return the current file position
-#define SYS_CHDIR 81 // Change current directory
-#define SYS_GETCWD 82 // Get current directory
-#define SYS_MOUNT 83 // Mount a filesystem
-#define SYS_SELECT 84 // Wait for file handles
-
-#define NUM_SYSCALLS 85
-#define SYS_DEBUG 0x100
-
-#ifndef __ASSEMBLER__
-static const char *cSYSCALL_NAMES[] = {
- "SYS_EXIT",
- "SYS_CLONE",
- "SYS_KILL",
- "SYS_SETFAULTHANDLER",
- "SYS_YIELD",
- "SYS_SLEEP",
- "SYS_WAITEVENT",
- "SYS_WAITTID",
- "SYS_SETNAME",
- "SYS_GETNAME",
- "SYS_GETTID",
- "SYS_GETPID",
- "SYS_SETPRI",
- "SYS_SENDMSG",
- "SYS_GETMSG",
- "SYS_GETTIME",
- "SYS_SPAWN",
- "SYS_EXECVE",
- "SYS_LOADBIN",
- "SYS_UNLOADBIN",
- "SYS_LOADMOD",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "SYS_GETPHYS",
- "SYS_MAP",
- "SYS_ALLOCATE",
- "SYS_UNMAP",
- "SYS_PREALLOC",
- "SYS_SETFLAGS",
- "SYS_SHAREWITH",
- "SYS_GETUID",
- "SYS_GETGID",
- "SYS_SETUID",
- "SYS_SETGID",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "SYS_OPEN",
- "SYS_REOPEN",
- "SYS_CLOSE",
- "SYS_READ",
- "SYS_WRITE",
- "SYS_IOCTL",
- "SYS_SEEK",
- "SYS_READDIR",
- "SYS_OPENCHILD",
- "SYS_GETACL",
- "SYS_SETACL",
- "SYS_FINFO",
- "SYS_MKDIR",
- "SYS_LINK",
- "SYS_SYMLINK",
- "SYS_UNLINK",
- "SYS_TELL",
- "SYS_CHDIR",
- "SYS_GETCWD",
- "SYS_MOUNT",
- "SYS_SELECT",
-
- ""
-};
-#endif
-
-#endif
+++ /dev/null
-; Acess2
-; System Calls List
-;
-
-%define SYS_EXIT 0 ;Kill this thread
-%define SYS_CLONE 1 ;Create a new thread
-%define SYS_KILL 2 ;Send a signal
-%define SYS_SETFAULTHANDLER 3 ;Set signal Handler
-%define SYS_YIELD 4 ;Yield remainder of timestamp
-%define SYS_SLEEP 5 ;Sleep until messaged or signaled
-%define SYS_WAITEVENT 6 ;Wait for an event
-%define SYS_WAITTID 7 ;Wait for a thread to do something
-%define SYS_SETNAME 8 ;Sets the name of the current thread
-%define SYS_GETNAME 9 ;Gets the name of a thread
-%define SYS_GETTID 10 ;Get current thread ID
-%define SYS_GETPID 11 ;Get current thread group ID
-%define SYS_SETPRI 12 ;Set process priority
-%define SYS_SENDMSG 13 ;Send an IPC message
-%define SYS_GETMSG 14 ;Recieve an IPC message
-%define SYS_GETTIME 15 ;Get the current timestamp
-%define SYS_SPAWN 16 ;Spawn a new process
-%define SYS_EXECVE 17 ;Replace the current process
-%define SYS_LOADBIN 18 ;Load a binary into the current address space
-%define SYS_UNLOADBIN 19 ;Unload a loaded binary
-%define SYS_LOADMOD 20 ;Load a module into the kernel
-%define SYS_GETPHYS 32 ;Get the physical address of a page
-%define SYS_MAP 33 ;Map a physical address
-%define SYS_ALLOCATE 34 ;Allocate a page
-%define SYS_UNMAP 35 ;Unmap a page
-%define SYS_PREALLOC 36 ;Preallocate a page
-%define SYS_SETFLAGS 37 ;Set a page's flags
-%define SYS_SHAREWITH 38 ;Share a page with another thread
-%define SYS_GETUID 39 ;Get current User ID
-%define SYS_GETGID 40 ;Get current Group ID
-%define SYS_SETUID 41 ;Set current user ID
-%define SYS_SETGID 42 ;Set current Group ID
-%define SYS_OPEN 64 ;Open a file
-%define SYS_REOPEN 65 ;Close a file and reuse its handle
-%define SYS_CLOSE 66 ;Close a file
-%define SYS_READ 67 ;Read from an open file
-%define SYS_WRITE 68 ;Write to an open file
-%define SYS_IOCTL 69 ;Perform an IOCtl Call
-%define SYS_SEEK 70 ;Seek to a new position in the file
-%define SYS_READDIR 71 ;Read from an open directory
-%define SYS_OPENCHILD 72 ;Open a child entry in a directory
-%define SYS_GETACL 73 ;Get an ACL Value
-%define SYS_SETACL 74 ;Set an ACL Value
-%define SYS_FINFO 75 ;Get file information
-%define SYS_MKDIR 76 ;Create a new directory
-%define SYS_LINK 77 ;Create a new link to a file
-%define SYS_SYMLINK 78 ;Create a symbolic link
-%define SYS_UNLINK 79 ;Delete a file
-%define SYS_TELL 80 ;Return the current file position
-%define SYS_CHDIR 81 ;Change current directory
-%define SYS_GETCWD 82 ;Get current directory
-%define SYS_MOUNT 83 ;Mount a filesystem
-%define SYS_SELECT 84 ;Wait for file handles
+++ /dev/null
-/*
- * Acess2 Kernel
- */
-#ifndef _THREADS_H_
-#define _THREADS_H_
-
-#include <arch.h>
-#include <signal.h>
-//#include <proc.h>
-
-enum eFaultNumbers
-{
- FAULT_MISC,
- FAULT_PAGE,
- FAULT_ACCESS,
- FAULT_DIV0,
- FAULT_OPCODE,
- FAULT_FLOAT
-};
-
-#define GETMSG_IGNORE ((void*)-1)
-
-typedef struct sThread tThread;
-
-// === FUNCTIONS ===
-extern void Threads_SetFaultHandler(Uint Handler);
-
-extern int Threads_SetUID(tUID ID);
-extern int Threads_SetGID(tUID ID);
-extern tTID Threads_WaitTID(int TID, int *Status);
-
-
-extern int *Threads_GetMaxFD(void);
-extern char **Threads_GetCWD(void);
-extern char **Threads_GetChroot(void);
-
-extern int Proc_SendMessage(Uint Dest, int Length, void *Data);
-extern int Proc_GetMessage(Uint *Source, void *Buffer);
-
-#endif
+++ /dev/null
-/*
- * Internal Threading header
- * - Only for use by stuff that needs access to the thread type.
- */
-#ifndef _THREADS_INT_H_
-#define _THREADS_INT_H_
-
-#include <threads.h>
-#include <proc.h>
-
-
-typedef struct sProcess tProcess;
-
-/**
- * \brief IPC Message
- */
-typedef struct sMessage
-{
- struct sMessage *Next; //!< Next message in thread's inbox
- tTID Source; //!< Source thread ID
- Uint Length; //!< Length of message data in bytes
- Uint8 Data[]; //!< Message data
-} tMsg;
-
-/**
- * \brief Process state
- */
-struct sProcess
-{
- tPID PID;
- int nThreads;
-
- tUID UID; //!< User ID
- tGID GID; //!< User and Group
- tMemoryState MemState;
-
- int MaxFD;
- char *CurrentWorkingDir;
- char *RootDir;
-};
-
-/**
- * \brief Core threading structure
- *
- */
-struct sThread
-{
- // --- threads.c's
- /**
- * \brief Next thread in current list
- * \note Required to be first for linked list hacks to work
- */
- struct sThread *Next;
- struct sThread *GlobalNext; //!< Next thread in global list
- struct sThread *GlobalPrev; //!< Previous thread in global list
- tShortSpinlock IsLocked; //!< Thread's spinlock
- volatile int Status; //!< Thread Status
- void *WaitPointer; //!< What (Mutex/Thread/other) is the thread waiting on
- int RetStatus; //!< Return Status
-
- tTID TID; //!< Thread ID
- struct sProcess *Process; //!< Thread Group / Process
- struct sThread *Parent; //!< Parent Thread
- char *ThreadName; //!< Name of thread
-
- // --- arch/proc.c's responsibility
- //! Kernel Stack Base
- tVAddr KernelStack;
-
- //! State on task switch
- tTaskState SavedState;
-
- // --- threads.c's
- int CurFaultNum; //!< Current fault number, 0: none
- tVAddr FaultHandler; //!< Fault Handler
-
- tMsg * volatile Messages; //!< Message Queue
- tMsg *LastMessage; //!< Last Message (speeds up insertion)
-
- int Quantum, Remaining; //!< Quantum Size and remaining timesteps
- int Priority; //!< Priority - 0: Realtime, higher means less time
-
- int _errno;
-
- volatile int CurCPU;
-
- bool bInstrTrace;
-
- // --- event.c
- Uint32 EventState;
-};
-
-
-enum {
- THREAD_STAT_NULL, // Invalid process
- THREAD_STAT_ACTIVE, // Running and schedulable process
- THREAD_STAT_SLEEPING, // Message Sleep
- THREAD_STAT_MUTEXSLEEP, // Mutex Sleep
- THREAD_STAT_SEMAPHORESLEEP, // Semaphore Sleep
- THREAD_STAT_QUEUESLEEP, // Queue
- THREAD_STAT_EVENTSLEEP, // Event sleep
- THREAD_STAT_WAITING, // ??? (Waiting for a thread)
- THREAD_STAT_PREINIT, // Being created
- THREAD_STAT_ZOMBIE, // Died/Killed, but parent not informed
- THREAD_STAT_DEAD, // Awaiting burial (free)
- THREAD_STAT_BURIED // If it's still on the list here, something's wrong
-};
-static const char * const casTHREAD_STAT[] = {
- "THREAD_STAT_NULL",
- "THREAD_STAT_ACTIVE",
- "THREAD_STAT_SLEEPING",
- "THREAD_STAT_MUTEXSLEEP",
- "THREAD_STAT_SEMAPHORESLEEP",
- "THREAD_STAT_QUEUESLEEP",
- "THREAD_STAT_EVENTSLEEP",
- "THREAD_STAT_WAITING",
- "THREAD_STAT_PREINIT",
- "THREAD_STAT_ZOMBIE",
- "THREAD_STAT_DEAD",
- "THREAD_STAT_BURIED"
-};
-
-// === GLOBALS ===
-extern BOOL gaThreads_NoTaskSwitch[MAX_CPUS];
-extern tShortSpinlock glThreadListLock;
-
-// === FUNCTIONS ===
-extern tThread *Proc_GetCurThread(void);
-
-extern tThread *Threads_GetThread(Uint TID);
-extern void Threads_SetPriority(tThread *Thread, int Pri);
-extern int Threads_Wake(tThread *Thread);
-extern void Threads_Kill(tThread *Thread, int Status);
-extern void Threads_AddActive(tThread *Thread);
-extern tThread *Threads_RemActive(void);
-extern void Threads_Delete(tThread *Thread);
-extern tThread *Threads_GetNextToRun(int CPU, tThread *Last);
-
-extern tThread *Threads_CloneTCB(Uint Flags);
-extern tThread *Threads_CloneThreadZero(void);
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * timers.h
- * - Kernel timers
- */
-#ifndef _KERNEL_TIMERS_H_
-#define _KERNEL_TIMERS_H_
-/**
- * \file timers.h
- * \brief Kernel timers
- */
-
-typedef struct sTimer tTimer;
-
-/**
- * \brief Timer callback function
- */
-typedef void (tTimerCallback)(void *);
-
-/**
- * \brief Creates a one-shot timer
- * \param Delta Period of the timer
- * \param Callback Function to call each time
- * \param Argument Argument to pass to the callback
- */
-extern tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
-/**
- * \brief Removed an active timer
- */
-extern void Time_RemoveTimer(tTimer *Timer);
-/**
- * \brief Wait for a period of milliseconds
- */
-extern void Time_Delay(int Delay);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 Core
- *
- * include/tpl_mm_phys_bitmap.h
- * Physical Memory Manager Template
- */
-/*
- * Bitmap Edition
- *
- * Uses 4.125+PtrSize bytes per page
- */
-
-#define MM_PAGE_REFCOUNTS MM_PMM_BASE
-#define MM_PAGE_NODES (MM_PMM_BASE+(MM_MAXPHYSPAGE*sizeof(Uint32)))
-#define MM_PAGE_BITMAP (MM_PAGE_NODES+(MM_MAXPHYSPAGE*sizeof(void*)))
-
-#define PAGE_BITMAP_FREE(__pg) (gaPageBitmaps[(__pg)/32] & (1LL << ((__pg)&31)))
-#define PAGE_BITMAP_SETFREE(__pg) do{gaPageBitmaps[(__pg)/32] |= (1LL << ((__pg)&31));}while(0)
-#define PAGE_BITMAP_SETUSED(__pg) do{gaPageBitmaps[(__pg)/32] &= ~(1LL << ((__pg)&31));}while(0)
-
-// === PROTOTYPES ===
-//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
-//tPAddr MM_AllocPhysRange(int Num, int Bits);
-//tPAddr MM_AllocPhys(void);
-//void MM_RefPhys(tPAddr PAddr);
-//void MM_DerefPhys(tPAddr PAddr);
- int MM_int_GetRangeID( tPAddr Addr );
- int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length );
-void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap);
-
-// === GLOBALS ===
-tMutex glPhysicalPages;
-void **gapPageNodes = (void*)MM_PAGE_NODES; //!< Associated VFS Node for each page
-Uint32 *gaiPageReferences = (void*)MM_PAGE_REFCOUNTS; // Reference Counts
-Uint32 *gaPageBitmaps = (void*)MM_PAGE_BITMAP; // Used bitmap (1 == avail)
-Uint64 giMaxPhysPage = 0; // Maximum Physical page
- int gbPMM_Init = 0;
- int giPhysFirstFree;
- int giPhysLastFree;
- int giPhysNumFree;
-
-// === CODE ===
-/**
- * \brief Initialise the physical memory manager with a passed memory map
- */
-void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap)
-{
- int mapIndex = 0;
- tPAddr rangeStart, rangeLen;
-
- if( MM_PAGE_BITMAP + (MM_MAXPHYSPAGE/8) > MM_PMM_END ) {
- Log_KernelPanic("PMM", "Config Error, PMM cannot fit data in allocated range");
- }
-
- giMaxPhysPage = MaxRAMPage;
-
-// for( i = 0; i < MM_RANGE_MAX; i ++ )
-// gaiPhysRangeFirstFree[i] = -1;
- giPhysFirstFree = -1;
-
- while( MM_int_GetMapEntry(MemoryMap, mapIndex++, &rangeStart, &rangeLen) )
- {
- tVAddr bitmap_page;
-
- LOG("Range %i, %P to %P", mapIndex-1, rangeStart, rangeLen);
- rangeStart /= PAGE_SIZE;
- rangeLen /= PAGE_SIZE;
-
- giPhysNumFree += rangeLen;
-
- LOG("rangeStart = 0x%x, rangeLen = 0x%x", rangeStart, rangeLen);
-
- if( giPhysFirstFree == -1 || giPhysFirstFree > rangeStart )
- giPhysFirstFree = rangeStart;
-
- if( giPhysLastFree < rangeStart + rangeLen )
- giPhysLastFree = rangeStart + rangeLen;
-
- LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree);
-
- bitmap_page = (tVAddr)&gaPageBitmaps[rangeStart/32];
- bitmap_page &= ~(PAGE_SIZE-1);
-
- // Only need to allocate bitmaps
- if( !MM_GetPhysAddr( bitmap_page ) ) {
- if( !MM_Allocate( bitmap_page ) ) {
- Log_KernelPanic("PMM", "Out of memory during init, this is bad");
- return ;
- }
-// memset( (void*)bitmap_page, 0, (rangeStart/8) & ~(PAGE_SIZE-1) );
- memset( (void*)bitmap_page, 0, PAGE_SIZE );
- }
-
- // Align to 32 pages
- for( ; (rangeStart & 31) && rangeLen > 0; rangeStart++, rangeLen-- ) {
- gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31);
- LOG("gaPageBitmaps[%i] = 0x%x", rangeStart/32, gaPageBitmaps[rangeStart/32]);
- }
- // Mark blocks of 32 as avail
- for( ; rangeLen > 31; rangeStart += 32, rangeLen -= 32 ) {
- gaPageBitmaps[rangeStart / 32] = -1;
- }
- // Mark the tail
- for( ; rangeLen > 0; rangeStart ++, rangeLen -- ) {
- gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31);
- }
- }
-
- gbPMM_Init = 1;
-
- LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree);
- LEAVE('-');
-}
-
-/**
- * \brief Allocate a contiguous range of physical pages with a maximum
- * bit size of \a MaxBits
- * \param Pages Number of pages to allocate
- * \param MaxBits Maximum size of the physical address
- * \note If \a MaxBits is <= 0, any sized address is used (with preference
- * to higher addresses)
- */
-tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
-{
- tPAddr addr, ret;
- int nFree = 0, i;
-
- ENTER("iPages iBits", Pages, MaxBits);
-
- Mutex_Acquire(&glPhysicalPages);
-
- // Check if there is enough in the range
- if(giPhysNumFree >= Pages)
- {
- LOG("{0x%x -> 0x%x}", giPhysFirstFree, giPhysLastFree);
- // Do a cheap scan, scanning upwards from the first free page in
- // the range
- nFree = 0;
- addr = giPhysFirstFree;
- while( addr <= giPhysLastFree )
- {
- #if USE_SUPER_BITMAP
- // Check the super bitmap
- if( gaSuperBitmap[addr / (32*32)] == 0 )
- {
- LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
- nFree = 0;
- addr += (32*32);
- addr &= ~(32*32-1); // (1LL << 6+6) - 1
- continue;
- }
- #endif
- LOG("gaPageBitmaps[%i] = 0x%x", addr/32, gaPageBitmaps[addr/32]);
- // Check page block (32 pages)
- if( gaPageBitmaps[addr / 32] == 0) {
- LOG("nFree = %i = 0 (block) (0x%x)", nFree, addr);
- nFree = 0;
- addr += 32;
- addr &= ~31;
- continue;
- }
- // Check individual page
- if( !(gaPageBitmaps[addr / 32] & (1LL << (addr & 31))) )
- {
- LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
- nFree = 0;
- addr ++;
- continue;
- }
- nFree ++;
- addr ++;
- LOG("nFree(%i) == %i (1x%x)", nFree, Pages, addr);
- if(nFree == Pages)
- break;
- }
- LOG("nFree = %i", nFree);
- // If we don't find a contiguous block, nFree will not be equal
- // to Num, so we set it to zero and do the expensive lookup.
- if(nFree != Pages) nFree = 0;
- }
-
- if( !nFree )
- {
-#if 0
- // Oops. ok, let's do an expensive check (scan down the list
- // until a free range is found)
- nFree = 1;
- addr = gaiPhysRangeLastFree[ rangeID ];
- // TODO
-#endif
- Mutex_Release(&glPhysicalPages);
- // TODO: Page out
- // ATM. Just Warning
- Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
- Log_Warning("PMM",
- "Out of memory (unable to fulfil request for %i pages)",
- Pages
- );
- LEAVE('i', 0);
- return 0;
- }
- LOG("nFree = %i, addr = 0x%08x", nFree, (addr-Pages) << 12);
-
- // Mark pages as allocated
- addr -= Pages;
- for( i = 0; i < Pages; i++, addr++ )
- {
- // Mark as used
- PAGE_BITMAP_SETUSED(addr);
- // Maintain first possible free
- giPhysNumFree --;
- if(addr == giPhysFirstFree)
- giPhysFirstFree += 1;
-
- LOG("if( MM_GetPhysAddr( %p ) )", &gaiPageReferences[addr]);
- // Mark as referenced if the reference count page is valid
- if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) ) {
- gaiPageReferences[addr] = 1;
- }
- }
- ret = addr - Pages; // Save the return address
- LOG("ret = %x", ret);
-
- #if TRACE_ALLOCS
- LogF("MM_AllocPhysRange: %P (%i pages)\n", ret, Pages);
- if(Pages > 1) {
- LogF(" also");
- for(i = 1; i < Pages; i++)
- LogF(" %P", ret+i);
- LogF("\n");
- }
- #endif
-
- #if USE_SUPER_BITMAP
- // Update super bitmap
- Pages += addr & (32-1);
- addr &= ~(32-1);
- Pages = (Pages + (32-1)) & ~(32-1);
- for( i = 0; i < Pages/32; i++ )
- {
- if( gaPageBitmaps[ addr / 32 ] + 1 == 0 )
- gaSuperBitmap[addr / (32*32)] |= 1LL << ((addr / 32) & 31);
- }
- #endif
-
- Mutex_Release(&glPhysicalPages);
- LEAVE('x', ret << 12);
- return ret << 12;
-}
-
-/**
- * \brief Allocate a single physical page, with no preference as to address size.
- */
-tPAddr MM_AllocPhys(void)
-{
- int i;
-
- if( !gbPMM_Init )
- {
- // Hack to allow allocation during setup
- for(i = 0; i < NUM_STATIC_ALLOC; i++) {
- if( gaiStaticAllocPages[i] ) {
- tPAddr ret = gaiStaticAllocPages[i];
- gaiStaticAllocPages[i] = 0;
- Log("MM_AllocPhys: Return %x, static alloc %i", ret, i);
- return ret;
- }
- }
-
- tPAddr ret = 0;
- for( ret = 0; ret < giMaxPhysPage; ret ++ )
- {
- if( !MM_GetPhysAddr( (tVAddr)&gaPageBitmaps[ret/32] ) ) {
- ret += PAGE_SIZE*8;
- continue ;
- }
- if( gaPageBitmaps[ret/32] == 0 ) {
- ret += 32-1;
- continue ;
- }
- if( gaPageBitmaps[ret/32] & (1 << (ret&31)) ) {
- gaPageBitmaps[ret/32] &= ~(1 << (ret&31));
- return ret * PAGE_SIZE;
- }
- }
- Log_Error("PMM", "MM_AllocPhys failed duing init");
- return 0;
- }
- #if TRACE_ALLOCS
- Log("AllocPhys by %p", __builtin_return_address(0));
- #endif
-
- return MM_AllocPhysRange(1, -1);
-}
-
-/**
- * \brief Reference a physical page
- */
-void MM_RefPhys(tPAddr PAddr)
-{
- tPAddr page = PAddr / PAGE_SIZE;
- tVAddr refpage = (tVAddr)&gaiPageReferences[page] & ~(PAGE_SIZE-1);
-
- if( page >= giMaxPhysPage ) return ;
-
- if( PAGE_BITMAP_FREE(page) )
- {
- // Allocate
- PAGE_BITMAP_SETUSED(page);
- #if USE_SUPER_BITMAP
- if( gaPageBitmaps[page / 32] == 0 )
- gaSuperBitmap[page / (32*32)] &= ~(1LL << ((page / 32) & 31));
- #endif
- if( MM_GetPhysAddr( refpage ) )
- gaiPageReferences[page] = 1;
- }
- else
- {
- // Reference again
- if( !MM_GetPhysAddr( refpage ) )
- {
- int pages_per_page, basepage, i;
- if( MM_Allocate(refpage) == 0 ) {
- // Out of memory, can this be resolved?
- // TODO: Reclaim memory
- Log_Error("PMM", "Out of memory (MM_RefPhys)");
- return ;
- }
- pages_per_page = PAGE_SIZE/sizeof(*gaiPageReferences);
- basepage = page & ~(pages_per_page-1);
- for( i = 0; i < pages_per_page; i ++ ) {
- if( PAGE_BITMAP_FREE(basepage+i) )
- gaiPageReferences[basepage+i] = 0;
- else
- gaiPageReferences[basepage+i] = 1;
- }
- gaiPageReferences[page] = 2;
- }
- else
- gaiPageReferences[ page ] ++;
- }
-}
-
-int MM_GetRefCount(tPAddr PAddr)
-{
- PAddr >>= 12;
- if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) {
- return gaiPageReferences[PAddr];
- }
-
- if( gaPageBitmaps[ PAddr / 32 ] & (1LL << (PAddr&31)) ) {
- return 1;
- }
-
- return 0;
-}
-
-/**
- * \brief Dereference a physical page
- */
-void MM_DerefPhys(tPAddr PAddr)
-{
- Uint64 page = PAddr >> 12;
-
- if( PAddr >> 12 > giMaxPhysPage ) return ;
-
- ENTER("PPAddr", PAddr);
-
- if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) )
- {
- if( gaiPageReferences[page] > 0 )
- gaiPageReferences[ page ] --;
- if( gaiPageReferences[ page ] == 0 ) {
- gaPageBitmaps[ page / 32 ] |= 1 << (page&31);
- // TODO: Catch when all pages in this range have been dereferenced
- }
- }
- else
- gaPageBitmaps[ page / 32 ] |= 1 << (page&31);
- // Clear node if needed
- if( MM_GetPhysAddr( (tVAddr)&gapPageNodes[page] ) ) {
- gapPageNodes[page] = NULL;
- // TODO: Catch when all pages in this range are not using nodes
- }
-
- // Update the free counts if the page was freed
- if( gaPageBitmaps[ page / 32 ] & (1LL << (page&31)) )
- {
- giPhysNumFree ++;
- if( giPhysFirstFree == -1 || giPhysFirstFree > page )
- giPhysFirstFree = page;
- if( giPhysLastFree < page )
- giPhysLastFree = page;
- }
-
- #if USE_SUPER_BITMAP
- // If the bitmap entry is not zero, set the bit free in the super bitmap
- if(gaPageBitmaps[ page / 32 ] != 0 ) {
- gaSuperBitmap[page / (32*32)] |= 1LL << ((page / 32) & 31);
- }
- #endif
- LEAVE('-');
-}
-
-int MM_SetPageNode(tPAddr PAddr, void *Node)
-{
- tPAddr page = PAddr >> 12;
- tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1);
-
- if( !MM_GetRefCount(PAddr) ) return 1;
-
- if( !MM_GetPhysAddr(node_page) ) {
- if( !MM_Allocate(node_page) )
- return -1;
- memset( (void*)node_page, 0, PAGE_SIZE );
- }
-
- gapPageNodes[page] = Node;
- return 0;
-}
-
-int MM_GetPageNode(tPAddr PAddr, void **Node)
-{
- if( !MM_GetRefCount(PAddr) ) return 1;
- PAddr >>= 12;
-
- if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) {
- *Node = NULL;
- return 0;
- }
-
- *Node = gapPageNodes[PAddr];
- return 0;
-}
-
+++ /dev/null
-/*
- * Acess2 Core
- *
- * include/tpl_mm_phys.h
- * Physical Memory Manager Template
- */
-#define DEBUG 0
-
-/**
- * \file tpl_mm_phys_stack.h
- * \brief Template physical memory manager
- *
- * An extensible physical memory manager
- *
- * Usage: Requires NUM_MM_PHYS_RANGES to be set to the number of address
- * "classes" wanted.
- * MAX_PHYS_PAGES - Used to calculate structure sizes
- * PADDR_TYPE
- */
-
-// === PROTOTYPES ===
-//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
-//tPAddr MM_AllocPhysRange(int Num, int Bits);
-//tPAddr MM_AllocPhys(void);
-//void MM_RefPhys(tPAddr PAddr);
-//void MM_DerefPhys(tPAddr PAddr);
- int MM_int_GetRangeID( tPAddr Addr );
- int MM_int_IsPhysUnavail( tPageNum Page );
-
-// === GLOBALS ===
-tMutex glPhysicalPages;
-//Uint64 *gaPageStacks[NUM_MM_PHYS_RANGES]; // Page stacks
- int giPageStackSizes[NUM_MM_PHYS_RANGES]; // Points to the first unused slot
-Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
-Uint64 giMaxPhysPage = 0; // Maximum Physical page
-
-// === CODE ===
-/**
- * \brief Initialise the physical memory map using a Multiboot 1 map
- */
-void MM_Tpl_InitPhys(int MaxRAMPage)
-{
- const int PAGE_SIZE = 0x1000;
- const int pagesPerPageOnStack = PAGE_SIZE / sizeof(gaPageStack[0]);
- int i;
- tPageNum page;
-
-// ENTER("pMBoot=%p", MBoot);
-
- giMaxPhysPage = MaxRAMPage;
-
- tPAddr page = 0;
- for( i = 0; i < NUM_MM_PHYS_RANGES; i ++ )
- {
- for( ; page < giPageRangeMax[i] && page < giMaxPhysPage; page ++ )
- {
- int rangeSize;
-
- rangeSize = MM_int_IsPhysUnavail(page);
- if( rangeSize > 0 ) {
- page += rangeSize;
- continue;
- }
- // Page is avaliable for use
-
- // Check if the page stack is allocated
- tVAddr stack_page = &gaPageStacks[i][giPageStackSizes[i]&~pagesPerPageOnStack];
- if( !MM_GetPhysAddr( stack_page ) ) {
- MM_Map( stack_page, page*PAGE_SIZE );
- }
- else {
- gaPageStacks[i][ giPageStackSizes[i] ] = page;
- giPageStackSizes[i] ++;
- }
- }
- }
-
- LEAVE('-');
-}
-
-/**
- * \brief Allocate a contiguous range of physical pages with a maximum
- * bit size of \a MaxBits
- * \param Pages Number of pages to allocate
- * \param MaxBits Maximum size of the physical address
- * \note If \a MaxBits is <= 0, any sized address is used (with preference
- * to higher addresses)
- */
-tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
-{
- tPAddr addr, ret;
- int rangeID;
- int nFree = 0, i;
-
- ENTER("iPages iBits", Pages, MaxBits);
-
- if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case
- rangeID = MM_PHYS_MAX;
- else
- rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 );
-
- LOG("rangeID = %i", rangeID);
-
- Mutex_Acquire(&glPhysicalPages);
-
- // Check if the range actually has any free pages
- while(giPageStackSizes[rangeID] == 0 && rangeID)
- rangeID --;
-
- LOG("rangeID = %i", rangeID);
-
- // What the? Oh, man. No free pages
- if(giPageStackSizes[rangeID] == 0) {
- Mutex_Release(&glPhysicalPages);
- // TODO: Page out / attack the cache
- // ATM. Just Warning
- Warning(" MM_AllocPhysRange: Out of free pages");
- Log_Warning("Arch",
- "Out of memory (unable to fulfil request for %i pages), zero remaining",
- Pages
- );
- LEAVE('i', 0);
- return 0;
- }
-
- // Check if there is enough in the range
- if(giPhysRangeFree[rangeID] >= Pages)
- {
- LOG("{%i,0x%x -> 0x%x}",
- giPhysRangeFree[rangeID],
- giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID]
- );
- // Do a cheap scan, scanning upwards from the first free page in
- // the range
- nFree = 0;
- addr = giPhysRangeFirst[ rangeID ];
- while( addr <= giPhysRangeLast[ rangeID ] )
- {
- //Log(" MM_AllocPhysRange: addr = 0x%x", addr);
- // Check the super bitmap
- if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) {
- LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
- nFree = 0;
- addr += 1LL << (6+6);
- addr &= ~0xFFF; // (1LL << 6+6) - 1
- continue;
- }
- // Check page block (64 pages)
- if( gaMainBitmap[addr >> 6] + 1 == 0) {
- LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
- nFree = 0;
- addr += 1LL << (6);
- addr &= ~0x3F;
- continue;
- }
- // Check individual page
- if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) {
- LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
- nFree = 0;
- addr ++;
- continue;
- }
- nFree ++;
- addr ++;
- LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr);
- if(nFree == Pages)
- break;
- }
- LOG("nFree = %i", nFree);
- // If we don't find a contiguous block, nFree will not be equal
- // to Num, so we set it to zero and do the expensive lookup.
- if(nFree != Pages) nFree = 0;
- }
-
- if( !nFree )
- {
- // Oops. ok, let's do an expensive check (scan down the list
- // until a free range is found)
- nFree = 1;
- addr = giPhysRangeLast[ rangeID ];
- // TODO
- Mutex_Release(&glPhysicalPages);
- // TODO: Page out
- // ATM. Just Warning
- Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
- Log_Warning("Arch",
- "Out of memory (unable to fulfil request for %i pages)",
- Pages
- );
- LEAVE('i', 0);
- return 0;
- }
- LOG("nFree = %i, addr = 0x%08x", nFree, addr);
-
- // Mark pages as allocated
- addr -= Pages;
- for( i = 0; i < Pages; i++, addr++ )
- {
- gaMainBitmap[addr >> 6] |= 1LL << (addr & 63);
- rangeID = MM_int_GetRangeID(addr << 12);
- giPhysRangeFree[ rangeID ] --;
- LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]);
- if(addr == giPhysRangeFirst[ rangeID ])
- giPhysRangeFirst[ rangeID ] += 1;
- }
- ret = addr; // Save the return address
-
- // Update super bitmap
- Pages += addr & (64-1);
- addr &= ~(64-1);
- Pages = (Pages + (64-1)) & ~(64-1);
- for( i = 0; i < Pages/64; i++ )
- {
- if( gaMainBitmap[ addr >> 6 ] + 1 == 0 )
- gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63);
- }
-
- Mutex_Release(&glPhysicalPages);
- LEAVE('x', ret << 12);
- return ret << 12;
-}
-
-/**
- * \brief Allocate a single physical page, with no preference as to address
- * size.
- */
-tPAddr MM_AllocPhys(void)
-{
- int i;
-
- // Hack to allow allocation during setup
- for(i = 0; i < NUM_STATIC_ALLOC; i++) {
- if( gaiStaticAllocPages[i] ) {
- tPAddr ret = gaiStaticAllocPages[i];
- gaiStaticAllocPages[i] = 0;
- Log("MM_AllocPhys: Return %x, static alloc %i", ret, i);
- return ret;
- }
- }
-
- return MM_AllocPhysRange(1, -1);
-}
-
-/**
- * \brief Reference a physical page
- */
-void MM_RefPhys(tPAddr PAddr)
-{
- Uint64 page = PAddr >> 12;
-
- if( PAddr >> 12 > giMaxPhysPage ) return ;
-
- if( gaMainBitmap[ page >> 6 ] & (1LL << (page&63)) )
- {
- // Reference again
- gaMultiBitmap[ page >> 6 ] |= 1LL << (page&63);
- gaiPageReferences[ page ] ++;
- }
- else
- {
- // Allocate
- gaMainBitmap[page >> 6] |= 1LL << (page&63);
- if( gaMainBitmap[page >> 6 ] + 1 == 0 )
- gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63);
- }
-}
-
-/**
- * \brief Dereference a physical page
- */
-void MM_DerefPhys(tPAddr PAddr)
-{
- Uint64 page = PAddr >> 12;
-
- if( PAddr >> 12 > giMaxPhysPage ) return ;
-
- if( gaMultiBitmap[ page >> 6 ] & (1LL << (page&63)) ) {
- gaiPageReferences[ page ] --;
- if( gaiPageReferences[ page ] == 1 )
- gaMultiBitmap[ page >> 6 ] &= ~(1LL << (page&63));
- if( gaiPageReferences[ page ] == 0 )
- gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63));
- }
- else
- gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63));
-
- // Update the free counts if the page was freed
- if( !(gaMainBitmap[ page >> 6 ] & (1LL << (page&63))) )
- {
- int rangeID;
- rangeID = MM_int_GetRangeID( PAddr );
- giPhysRangeFree[ rangeID ] ++;
- if( giPhysRangeFirst[rangeID] > page )
- giPhysRangeFirst[rangeID] = page;
- if( giPhysRangeLast[rangeID] < page )
- giPhysRangeLast[rangeID] = page;
- }
-
- // If the bitmap entry is not -1, unset the bit in the super bitmap
- if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) {
- gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63));
- }
-}
-
-/**
- * \brief Takes a physical address and returns the ID of its range
- * \param Addr Physical address of page
- * \return Range ID from eMMPhys_Ranges
- */
-int MM_int_GetRangeID( tPAddr Addr )
-{
- if(Addr >> 32)
- return MM_PHYS_MAX;
- else if(Addr >> 24)
- return MM_PHYS_32BIT;
- else if(Addr >> 20)
- return MM_PHYS_24BIT;
- else if(Addr >> 16)
- return MM_PHYS_20BIT;
- else
- return MM_PHYS_16BIT;
-}
+++ /dev/null
-/*
- * Acess2
- * VFS Common Header
- */
-/**
- * \file vfs.h
- * \brief Acess VFS Layer
- *
- * The Acess Virtual File System (VFS) provides abstraction of multiple
- * physical filesystems, network storage and devices (both hardware and
- * virtual) to the user.
- *
- * The core of the VFS is the concept of a \ref tVFS_Node "VFS Node".
- * A VFS Node represents a "file" in the VFS tree, this can be any sort
- * of file (an ordinary file, a directory, a symbolic link or a device)
- * depending on the bits set in the \ref tVFS_Node.Flags Flags field.
- * - For more information see "VFS Node Flags"
- */
-#ifndef _VFS_H
-#define _VFS_H
-
-#include <acess.h>
-
-/**
- * \brief Thread list datatype for VFS_Select
- */
-typedef struct sVFS_SelectList tVFS_SelectList;
-
-typedef struct sVFS_NodeType tVFS_NodeType;
-
-/**
- * \name tVFS_Node Flags
- * \brief Flag values for tVFS_Node.Flags
- * \{
- */
-//! \todo Is this still needed
-#define VFS_FFLAG_READONLY 0x01 //!< Readonly File
-/**
- * \brief Directory Flag
- *
- * This flag marks the tVFS_Node as describing a directory, and says
- * that the tVFS_Node.FindDir, tVFS_Node.ReadDir, tVFS_Node.MkNod and
- * tVFS_Node.Relink function pointers are valid.
- * For a directory the tVFS_Node.Size field contains the number of files
- * within the directory, or -1 for undetermined.
- */
-#define VFS_FFLAG_DIRECTORY 0x02
-/**
- * \brief Symbolic Link Flag
- *
- * Marks a file as a symbolic link
- */
-#define VFS_FFLAG_SYMLINK 0x04
-/**
- * \brief Set User ID Flag
- *
- * Allows an executable file to change it's executing user to the file's
- * owner.
- * In the case of a directory, it means that all immediate children will
- * inherit the UID of the parent.
- */
-#define VFS_FFLAG_SETUID 0x08
-/**
- * \brief Set Group ID Flag
- *
- * Allows an executable file to change it's executing group to the file's
- * owning group.
- * In the case of a directory, it means that all immediate children will
- * inherit the GID of the parent.
- */
-#define VFS_FFLAG_SETGID 0x10
-
-/**
- * \brief "Don't do Write-Back" Flag
- *
- * Stops the VFS from calling tVFS_Node.Write on dirty pages when a region
- * is unmapped. Nice for read-only files and memory-only files (or
- * pseudo-readonly filesystems)
- */
-#define VFS_FFLAG_NOWRITEBACK
-/**
- * \}
- */
-
-/**
- * \brief Represents a node (file or directory) in the VFS tree
- *
- * This structure provides the VFS with the functions required to read/write
- * the file (or directory) that it represents.
- */
-typedef struct sVFS_Node
-{
- /**
- * \name Identifiers
- * \brief Fields used by the driver to identify what data this node
- * corresponds to.
- * \{
- */
- Uint64 Inode; //!< Inode ID - Must identify the node uniquely if tVFS_Driver.GetNodeFromINode is non-NULL
- Uint ImplInt; //!< Implementation Usable Integer
- void *ImplPtr; //!< Implementation Usable Pointer
- /**
- * \}
- */
-
- /**
- * \name Node State
- * \brief Stores the misc information about the node
- * \{
- */
- int ReferenceCount; //!< Number of times the node is used
-
- Uint64 Size; //!< File Size
-
- Uint32 Flags; //!< File Flags
-
- /**
- * \brief Pointer to cached data (FS Specific)
- * \note The Inode_* functions will free when the node is uncached
- * this if needed
- */
- void *Data;
-
- /**
- * \brief Node mutex
- * \note Provided for the Filesystem driver's use
- */
- tMutex Lock;
-
- /**
- * \}
- */
-
- /**
- * \name Times
- * \{
- */
- Sint64 ATime; //!< Last Accessed Time
- Sint64 MTime; //!< Last Modified Time
- Sint64 CTime; //!< Creation Time
- /**
- * \}
- */
-
- /**
- * \name Access control
- * \{
- */
- tUID UID; //!< ID of Owning User
- tGID GID; //!< ID of Owning Group
-
- int NumACLs; //!< Number of ACL entries
- tVFS_ACL *ACLs; //!< Access Controll List pointer
- /**
- * \}
- */
-
- /**
- * \name VFS_Select() fields
- * \note Used by the VFS internals, drivers should use VFS_Mark*
- * \{
- */
- int DataAvaliable;
- tVFS_SelectList *ReadThreads; //!< Threads waiting to read
- int BufferFull;
- tVFS_SelectList *WriteThreads; //!< Threads waiting to write
- int ErrorOccurred;
- tVFS_SelectList *ErrorThreads; //!< Threads waiting for an error
- /**
- * \}
- */
-
- /**
- * \name VFS_MMap() fields
- * \{
- */
- void *MMapInfo;
- /**
- * \}
- */
-
- /**
- * \brief Functions associated with the node
- */
- tVFS_NodeType *Type;
-} tVFS_Node;
-
-/**
- * \brief Functions for a specific node type
- */
-struct sVFS_NodeType
-{
- /**
- * \brief Debug name for the type
- */
- const char *TypeName;
-
- /**
- * \name Common Functions
- * \brief Functions that are used no matter the value of .Flags
- * \{
- */
- /**
- * \brief Reference the node
- * \param Node Pointer to this node
- */
- void (*Reference)(struct sVFS_Node *Node);
- /**
- * \brief Close (dereference) the node
- * \param Node Pointer to this node
- *
- * Usually .Close is used to write any changes to the node back to
- * the persistent storage.
- */
- void (*Close)(struct sVFS_Node *Node);
-
- /**
- * \brief Send an IO Control
- * \param Node Pointer to this node
- * \param Id IOCtl call number
- * \param Data Pointer to data to pass to the driver
- * \return Implementation defined
- */
- int (*IOCtl)(struct sVFS_Node *Node, int Id, void *Data);
-
- /**
- * \}
- */
-
- /**
- * \name Buffer Functions
- * \brief Functions for accessing a buffer-type file (normal file or
- * symbolic link)
- * \{
- */
-
- /**
- * \brief Read from the file
- * \param Node Pointer to this node
- * \param Offset Byte offset in the file
- * \param Length Number of bytes to read
- * \param Buffer Destination for read data
- * \return Number of bytes read
- */
- Uint64 (*Read)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- /**
- * \brief Write to the file
- * \param Node Pointer to this node
- * \param Offset Byte offser in the file
- * \param Length Number of bytes to write
- * \param Buffer Source of written data
- * \return Number of bytes read
- */
- Uint64 (*Write)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
-
- /**
- * \brief Map a region of a file into memory
- * \param Node Pointer to this node
- * \param Offset Start of the region (page aligned)
- * \param Length Length of the region (page aligned)
- * \param Dest Location to which to map
- * \return Boolean Failure
- * \note If NULL, the VFS implements it using .Read
- */
- int (*MMap)(struct sVFS_Node *Node, Uint64 Offset, int Length, void *Dest);
-
- /**
- * \}
- */
-
- /**
- * \name Directory Functions
- * \{
- */
- /**
- * \brief Find an directory entry by name
- * \param Node Pointer to this node
- * \param Name Name of the file wanted
- * \return Pointer to the requested node or NULL if it cannot be found
- * \note The node returned must be accessable until ::tVFS_Node.Close
- * is called and ReferenceCount reaches zero.
- */
- struct sVFS_Node *(*FindDir)(struct sVFS_Node *Node, const char *Name);
-
- /**
- * \brief Read from a directory
- * \param Node Pointer to this node
- * \param Pos Offset in the directory
- * \return Pointer to the name of the item on the heap (will be freed
- * by the caller). If the directory end has been reached, NULL
- * will be returned.
- * If an item is required to be skipped either &::NULLNode,
- * ::VFS_SKIP or ::VFS_SKIPN(0...1023) will be returned.
- */
- char *(*ReadDir)(struct sVFS_Node *Node, int Pos);
-
- /**
- * \brief Create a node in a directory
- * \param Node Pointer to this node
- * \param Name Name of the new child
- * \param Flags Flags to apply to the new child (directory or symlink)
- * \return Zero on Success, non-zero on error (see errno.h)
- */
- int (*MkNod)(struct sVFS_Node *Node, const char *Name, Uint Flags);
-
- /**
- * \brief Relink (Rename/Remove) a file/directory
- * \param Node Pointer to this node
- * \param OldName Name of the item to move/delete
- * \param NewName New name (or NULL if unlinking is wanted)
- * \return Zero on Success, non-zero on error (see errno.h)
- */
- int (*Relink)(struct sVFS_Node *Node, const char *OldName, const char *NewName);
-
- /**
- * \brief Link a node to a name
- * \param Node Pointer to this node (directory)
- * \param Child Node to create a new link to
- * \param NewName Name for the new link
- * \retur Zeron on success, non-zero on error (see errno.h)
- */
- int (*Link)(struct sVFS_Node *Node, struct sVFS_Node *Child, const char *NewName);
-
- /**
- * \}
- */
-};
-
-/**
- * \brief VFS Driver (Filesystem) Definition
- */
-typedef struct sVFS_Driver
-{
- /**
- * \brief Unique Identifier for this filesystem type
- */
- const char *Name;
- /**
- * \brief Flags applying to this driver
- */
- Uint Flags;
-
- /**
- * \brief Callback to mount a device
- */
- tVFS_Node *(*InitDevice)(const char *Device, const char **Options);
- /**
- * \brief Callback to unmount a device
- */
- void (*Unmount)(tVFS_Node *Node);
- /**
- * \brief Retrieve a VFS node from an inode
- */
- tVFS_Node *(*GetNodeFromINode)(tVFS_Node *RootNode, Uint64 InodeValue);
- /**
- * \brief Used internally (next driver in the chain)
- */
- struct sVFS_Driver *Next;
-} tVFS_Driver;
-
-// === GLOBALS ===
-//! \brief Maximum number of elements that can be skipped in one return
-#define VFS_MAXSKIP ((void*)1024)
-//! \brief Skip a single entry in readdir
-#define VFS_SKIP ((void*)1)
-//! \brief Skip \a n entries in readdir
-#define VFS_SKIPN(n) ((void*)(n))
-
-extern tVFS_Node NULLNode; //!< NULL VFS Node (Ignored/Skipped)
-/**
- * \name Static ACLs
- * \brief Simple ACLs to aid writing drivers
- * \{
- */
-extern tVFS_ACL gVFS_ACL_EveryoneRWX; //!< Everyone Read/Write/Execute
-extern tVFS_ACL gVFS_ACL_EveryoneRW; //!< Everyone Read/Write
-extern tVFS_ACL gVFS_ACL_EveryoneRX; //!< Everyone Read/Execute
-extern tVFS_ACL gVFS_ACL_EveryoneRO; //!< Everyone Read only
-/**
- * \}
- */
-
-// === FUNCTIONS ===
-/**
- * \fn int VFS_AddDriver(tVFS_Driver *Info)
- * \brief Registers the driver with the DevFS layer
- * \param Info Driver information structure
- */
-extern int VFS_AddDriver(tVFS_Driver *Info);
-/**
- * \brief Get the information structure of a driver given its name
- * \param Name Name of filesystem driver to find
- */
-extern tVFS_Driver *VFS_GetFSByName(const char *Name);
-
-
-/**
- * \brief Prepare a node for use
- */
-extern void VFS_InitNode(tVFS_Node *Node);
-
-/**
- * \brief Clean up a node, ready for deletion
- * \note This should ALWAYS be called before a node is freed, as it
- * cleans up VFS internal structures.
- */
-extern void VFS_CleanupNode(tVFS_Node *Node);
-
-/**
- * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group)
- * \brief Transforms Unix Permssions into Acess ACLs
- * \param Mode Unix RWXrwxRWX mask
- * \param Owner UID of the file's owner
- * \param Group GID of the file's owning group
- * \return An array of 3 Acess ACLs
- */
-extern tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group);
-
-/**
- * \brief Flags fro \a TypeFlag of VFS_SelectNode
- * \{
- */
-#define VFS_SELECT_READ 0x01
-#define VFS_SELECT_WRITE 0x02
-#define VFS_SELECT_ERROR 0x04
-/**
- * \}
- */
-
-/**
- * \brief Wait for an event on a node
- * \param Node Node to wait on
- * \param Type Type of wait
- * \param Timeout Time to wait (NULL for infinite wait)
- * \param Name Name to show in debug output
- * \return Number of nodes that actioned (0 or 1)
- */
-extern int VFS_SelectNode(tVFS_Node *Node, int Type, tTime *Timeout, const char *Name);
-
-/**
- * \brief Change the full flag on a node
- */
-extern int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
-/**
- * \brief Alter the space avaliable flag on a node
- */
-extern int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
-/**
- * \brief Alter the error flags on a node
- */
-extern int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
-
-// --- Node Cache --
-/**
- * \name Node Cache
- * \brief Functions to allow a node to be cached in memory by the VFS
- *
- * These functions store a node for the driver, to prevent it from having
- * to re-generate the node on each call to FindDir. It also allows for
- * fast cleanup when a filesystem is unmounted.
- * \{
- */
-/**
- * \fn int Inode_GetHandle(void)
- * \brief Gets a unique handle to the Node Cache
- * \return A unique handle for use for the rest of the Inode_* functions
- */
-extern int Inode_GetHandle(void);
-/**
- * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode)
- * \brief Gets an inode from the node cache
- * \param Handle A handle returned by Inode_GetHandle()
- * \param Inode Value of the Inode field of the ::tVFS_Node you want
- * \return A pointer to the cached node
- */
-extern tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode);
-/**
- * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node)
- * \brief Caches a node in the Node Cache
- * \param Handle A handle returned by Inode_GetHandle()
- * \param Node A pointer to the node to be cached (a copy is taken)
- * \return A pointer to the node in the node cache
- */
-extern tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node);
-/**
- * \fn int Inode_UncacheNode(int Handle, Uint64 Inode)
- * \brief Dereferences (and removes if needed) a node from the cache
- * \param Handle A handle returned by Inode_GetHandle()
- * \param Inode Value of the Inode field of the ::tVFS_Node you want to remove
- */
-extern void Inode_UncacheNode(int Handle, Uint64 Inode);
-/**
- * \fn void Inode_ClearCache(int Handle)
- * \brief Clears the cache for a handle
- * \param Handle A handle returned by Inode_GetHandle()
- */
-extern void Inode_ClearCache(int Handle);
-
-/**
- * \}
- */
-
-#endif
+++ /dev/null
-/**
- * \file vfs_ext.h
- * \brief Exported VFS Definitions
- * \author John Hodge (thePowersGang)
- */
-#ifndef _VFS_EXT_H
-#define _VFS_EXT_H
-
-//! Inode number type
-typedef Uint64 tInode;
-
-//! Mountpoint identifier type
-typedef Uint32 tMount;
-
-// === CONSTANTS ===
-//! Maximum size of a Memory Path generated by VFS_GetMemPath
-#define VFS_MEMPATH_SIZE (3 + (BITS/4)*2)
-/**
- * \name Flags for VFS_Open
- * \{
- */
-//! Open for execution
-#define VFS_OPENFLAG_EXEC 0x01
-//! Open for reading
-#define VFS_OPENFLAG_READ 0x02
-//! Open for writing
-#define VFS_OPENFLAG_WRITE 0x04
-//! Do not resolve the final symbolic link
-#define VFS_OPENFLAG_NOLINK 0x40
-//! Create the file if it doesn't exist
-#define VFS_OPENFLAG_CREATE 0x80
-//! Open as a user
-#define VFS_OPENFLAG_USER 0x8000
-/**
- * \}
- */
-//! Marks a VFS handle as belonging to the kernel
-#define VFS_KERNEL_FLAG 0x40000000
-
-//! Architectual maximum number of file descriptors
-#define MAX_FILE_DESCS 128
-
-/**
- * \brief VFS_Seek directions
- */
-enum eVFS_SeekDirs
-{
- SEEK_SET = 1, //!< Set the current file offset
- SEEK_CUR = 0, //!< Seek relative to the current position
- SEEK_END = -1 //!< Seek from the end of the file backwards
-};
-
-/**
- * \name ACL Permissions
- * \{
- */
-/**
- * \brief Readable
- */
-#define VFS_PERM_READ 0x00000001
-/**
- * \brief Writeable
- */
-#define VFS_PERM_WRITE 0x00000002
-/**
- * \brief Append allowed
- */
-#define VFS_PERM_APPEND 0x00000004
-/**
- * \brief Executable
- */
-#define VFS_PERM_EXECUTE 0x00000008
-/**
- * \brief All permissions granted
- */
-#define VFS_PERM_ALL 0x7FFFFFFF // Mask for permissions
-/**
- * \brief Denies instead of granting permissions
- * \note Denials take precedence
- */
-#define VFS_PERM_DENY 0x80000000 // Inverts permissions
-/**
- * \}
- */
-
-/**
- * \brief MMap protection flags
- * \{
- */
-#define MMAP_PROT_READ 0x001 //!< Readable memory
-#define MMAP_PROT_WRITE 0x002 //!< Writable memory
-#define MMAP_PROT_EXEC 0x004 //!< Executable memory
-/**
- * \}
- */
-
-/**
- * \brief MMap mapping flags
- * \{
- */
-#define MMAP_MAP_SHARED 0x001 //!< Shared with all other users of the FD
-#define MMAP_MAP_PRIVATE 0x002 //!< Local (COW) copy
-#define MMAP_MAP_FIXED 0x004 //!< Load to a fixed address
-#define MMAP_MAP_ANONYMOUS 0x008 //!< Not associated with a FD
-/**
- * \}
- */
-
-// -- System Call Structures ---
-/**
- * \brief ACL Defintion Structure
- */
-typedef struct sVFS_ACL
-{
- struct {
- unsigned Group: 1; //!< Group (as opposed to user) flag
- unsigned ID: 31; //!< ID of Group/User (-1 for nobody/world)
- };
- struct {
- unsigned Inv: 1; //!< Invert Permissions
- unsigned Perms: 31; //!< Permission Flags
- };
-} tVFS_ACL;
-
-/**
- * \brief SYS_FINFO structure
- */
-typedef struct sFInfo
-{
- tMount mount; //!< Mountpoint ID
- tInode inode; //!< Inode
- Uint32 uid; //!< Owning User ID
- Uint32 gid; //!< Owning Group ID
- Uint32 flags; //!< File flags
- Uint64 size; //!< File Size
- Sint64 atime; //!< Last Accessed time
- Sint64 mtime; //!< Last modified time
- Sint64 ctime; //!< Creation time
- Sint32 numacls; //!< Total number of ACL entries
- tVFS_ACL acls[]; //!< ACL buffer (size is passed in the \a MaxACLs argument to VFS_FInfo)
-} PACKED tFInfo;
-
-/**
- * \brief fd_set for select()
- */
-typedef struct
-{
- //! Bitmap of set file descriptors
- Uint16 flags[MAX_FILE_DESCS/16];
-} fd_set;
-
-/**
- * \brief Clear a descriptor flag in a fd_set
- * \param fd File descriptor to clear
- * \param fdsetp Set to modify
- */
-#define FD_CLR(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&=~(1<<((fd)%16)))
-/**
- * \brief Set a descriptor flag in a fd_set
- * \param fd File descriptor to set
- * \param fdsetp Set to modify
- */
-#define FD_SET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]|=~(1<<((fd)%16)))
-/**
- * \brief Test a descriptor flag in a fd_set
- * \param fd File descriptor to test
- * \param fdsetp Set to modify
- */
-#define FD_ISSET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&(1<<((fd)%16)))
-
-// === FUNCTIONS ===
-/**
- * \brief Initialise the VFS (called by system.c)
- * \return Boolean Success
- */
-extern int VFS_Init(void);
-
-/**
- * \brief Open a file
- * \param Path Absolute or relative path to the file
- * \param Flags Flags defining how to open the file
- * \return VFS Handle (an integer) or -1 if an error occured
- * \note Calls VFS_OpenEx(Path, Flags, 0)
- */
-extern int VFS_Open(const char *Path, Uint Flags);
-/**
- * \brief Open a file
- * \param Path Absolute or relative path to the file
- * \param Flags Flags defining how to open the file
- * \param Mode Mode for newly created file (POSIX compatability)
- * \return VFS Handle (an integer) or -1 if an error occured
- */
-extern int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode);
-/**
- * \brief Opens a file via an open directory
- * \note The file to open must be a direct child of the parent
- * \param FD Parent Directory
- * \param Name Child name
- * \param Mode Open mode
- * \return File handle (same as returned from VFS_Open)
- */
-extern int VFS_OpenChild(int FD, const char *Name, Uint Mode);
-/**
- * \brief Opens a file given a mount ID and an inode number
- * \param Mount Mount ID returned by FInfo
- * \param Inode Inode number from FInfo
- * \param Mode Open mode (see VFS_Open)
- * \return File handle (same as VFS_Open)
- */
-extern int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode);
-
-/**
- * \brief Close a currently open file
- * \param FD Handle returned by ::VFS_Open
- */
-extern void VFS_Close(int FD);
-
-/**
- * \brief Get file information from an open file
- * \param FD File handle returned by ::VFS_Open
- * \param Dest Destination for the read information
- * \param MaxACLs Number of ACL slots allocated in the \a Dest structure
- * \return Boolean Success
- *
- * If the \a NumACLs is smaller than the number of ACLs the node has, only
- * \a NumACLs will be copied into \a Dest, but the tFInfo.numacls field
- * will be set to the true ammount of ACLs. It is up to the user to do with
- * this information how they like.
- */
-extern int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs);
-/**
- * \brief Gets the permissions appling to a user/group.
- * \param FD File handle returned by ::VFS_Open
- * \param Dest ACL information structure to edit
- * \return Boolean success
- *
- * This function sets the tVFS_ACL.Inv and tVFS_ACL.Perms fields to what
- * permissions the user/group specied in tVFS_ACL.ID has on the file.
- */
-extern int VFS_GetACL(int FD, tVFS_ACL *Dest);
-/**
- * \brief Changes the user's current working directory
- * \param Dest New working directory (either absolute or relative to the current)
- * \return Boolean Success
- */
-extern int VFS_ChDir(const char *Dest);
-/**
- * \brief Change the current virtual root for the user
- * \param New New virtual root (same as ::VFS_ChDir but cannot go
- * above the current virtual root)
- * \return Boolean success
- */
-extern int VFS_ChRoot(const char *New);
-
-/**
- * \brief Change the location of the current file pointer
- * \param FD File handle returned by ::VFS_Open
- * \param Offset Offset within the file to go to
- * \param Whence A direction from ::eVFS_SeekDirs
- * \return Boolean success
- */
-extern int VFS_Seek(int FD, Sint64 Offset, int Whence);
-/**
- * \brief Returns the current file pointer
- * \param FD File handle returned by ::VFS_Open
- * \return Current file position
- */
-extern Uint64 VFS_Tell(int FD);
-
-/**
- * \brief Reads data from a file
- * \param FD File handle returned by ::VFS_Open
- * \param Length Number of bytes to read from the file
- * \param Buffer Destination of read data
- * \return Number of read bytes
- */
-extern Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer);
-/**
- * \brief Writes data to a file
- * \param FD File handle returned by ::VFS_Open
- * \param Length Number of bytes to write to the file
- * \param Buffer Source of written data
- * \return Number of bytes written
- */
-extern Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer);
-
-/**
- * \brief Reads from a specific offset in the file
- * \param FD File handle returned by ::VFS_Open
- * \param Offset Byte offset in the file
- * \param Length Number of bytes to read from the file
- * \param Buffer Source of read data
- * \return Number of bytes read
- */
-extern Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer);
-/**
- * \brief Writes to a specific offset in the file
- * \param FD File handle returned by ::VFS_Open
- * \param Offset Byte offset in the file
- * \param Length Number of bytes to write to the file
- * \param Buffer Source of written data
- * \return Number of bytes written
- */
-extern Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer);
-
-/**
- * \brief Sends an IOCtl request to the driver
- * \param FD File handle returned by ::VFS_Open
- * \param ID IOCtl call ID (driver specific)
- * \param Buffer Data pointer to send to the driver
- * \return Driver specific response
- */
-extern int VFS_IOCtl(int FD, int ID, void *Buffer);
-
-/**
- * \brief Creates a VFS Memory path from a pointer and size
- * \param Dest Destination for the created path
- * \param Base Base of the memory file
- * \param Length Length of the memory buffer
- * \note A maximum of VFS_MEMPATH_SIZE bytes will be required in \a Dest
- */
-extern void VFS_GetMemPath(char *Dest, void *Base, Uint Length);
-/**
- * \brief Gets the canoical (true) path of a file
- * \param Path File path (may contain symlinks, relative elements etc.)
- * \return Absolute path with no symlinks
- */
-extern char *VFS_GetTruePath(const char *Path);
-
-/**
- * \brief Mounts a filesystem
- * \param Device Device to mount
- * \param MountPoint Location to mount
- * \param Filesystem Filesystem to use
- * \param Options Options string to pass the driver
- * \return 1 on succes, -1 on error
- */
-extern int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
-/**
- * \brief Create a new directory
- * \param Path Path to new directory (absolute or relative)
- * \return Boolean success
- * \note The parent of the directory must exist
- */
-extern int VFS_MkDir(const char *Path);
-/**
- * \brief Create a symbolic link
- * \param Name Name of the symbolic link
- * \param Link File the symlink points to
- * \return Boolean success (\a Link is not tested for existence)
- */
-extern int VFS_Symlink(const char *Name, const char *Link);
-/**
- * \brief Read from a directory
- * \param FD File handle returned by ::VFS_Open
- * \param Dest Destination array for the file name (max 255 bytes)
- * \return Boolean Success
- */
-extern int VFS_ReadDir(int FD, char *Dest);
-/**
- * \brief Wait for an aciton on a file descriptor
- * \param MaxHandle Maximum set handle in \a *Handles
- * \param ReadHandles Handles to wait for data on
- * \param WriteHandles Handles to wait to write to
- * \param ErrHandles Handle to wait for errors on
- * \param Timeout Timeout for select() (if null, there is no timeout), if zero select() is non blocking
- * \param ExtraEvents Extra event set to wait on
- * \param IsKernel Use kernel handles as opposed to user handles
- * \return Number of handles that actioned
- */
-extern int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel);
-
-/**
- * \brief Map a file into memory
- * \param DestHint Suggested place for read data
- * \param Length Size of area to map
- * \param Protection Protection type (see `man mmap`)
- * \param Flags Mapping flags
- * \param FD File descriptor to load from
- * \param Offset Start of region
- */
-extern void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset);
-
-/**
- * \brief Unmap memory allocated by VFS_MMap
- * \param Addr Address of data to unmap
- * \param Length Length of data
- */
-extern int VFS_MUnmap(void *Addr, size_t Length);
-#endif
+++ /dev/null
-/*
- * Acess Micro - VFS Server Ver 1
- */
-#ifndef _VFS_INT_H
-#define _VFS_INT_H
-
-#include "vfs.h"
-
-// === TYPES ===
-typedef struct sVFS_Mount {
- struct sVFS_Mount *Next;
- char *MountPoint;
- size_t MountPointLen;
- Uint32 Identifier;
- char *Device;
- char *Options;
- tVFS_Driver *Filesystem;
- tVFS_Node *RootNode;
- char StrData[];
-} tVFS_Mount;
-
-typedef struct sVFS_Handle {
- tVFS_Node *Node;
- tVFS_Mount *Mount;
- Uint64 Position;
- Uint Mode;
-} tVFS_Handle;
-
-typedef struct sVFS_Proc {
- struct sVFS_Proc *Next;
- int ID;
- int CwdLen;
- Uint UID, GID;
- char *Cwd;
- int MaxHandles;
- tVFS_Handle Handles[];
-} tVFS_Proc;
-
-typedef struct sVFS_MMapPage {
- Uint64 FileOffset;
- tPAddr PAddr;
-} tVFS_MMapPage;
-
-// === GLOBALS ===
-extern tVFS_Mount *gVFS_Mounts;
-
-// === PROTOTYPES ===
-// --- open.c ---
-extern char *VFS_GetAbsPath(const char *Path);
-extern tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint);
-extern tVFS_Handle *VFS_GetHandle(int FD);
-// --- acls.c ---
-extern int VFS_CheckACL(tVFS_Node *Node, Uint Permissions);
-// --- mount.c ---
-extern tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID);
-// --- dir.c ---
-extern int VFS_MkNod(const char *Path, Uint Flags);
-
-
-// --- VFS Helpers ---
-static inline void _CloseNode(tVFS_Node *Node)
-{
- if(Node && Node->Type && Node->Type->Close)
- Node->Type->Close( Node );
-}
-
-
-#endif
+++ /dev/null
-/*
- * AcessMicro VFS
- * - RAM Filesystem Coommon Structures
- */
-#ifndef _RAMFS_H
-#define _RAMFS_H
-#include <vfs.h>
-
-typedef struct sRamFS_File {
- struct sRamFS_File *Next;
- struct sRamFS_File *Parent;
- char Name[32];
- tVFS_Node Node;
- union {
- struct sRamFS_File *FirstChild;
- char *Bytes;
- } Data;
-} tRamFS_File;
-
-#endif
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * include/vfs_threads.h
- * - Handle maintainance functions for the VFS used by threading code
- */
-#ifndef _VFS_THREADS_H_
-#define _VFS_THREADS_H_
-
-// === FUNCTIONS ===
-extern void VFS_ReferenceUserHandles(void);
-extern void VFS_CloseAllUserHandles(void);
-
-extern void *VFS_SaveHandles(int NumFDs, int *FDs);
-extern void VFS_RestoreHandles(int NumFDs, void *Handles);
-extern void VFS_FreeSavedHandles(int NumFDs, void *Handles);
-
-#endif
+++ /dev/null
-/**
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * workqueue.h
- * - FIFO Queue
- */
-#ifndef _WORKQUEUE_H_
-#define _WORKQUEUE_H_
-
-#include <acess.h>
-
-typedef struct sWorkqueue tWorkqueue;
-
-struct sWorkqueue
-{
- tShortSpinlock Protector;
- const char *Name;
- int NextOffset;
-
- void *Head;
- void *Tail;
- struct sThread *Sleeper;
-};
-
-extern void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset);
-extern void *Workqueue_GetWork(tWorkqueue *Queue);
-extern void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2
- * Common Library Functions
- */
-#include <acess.h>
-#include <hal_proc.h>
-
-// === CONSTANTS ===
-#define RANDOM_SEED 0xACE55052
-#define RANDOM_A 0x00731ADE
-#define RANDOM_C 12345
-#define RANDOM_SPRUCE 0xf12b039
-// Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec
-const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
-#define UNIX_TO_2K ((30*365*3600*24) + (7*3600*24)) //Normal years + leap years
-
-// === PROTOTYPES ===
-#if 0
- int atoi(const char *string);
-void itoa(char *buf, Uint64 num, int base, int minLength, char pad);
- int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
- int sprintf(char *__s, const char *__format, ...);
-#endif
- int tolower(int c);
-#if 0
- int strucmp(const char *Str1, const char *Str2);
-char *strchr(const char *__s, int __c);
- int strpos(const char *Str, char Ch);
- Uint8 ByteSum(void *Ptr, int Size);
-size_t strlen(const char *__s);
-char *strcpy(char *__str1, const char *__str2);
-char *strncpy(char *__str1, const char *__str2, size_t max);
- int strcmp(const char *str1, const char *str2);
- int strncmp(const char *str1, const char *str2, size_t num);
-char *_strdup(const char *File, int Line, const char *Str);
-char **str_split(const char *__str, char __ch);
- int strpos8(const char *str, Uint32 Search);
- int ReadUTF8(Uint8 *str, Uint32 *Val);
- int WriteUTF8(Uint8 *str, Uint32 Val);
- int DivUp(int num, int dem);
-Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year);
-void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
- int rand(void);
-
- int CheckString(char *String);
- int CheckMem(void *Mem, int NumBytes);
-
- int ModUtil_LookupString(char **Array, char *Needle);
- int ModUtil_SetIdent(char *Dest, char *Value);
-
- int Hex(char *Dest, size_t Size, const Uint8 *SourceData);
- int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString);
-#endif
-
-// === EXPORTS ===
-EXPORT(atoi);
-EXPORT(itoa);
-EXPORT(vsnprintf);
-EXPORT(sprintf);
-EXPORT(tolower);
-EXPORT(strucmp);
-EXPORT(strchr);
-EXPORT(strpos);
-EXPORT(ByteSum);
-EXPORT(strlen);
-EXPORT(strcpy);
-EXPORT(strncpy);
-EXPORT(strcat);
-EXPORT(strncat);
-EXPORT(strcmp);
-EXPORT(strncmp);
-//EXPORT(strdup);
-EXPORT(_strdup); // Takes File/Line too
-EXPORT(str_split);
-EXPORT(strpos8);
-EXPORT(DivUp);
-EXPORT(ReadUTF8);
-EXPORT(WriteUTF8);
-EXPORT(timestamp);
-EXPORT(CheckString);
-EXPORT(CheckMem);
-EXPORT(ModUtil_LookupString);
-EXPORT(ModUtil_SetIdent);
-EXPORT(UnHex);
-EXPORT(SwapEndian16);
-EXPORT(SwapEndian32);
-EXPORT(memmove);
-
-// === CODE ===
-/**
- * \brief Convert a string into an integer
- */
-int atoi(const char *string)
-{
- int ret = 0;
- ParseInt(string, &ret);
- return ret;
-}
-int ParseInt(const char *string, int *Val)
-{
- int ret = 0;
- int bNeg = 0;
- const char *orig_string = string;
-
- //Log("atoi: (string='%s')", string);
-
- // Clear non-numeric characters
- while( !('0' <= *string && *string <= '9') && *string != '-' ) string++;
- if( *string == '-' ) {
- bNeg = 1;
- while( !('0' <= *string && *string <= '9') ) string++;
- }
-
- if(*string == '0')
- {
- string ++;
- if(*string == 'x')
- {
- // Hex
- string ++;
- for( ;; string ++ )
- {
- if('0' <= *string && *string <= '9') {
- ret *= 16;
- ret += *string - '0';
- }
- else if('A' <= *string && *string <= 'F') {
- ret *= 16;
- ret += *string - 'A' + 10;
- }
- else if('a' <= *string && *string <= 'f') {
- ret *= 16;
- ret += *string - 'a' + 10;
- }
- else
- break;
- }
- }
- else // Octal
- {
- for( ; '0' <= *string && *string <= '7'; string ++ )
- {
- ret *= 8;
- ret += *string - '0';
- }
- }
- }
- else // Decimal
- {
- for( ; '0' <= *string && *string <= '9'; string++)
- {
- ret *= 10;
- ret += *string - '0';
- }
- // Error check
- if( ret == 0 ) return 0;
- }
-
- if(bNeg) ret = -ret;
-
- //Log("atoi: RETURN %i", ret);
-
- if(Val) *Val = ret;
-
- return string - orig_string;
-}
-
-static const char cUCDIGITS[] = "0123456789ABCDEF";
-/**
- * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
- * \brief Convert an integer into a character string
- */
-void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
-{
- char tmpBuf[64+1];
- int pos=0, i;
- Uint64 rem;
-
- // Sanity check
- if(!buf) return;
-
- // Sanity Check
- if(base > 16 || base < 2) {
- buf[0] = 0;
- return;
- }
-
- // Convert
- while(num > base-1) {
- num = DivMod64U(num, base, &rem); // Shift `num` and get remainder
- tmpBuf[pos] = cUCDIGITS[ rem ];
- pos++;
- }
- tmpBuf[pos++] = cUCDIGITS[ num ]; // Last digit of `num`
-
- // Put in reverse
- i = 0;
- minLength -= pos;
- while(minLength-- > 0) buf[i++] = pad;
- while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters
- buf[i] = 0;
-}
-
-/**
- * \brief Append a character the the vsnprintf output
- */
-#define PUTCH(c) _putch(c)
-#define GETVAL() do {\
- if(isLongLong) val = va_arg(args, Uint64);\
- else val = va_arg(args, unsigned int);\
- }while(0)
-/**
- * \brief VArg String Number Print Formatted
- */
-int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args)
-{
- char c, pad = ' ';
- int minSize = 0, precision = -1, len;
- char tmpBuf[34]; // For Integers
- const char *p = NULL;
- int isLongLong = 0;
- Uint64 val;
- size_t pos = 0;
- // Flags
- int bPadLeft = 0;
-
- auto void _putch(char ch);
-
- void _putch(char ch)
- {
- if(pos < __maxlen)
- {
- if(__s) __s[pos] = ch;
- pos ++;
- }
- }
-
- while((c = *__format++) != 0)
- {
- // Non control character
- if(c != '%') { PUTCH(c); continue; }
-
- c = *__format++;
- if(c == '\0') break;
-
- // Literal %
- if(c == '%') { PUTCH('%'); continue; }
-
- // Pointer - Done first for debugging
- if(c == 'p') {
- Uint ptr = va_arg(args, Uint);
- PUTCH('*'); PUTCH('0'); PUTCH('x');
- for( len = BITS/4; len --; )
- PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] );
- continue ;
- }
-
- // - Padding Side Flag
- if(c == '-') {
- bPadLeft = 1;
- c = *__format++;
- }
-
- // - Padding
- if(c == '0') {
- pad = '0';
- c = *__format++;
- }
- else
- pad = ' ';
-
- // - Minimum length
- if(c == '*') { // Dynamic length
- minSize = va_arg(args, unsigned int);
- c = *__format++;
- }
- else if('1' <= c && c <= '9')
- {
- minSize = 0;
- while('0' <= c && c <= '9')
- {
- minSize *= 10;
- minSize += c - '0';
- c = *__format++;
- }
- }
- else
- minSize = 0;
-
- // - Precision
- precision = -1;
- if( c == '.' ) {
- c = *__format++;
-
- if(c == '*') { // Dynamic length
- precision = va_arg(args, unsigned int);
- c = *__format++;
- }
- else if('1' <= c && c <= '9')
- {
- precision = 0;
- while('0' <= c && c <= '9')
- {
- precision *= 10;
- precision += c - '0';
- c = *__format++;
- }
- }
- }
-
- // - Default, Long or LongLong?
- isLongLong = 0;
- if(c == 'l') // Long is actually the default on x86
- {
- c = *__format++;
- if(c == 'l') {
- c = *__format++;
- isLongLong = 1;
- }
- }
-
- // - Now get the format code
- p = tmpBuf;
- switch(c)
- {
- case 'd':
- case 'i':
- GETVAL();
- if( isLongLong && val >> 63 ) {
- PUTCH('-');
- val = -val;
- }
- else if( !isLongLong && val >> 31 ) {
- PUTCH('-');
- val = -(Sint32)val;
- }
- itoa(tmpBuf, val, 10, minSize, pad);
- goto printString;
- case 'u': // Unsigned
- GETVAL();
- itoa(tmpBuf, val, 10, minSize, pad);
- goto printString;
- case 'P': // Physical Address
- PUTCH('0');
- PUTCH('x');
- if(sizeof(tPAddr) > 4) isLongLong = 1;
- GETVAL();
- itoa(tmpBuf, val, 16, minSize, pad);
- goto printString;
- case 'X': // Hex
- if(BITS == 64)
- isLongLong = 1; // TODO: Handle non-x86 64-bit archs
- GETVAL();
- itoa(tmpBuf, val, 16, minSize, pad);
- goto printString;
-
- case 'x': // Lower case hex
- GETVAL();
- itoa(tmpBuf, val, 16, minSize, pad);
- goto printString;
- case 'o': // Octal
- GETVAL();
- itoa(tmpBuf, val, 8, minSize, pad);
- goto printString;
- case 'b':
- GETVAL();
- itoa(tmpBuf, val, 2, minSize, pad);
- goto printString;
-
- case 'B': //Boolean
- val = va_arg(args, unsigned int);
- if(val) p = "True";
- else p = "False";
- goto printString;
-
- // String - Null Terminated Array
- case 's':
- p = va_arg(args, char*); // Get Argument
- if( !p || !CheckString(p) ) p = "(inval)"; // Avoid #PFs
- printString:
- if(!p) p = "(null)";
- len = strlen(p);
- if( !bPadLeft ) while(len++ < minSize) PUTCH(pad);
- while(*p && precision--) PUTCH(*p++);
- if( bPadLeft ) while(len++ < minSize) PUTCH(pad);
- break;
-
- case 'C': // Non-Null Terminated Character Array
- p = va_arg(args, char*);
- if( !CheckMem(p, minSize) ) continue; // No #PFs please
- if(!p) goto printString;
- while(minSize--) PUTCH(*p++);
- break;
-
- // Single Character
- case 'c':
- default:
- GETVAL();
- PUTCH( (Uint8)val );
- break;
- }
- }
-
- if(__s && pos != __maxlen)
- __s[pos] = '\0';
-
- return pos;
-}
-#undef PUTCH
-
-/**
- */
-int sprintf(char *__s, const char *__format, ...)
-{
- va_list args;
- int ret;
-
- va_start(args, __format);
- ret = vsnprintf(__s, -1, __format, args);
- va_end(args);
-
- return ret;
-}
-
-/**
- * \fn int tolower(int c)
- * \brief Converts a character to lower case
- */
-int tolower(int c)
-{
- if('A' <= c && c <= 'Z')
- return c - 'A' + 'a';
- return c;
-}
-
-/**
- * \fn int strucmp(const char *Str1, const char *Str2)
- * \brief Compare \a Str1 and \a Str2 case-insensitively
- */
-int strucmp(const char *Str1, const char *Str2)
-{
- while(*Str1 && tolower(*Str1) == tolower(*Str2))
- Str1++, Str2++;
- return tolower(*Str1) - tolower(*Str2);
-}
-
-/**
- * \brief Locate a byte in a string
- */
-char *strchr(const char *__s, int __c)
-{
- for( ; *__s; __s ++ )
- {
- if( *__s == __c ) return (char*)__s;
- }
- return NULL;
-}
-
-/**
- * \fn int strpos(const char *Str, char Ch)
- * \brief Search a string for an ascii character
- */
-int strpos(const char *Str, char Ch)
-{
- int pos;
- for(pos=0;Str[pos];pos++)
- {
- if(Str[pos] == Ch) return pos;
- }
- return -1;
-}
-
-/**
- * \fn Uint8 ByteSum(void *Ptr, int Size)
- * \brief Adds the bytes in a memory region and returns the sum
- */
-Uint8 ByteSum(const void *Ptr, int Size)
-{
- Uint8 sum = 0;
- const Uint8 *data = Ptr;
- while(Size--) sum += *(data++);
- return sum;
-}
-
-/**
- * \fn size_t strlen(const char *__str)
- * \brief Get the length of string
- */
-size_t strlen(const char *__str)
-{
- size_t ret = 0;
- while(*__str++) ret++;
- return ret;
-}
-
-/**
- * \brief Copy a string to a new location
- */
-char *strcpy(char *__str1, const char *__str2)
-{
- while(*__str2)
- *__str1++ = *__str2++;
- *__str1 = '\0'; // Terminate String
- return __str1;
-}
-
-/**
- * \brief Copy a string to a new location
- * \note Copies at most `max` chars
- */
-char *strncpy(char *__str1, const char *__str2, size_t __max)
-{
- while(*__str2 && __max-- >= 1)
- *__str1++ = *__str2++;
- if(__max)
- *__str1 = '\0'; // Terminate String
- return __str1;
-}
-
-/**
- * \brief Append a string to another
- */
-char *strcat(char *__dest, const char *__src)
-{
- while(*__dest++);
- __dest--;
- while(*__src)
- *__dest++ = *__src++;
- *__dest = '\0';
- return __dest;
-}
-
-/**
- * \brief Append at most \a n chars to a string from another
- * \note At most n+1 chars are written (the dest is always zero terminated)
- */
-char *strncat(char *__dest, const char *__src, size_t n)
-{
- while(*__dest++);
- while(*__src && n-- >= 1)
- *__dest++ = *__src++;
- *__dest = '\0';
- return __dest;
-}
-
-/**
- * \fn int strcmp(const char *str1, const char *str2)
- * \brief Compare two strings return the difference between
- * the first non-matching characters.
- */
-int strcmp(const char *str1, const char *str2)
-{
- while(*str1 && *str1 == *str2)
- str1++, str2++;
- return *str1 - *str2;
-}
-
-/**
- * \fn int strncmp(const char *Str1, const char *Str2, size_t num)
- * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters
- */
-int strncmp(const char *Str1, const char *Str2, size_t num)
-{
- if(num == 0) return 0; // TODO: Check what should officially happen here
- while(--num && *Str1 && *Str1 == *Str2)
- Str1++, Str2++;
- return *Str1-*Str2;
-}
-
-#if 0
-/**
- * \fn char *strdup(const char *Str)
- * \brief Duplicates a string
- */
-char *strdup(const char *Str)
-{
- char *ret;
- ret = malloc(strlen(Str)+1);
- if( !ret ) return NULL;
- strcpy(ret, Str);
- return ret;
-}
-#else
-
-/**
- * \fn char *_strdup(const char *File, int Line, const char *Str)
- * \brief Duplicates a string
- */
-char *_strdup(const char *File, int Line, const char *Str)
-{
- char *ret;
- ret = Heap_Allocate(File, Line, strlen(Str)+1);
- if( !ret ) return NULL;
- strcpy(ret, Str);
- return ret;
-}
-#endif
-
-/**
- * \brief Split a string using the passed character
- * \return NULL terminated array of strings on the heap
- * \param __str String to split
- * \param __ch Character to split by
- */
-char **str_split(const char *__str, char __ch)
-{
- int i, j;
- int len = 1;
- char **ret;
- char *start;
-
- for( i = 0; __str[i]; i++ )
- {
- if(__str[i] == __ch)
- len ++;
- }
-
- ret = malloc( sizeof(char*)*(len+1) + (i + 1) );
- if( !ret ) return NULL;
-
- j = 1;
- start = (char *)&ret[len+1];
- ret[0] = start;
- for( i = 0; __str[i]; i++ )
- {
- if(__str[i] == __ch) {
- *start++ = '\0';
- ret[j++] = start;
- }
- else {
- *start++ = __str[i];
- }
- }
- *start = '\0';
- ret[j] = NULL;
-
- return ret;
-}
-
-/**
- * \fn int DivUp(int num, int dem)
- * \brief Divide two numbers, rounding up
- * \param num Numerator
- * \param dem Denominator
- */
-int DivUp(int num, int dem)
-{
- return (num+dem-1)/dem;
-}
-
-/**
- * \fn int strpos8(const char *str, Uint32 search)
- * \brief Search a string for a UTF-8 character
- */
-int strpos8(const char *str, Uint32 Search)
-{
- int pos;
- Uint32 val = 0;
- for(pos=0;str[pos];pos++)
- {
- // ASCII Range
- if(Search < 128) {
- if(str[pos] == Search) return pos;
- continue;
- }
- if(*(Uint8*)(str+pos) < 128) continue;
-
- pos += ReadUTF8( (Uint8*)&str[pos], &val );
- if(val == Search) return pos;
- }
- return -1;
-}
-
-/**
- * \fn int ReadUTF8(Uint8 *str, Uint32 *Val)
- * \brief Read a UTF-8 character from a string
- */
-int ReadUTF8(const Uint8 *str, Uint32 *Val)
-{
- *Val = 0xFFFD; // Assume invalid character
-
- // ASCII
- if( !(*str & 0x80) ) {
- *Val = *str;
- return 1;
- }
-
- // Middle of a sequence
- if( (*str & 0xC0) == 0x80 ) {
- return 1;
- }
-
- // Two Byte
- if( (*str & 0xE0) == 0xC0 ) {
- *Val = (*str & 0x1F) << 6; // Upper 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
- return 2;
- }
-
- // Three Byte
- if( (*str & 0xF0) == 0xE0 ) {
- *Val = (*str & 0x0F) << 12; // Upper 4 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 6; // Middle 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
- return 3;
- }
-
- // Four Byte
- if( (*str & 0xF1) == 0xF0 ) {
- *Val = (*str & 0x07) << 18; // Upper 3 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits
- str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
- return 4;
- }
-
- // UTF-8 Doesn't support more than four bytes
- return 4;
-}
-
-/**
- * \fn int WriteUTF8(Uint8 *str, Uint32 Val)
- * \brief Write a UTF-8 character sequence to a string
- */
-int WriteUTF8(Uint8 *str, Uint32 Val)
-{
- // ASCII
- if( Val < 128 ) {
- if( str ) {
- *str = Val;
- }
- return 1;
- }
-
- // Two Byte
- if( Val < 0x8000 ) {
- if( str ) {
- *str++ = 0xC0 | (Val >> 6);
- *str++ = 0x80 | (Val & 0x3F);
- }
- return 2;
- }
-
- // Three Byte
- if( Val < 0x10000 ) {
- if( str ) {
- *str++ = 0xE0 | (Val >> 12);
- *str++ = 0x80 | ((Val >> 6) & 0x3F);
- *str++ = 0x80 | (Val & 0x3F);
- }
- return 3;
- }
-
- // Four Byte
- if( Val < 0x110000 ) {
- if( str ) {
- *str++ = 0xF0 | (Val >> 18);
- *str++ = 0x80 | ((Val >> 12) & 0x3F);
- *str++ = 0x80 | ((Val >> 6) & 0x3F);
- *str++ = 0x80 | (Val & 0x3F);
- }
- return 4;
- }
-
- // UTF-8 Doesn't support more than four bytes
- return 0;
-}
-
-/**
- * \fn Uint64 timestamp(int sec, int mins, int hrs, int day, int month, int year)
- * \brief Converts a date into an Acess Timestamp
- */
-Sint64 timestamp(int sec, int min, int hrs, int day, int month, int year)
-{
- int is_leap;
- Sint64 stamp;
-
- if( !(0 <= sec && sec < 60) ) return 0;
- if( !(0 <= min && min < 60) ) return 0;
- if( !(0 <= hrs && hrs < 24) ) return 0;
- if( !(0 <= day && day < 31) ) return 0;
- if( !(0 <= month && month < 12) ) return 0;
-
- stamp = DAYS_BEFORE[month] + day;
-
- // Every 4 years
- // - every 100 years
- // + every 400 years
- is_leap = (year % 4 == 0) - (year % 100 == 0) + (year % 400 == 0);
- ASSERT(is_leap == 0 || is_leap == 1);
-
- if( is_leap && month > 1 ) // Leap year and after feb
- stamp += 1;
-
- // Get seconds before the first of specified year
- year -= 2000; // Base off Y2K
- // base year days + total leap year days
- stamp += year*365 + (year/400) - (year/100) + (year/4);
-
- stamp *= 3600*24;
- stamp += UNIX_TO_2K;
- stamp += sec;
- stamp += min*60;
- stamp += hrs*3600;
-
- return stamp * 1000;
-}
-
-void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms)
-{
- int is_leap = 0, i;
-
- auto Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R);
-
- Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R)
- {
- int sign = (N < 0) != (D < 0);
- if(N < 0) N = -N;
- if(D < 0) D = -D;
- if(sign)
- return -DivMod64U(N, D, (Uint64*)R);
- else
- return DivMod64U(N, D, (Uint64*)R);
- }
-
- // Get time
- // TODO: Leap-seconds?
- {
- Sint64 rem;
- TS = DivMod64( TS, 1000, &rem );
- *ms = rem;
- TS = DivMod64( TS, 60, &rem );
- *sec = rem;
- TS = DivMod64( TS, 60, &rem );
- *mins = rem;
- TS = DivMod64( TS, 24, &rem );
- *hrs = rem;
- }
-
- // Adjust to Y2K
- TS -= UNIX_TO_2K/(3600*24);
-
- // Year (400 yr blocks) - (400/4-3) leap years
- *year = 400 * DivMod64( TS, 365*400 + (400/4-3), &TS );
- if( TS < 366 ) // First year in 400 is a leap
- is_leap = 1;
- else
- {
- // 100 yr blocks - 100/4-1 leap years
- *year += 100 * DivMod64( TS, 365*100 + (100/4-1), &TS );
- if( TS < 366 ) // First year in 100 isn't a leap
- is_leap = 0;
- else
- {
- *year += 4 * DivMod64( TS, 365*4 + 1, &TS );
- if( TS < 366 ) // First year in 4 is a leap
- is_leap = 1;
- else
- {
- *year += DivMod64( TS, 356, &TS );
- }
- }
- }
- *year += 2000;
-
- ASSERT(TS >= 0);
-
- *day = 0;
- // Month (if after the first of march, which is 29 Feb in a leap year)
- if( is_leap && TS > DAYS_BEFORE[2] ) {
- TS -= 1; // Shifts 29 Feb to 28 Feb
- *day = 1;
- }
- // Get what month it is
- for( i = 0; i < 12; i ++ ) {
- if( TS < DAYS_BEFORE[i] )
- break;
- }
- *month = i - 1;
- // Get day
- TS -= DAYS_BEFORE[i-1];
- *day += TS; // Plus offset from leap handling above
-}
-
-/**
- * \fn int rand()
- * \brief Pseudo random number generator
- */
-int rand(void)
-{
- #if 0
- static Uint state = RANDOM_SEED;
- Uint old = state;
- // Get the next state value
- giRandomState = (RANDOM_A*state + RANDOM_C);
- // Check if it has changed, and if it hasn't, change it
- if(state == old) state += RANDOM_SPRUCE;
- return state;
- #else
- // http://en.wikipedia.org/wiki/Xorshift
- // 2010-10-03
- static Uint32 x = 123456789;
- static Uint32 y = 362436069;
- static Uint32 z = 521288629;
- static Uint32 w = 88675123;
- Uint32 t;
-
- t = x ^ (x << 11);
- x = y; y = z; z = w;
- return w = w ^ (w >> 19) ^ t ^ (t >> 8);
- #endif
-}
-
-/* *
- * \name Memory Validation
- * \{
- */
-/**
- * \brief Checks if a string resides fully in valid memory
- */
-int CheckString(const char *String)
-{
- tVAddr addr;
- int bUser;
-
- addr = (tVAddr)String;
-
- if( !MM_GetPhysAddr( addr ) )
- return 0;
-
- // Check 1st page
- bUser = MM_IsUser( addr );
-
- while( *(char*)addr )
- {
- if( (addr & (PAGE_SIZE-1)) == 0 )
- {
- if(bUser && !MM_IsUser(addr) )
- return 0;
- if(!bUser && !MM_GetPhysAddr(addr) )
- return 0;
- }
- addr ++;
- }
- return 1;
-}
-
-/**
- * \brief Check if a sized memory region is valid memory
- * \return Boolean success
- */
-int CheckMem(const void *Mem, int NumBytes)
-{
- return MM_IsValidBuffer( (tVAddr)Mem, NumBytes );
-}
-/* *
- * \}
- */
-
-/**
- * \brief Search a string array for \a Needle
- * \note Helper function for eTplDrv_IOCtl::DRV_IOCTL_LOOKUP
- */
-int ModUtil_LookupString(const char **Array, const char *Needle)
-{
- int i;
- if( !CheckString(Needle) ) return -1;
- for( i = 0; Array[i]; i++ )
- {
- if(strcmp(Array[i], Needle) == 0) return i;
- }
- return -1;
-}
-
-int ModUtil_SetIdent(char *Dest, const char *Value)
-{
- if( !CheckMem(Dest, 32) ) return -1;
- strncpy(Dest, Value, 32);
- return 1;
-}
-
-int Hex(char *Dest, size_t Size, const Uint8 *SourceData)
-{
- int i;
- for( i = 0; i < Size; i ++ )
- {
- sprintf(Dest + i*2, "%02x", SourceData[i]);
- }
- return i*2;
-}
-
-/**
- * \brief Convert a string of hexadecimal digits into a byte stream
- */
-int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString)
-{
- int i;
-
- for( i = 0; i < DestSize*2; i += 2 )
- {
- Uint8 val = 0;
-
- if(SourceString[i] == '\0') break;
-
- if('0' <= SourceString[i] && SourceString[i] <= '9')
- val |= (SourceString[i]-'0') << 4;
- else if('A' <= SourceString[i] && SourceString[i] <= 'F')
- val |= (SourceString[i]-'A'+10) << 4;
- else if('a' <= SourceString[i] && SourceString[i] <= 'f')
- val |= (SourceString[i]-'a'+10) << 4;
-
- if(SourceString[i+1] == '\0') break;
-
- if('0' <= SourceString[i+1] && SourceString[i+1] <= '9')
- val |= (SourceString[i+1] - '0');
- else if('A' <= SourceString[i+1] && SourceString[i+1] <= 'F')
- val |= (SourceString[i+1] - 'A' + 10);
- else if('a' <= SourceString[i+1] && SourceString[i+1] <= 'f')
- val |= (SourceString[i+1] - 'a' + 10);
-
- Dest[i/2] = val;
- }
- return i/2;
-}
-
-Uint16 SwapEndian16(Uint16 Val)
-{
- return ((Val&0xFF)<<8) | ((Val>>8)&0xFF);
-}
-Uint32 SwapEndian32(Uint32 Val)
-{
- return ((Val&0xFF)<<24) | ((Val&0xFF00)<<8) | ((Val>>8)&0xFF00) | ((Val>>24)&0xFF);
-}
-
-void *memmove(void *__dest, const void *__src, size_t len)
-{
- size_t block_size;
- char *dest = __dest;
- const char *src = __src;
- void *ret = __dest;
-
- if( len == 0 || dest == src )
- return dest;
-
- if( (tVAddr)dest > (tVAddr)src + len )
- return memcpy(dest, src, len);
- if( (tVAddr)dest + len < (tVAddr)src )
- return memcpy(dest, src, len);
-
- // NOTE: Assumes memcpy works forward
- if( (tVAddr)dest < (tVAddr)src )
- return memcpy(dest, src, len);
-
- if( (tVAddr)dest < (tVAddr)src )
- block_size = (tVAddr)src - (tVAddr)dest;
- else
- block_size = (tVAddr)dest - (tVAddr)src;
-
- block_size &= ~0xF;
-
- while(len >= block_size)
- {
- memcpy(dest, src, block_size);
- len -= block_size;
- dest += block_size;
- src += block_size;
- }
- memcpy(dest, src, len);
- return ret;
-
-}
-
+++ /dev/null
-/*
- * Acess 2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * logging.c - Kernel Logging Service
- */
-#include <acess.h>
-#include <adt.h>
-
-#define CACHE_MESSAGES 0
-#define PRINT_ON_APPEND 1
-#define USE_RING_BUFFER 1
-#define RING_BUFFER_SIZE 4096
-
-// === CONSTANTS ===
-enum eLogLevels
-{
- LOG_LEVEL_KPANIC,
- LOG_LEVEL_PANIC,
- LOG_LEVEL_FATAL,
- LOG_LEVEL_ERROR,
- LOG_LEVEL_WARNING,
- LOG_LEVEL_NOTICE,
- LOG_LEVEL_LOG,
- LOG_LEVEL_DEBUG,
- NUM_LOG_LEVELS
-};
-const char *csaLevelColours[] = {
- "\x1B[35m", "\x1B[34m", "\x1B[36m", "\x1B[31m",
- "\x1B[33m", "\x1B[32m", "\x1B[0m", "\x1B[0m"
- };
-const char *csaLevelCodes[] = {"k","p","f","e","w","n","l","d"};
-
-// === TYPES ===
-typedef struct sLogEntry
-{
- struct sLogEntry *Next;
- struct sLogEntry *LevelNext;
- Sint64 Time;
- int Level;
- int Length;
- char Ident[9];
- char Data[];
-} tLogEntry;
-typedef struct sLogList
-{
- tLogEntry *Head;
- tLogEntry *Tail;
-} tLogList;
-
-// === PROTOTYPES ===
-void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args);
-static void Log_Int_PrintMessage(tLogEntry *Entry);
-//void Log_KernelPanic(const char *Ident, const char *Message, ...);
-//void Log_Panic(const char *Ident, const char *Message, ...);
-//void Log_Error(const char *Ident, const char *Message, ...);
-//void Log_Warning(const char *Ident, const char *Message, ...);
-//void Log_Notice(const char *Ident, const char *Message, ...);
-//void Log_Log(const char *Ident, const char *Message, ...);
-//void Log_Debug(const char *Ident, const char *Message, ...);
-
-// === EXPORTS ===
-EXPORT(Log_Panic);
-EXPORT(Log_Error);
-EXPORT(Log_Warning);
-EXPORT(Log_Notice);
-EXPORT(Log_Log);
-EXPORT(Log_Debug);
-
-// === GLOBALS ===
-tShortSpinlock glLogOutput;
-#if USE_RING_BUFFER
-Uint8 gaLog_RingBufferData[sizeof(tRingBuffer)+RING_BUFFER_SIZE];
-tRingBuffer *gpLog_RingBuffer = (void*)gaLog_RingBufferData;
-#else
-tMutex glLog;
-tLogList gLog;
-tLogList gLog_Levels[NUM_LOG_LEVELS];
-#endif
-
-// === CODE ===
-/**
- * \brief Adds an event to the log
- */
-void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args)
-{
- int len;
- tLogEntry *ent;
- va_list args_tmp;
-
- if( Level >= NUM_LOG_LEVELS ) return;
-
- va_copy(args_tmp, Args);
- len = vsnprintf(NULL, 256, Format, args_tmp);
-
- //Log("len = %i", len);
-
- #if USE_RING_BUFFER || !CACHE_MESSAGES
- {
- char buf[sizeof(tLogEntry)+len+1];
- ent = (void*)buf;
- #else
- ent = malloc(sizeof(tLogEntry)+len+1);
- #endif
- ent->Time = now();
- strncpy(ent->Ident, Ident, 8);
- ent->Ident[8] = '\0';
- ent->Level = Level;
- ent->Length = len;
- vsnprintf( ent->Data, len+1, Format, Args );
-
- #if CACHE_MESSAGES
- # if USE_RING_BUFFER
- {
- #define LOG_HDR_LEN (14+1+2+8+2)
- char newData[ LOG_HDR_LEN + len + 2 + 1 ];
- char _ident[9];
- strncpy(_ident, Ident, 9);
- sprintf( newData, "%014lli%s [%-8s] ",
- ent->Time, csaLevelCodes[Level], Ident);
- strcpy( newData + LOG_HDR_LEN, ent->Data );
- strcpy( newData + LOG_HDR_LEN + len, "\r\n" );
- gpLog_RingBuffer->Space = RING_BUFFER_SIZE; // Needed to init the buffer
- RingBuffer_Write( gpLog_RingBuffer, newData, LOG_HDR_LEN + len + 2 );
- }
- # else
- Mutex_Acquire( &glLog );
-
- ent->Next = gLog.Tail;
- if(gLog.Head)
- gLog.Tail = ent;
- else
- gLog.Tail = gLog.Head = ent;
-
- ent->LevelNext = gLog_Levels[Level].Tail;
- if(gLog_Levels[Level].Head)
- gLog_Levels[Level].Tail = ent;
- else
- gLog_Levels[Level].Tail = gLog_Levels[Level].Head = ent;
-
- Mutex_Release( &glLog );
- # endif
- #endif
-
- #if PRINT_ON_APPEND || !CACHE_MESSAGES
- Log_Int_PrintMessage( ent );
- #endif
-
- #if USE_RING_BUFFER || !CACHE_MESSAGES
- }
- #endif
-}
-
-/**
- * \brief Prints a log message to the debug console
- */
-void Log_Int_PrintMessage(tLogEntry *Entry)
-{
- SHORTLOCK( &glLogOutput );
- LogF("%s%014lli%s [%-8s] %i - %s",
- csaLevelColours[Entry->Level],
- Entry->Time,
- csaLevelCodes[Entry->Level],
- Entry->Ident,
- Threads_GetTID(),
- Entry->Data
- );
- LogF("\x1B[0m\r\n"); // Separate in case Entry->Data is too long
- SHORTREL( &glLogOutput );
-}
-
-/**
- * \brief KERNEL PANIC!!!!
- */
-void Log_KernelPanic(const char *Ident, const char *Message, ...)
-{
- va_list args;
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_KPANIC, Message, args);
- va_end(args);
- Panic("Log_KernelPanic - %s", Ident);
-}
-
-/**
- * \brief Panic Message - Driver Unrecoverable error
- */
-void Log_Panic(const char *Ident, const char *Message, ...)
-{
- va_list args;
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_PANIC, Message, args);
- va_end(args);
-}
-
-/**
- * \brief Error Message - Recoverable Error
- */
-void Log_Error(const char *Ident, const char *Message, ...)
-{
- va_list args;
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_ERROR, Message, args);
- va_end(args);
-}
-
-/**
- * \brief Warning Message - Something the user should know
- */
-void Log_Warning(const char *Ident, const char *Message, ...)
-{
- va_list args;
-
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_WARNING, Message, args);
- va_end(args);
-}
-
-/**
- * \brief Notice Message - Something the user might like to know
- */
-void Log_Notice(const char *Ident, const char *Message, ...)
-{
- va_list args;
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_NOTICE, Message, args);
- va_end(args);
-}
-
-/**
- * \brief Log Message - Possibly useful information
- */
-void Log_Log(const char *Ident, const char *Message, ...)
-{
- va_list args;
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_LOG, Message, args);
- va_end(args);
-}
-
-/**
- * \brief Debug Message - Only a developer would want this info
- */
-void Log_Debug(const char *Ident, const char *Message, ...)
-{
- va_list args;
- va_start(args, Message);
- Log_AddEvent(Ident, LOG_LEVEL_DEBUG, Message, args);
- va_end(args);
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * messages.c
- * - IPC Messages
- */
-#define DEBUG 0
-#include <acess.h>
-#include <threads.h>
-#include <threads_int.h>
-#include <errno.h>
-#include <events.h>
-
-// === CODE ===
-/**
- * \fn int Proc_SendMessage(Uint Dest, int Length, void *Data)
- * \brief Send an IPC message
- * \param Dest Destination Thread
- * \param Length Length of the message
- * \param Data Message data
- */
-int Proc_SendMessage(Uint Dest, int Length, void *Data)
-{
- tThread *thread;
- tMsg *msg;
-
- ENTER("pErr iDest iLength pData", Err, Dest, Length, Data);
-
- if(Length <= 0 || !Data) {
- errno = -EINVAL;
- LEAVE_RET('i', -1);
- }
-
- // TODO: Check message length against global/per-thread maximums
- // TODO: Restrict queue length
-
- // Get thread
- thread = Threads_GetThread( Dest );
-
- // Error check
- if(!thread) LEAVE_RET('i', -1);
-
- // Get Spinlock
- SHORTLOCK( &thread->IsLocked );
-
- // Check if thread is still alive
- if(thread->Status == THREAD_STAT_DEAD) {
- SHORTREL( &thread->IsLocked );
- LEAVE_RET('i', -1);
- }
-
- // Create message
- msg = malloc( sizeof(tMsg)+Length );
- msg->Next = NULL;
- msg->Source = Proc_GetCurThread()->TID;
- msg->Length = Length;
- memcpy(msg->Data, Data, Length);
-
- // If there are already messages
- if(thread->LastMessage) {
- thread->LastMessage->Next = msg;
- thread->LastMessage = msg;
- } else {
- thread->Messages = msg;
- thread->LastMessage = msg;
- }
-
- SHORTREL(&thread->IsLocked);
-
- // Wake the thread
- LOG("Waking %p (%i %s)", thread, thread->TID, thread->ThreadName);
- Threads_PostEvent( thread, THREAD_EVENT_IPCMSG );
-
- LEAVE_RET('i', 0);
-}
-
-/**
- * \fn int Proc_GetMessage(Uint *Source, void *Buffer)
- * \brief Gets a message
- * \param Source Where to put the source TID
- * \param Buffer Buffer to place the message data (set to NULL to just get message length)
- * \return Message length
- */
-int Proc_GetMessage(Uint *Source, void *Buffer)
-{
- int ret;
- void *tmp;
- tThread *cur = Proc_GetCurThread();
-
- ENTER("pSource pBuffer", Source, Buffer);
-
- // Check if queue has any items
- if(!cur->Messages) {
- LEAVE('i', 0);
- return 0;
- }
-
- SHORTLOCK( &cur->IsLocked );
-
- if(Source) {
- *Source = cur->Messages->Source;
- LOG("*Source = %i", *Source);
- }
-
- // Get message length
- if( !Buffer ) {
- ret = cur->Messages->Length;
- SHORTREL( &cur->IsLocked );
- LEAVE('i', ret);
- return ret;
- }
-
- // Get message
- if(Buffer != GETMSG_IGNORE)
- {
- if( !CheckMem( Buffer, cur->Messages->Length ) )
- {
- LOG("Invalid buffer");
- errno = -EINVAL;
- SHORTREL( &cur->IsLocked );
- LEAVE('i', -1);
- return -1;
- }
- LOG("Copied to buffer");
- memcpy(Buffer, cur->Messages->Data, cur->Messages->Length);
- }
- ret = cur->Messages->Length;
-
- // Remove from list
- tmp = cur->Messages;
- cur->Messages = cur->Messages->Next;
- if(cur->Messages == NULL) cur->LastMessage = NULL;
-
- SHORTREL( &cur->IsLocked );
-
- free(tmp); // Free outside of lock
-
- LEAVE('i', ret);
- return ret;
-}
+++ /dev/null
-/*
- * Acess2
- * - Module Loader
- */
-#define DEBUG 0
-#include <acess.h>
-#include <modules.h>
-
-#define USE_EDI 0
-#define USE_UDI 0
-
-// === PROTOTYPES ===
- int Module_int_Initialise(tModule *Module, const char *ArgString);
-void Modules_int_GetBuiltinArray(void);
-void Modules_LoadBuiltins(void);
-void Modules_SetBuiltinParams(const char *Name, char *ArgString);
- int Modules_InitialiseBuiltin(const char *Name);
-// int Module_RegisterLoader(tModuleLoader *Loader);
-// int Module_LoadMem(void *Buffer, Uint Length, char *ArgString);
-// int Module_LoadFile(char *Path, char *ArgString);
- int Module_int_ResolveDeps(tModule *Info);
- int Module_IsLoaded(const char *Name);
-// int Module_EnsureLoaded(const char *Name);
-
-// === EXPORTS ===
-EXPORT(Module_RegisterLoader);
-
-// === IMPORTS ===
-#if USE_UDI
-extern int UDI_LoadDriver(void *Base);
-#endif
-extern void StartupPrint(const char *Str);
-extern tModule gKernelModules;
-extern tModule gKernelModulesEnd;
-
-// === GLOBALS ===
- int giNumBuiltinModules = 0;
-tShortSpinlock glModuleSpinlock;
-tModule *gLoadedModules = NULL;
-tModuleLoader *gModule_Loaders = NULL;
-tModule *gLoadingModules = NULL;
-tModule **gapBuiltinModules = NULL;
-char **gasBuiltinModuleArgs;
-
-// === CODE ===
-/**
- * \brief Initialises a module
- * \param Module Pointer to the module header
- * \param ArgString Comma separated list of module arguments
- * \return Zero on success, eModuleErrors or -1 on error
- * \retval -1 Returned if a dependency fails, or a circular dependency
- * exists.
- * \retval 0 Returned on success
- * \retval >0 Error code form the module's initialisation function
- */
-int Module_int_Initialise(tModule *Module, const char *ArgString)
-{
- int i, j;
- int ret;
- const char **deps;
- char **args;
- tModule *mod;
-
- ENTER("pModule", Module);
- LOG("Module->Magic = 0x%x", Module->Magic);
- if(Module->Magic != MODULE_MAGIC) {
- Log_Warning(
- "Module",
- "Module %p is no a valid Acess2 module (0x%08x != 0x%08x)",
- Module, Module->Magic, MODULE_MAGIC
- );
- LEAVE('i', MODULE_ERR_BADMODULE);
- return MODULE_ERR_BADMODULE;
- }
- LOG("Module->Name = %p \"%s\"", Module->Name, Module->Name);
-
- if(Module->Arch != MODULE_ARCH_ID) {
- Log_Warning(
- "Module",
- "Module %p (%s) is for another architecture (%i)",
- Module, Module->Name, Module->Arch
- );
- }
-
- deps = Module->Dependencies;
-
- // Check if the module has been loaded
- for( mod = gLoadedModules; mod; mod = mod->Next )
- {
- if(mod == Module) LEAVE_RET('i', 0);
- }
-
- // Add to the "loading" (prevents circular deps)
- Module->Next = gLoadingModules;
- gLoadingModules = Module;
-
- // Scan dependency list
- for( j = 0; deps && deps[j]; j++ )
- {
- // Check if the module is already loaded
- for( mod = gLoadedModules; mod; mod = mod->Next )
- {
- if(strcmp(deps[j], mod->Name) == 0)
- break;
- }
- if( mod ) continue; // Dependency is loaded, check the rest
-
- // Ok, check if it's loading
- for( mod = gLoadingModules->Next; mod; mod = mod->Next )
- {
- if(strcmp(deps[j], mod->Name) == 0)
- break;
- }
- if( mod ) {
- Log_Warning("Module", "Circular dependency detected (%s and %s)",
- mod->Name, Module->Name);
- LEAVE_RET('i', -1);
- }
-
- // So, if it's not loaded, we better load it then
- for( i = 0; i < giNumBuiltinModules; i ++ )
- {
- if( strcmp(deps[j], gapBuiltinModules[i]->Name) == 0 )
- break;
- }
- if( i == giNumBuiltinModules ) {
- Log_Warning("Module", "Dependency '%s' for module '%s' failed",
- deps[j], Module->Name);
- return -1;
- }
-
- // Dependency is not loaded, so load it
- ret = Module_int_Initialise(
- gapBuiltinModules[i],
- gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL
- );
- if( ret )
- {
- // The only "ok" error is NOTNEEDED
- if(ret != MODULE_ERR_NOTNEEDED)
- LEAVE_RET('i', -1);
- }
- }
-
- // All Dependencies OK? Initialise
- StartupPrint(Module->Name);
- Log_Log("Module", "Starting %p '%s' v%i.%i",
- Module, Module->Name,
- Module->Version >> 8, Module->Version & 0xFF
- );
-
- if( ArgString )
- args = str_split( ArgString, ',' );
- else
- args = NULL;
-
- ret = Module->Init(args);
-
- if(args) free(args);
-
- // Remove from loading list
- gLoadingModules = gLoadingModules->Next;
-
- if( ret != MODULE_ERR_OK ) {
- switch(ret)
- {
- case MODULE_ERR_MISC:
- Log_Warning("Module", "Unable to load, reason: Miscelanious");
- break;
- case MODULE_ERR_NOTNEEDED:
- Log_Debug("Module", "Unable to load, reason: Module not needed");
- break;
- case MODULE_ERR_MALLOC:
- Log_Warning("Module", "Unable to load, reason: Error in malloc/realloc/calloc, probably not good");
- break;
- default:
- Log_Warning("Module", "Unable to load reason - Unknown code %i", ret);
- break;
- }
- LEAVE_RET('i', ret);
- return ret;
- }
- LOG("ret = %i", ret);
-
- // Add to loaded list
- SHORTLOCK( &glModuleSpinlock );
- Module->Next = gLoadedModules;
- gLoadedModules = Module;
- SHORTREL( &glModuleSpinlock );
-
- LEAVE_RET('i', 0);
-}
-
-/**
- * \brief Scans the builtin modules and creates an array of them
- */
-void Modules_int_GetBuiltinArray(void)
-{
- int i;
- tModule *module;
-
- // Count
- module = &gKernelModules;
- i = 0;
- while( (tVAddr)module < (tVAddr)&gKernelModulesEnd )
- {
- if(module->Magic == MODULE_MAGIC) {
- i ++;
- module ++;
- }
- else
- module = (void*)( (tVAddr)module + 4 );
- }
-
- // Create
- giNumBuiltinModules = i;
- gasBuiltinModuleArgs = calloc( giNumBuiltinModules, sizeof(char*) );
- gapBuiltinModules = malloc( giNumBuiltinModules * sizeof(tModule*) );
-
-
- // Fill
- module = &gKernelModules;
- i = 0;
- while( (tVAddr)module < (tVAddr)&gKernelModulesEnd )
- {
- if(module->Magic == MODULE_MAGIC) {
- gapBuiltinModules[i] = module;
- i ++;
- module ++;
- }
- else
- module = (void*)( (tVAddr)module + 4 );
- }
-}
-
-/**
- * \brief Initialises builtin modules
- */
-void Modules_LoadBuiltins()
-{
- int i;
-
- if( !gapBuiltinModules )
- Modules_int_GetBuiltinArray();
-
- for( i = 0; i < giNumBuiltinModules; i++ )
- {
- Module_int_Initialise(
- gapBuiltinModules[i],
- (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL)
- );
- }
-
- if( gasBuiltinModuleArgs != NULL )
- free(gasBuiltinModuleArgs);
-}
-
-/**
- * \brief Initialise a builtin module given it's name
- *
- * E.g. Used by VTerm to load an alternate video driver at runtime
- */
-int Modules_InitialiseBuiltin(const char *Name)
-{
- int i;
-
- // Check if it's loaded
- if( Module_IsLoaded(Name) )
- return 0;
-
- if( !gapBuiltinModules )
- Modules_int_GetBuiltinArray();
-
- for( i = 0; i < giNumBuiltinModules; i++ )
- {
- if( strcmp(gapBuiltinModules[i]->Name, Name) == 0 ) {
- return Module_int_Initialise(gapBuiltinModules[i],
- (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL)
- );
- }
- }
- return -1;
-}
-
-/**
- * \brief Sets the parameters for a builtin module
- */
-void Modules_SetBuiltinParams(const char *Name, char *ArgString)
-{
- int i;
-
- if( gasBuiltinModuleArgs == NULL )
- {
- Modules_int_GetBuiltinArray();
- }
-
- // I hate expensive scans
- for( i = 0; i < giNumBuiltinModules; i++ )
- {
- if(strcmp( gapBuiltinModules[i]->Name, Name ) == 0) {
- gasBuiltinModuleArgs[i] = ArgString;
- return ;
- }
- }
-
- Log_Warning("Modules", "Unknown builtin kernel module '%s'", Name);
-}
-
-/**
- * \brief Registers a tModuleLoader with the kernel
- * \param Loader Pointer to loader structure (must be persistent)
- */
-int Module_RegisterLoader(tModuleLoader *Loader)
-{
- if(!Loader) return 1;
-
- Loader->Next = gModule_Loaders;
- gModule_Loaders = Loader;
-
- return 0;
-}
-
-/**
- * \fn int Module_LoadMem(void *Buffer, Uint Length, char *ArgString)
- * \brief Load a module from a memory location
- */
-int Module_LoadMem(void *Buffer, Uint Length, const char *ArgString)
-{
- char path[VFS_MEMPATH_SIZE];
-
- VFS_GetMemPath(path, Buffer, Length);
-
- return Module_LoadFile( path, ArgString );
-}
-
-/**
- * \fn int Module_LoadFile(const char *Path, const char *ArgString)
- * \brief Load a module from a file
- */
-int Module_LoadFile(const char *Path, const char *ArgString)
-{
- void *base;
- tModule *info;
-
- // Load Binary
- base = Binary_LoadKernel(Path);
-
- // Error check
- if(base == NULL) {
- Log_Warning("Module", "Module_LoadFile - Unable to load '%s'", Path);
- return 0;
- }
-
- // Check for Acess Driver
- if( Binary_FindSymbol(base, "DriverInfo", (Uint*)&info ) == 0 )
- {
- tModuleLoader *tmp;
- for( tmp = gModule_Loaders; tmp; tmp = tmp->Next)
- {
- if( tmp->Detector(base) == 0 ) continue;
-
- return tmp->Loader(base);
- }
-
- #if USE_EDI
- // Check for EDI Driver
- if( Binary_FindSymbol(base, "driver_init", NULL ) != 0 )
- {
- return Module_InitEDI( base ); // And intialise
- }
- #endif
-
- // Unknown module type?, return error
- Binary_Unload(base);
- #if USE_EDI
- Log_Warning("Module", "Module '%s' has neither a Module Info struct, nor an EDI entrypoint", Path);
- #else
- Log_Warning("Module", "Module '%s' does not have a Module Info struct", Path);
- #endif
- return 0;
- }
-
- // Initialise (and register)
- if( Module_int_Initialise( info, ArgString ) )
- {
- Binary_Unload(base);
- return 0;
- }
-
- return 1;
-}
-
-/**
- * \fn int Module_int_ResolveDeps(tModule *Info)
- * \brief Resolves the dependencies
- * \todo Implement
- * \note Currently does not resolve the dependencies, just checks them
- */
-int Module_int_ResolveDeps(tModule *Info)
-{
- const char **names = Info->Dependencies;
-
- // Walk dependencies array
- for( ; *names; names++ )
- {
- // Check if the module is loaded
- if( !Module_IsLoaded(*names) ) {
- Log_Warning("Module", "Module `%s' requires `%s', which is not loaded\n", Info->Name, *names);
- return 0;
- }
- }
- return 1;
-}
-
-/**
- * \fn int Module_IsLoaded(const char *Name)
- * \brief Checks if a module is loaded
- * \param Name Name of module to find
- */
-int Module_IsLoaded(const char *Name)
-{
- tModule *mod = gLoadedModules;
-
- // Scan loaded list
- for( ; mod; mod = mod->Next )
- {
- // If found, return true
- if(strcmp(mod->Name, Name) == 0)
- return 1;
- }
- // not found - return false
- return 0;
-}
-
-/**
- * \brief Load a module if needed
- */
-int Module_EnsureLoaded(const char *Name)
-{
- if( Module_IsLoaded(Name) )
- return 0;
-
- if( Modules_InitialiseBuiltin(Name) == 0 )
- return 0;
-
- // TODO: Load from a file?
-
- return -1;
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * mutex.c
- * - Mutexes
- */
-#include <acess.h>
-#include <threads_int.h>
-#include <mutex.h>
-
-// === PROTOTYPES ===
-#if 0
- int Mutex_Acquire(tMutex *Mutex);
-void Mutex_Release(tMutex *Mutex);
- int Mutex_IsLocked(tMutex *Mutex);
-#endif
-
-// === CODE ===
-//
-// Acquire mutex (see mutex.h for documentation)
-//
-int Mutex_Acquire(tMutex *Mutex)
-{
- tThread *us = Proc_GetCurThread();
-
- // Get protector
- SHORTLOCK( &Mutex->Protector );
-
-// Log("Mutex_Acquire: (%p)", Mutex);
-
- // Check if the lock is already held
- if( Mutex->Owner ) {
- SHORTLOCK( &glThreadListLock );
- // - Remove from active list
- us = Threads_RemActive();
- us->Next = NULL;
- // - Mark as sleeping
- us->Status = THREAD_STAT_MUTEXSLEEP;
- us->WaitPointer = Mutex;
-
- // - Add to waiting
- if(Mutex->LastWaiting) {
- Mutex->LastWaiting->Next = us;
- Mutex->LastWaiting = us;
- }
- else {
- Mutex->Waiting = us;
- Mutex->LastWaiting = us;
- }
-
- #if DEBUG_TRACE_STATE
- Log("%p (%i %s) waiting on mutex %p",
- us, us->TID, us->ThreadName, Mutex);
- #endif
-
- #if 0
- {
- int i = 0;
- tThread *t;
- for( t = Mutex->Waiting; t; t = t->Next, i++ )
- Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i,
- t, t->TID, t->ThreadName);
- }
- #endif
-
- SHORTREL( &glThreadListLock );
- SHORTREL( &Mutex->Protector );
- while(us->Status == THREAD_STAT_MUTEXSLEEP) Threads_Yield();
- // We're only woken when we get the lock
- us->WaitPointer = NULL;
- }
- // Ooh, let's take it!
- else {
- Mutex->Owner = us;
- SHORTREL( &Mutex->Protector );
- }
-
- #if 0
- extern tMutex glPhysAlloc;
- if( Mutex != &glPhysAlloc )
- LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0));
- #endif
-
- return 0;
-}
-
-// Release a mutex
-void Mutex_Release(tMutex *Mutex)
-{
- SHORTLOCK( &Mutex->Protector );
- //Log("Mutex_Release: (%p)", Mutex);
- if( Mutex->Waiting ) {
- Mutex->Owner = Mutex->Waiting; // Set owner
- Mutex->Waiting = Mutex->Waiting->Next; // Next!
- // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
- if( Mutex->LastWaiting == Mutex->Owner )
- Mutex->LastWaiting = NULL;
-
- // Wake new owner
- if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
- Threads_AddActive(Mutex->Owner);
- }
- else {
- Mutex->Owner = NULL;
- }
- SHORTREL( &Mutex->Protector );
-
- #if 0
- extern tMutex glPhysAlloc;
- if( Mutex != &glPhysAlloc )
- LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0));
- #endif
-}
-
-// Check if a mutex is locked
-int Mutex_IsLocked(tMutex *Mutex)
-{
- return Mutex->Owner != NULL;
-}
-
-// === EXPORTS ===
-EXPORT(Mutex_Acquire);
-EXPORT(Mutex_Release);
-EXPORT(Mutex_IsLocked);
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * semaphore.c
- * - Semaphores
- */
-#include <acess.h>
-#include <semaphore.h>
-#include <threads_int.h>
-
-#define SEMAPHORE_DEBUG 0 // Debug semaphores
-
-// === CODE ===
-//
-// Initialise a semaphore
-//
-void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
-{
- memset(Sem, 0, sizeof(tSemaphore));
- Sem->Value = Value;
- Sem->ModName = Module;
- Sem->Name = Name;
- Sem->MaxValue = MaxValue;
-}
-//
-// Wait for items to be avaliable
-//
-int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
-{
- tThread *us;
- int taken;
- if( MaxToTake < 0 ) {
- Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
- MaxToTake, Sem, Sem->Name);
- }
-
- SHORTLOCK( &Sem->Protector );
-
- // Check if there's already items avaliable
- if( Sem->Value > 0 )
- {
- // Take what we need
- if( MaxToTake && Sem->Value > MaxToTake )
- taken = MaxToTake;
- else
- taken = Sem->Value;
- Sem->Value -= taken;
- }
- else
- {
- SHORTLOCK( &glThreadListLock );
-
- // - Remove from active list
- us = Threads_RemActive();
- us->Next = NULL;
- // - Mark as sleeping
- us->Status = THREAD_STAT_SEMAPHORESLEEP;
- us->WaitPointer = Sem;
- us->RetStatus = MaxToTake; // Use RetStatus as a temp variable
-
- // - Add to waiting
- if(Sem->LastWaiting) {
- Sem->LastWaiting->Next = us;
- Sem->LastWaiting = us;
- }
- else {
- Sem->Waiting = us;
- Sem->LastWaiting = us;
- }
-
- #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
- Log("%p (%i %s) waiting on semaphore %p %s:%s",
- us, us->TID, us->ThreadName,
- Sem, Sem->ModName, Sem->Name);
- #endif
-
- SHORTREL( &Sem->Protector ); // Release first to make sure it is released
- SHORTREL( &glThreadListLock );
- while( us->Status == THREAD_STAT_SEMAPHORESLEEP )
- {
- Threads_Yield();
- if(us->Status == THREAD_STAT_SEMAPHORESLEEP)
- Log_Warning("Threads", "Semaphore %p %s:%s re-schedulued while asleep",
- Sem, Sem->ModName, Sem->Name);
- }
- #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
- Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name);
- #endif
- // We're only woken when there's something avaliable (or a signal arrives)
- us->WaitPointer = NULL;
-
- taken = us->RetStatus;
-
- // Get the lock again
- SHORTLOCK( &Sem->Protector );
- }
-
- // While there is space, and there are thread waiting
- // wake the first thread and give it what it wants (or what's left)
- while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
- {
- int given;
- tThread *toWake = Sem->Signaling;
-
- Sem->Signaling = Sem->Signaling->Next;
- // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
- if( Sem->Signaling == NULL )
- Sem->LastSignaling = NULL;
-
- // Figure out how much to give
- if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
- given = toWake->RetStatus;
- else
- given = Sem->MaxValue - Sem->Value;
- Sem->Value -= given;
-
-
- #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
- Log("%p (%i %s) woken by wait on %p %s:%s",
- toWake, toWake->TID, toWake->ThreadName,
- Sem, Sem->ModName, Sem->Name);
- #endif
-
- // Save the number we gave to the thread's status
- toWake->RetStatus = given;
-
- // Wake the sleeper
- SHORTLOCK( &glThreadListLock );
- if( toWake->Status != THREAD_STAT_ACTIVE )
- Threads_AddActive(toWake);
- SHORTREL( &glThreadListLock );
- }
- SHORTREL( &Sem->Protector );
-
- #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
- Log("Semaphore %p %s:%s took %i by wait",
- Sem, Sem->ModName, Sem->Name, taken);
- #endif
-
- return taken;
-}
-
-//
-// Add items to a semaphore
-//
-int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
-{
- int given;
- int added;
-
- if( AmmountToAdd < 0 ) {
- Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
- AmmountToAdd, Sem, Sem->Name);
- }
- SHORTLOCK( &Sem->Protector );
-
- // Check if we have to block
- if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
- {
- tThread *us;
- #if 0
- Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
- Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
- #endif
-
- SHORTLOCK( &glThreadListLock );
- // - Remove from active list
- us = Threads_RemActive();
- us->Next = NULL;
- // - Mark as sleeping
- us->Status = THREAD_STAT_SEMAPHORESLEEP;
- us->WaitPointer = Sem;
- us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable
-
- // - Add to waiting
- if(Sem->LastSignaling) {
- Sem->LastSignaling->Next = us;
- Sem->LastSignaling = us;
- }
- else {
- Sem->Signaling = us;
- Sem->LastSignaling = us;
- }
-
- #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
- Log("%p (%i %s) signaling semaphore %p %s:%s",
- us, us->TID, us->ThreadName,
- Sem, Sem->ModName, Sem->Name);
- #endif
-
- SHORTREL( &glThreadListLock );
- SHORTREL( &Sem->Protector );
- while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
- // We're only woken when there's something avaliable
- us->WaitPointer = NULL;
-
- added = us->RetStatus;
-
- // Get the lock again
- SHORTLOCK( &Sem->Protector );
- }
- // Non blocking
- else
- {
- // Figure out how much we need to take off
- if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
- added = Sem->MaxValue - Sem->Value;
- else
- added = AmmountToAdd;
- Sem->Value += added;
- }
-
- // While there are items avaliable, and there are thread waiting
- // wake the first thread and give it what it wants (or what's left)
- while( Sem->Value && Sem->Waiting )
- {
- tThread *toWake = Sem->Waiting;
-
- // Remove thread from list (double ended, so clear LastWaiting if needed)
- Sem->Waiting = Sem->Waiting->Next;
- if( Sem->Waiting == NULL )
- Sem->LastWaiting = NULL;
-
- // Figure out how much to give to woken thread
- // - Requested count is stored in ->RetStatus
- if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
- given = toWake->RetStatus;
- else
- given = Sem->Value;
- Sem->Value -= given;
-
- // Save the number we gave to the thread's status
- toWake->RetStatus = given;
-
- if(toWake->bInstrTrace)
- Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
- #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
- Log("%p (%i %s) woken by signal on %p %s:%s",
- toWake, toWake->TID, toWake->ThreadName,
- Sem, Sem->ModName, Sem->Name);
- #endif
-
- // Wake the sleeper
- if( toWake->Status != THREAD_STAT_ACTIVE )
- Threads_AddActive(toWake);
- else
- Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
- }
- SHORTREL( &Sem->Protector );
-
- return added;
-}
-
-//
-// Get the current value of a semaphore
-//
-int Semaphore_GetValue(tSemaphore *Sem)
-{
- return Sem->Value;
-}
-
-// === EXPORTS ===
-EXPORT(Semaphore_Init);
-EXPORT(Semaphore_Wait);
-EXPORT(Semaphore_Signal);
+++ /dev/null
-/*
- * AcessOS Microkernel Version
- * syscalls.c
- */
-#define DEBUG 0
-
-#include <acess.h>
-#include <syscalls.h>
-#include <proc.h>
-#include <hal_proc.h>
-#include <errno.h>
-#include <threads.h>
-#include <events.h>
-
-#define CHECK_NUM_NULLOK(v,size) \
- if((v)&&!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;}
-#define CHECK_STR_NULLOK(v) \
- if((v)&&!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;}
-#define CHECK_NUM_NONULL(v,size) \
- if(!(v)||!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;}
-#define CHECK_STR_NONULL(v) \
- if(!(v)||!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;}
-#define CHECK_STR_ARRAY(arr) do {\
- int i;\
- char **tmp = (char**)arr; \
- CHECK_NUM_NONULL( tmp, sizeof(char**) ); \
- for(i=0;tmp[i];i++) { \
- CHECK_STR_NONULL( tmp[i] ); \
- CHECK_NUM_NONULL( &tmp[i+1], sizeof(char*) ); \
- }\
- if(tmp[i]) break;\
-} while(0)
-
-// === IMPORTS ===
-extern Uint Binary_Load(const char *file, Uint *entryPoint);
-
-// === PROTOTYPES ===
-void SyscallHandler(tSyscallRegs *Regs);
- int Syscall_ValidString(const char *Addr);
- int Syscall_Valid(int Size, const void *Addr);
-
-// === CODE ===
-// TODO: Do sanity checking on arguments, ATM the user can really fuck with the kernel
-void SyscallHandler(tSyscallRegs *Regs)
-{
- Uint64 ret = 0;
- Uint err = -EOK;
- int callNum = Regs->Num;
-
- #if DEBUG < 2
- if(callNum != SYS_READ && callNum != SYS_WRITE) {
- #endif
- ENTER("iThread iNum", Threads_GetTID(), callNum);
- if(callNum < NUM_SYSCALLS)
- LOG("Syscall %s", cSYSCALL_NAMES[callNum]);
- LOG("Arg1: 0x%x, Arg2: 0x%x, Arg3: 0x%x, Arg4: 0x%x", Regs->Arg1, Regs->Arg2, Regs->Arg3, Regs->Arg4);
- #if DEBUG < 2
- }
- #endif
-
- switch(Regs->Num)
- {
- // -- Exit the current thread
- case SYS_EXIT: Threads_Exit(0, Regs->Arg1); break;
-
- // -- Put the current thread to sleep
- case SYS_SLEEP: Threads_Sleep(); break;
-
- // -- Yield current timeslice
- case SYS_YIELD: Threads_Yield(); break;
-
- // -- Set Error Handler
- case SYS_SETFAULTHANDLER:
- Threads_SetFaultHandler(Regs->Arg1);
- break;
-
- // -- Clone the current thread
- case SYS_CLONE:
- // Call clone system call
- ret = Proc_Clone(Regs->Arg1);
- break;
-
- // -- Send a signal
- case SYS_KILL:
- err = -ENOSYS;
- ret = -1;
- break;
-
- // -- Wait fr an event
- case SYS_WAITEVENT:
- // Message mask
- ret = Threads_WaitEvents(Regs->Arg1);
- break;
-
- // -- Wait for a thread
- case SYS_WAITTID:
- // Sanity Check (Status can be NULL)
- CHECK_NUM_NULLOK( (int*)Regs->Arg2, sizeof(int) );
- // TID, *Status
- ret = Threads_WaitTID(Regs->Arg1, (int*)Regs->Arg2);
- break;
-
- // -- Get the physical address of a page
- case SYS_GETPHYS:
- ret = MM_GetPhysAddr(Regs->Arg1);
- break;
-
- // -- Map an address
- case SYS_MAP: MM_Map(Regs->Arg1, Regs->Arg2); break;
-
- // -- Allocate an address
- case SYS_ALLOCATE: ret = MM_Allocate(Regs->Arg1); break;
-
- // -- Unmap an address
- case SYS_UNMAP: MM_Deallocate(Regs->Arg1); break;
-
- // -- Get Thread/Process IDs
- case SYS_GETTID: ret = Threads_GetTID(); break;
- case SYS_GETPID: ret = Threads_GetPID(); break;
-
- // -- Get User/Group IDs
- case SYS_GETUID: ret = Threads_GetUID(); break;
- case SYS_GETGID: ret = Threads_GetGID(); break;
-
- // -- Set User/Group IDs
- case SYS_SETUID: ret = Threads_SetUID(Regs->Arg1); break;
- case SYS_SETGID: ret = Threads_SetGID(Regs->Arg1); break;
-
- // -- Send Message
- case SYS_SENDMSG:
- CHECK_NUM_NONULL( (void*)Regs->Arg3, Regs->Arg2 );
- // Destination, Size, *Data
- ret = Proc_SendMessage(Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3);
- break;
- // -- Check for messages
- case SYS_GETMSG:
- CHECK_NUM_NULLOK( (Uint*)Regs->Arg1, sizeof(Uint) );
- // NOTE: Can't do range checking as we don't know the size
- // - Should be done by Proc_GetMessage
- if( Regs->Arg2 && Regs->Arg2 != -1 && !MM_IsUser(Regs->Arg2) ) {
- err = -EINVAL; ret = -1; break;
- }
- // *Source, *Data
- ret = Proc_GetMessage((Uint*)Regs->Arg1, (void*)Regs->Arg2);
- break;
-
- // -- Get the current timestamp
- case SYS_GETTIME:
- ret = now();
- break;
-
- // -- Set the thread's name
- case SYS_SETNAME:
- CHECK_STR_NONULL( (char*) Regs->Arg1);
- Threads_SetName( (char*)Regs->Arg1 );
- break;
-
- // ---
- // Binary Control
- // ---
- // -- Create a new process
- case SYS_SPAWN:
- CHECK_STR_NONULL((const char*)Regs->Arg1);
- CHECK_STR_ARRAY((const char**)Regs->Arg2);
- CHECK_STR_ARRAY((const char**)Regs->Arg3);
- CHECK_NUM_NONULL((void*)Regs->Arg5, Regs->Arg4*sizeof(int));
- ret = Proc_SysSpawn(
- (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3,
- Regs->Arg4, (int*)Regs->Arg5
- );
- break;
- // -- Replace the current process with another
- case SYS_EXECVE:
- CHECK_STR_NONULL((char*)Regs->Arg1);
- CHECK_STR_ARRAY( (char**)Regs->Arg2 );
- if( Regs->Arg3 )
- CHECK_STR_ARRAY( (char**)Regs->Arg3 );
- LEAVE('s', "Assuming 0");
- // Path, **Argv, **Envp, DataSize (=0 to tell it to create a copy)
- ret = Proc_Execve(
- (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3,
- 0
- );
- break;
- // -- Load a binary into the current process
- case SYS_LOADBIN:
- CHECK_STR_NONULL( (char*)Regs->Arg1 );
- CHECK_NUM_NONULL( (Uint*)Regs->Arg2, sizeof(Uint) );
- // Path, *Entrypoint
- ret = Binary_Load((char*)Regs->Arg1, (Uint*)Regs->Arg2);
- break;
-
- // ---
- // Virtual Filesystem
- // ---
- case SYS_OPEN:
- CHECK_STR_NONULL( (char*)Regs->Arg1 );
- LOG("VFS_Open(\"%s\", 0x%x)", (char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER);
- ret = VFS_Open((char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER);
- break;
-
- case SYS_CLOSE:
- LOG("VFS_Close(%i)", Regs->Arg1);
- VFS_Close( Regs->Arg1 );
- break;
-
- case SYS_SEEK:
- #if BITS == 64
- ret = VFS_Seek( Regs->Arg1, Regs->Arg2, Regs->Arg3 );
- #else
- ret = VFS_Seek( Regs->Arg1, Regs->Arg2|(((Uint64)Regs->Arg3)<<32), Regs->Arg4 );
- #endif
- break;
-
- case SYS_TELL:
- ret = VFS_Tell( Regs->Arg1 );
- break;
-
- case SYS_WRITE:
- CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
- ret = VFS_Write( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 );
- break;
-
- case SYS_READ:
- CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
- ret = VFS_Read( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 );
- break;
-
- case SYS_FINFO:
- CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tFInfo) + Regs->Arg3*sizeof(tVFS_ACL) );
- // FP, Dest, MaxACLs
- ret = VFS_FInfo( Regs->Arg1, (void*)Regs->Arg2, Regs->Arg3 );
- break;
-
- // Get ACL Value
- case SYS_GETACL:
- CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tVFS_ACL) );
- ret = VFS_GetACL( Regs->Arg1, (void*)Regs->Arg2 );
- break;
-
- // Read Directory
- case SYS_READDIR:
- // TODO: What if the filename is longer?
- // Maybe force it to be a 256 byte buffer
- CHECK_NUM_NONULL( (void*)Regs->Arg2, 256 );
- ret = VFS_ReadDir( Regs->Arg1, (void*)Regs->Arg2 );
- break;
-
- // Open a file that is a entry in an open directory
- case SYS_OPENCHILD:
- CHECK_STR_NONULL( (char*)Regs->Arg2 );
- ret = VFS_OpenChild( Regs->Arg1, (char*)Regs->Arg2, Regs->Arg3 | VFS_OPENFLAG_USER);
- break;
-
- // Change Directory
- case SYS_CHDIR:
- CHECK_STR_NONULL( (const char*)Regs->Arg1 );
- ret = VFS_ChDir( (const char*)Regs->Arg1 );
- break;
-
- // IO Control
- case SYS_IOCTL:
- // All sanity checking should be done by the driver
- if( Regs->Arg3 && !MM_IsUser(Regs->Arg3) ) {
- err = -EINVAL; ret = -1; break;
- }
- ret = VFS_IOCtl( Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3 );
- break;
-
- // Mount a filesystem
- case SYS_MOUNT:
- // Only root can mount filesystems
- if(Threads_GetUID() != 0) {
- err = -EACCES;
- ret = -1;
- break;
- }
- // Sanity check the paths
- if(!Syscall_ValidString((char*)Regs->Arg1)
- || !Syscall_ValidString((char*)Regs->Arg2)
- || !Syscall_ValidString((char*)Regs->Arg3)
- || !Syscall_ValidString((char*)Regs->Arg4) ) {
- err = -EINVAL;
- ret = -1;
- break;
- }
- ret = VFS_Mount(
- (char*)Regs->Arg1, // Device
- (char*)Regs->Arg2, // Mount point
- (char*)Regs->Arg3, // Filesystem
- (char*)Regs->Arg4 // Options
- );
- break;
-
- // Wait on a set of handles
- case SYS_SELECT:
- // Sanity checks
- if( (Regs->Arg2 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg2))
- || (Regs->Arg3 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg3))
- || (Regs->Arg4 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg4))
- || (Regs->Arg5 && !Syscall_Valid(sizeof(tTime), (void*)Regs->Arg5)) )
- {
- err = -EINVAL;
- ret = -1;
- break;
- }
- // Perform the call
- ret = VFS_Select(
- Regs->Arg1, // Max handle
- (fd_set *)Regs->Arg2, // Read
- (fd_set *)Regs->Arg3, // Write
- (fd_set *)Regs->Arg4, // Errors
- (tTime *)Regs->Arg5, // Timeout
- (Uint32)Regs->Arg6, // Extra wakeup events
- 0 // User handles
- );
- break;
-
- // -- Debug
- //#if DEBUG_BUILD
- case SYS_DEBUG:
- CHECK_STR_NONULL( (char*)Regs->Arg1 );
- LogF("Log: %08lli [%i] ", now(), Threads_GetTID());
- LogF((const char*)Regs->Arg1,
- Regs->Arg2, Regs->Arg3, Regs->Arg4, Regs->Arg5, Regs->Arg6);
- LogF("\r\n");
- break;
- //#endif
-
- // -- Default (Return Error)
- default:
- Log_Warning("Syscalls", "Unknown System Call %i", Regs->Num);
- if(Regs->Num < NUM_SYSCALLS)
- Log_Warning("Syscall", " named '%s'", cSYSCALL_NAMES[Regs->Num]);
- err = -ENOSYS;
- ret = -1;
- break;
- }
-
- if(err == 0) err = errno;
-
- if(err != 0) {
- LOG("ID: %i, Return errno = %i", Regs->Num, err);
- }
-
- #if BITS < 64
- Regs->Return = ret&0xFFFFFFFF;
- Regs->RetHi = ret >> 32;
- #else
- Regs->Return = ret;
- #endif
- Regs->Error = err;
- #if DEBUG
- # if DEBUG < 2
- if( callNum != SYS_READ && callNum != SYS_WRITE ) {
- # endif
- LOG("err = %i", err);
- if( callNum == SYS_EXECVE )
- LOG("Actual %i", ret);
- else
- LEAVE('x', ret);
- # if DEBUG < 2
- }
- # endif
- #endif
-}
-
-/**
- * \fn int Syscall_ValidString(const char *Addr)
- * \brief Checks if a memory address contains a valid string
- */
-int Syscall_ValidString(const char *Addr)
-{
- // Check if the memory is user memory
- if(!MM_IsUser( (tVAddr) Addr)) return 0;
-
- return CheckString( Addr );
-}
-
-/**
- * \fn int Syscall_Valid(int Size, const void *Addr)
- * \brief Checks if a memory address is valid
- */
-int Syscall_Valid(int Size, const void *Addr)
-{
- if(!MM_IsUser( (tVAddr)Addr )) return 0;
-
- return CheckMem( Addr, Size );
-}
+++ /dev/null
-
-0
-SYS_EXIT Kill this thread
-SYS_CLONE Create a new thread
-SYS_KILL Send a signal
-SYS_SETFAULTHANDLER Set signal Handler
-SYS_YIELD Yield remainder of timestamp
-SYS_SLEEP Sleep until messaged or signaled
-SYS_WAITEVENT Wait for an event
-SYS_WAITTID Wait for a thread to do something
-
-SYS_SETNAME Sets the name of the current thread
-SYS_GETNAME Gets the name of a thread
-SYS_GETTID Get current thread ID
-SYS_GETPID Get current thread group ID
-SYS_SETPRI Set process priority
-
-SYS_SENDMSG Send an IPC message
-SYS_GETMSG Recieve an IPC message
-
-SYS_GETTIME Get the current timestamp
-
-SYS_SPAWN Spawn a new process
-SYS_EXECVE Replace the current process
-SYS_LOADBIN Load a binary into the current address space
-SYS_UNLOADBIN Unload a loaded binary
-SYS_LOADMOD Load a module into the kernel
-
-32
-SYS_GETPHYS Get the physical address of a page
-SYS_MAP Map a physical address
-SYS_ALLOCATE Allocate a page
-SYS_UNMAP Unmap a page
-SYS_PREALLOC Preallocate a page
-SYS_SETFLAGS Set a page's flags
-SYS_SHAREWITH Share a page with another thread
-
-SYS_GETUID Get current User ID
-SYS_GETGID Get current Group ID
-SYS_SETUID Set current user ID
-SYS_SETGID Set current Group ID
-
-64
-SYS_OPEN Open a file
-SYS_REOPEN Close a file and reuse its handle
-SYS_CLOSE Close a file
-SYS_READ Read from an open file
-SYS_WRITE Write to an open file
-SYS_IOCTL Perform an IOCtl Call
-SYS_SEEK Seek to a new position in the file
-SYS_READDIR Read from an open directory
-SYS_OPENCHILD Open a child entry in a directory
-SYS_GETACL Get an ACL Value
-SYS_SETACL Set an ACL Value
-SYS_FINFO Get file information
-SYS_MKDIR Create a new directory
-SYS_LINK Create a new link to a file
-SYS_SYMLINK Create a symbolic link
-SYS_UNLINK Delete a file
-SYS_TELL Return the current file position
-SYS_CHDIR Change current directory
-SYS_GETCWD Get current directory
-SYS_MOUNT Mount a filesystem
-SYS_SELECT Wait for file handles
+++ /dev/null
-/*
- * Acess 2 Kernel
- * - By John Hodge (thePowersGang)
- * system.c
- * - Architecture Independent System Init
- */
-#define DEBUG 0
-#include <acess.h>
-
-// === IMPORTS ===
-extern void Arch_LoadBootModules(void);
-extern int Modules_LoadBuiltins(void);
-extern void Modules_SetBuiltinParams(char *Name, char *ArgString);
-extern void Debug_SetKTerminal(const char *File);
-
-// === PROTOTYPES ===
-void System_Init(char *Commandline);
-void System_ParseCommandLine(char *ArgString);
-void System_ExecuteCommandLine(void);
-void System_ParseVFS(char *Arg);
-void System_ParseModuleArgs(char *Arg);
-void System_ParseSetting(char *Arg);
-
-// === GLOBALS ===
-const char *gsInitBinary = "/Acess/SBin/init";
-char *argv[32];
- int argc;
-
-// === CODE ===
-void System_Init(char *CommandLine)
-{
- // Parse Kernel's Command Line
- System_ParseCommandLine(CommandLine);
-
- // Initialise modules
- Log_Log("Config", "Initialising builtin modules...");
- Modules_LoadBuiltins();
- Arch_LoadBootModules();
-
- System_ExecuteCommandLine();
-
- // - Execute the Config Script
- Log_Log("Config", "Spawning init '%s'", gsInitBinary);
- Proc_Spawn(gsInitBinary);
-
- // Set the debug to be echoed to the terminal
- Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)");
- Debug_SetKTerminal("/Devices/VTerm/7");
-}
-
-/**
- * \fn void System_ParseCommandLine(char *ArgString)
- * \brief Parses the kernel's command line and sets the environment
- */
-void System_ParseCommandLine(char *ArgString)
-{
- int i;
- char *str;
-
- Log_Log("Config", "Kernel Invocation (%p) \"%s\"", ArgString, ArgString);
-
- // --- Get Arguments ---
- str = ArgString;
- for( argc = 0; argc < 32; argc++ )
- {
- // Eat Whitespace
- while(*str == ' ') str++;
- // Check for the end of the string
- if(*str == '\0') { argc--; break;}
- argv[argc] = str;
- if(*str == '"') {
- while(*str && !(*str == '"' && str[-1] != '\\'))
- str ++;
- }
- else {
- while(*str && *str != ' ')
- str++;
- }
- if(*str == '\0') break; // Check for EOS
- *str = '\0'; // Cap off argument string
- str ++; // and increment the string pointer
- }
- if(argc < 32)
- argc ++; // Count last argument
-
- // --- Parse Arguments (Pass 1) ---
- for( i = 1; i < argc; i++ )
- {
- switch(argv[i][0])
- {
- // --- VFS ---
- // Ignored on this pass
- case '/':
- break;
-
- // --- Module Paramaters ---
- // -VTerm:Width=640,Height=480,Scrollback=2
- case '-':
- System_ParseModuleArgs( argv[i] );
- break;
- // --- Config Options ---
- // SCRIPT=/Acess/Conf/BootConf.cfg
- default:
- System_ParseSetting( argv[i] );
- break;
- }
- }
-}
-
-void System_ExecuteCommandLine(void)
-{
- int i;
- if(argc > 0)
- LOG("Invocation '%s'", argv[0]);
- for( i = 1; i < argc; i++ )
- {
- LOG("argv[%i] = '%s'", i, argv[i]);
- switch(argv[i][0])
- {
- // --- VFS ---
- // Mount /System=ext2:/Devices/ATA/A1
- // Symlink /Acess=/System/Acess2
- case '/':
- System_ParseVFS( argv[i] );
- break;
- }
- }
-}
-
-/**
- * \fn void System_ParseVFS(char *Arg)
- */
-void System_ParseVFS(char *Arg)
-{
- char *value;
- int fd;
-
- value = Arg;
- // Search for the '=' token
- while( *value && *value != '=' )
- value++;
-
- // Check if the equals was found
- if( *value == '\0' ) {
- Log_Warning("Config", "Expected '=' in the string '%s'", Arg);
- return ;
- }
-
- // Edit string
- *value = '\0'; value ++;
-
- // Check assignment type
- // - Symbolic Link <link>=<destination>
- if(value[0] == '/')
- {
- Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value);
- VFS_Symlink(Arg, value);
- }
- // - Mount <mountpoint>=<fs>:<device>
- else
- {
- char *dev = value;
- // Find colon
- while(*dev && *dev != ':') dev++;
- if(*dev) {
- *dev = '\0';
- dev++; // Eat ':'
- }
- // Create Mountpoint
- if( (fd = VFS_Open(Arg, 0)) == -1 ) {
- Log_Log("Config", "Creating directory '%s'", Arg, value);
- VFS_MkDir( Arg );
- } else {
- VFS_Close(fd);
- }
- // Mount
- Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value);
- VFS_Mount(dev, Arg, value, "");
- }
-}
-
-/**
- * \brief Parse a module argument string
- * \param Arg Argument string
- */
-void System_ParseModuleArgs(char *Arg)
-{
- char *name, *args;
- int i;
-
- // Remove '-'
- name = Arg + 1;
-
- // Find the start of the args
- i = strpos(name, ':');
- if( i == -1 ) {
- Log_Warning("Config", "Module spec with no arguments");
- #if 1
- return ;
- #else
- i = strlen(name);
- args = name + i;
- #endif
- }
- else {
- name[i] = '\0';
- args = name + i + 1;
- }
-
- Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args);
- Modules_SetBuiltinParams(name, args);
-}
-
-/**
- * \fn void System_ParseSetting(char *Arg)
- */
-void System_ParseSetting(char *Arg)
-{
- char *value;
- value = Arg;
-
- // Search for the '=' token
- while( *value && *value != '=' )
- value++;
-
- // Check for boolean/flag (no '=')
- if(*value == '\0')
- {
- //if(strcmp(Arg, "") == 0) {
- //} else {
- Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg);
- //}
- }
- else
- {
- *value = '\0'; // Remove '='
- value ++; // and eat it's position
-
- if(strcmp(Arg, "INIT") == 0) {
- Log_Log("Config", "Init binary: '%s'", value);
- if(strlen(value) == 0)
- gsInitBinary = NULL;
- else
- gsInitBinary = value;
- }
- else {
- Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg);
- }
-
- }
-}
-
+++ /dev/null
-/*
- * Acess2
- * threads.c
- * - Common Thread Control
- */
-#include <acess.h>
-#include <threads.h>
-#include <threads_int.h>
-#include <errno.h>
-#include <hal_proc.h>
-#include <semaphore.h>
-#include <vfs_threads.h> // VFS Handle maintainence
-
-// Configuration
-#define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
-#define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
-
-// --- Schedulers ---
-#define SCHED_UNDEF 0
-#define SCHED_LOTTERY 1 // Lottery scheduler
-#define SCHED_RR_SIM 2 // Single Queue Round Robin
-#define SCHED_RR_PRI 3 // Multi Queue Round Robin
-// Set scheduler type
-#define SCHEDULER_TYPE SCHED_RR_PRI
-
-// === CONSTANTS ===
-#define DEFAULT_QUANTUM 5
-#define DEFAULT_PRIORITY 5
-#define MIN_PRIORITY 10
-
-// === IMPORTS ===
-
-// === TYPE ===
-typedef struct
-{
- tThread *Head;
- tThread *Tail;
-} tThreadList;
-
-// === PROTOTYPES ===
-void Threads_Init(void);
-#if 0
-void Threads_Delete(tThread *Thread);
- int Threads_SetName(const char *NewName);
-#endif
-char *Threads_GetName(tTID ID);
-#if 0
-void Threads_SetPriority(tThread *Thread, int Pri);
-tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
- int Threads_WaitTID(int TID, int *status);
-tThread *Threads_GetThread(Uint TID);
-#endif
-tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
-void Threads_int_AddToList(tThreadList *List, tThread *Thread);
-#if 0
-void Threads_Exit(int TID, int Status);
-void Threads_Kill(tThread *Thread, int Status);
-void Threads_Yield(void);
-void Threads_Sleep(void);
- int Threads_Wake(tThread *Thread);
-void Threads_AddActive(tThread *Thread);
-tThread *Threads_RemActive(void);
-#endif
-void Threads_ToggleTrace(int TID);
-void Threads_Fault(int Num);
-void Threads_SegFault(tVAddr Addr);
-#if 0
- int Threads_GetPID(void);
- int Threads_GetTID(void);
-tUID Threads_GetUID(void);
-tGID Threads_GetGID(void);
- int Threads_SetUID(Uint *Errno, tUID ID);
- int Threads_SetGID(Uint *Errno, tUID ID);
-#endif
-void Threads_Dump(void);
-void Threads_DumpActive(void);
-
-// === GLOBALS ===
-// -- Core Thread --
-struct sProcess gProcessZero = {
- };
-// Only used for the core kernel
-tThread gThreadZero = {
- .Status = THREAD_STAT_ACTIVE, // Status
- .ThreadName = (char*)"ThreadZero", // Name
- .Quantum = DEFAULT_QUANTUM, // Default Quantum
- .Remaining = DEFAULT_QUANTUM, // Current Quantum
- .Priority = DEFAULT_PRIORITY // Number of tickets
- };
-// -- Processes --
-// --- Locks ---
-tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
-// --- Current State ---
-volatile int giNumActiveThreads = 0; // Number of threads on the active queue
-volatile Uint giNextTID = 1; // Next TID to allocate
-// --- Thread Lists ---
-tThread *gAllThreads = NULL; // All allocated threads
-tThreadList gSleepingThreads; // Sleeping Threads
- int giNumCPUs = 1; // Number of CPUs
-BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
-// --- Scheduler Types ---
-#if SCHEDULER_TYPE == SCHED_LOTTERY
-const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
-volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
-tThreadList gActiveThreads; // Currently Running Threads
-#elif SCHEDULER_TYPE == SCHED_RR_SIM
-tThreadList gActiveThreads; // Currently Running Threads
-#elif SCHEDULER_TYPE == SCHED_RR_PRI
-tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
-#else
-# error "Unkown scheduler type"
-#endif
-
-// === CODE ===
-/**
- * \fn void Threads_Init(void)
- * \brief Initialse the thread list
- */
-void Threads_Init(void)
-{
- ArchThreads_Init();
-
- Log_Debug("Threads", "Offsets of tThread");
- Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
- Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
-
- // Create Initial Task
-// #if SCHEDULER_TYPE == SCHED_RR_PRI
-// gaActiveThreads[gThreadZero.Priority].Head = &gThreadZero;
-// gaActiveThreads[gThreadZero.Priority].Tail = &gThreadZero;
-// #else
-// gActiveThreads.Head = &gThreadZero;
-// gActiveThreads.Tail = &gThreadZero;
-// #endif
-
- gAllThreads = &gThreadZero;
- giNumActiveThreads = 1;
- gThreadZero.Process = &gProcessZero;
-
- Proc_Start();
-}
-
-void Threads_Delete(tThread *Thread)
-{
- // Set to dead
- Thread->Status = THREAD_STAT_BURIED;
-
- // Clear out process state
- Proc_ClearThread(Thread);
-
- Thread->Process->nThreads --;
- if( Thread->Process->nThreads == 0 )
- {
- tProcess *proc = Thread->Process;
- // VFS Cleanup
- VFS_CloseAllUserHandles();
- // Architecture cleanup
- Proc_ClearProcess( proc );
- // VFS Configuration strings
- if( proc->CurrentWorkingDir)
- free( proc->CurrentWorkingDir );
- if( proc->RootDir )
- free( proc->RootDir );
- // Process descriptor
- free( proc );
- }
-
- // Free name
- if( IsHeap(Thread->ThreadName) )
- free(Thread->ThreadName);
-
- // Remove from global list
- // TODO: Lock this too
- if( Thread == gAllThreads )
- gAllThreads = Thread->GlobalNext;
- else
- Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
-
- free(Thread);
-}
-
-/**
- * \fn void Threads_SetName(const char *NewName)
- * \brief Sets the current thread's name
- * \param NewName New name for the thread
- * \return Boolean Failure
- */
-int Threads_SetName(const char *NewName)
-{
- tThread *cur = Proc_GetCurThread();
- char *oldname = cur->ThreadName;
-
- // NOTE: There is a possibility of non-thread safety here
- // A thread could read the current name pointer before it is zeroed
-
- cur->ThreadName = NULL;
-
- if( IsHeap(oldname) ) free( oldname );
- cur->ThreadName = strdup(NewName);
-
- Log_Debug("Threads", "Thread renamed to '%s'", NewName);
-
- return 0;
-}
-
-/**
- * \fn char *Threads_GetName(int ID)
- * \brief Gets a thread's name
- * \param ID Thread ID (-1 indicates current thread)
- * \return Pointer to name
- * \retval NULL Failure
- */
-char *Threads_GetName(tTID ID)
-{
- if(ID == -1) {
- return Proc_GetCurThread()->ThreadName;
- }
- return Threads_GetThread(ID)->ThreadName;
-}
-
-/**
- * \fn void Threads_SetPriority(tThread *Thread, int Pri)
- * \brief Sets the priority of a task
- * \param Thread Thread to update ticket count (NULL means current thread)
- * \param Pri New priority
- */
-void Threads_SetPriority(tThread *Thread, int Pri)
-{
- // Get current thread
- if(Thread == NULL) Thread = Proc_GetCurThread();
- // Bounds checking
- // - If < 0, set to lowest priority
- // - Minumum priority is actualy a high number, 0 is highest
- if(Pri < 0) Pri = MIN_PRIORITY;
- if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
-
- // Do we actually have to do anything?
- if( Pri == Thread->Priority ) return;
-
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- if( Thread != Proc_GetCurThread() )
- {
- SHORTLOCK( &glThreadListLock );
- // Remove from old priority
- Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
- // And add to new
- Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
- Thread->Priority = Pri;
- SHORTREL( &glThreadListLock );
- }
- else
- Thread->Priority = Pri;
- #else
- // If this isn't the current thread, we need to lock
- if( Thread != Proc_GetCurThread() )
- {
- SHORTLOCK( &glThreadListLock );
-
- #if SCHEDULER_TYPE == SCHED_LOTTERY
- giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
- # if DEBUG_TRACE_TICKETS
- Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
- giFreeTickets,
- caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
- # endif
- #endif
- Thread->Priority = Pri;
- SHORTREL( &glThreadListLock );
- }
- else
- Thread->Priority = Pri;
- #endif
-
- #if DEBUG_TRACE_STATE
- Log("Threads_SetPriority: %p(%i %s) pri set %i",
- Thread, Thread->TID, Thread->ThreadName,
- Pri);
- #endif
-}
-
-/**
- * \brief Clone the TCB of the current thread
- * \param Flags Flags for something... (What is this for?)
- */
-tThread *Threads_CloneTCB(Uint Flags)
-{
- tThread *cur, *new;
- cur = Proc_GetCurThread();
-
- // Allocate and duplicate
- new = malloc(sizeof(tThread));
- if(new == NULL) { errno = -ENOMEM; return NULL; }
- memcpy(new, cur, sizeof(tThread));
-
- new->CurCPU = -1;
- new->Next = NULL;
- memset( &new->IsLocked, 0, sizeof(new->IsLocked));
- new->Status = THREAD_STAT_PREINIT;
- new->RetStatus = 0;
-
- // Get Thread ID
- new->TID = giNextTID++;
- new->Parent = cur;
- new->bInstrTrace = 0;
-
- // Clone Name
- new->ThreadName = strdup(cur->ThreadName);
-
- // Set Thread Group ID (PID)
- if(Flags & CLONE_VM) {
- tProcess *newproc, *oldproc;
- oldproc = cur->Process;
- new->Process = malloc( sizeof(struct sProcess) );
- newproc = new->Process;
- newproc->PID = new->TID;
- newproc->UID = oldproc->UID;
- newproc->GID = oldproc->GID;
- newproc->MaxFD = oldproc->MaxFD;
- if( oldproc->CurrentWorkingDir )
- newproc->CurrentWorkingDir = strdup( oldproc->CurrentWorkingDir );
- else
- newproc->CurrentWorkingDir = NULL;
- if( oldproc->RootDir )
- newproc->RootDir = strdup( oldproc->RootDir );
- else
- newproc->RootDir = NULL;
- newproc->nThreads = 1;
- // Reference all handles in the VFS
- VFS_ReferenceUserHandles();
- }
- else {
- new->Process->nThreads ++;
- }
-
- // Messages are not inherited
- new->Messages = NULL;
- new->LastMessage = NULL;
-
- // Set State
- new->Remaining = new->Quantum = cur->Quantum;
- new->Priority = cur->Priority;
- new->_errno = 0;
-
- // Set Signal Handlers
- new->CurFaultNum = 0;
- new->FaultHandler = cur->FaultHandler;
-
- // Maintain a global list of threads
- SHORTLOCK( &glThreadListLock );
- new->GlobalPrev = NULL; // Protect against bugs
- new->GlobalNext = gAllThreads;
- gAllThreads->GlobalPrev = new;
- gAllThreads = new;
- SHORTREL( &glThreadListLock );
-
- return new;
-}
-
-/**
- * \brief Clone the TCB of the kernel thread
- */
-tThread *Threads_CloneThreadZero(void)
-{
- tThread *new;
-
- // Allocate and duplicate
- new = malloc(sizeof(tThread));
- if(new == NULL) {
- return NULL;
- }
- memcpy(new, &gThreadZero, sizeof(tThread));
-
- new->Process->nThreads ++;
-
- new->CurCPU = -1;
- new->Next = NULL;
- memset( &new->IsLocked, 0, sizeof(new->IsLocked));
- new->Status = THREAD_STAT_PREINIT;
- new->RetStatus = 0;
-
- // Get Thread ID
- new->TID = giNextTID++;
- new->Parent = 0;
-
- // Clone Name
- new->ThreadName = NULL;
-
- // Messages are not inherited
- new->Messages = NULL;
- new->LastMessage = NULL;
-
- // Set State
- new->Remaining = new->Quantum = DEFAULT_QUANTUM;
- new->Priority = DEFAULT_PRIORITY;
- new->bInstrTrace = 0;
-
- // Set Signal Handlers
- new->CurFaultNum = 0;
- new->FaultHandler = 0;
-
- // Maintain a global list of threads
- SHORTLOCK( &glThreadListLock );
- new->GlobalPrev = NULL; // Protect against bugs
- new->GlobalNext = gAllThreads;
- gAllThreads->GlobalPrev = new;
- gAllThreads = new;
- SHORTREL( &glThreadListLock );
-
- return new;
-}
-
-/**
- * \brief Wait for a task to change state
- * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
- * \param Status Thread return status
- * \return TID of child that changed state
- */
-tTID Threads_WaitTID(int TID, int *Status)
-{
- // Any Child
- if(TID == -1) {
- Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
- return -1;
- }
-
- // Any peer/child thread
- if(TID == 0) {
- Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
- return -1;
- }
-
- // TGID = abs(TID)
- if(TID < -1) {
- Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
- return -1;
- }
-
- // Specific Thread
- if(TID > 0) {
- tThread *t = Threads_GetThread(TID);
- tTID ret;
-
- // Wait for the thread to die!
- // TODO: Handle child also being suspended if wanted
- while(t->Status != THREAD_STAT_ZOMBIE) {
- Threads_Sleep();
- Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
- Threads_GetTID(), t->TID, t->Status);
- }
-
- // Set return status
- ret = t->TID;
- switch(t->Status)
- {
- case THREAD_STAT_ZOMBIE:
- // Kill the thread
- t->Status = THREAD_STAT_DEAD;
- // TODO: Child return value?
- if(Status) *Status = t->RetStatus;
- // add to delete queue
- Threads_Delete( t );
- break;
- default:
- if(Status) *Status = -1;
- break;
- }
- return ret;
- }
-
- return -1;
-}
-
-/**
- * \brief Gets a thread given its TID
- * \param TID Thread ID
- * \return Thread pointer
- */
-tThread *Threads_GetThread(Uint TID)
-{
- tThread *thread;
-
- // Search global list
- for(thread = gAllThreads;
- thread;
- thread = thread->GlobalNext)
- {
- if(thread->TID == TID)
- return thread;
- }
-
- Log("Unable to find TID %i on main list\n", TID);
-
- return NULL;
-}
-
-/**
- * \brief Deletes an entry from a list
- * \param List Pointer to the list head
- * \param Thread Thread to find
- * \return \a Thread
- */
-tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
-{
- tThread *ret, *prev = NULL;
-
- for(ret = List->Head;
- ret && ret != Thread;
- prev = ret, ret = ret->Next
- );
-
- // Is the thread on the list
- if(!ret) {
- //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
- return NULL;
- }
-
- if( !prev ) {
- List->Head = Thread->Next;
- //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
- }
- else {
- prev->Next = Thread->Next;
- //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
- }
- if( Thread->Next == NULL )
- List->Tail = prev;
-
- return Thread;
-}
-
-void Threads_int_AddToList(tThreadList *List, tThread *Thread)
-{
- if( List->Head )
- List->Tail->Next = Thread;
- else
- List->Head = Thread;
- List->Tail = Thread;
- Thread->Next = NULL;
-}
-
-/**
- * \brief Exit the current process (or another?)
- * \param TID Thread ID to kill
- * \param Status Exit status
- */
-void Threads_Exit(int TID, int Status)
-{
- if( TID == 0 )
- Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
- else
- Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
-
- // Halt forever, just in case
- for(;;) HALT();
-}
-
-/**
- * \fn void Threads_Kill(tThread *Thread, int Status)
- * \brief Kill a thread
- * \param Thread Thread to kill
- * \param Status Status code to return to the parent
- */
-void Threads_Kill(tThread *Thread, int Status)
-{
- tMsg *msg;
- int isCurThread = Thread == Proc_GetCurThread();
-
- // TODO: Disown all children?
- #if 1
- {
- tThread *child;
- // TODO: I should keep a .Children list
- for(child = gAllThreads;
- child;
- child = child->GlobalNext)
- {
- if(child->Parent == Thread)
- child->Parent = &gThreadZero;
- }
- }
- #endif
-
- ///\note Double lock is needed due to overlap of lock areas
-
- // Lock thread (stop us recieving messages)
- SHORTLOCK( &Thread->IsLocked );
-
- // Clear Message Queue
- while( Thread->Messages )
- {
- msg = Thread->Messages->Next;
- free( Thread->Messages );
- Thread->Messages = msg;
- }
-
- // Lock thread list
- SHORTLOCK( &glThreadListLock );
-
- switch(Thread->Status)
- {
- case THREAD_STAT_PREINIT: // Only on main list
- break;
-
- // Currently active thread
- case THREAD_STAT_ACTIVE:
- if( Thread != Proc_GetCurThread() )
- {
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- tThreadList *list = &gaActiveThreads[Thread->Priority];
- #else
- tThreadList *list = &gActiveThreads;
- #endif
- if( Threads_int_DelFromQueue( list, Thread ) )
- {
- }
- else
- {
- Log_Warning("Threads",
- "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
- Thread, Thread->TID, Thread->ThreadName
- );
- }
- #if SCHEDULER_TYPE == SCHED_LOTTERY
- giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
- #endif
- }
- // Ensure that we are not rescheduled
- Thread->Remaining = 0; // Clear Remaining Quantum
- Thread->Quantum = 0; // Clear Quantum to indicate dead thread
-
- // Update bookkeeping
- giNumActiveThreads --;
- break;
- // Kill it while it sleeps!
- case THREAD_STAT_SLEEPING:
- if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
- {
- Log_Warning("Threads",
- "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
- Thread, Thread->TID, Thread->ThreadName
- );
- }
- break;
-
- // Brains!... You cannot kill something that is already dead
- case THREAD_STAT_ZOMBIE:
- Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
- Thread, Thread->TID, Thread->ThreadName);
- SHORTREL( &glThreadListLock );
- SHORTREL( &Thread->IsLocked );
- return ;
-
- default:
- Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
- Thread->Status);
- break;
- }
-
- // Save exit status
- Thread->RetStatus = Status;
-
- SHORTREL( &Thread->IsLocked );
-
- Thread->Status = THREAD_STAT_ZOMBIE;
- SHORTREL( &glThreadListLock );
- // TODO: Send something like SIGCHLD
- Threads_Wake( Thread->Parent );
-
- Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
-
- // And, reschedule
- if(isCurThread)
- {
- for( ;; )
- Proc_Reschedule();
- }
-}
-
-/**
- * \brief Yield remainder of the current thread's timeslice
- */
-void Threads_Yield(void)
-{
-// Log("Threads_Yield: by %p", __builtin_return_address(0));
- Proc_Reschedule();
-}
-
-/**
- * \fn void Threads_Sleep(void)
- * \brief Take the current process off the run queue
- */
-void Threads_Sleep(void)
-{
- tThread *cur = Proc_GetCurThread();
-
- // Acquire Spinlock
- SHORTLOCK( &glThreadListLock );
-
- // Don't sleep if there is a message waiting
- if( cur->Messages ) {
- SHORTREL( &glThreadListLock );
- return;
- }
-
- // Remove us from running queue
- Threads_RemActive();
- // Mark thread as sleeping
- cur->Status = THREAD_STAT_SLEEPING;
-
- // Add to Sleeping List (at the top)
- Threads_int_AddToList( &gSleepingThreads, cur );
-
- #if DEBUG_TRACE_STATE
- Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
- #endif
-
- // Release Spinlock
- SHORTREL( &glThreadListLock );
-
- while(cur->Status != THREAD_STAT_ACTIVE) {
- Proc_Reschedule();
- if( cur->Status != THREAD_STAT_ACTIVE )
- Log("%i - Huh? why am I up? zzzz...", cur->TID);
- }
-}
-
-
-/**
- * \brief Wakes a sleeping/waiting thread up
- * \param Thread Thread to wake
- * \return Boolean Failure (Returns ERRNO)
- * \warning This should ONLY be called with task switches disabled
- */
-int Threads_Wake(tThread *Thread)
-{
- if(!Thread)
- return -EINVAL;
-
- switch(Thread->Status)
- {
- case THREAD_STAT_ACTIVE:
- Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
- return -EALREADY;
-
- case THREAD_STAT_SLEEPING:
- SHORTLOCK( &glThreadListLock );
- // Remove from sleeping queue
- Threads_int_DelFromQueue(&gSleepingThreads, Thread);
-
- SHORTREL( &glThreadListLock );
- Threads_AddActive( Thread );
-
- #if DEBUG_TRACE_STATE
- Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
- #endif
- return -EOK;
-
- case THREAD_STAT_SEMAPHORESLEEP: {
- tSemaphore *sem;
- tThread *th, *prev=NULL;
-
- sem = Thread->WaitPointer;
-
- SHORTLOCK( &sem->Protector );
-
- // Remove from sleeping queue
- for( th = sem->Waiting; th; prev = th, th = th->Next )
- if( th == Thread ) break;
- if( th )
- {
- if(prev)
- prev->Next = Thread->Next;
- else
- sem->Waiting = Thread->Next;
- if(sem->LastWaiting == Thread)
- sem->LastWaiting = prev;
- }
- else
- {
- prev = NULL;
- for( th = sem->Signaling; th; prev = th, th = th->Next )
- if( th == Thread ) break;
- if( !th ) {
- Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
- Thread, Thread->TID, Thread->ThreadName,
- sem, sem->ModName, sem->Name);
- return -EINTERNAL;
- }
-
- if(prev)
- prev->Next = Thread->Next;
- else
- sem->Signaling = Thread->Next;
- if(sem->LastSignaling == Thread)
- sem->LastSignaling = prev;
- }
-
- Thread->RetStatus = 0; // It didn't get anything
- Threads_AddActive( Thread );
-
- #if DEBUG_TRACE_STATE
- Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
- #endif
- SHORTREL( &sem->Protector );
- } return -EOK;
-
- case THREAD_STAT_WAITING:
- Warning("Threads_Wake - Waiting threads are not currently supported");
- return -ENOTIMPL;
-
- case THREAD_STAT_DEAD:
- Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
- return -ENOTIMPL;
-
- default:
- Warning("Threads_Wake - Unknown process status (%i)\n", Thread->Status);
- return -EINTERNAL;
- }
-}
-
-/**
- * \brief Wake a thread given the TID
- * \param TID Thread ID to wake
- * \return Boolean Faulure (errno)
- */
-int Threads_WakeTID(tTID TID)
-{
- tThread *thread = Threads_GetThread(TID);
- int ret;
- if(!thread)
- return -ENOENT;
- ret = Threads_Wake( thread );
- //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
- return ret;
-}
-
-void Threads_ToggleTrace(int TID)
-{
- tThread *thread = Threads_GetThread(TID);
- if(!thread) return ;
- thread->bInstrTrace = !thread->bInstrTrace;
-}
-
-/**
- * \brief Adds a thread to the active queue
- */
-void Threads_AddActive(tThread *Thread)
-{
- SHORTLOCK( &glThreadListLock );
-
- if( Thread->Status == THREAD_STAT_ACTIVE ) {
- tThread *cur = Proc_GetCurThread();
- Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
- __builtin_return_address(0),
- GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
- SHORTREL( &glThreadListLock );
- return ;
- }
-
- // Set state
- Thread->Status = THREAD_STAT_ACTIVE;
-// Thread->CurCPU = -1;
- // Add to active list
- {
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- tThreadList *list = &gaActiveThreads[Thread->Priority];
- #else
- tThreadList *list = &gActiveThreads;
- #endif
- Threads_int_AddToList( list, Thread );
- }
-
- // Update bookkeeping
- giNumActiveThreads ++;
-
- #if SCHEDULER_TYPE == SCHED_LOTTERY
- {
- int delta;
- // Only change the ticket count if the thread is un-scheduled
- if(Thread->CurCPU != -1)
- delta = 0;
- else
- delta = caiTICKET_COUNTS[ Thread->Priority ];
-
- giFreeTickets += delta;
- # if DEBUG_TRACE_TICKETS
- Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
- GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
- giFreeTickets, delta
- );
- # endif
- }
- #endif
-
- SHORTREL( &glThreadListLock );
-}
-
-/**
- * \brief Removes the current thread from the active queue
- * \warning This should ONLY be called with the lock held
- * \return Current thread pointer
- */
-tThread *Threads_RemActive(void)
-{
- #if 0
- tThread *ret = Proc_GetCurThread();
-
- if( !IS_LOCKED(&glThreadListLock) ) {
- Log_KernelPanic("Threads", "Threads_RemActive called without lock held");
- return NULL;
- }
-
- // Delete from active queue
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
- #else
- if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
- #endif
- {
- Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue",
- ret, ret->TID, ret->ThreadName
- );
- return NULL;
- }
-
- ret->Next = NULL;
- ret->Remaining = 0;
-
- giNumActiveThreads --;
- // no need to decrement tickets, scheduler did it for us
-
- #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
- Log("CPU%i %p (%i %s) removed, giFreeTickets = %i [nc]",
- GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
- #endif
-
- return ret;
- #else
- return Proc_GetCurThread();
- #endif
-}
-
-/**
- * \fn void Threads_SetFaultHandler(Uint Handler)
- * \brief Sets the signal handler for a signal
- */
-void Threads_SetFaultHandler(Uint Handler)
-{
- //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
- Proc_GetCurThread()->FaultHandler = Handler;
-}
-
-/**
- * \fn void Threads_Fault(int Num)
- * \brief Calls a fault handler
- */
-void Threads_Fault(int Num)
-{
- tThread *thread = Proc_GetCurThread();
-
- if(!thread) return ;
-
- Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
-
- switch(thread->FaultHandler)
- {
- case 0: // Panic?
- Threads_Kill(thread, -1);
- HALT();
- return ;
- case 1: // Dump Core?
- Threads_Kill(thread, -1);
- HALT();
- return ;
- }
-
- // Double Fault? Oh, F**k
- if(thread->CurFaultNum != 0) {
- Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
- Threads_Kill(thread, -1); // For now, just kill
- HALT();
- }
-
- thread->CurFaultNum = Num;
-
- Proc_CallFaultHandler(thread);
-}
-
-/**
- * \fn void Threads_SegFault(tVAddr Addr)
- * \brief Called when a Segment Fault occurs
- */
-void Threads_SegFault(tVAddr Addr)
-{
- tThread *cur = Proc_GetCurThread();
- cur->bInstrTrace = 0;
- Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
- MM_DumpTables(0, USER_MAX);
- Threads_Fault( 1 );
- //Threads_Exit( 0, -1 );
-}
-
-// --- Process Structure Access Functions ---
-tPID Threads_GetPID(void)
-{
- return Proc_GetCurThread()->Process->PID;
-}
-tTID Threads_GetTID(void)
-{
- return Proc_GetCurThread()->TID;
-}
-tUID Threads_GetUID(void)
-{
- return Proc_GetCurThread()->Process->UID;
-}
-tGID Threads_GetGID(void)
-{
- return Proc_GetCurThread()->Process->GID;
-}
-
-int Threads_SetUID(tUID ID)
-{
- tThread *t = Proc_GetCurThread();
- if( t->Process->UID != 0 ) {
- errno = -EACCES;
- return -1;
- }
- Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
- t->Process->UID = ID;
- return 0;
-}
-
-int Threads_SetGID(tGID ID)
-{
- tThread *t = Proc_GetCurThread();
- if( t->Process->UID != 0 ) {
- errno = -EACCES;
- return -1;
- }
- Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
- t->Process->GID = ID;
- return 0;
-}
-
-// --- Per-thread storage ---
-int *Threads_GetErrno(void)
-{
- return &Proc_GetCurThread()->_errno;
-}
-
-// --- Configuration ---
-int *Threads_GetMaxFD(void)
-{
- return &Proc_GetCurThread()->Process->MaxFD;
-}
-char **Threads_GetChroot(void)
-{
- return &Proc_GetCurThread()->Process->RootDir;
-}
-char **Threads_GetCWD(void)
-{
- return &Proc_GetCurThread()->Process->CurrentWorkingDir;
-}
-// ---
-
-/**
- * \fn void Threads_Dump(void)
- */
-void Threads_DumpActive(void)
-{
- tThread *thread;
- tThreadList *list;
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- int i;
- #endif
-
- Log("Active Threads: (%i reported)", giNumActiveThreads);
-
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- for( i = 0; i < MIN_PRIORITY+1; i++ )
- {
- list = &gaActiveThreads[i];
- #else
- list = &gActiveThreads;
- #endif
- for(thread=list->Head;thread;thread=thread->Next)
- {
- Log(" %p %i (%i) - %s (CPU %i)",
- thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU);
- if(thread->Status != THREAD_STAT_ACTIVE)
- Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
- thread->Status, THREAD_STAT_ACTIVE);
- Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
- Log(" KStack 0x%x", thread->KernelStack);
- if( thread->bInstrTrace )
- Log(" Tracing Enabled");
- Proc_DumpThreadCPUState(thread);
- }
-
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- }
- #endif
-}
-
-/**
- * \fn void Threads_Dump(void)
- * \brief Dumps a list of currently running threads
- */
-void Threads_Dump(void)
-{
- tThread *thread;
-
- Log("--- Thread Dump ---");
- Threads_DumpActive();
-
- Log("All Threads:");
- for(thread=gAllThreads;thread;thread=thread->GlobalNext)
- {
- Log(" %p %i (%i) - %s (CPU %i)",
- thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU);
- Log(" State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
- switch(thread->Status)
- {
- case THREAD_STAT_MUTEXSLEEP:
- Log(" Mutex Pointer: %p", thread->WaitPointer);
- break;
- case THREAD_STAT_SEMAPHORESLEEP:
- Log(" Semaphore Pointer: %p", thread->WaitPointer);
- Log(" Semaphore Name: %s:%s",
- ((tSemaphore*)thread->WaitPointer)->ModName,
- ((tSemaphore*)thread->WaitPointer)->Name
- );
- break;
- case THREAD_STAT_ZOMBIE:
- Log(" Return Status: %i", thread->RetStatus);
- break;
- default: break;
- }
- Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
- Log(" KStack 0x%x", thread->KernelStack);
- if( thread->bInstrTrace )
- Log(" Tracing Enabled");
- Proc_DumpThreadCPUState(thread);
- }
-}
-
-/**
- * \brief Gets the next thread to run
- * \param CPU Current CPU
- * \param Last The thread the CPU was running
- */
-tThread *Threads_GetNextToRun(int CPU, tThread *Last)
-{
- tThread *thread;
-
- // If this CPU has the lock, we must let it complete
- if( CPU_HAS_LOCK( &glThreadListLock ) )
- return Last;
-
- // Don't change threads if the current CPU has switches disabled
- if( gaThreads_NoTaskSwitch[CPU] )
- return Last;
-
- // Lock thread list
- SHORTLOCK( &glThreadListLock );
-
- // Make sure the current (well, old) thread is marked as de-scheduled
- if(Last) Last->CurCPU = -1;
-
- // No active threads, just take a nap
- if(giNumActiveThreads == 0) {
- SHORTREL( &glThreadListLock );
- #if DEBUG_TRACE_TICKETS
- Log("No active threads");
- #endif
- return NULL;
- }
-
- #if 0
- #if SCHEDULER_TYPE != SCHED_RR_PRI
- // Special case: 1 thread
- if(giNumActiveThreads == 1) {
- if( gActiveThreads.Head->CurCPU == -1 )
- gActiveThreads.Head->CurCPU = CPU;
-
- SHORTREL( &glThreadListLock );
-
- if( gActiveThreads.Head->CurCPU == CPU )
- return gActiveThreads.Head;
-
- return NULL; // CPU has nothing to do
- }
- #endif
- #endif
-
- // Allow the old thread to be scheduled again
- if( Last ) {
- if( Last->Status == THREAD_STAT_ACTIVE ) {
- tThreadList *list;
- #if SCHEDULER_TYPE == SCHED_LOTTERY
- giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
- # if DEBUG_TRACE_TICKETS
- LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
- CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
- caiTICKET_COUNTS[ Last->Priority ]);
- # endif
- #endif
-
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- list = &gaActiveThreads[ Last->Priority ];
- #else
- list = &gActiveThreads;
- #endif
- // Add to end of list
- Threads_int_AddToList( list, Last );
- }
- #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
- else
- LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
- CPU, Last, Last->TID, Last->ThreadName, Last->Status);
- #endif
- Last->CurCPU = -1;
- }
-
- // ---
- // Lottery Scheduler
- // ---
- #if SCHEDULER_TYPE == SCHED_LOTTERY
- {
- int ticket, number;
- # if 1
- number = 0;
- for(thread = gActiveThreads.Head; thread; thread = thread->Next)
- {
- if(thread->Status != THREAD_STAT_ACTIVE)
- Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
- thread, thread->TID, thread->ThreadName, thread->Status);
- if(thread->Next == thread) {
- Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
- thread, thread->TID, thread->ThreadName, thread->Status);
- }
- number += caiTICKET_COUNTS[ thread->Priority ];
- }
- if(number != giFreeTickets) {
- Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
- giFreeTickets, number, CPU);
- }
- # endif
-
- // No free tickets (all tasks delegated to cores)
- if( giFreeTickets == 0 ) {
- SHORTREL(&glThreadListLock);
- return NULL;
- }
-
- // Get the ticket number
- ticket = number = rand() % giFreeTickets;
-
- // Find the next thread
- for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next )
- {
- if( caiTICKET_COUNTS[ thread->Priority ] > number) break;
- number -= caiTICKET_COUNTS[ thread->Priority ];
- }
-
- // If we didn't find a thread, something went wrong
- if(thread == NULL)
- {
- number = 0;
- for(thread=gActiveThreads;thread;thread=thread->Next) {
- if(thread->CurCPU >= 0) continue;
- number += caiTICKET_COUNTS[ thread->Priority ];
- }
- Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
- giFreeTickets, number);
- }
-
- // Remove
- if(prev)
- prev->Next = thread->Next;
- else
- gActiveThreads.Head = thread->Next;
- if(!thread->Next)
- gActiveThreads.Tail = prev;
-
- giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
- # if DEBUG_TRACE_TICKETS
- LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
- CPU, thread, thread->TID, thread->ThreadName,
- giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
- # endif
- }
-
- // ---
- // Priority based round robin scheduler
- // ---
- #elif SCHEDULER_TYPE == SCHED_RR_PRI
- {
- int i;
- thread = NULL;
- for( i = 0; i < MIN_PRIORITY + 1; i ++ )
- {
- if( !gaActiveThreads[i].Head )
- continue ;
-
- thread = gaActiveThreads[i].Head;
-
- // Remove from head
- gaActiveThreads[i].Head = thread->Next;
- if(!thread->Next)
- gaActiveThreads[i].Tail = NULL;
- thread->Next = NULL;
- break;
- }
-
- // Anything to do?
- if( !thread ) {
- SHORTREL(&glThreadListLock);
- return NULL;
- }
- if( thread->Status != THREAD_STAT_ACTIVE ) {
- LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName);
- }
- }
- #elif SCHEDULER_TYPE == SCHED_RR_SIM
- {
- // Get the next thread off the list
- thread = gActiveThreads.Head;
- gActiveThreads.Head = thread->Next;
- if(!thread->Next)
- gaActiveThreads.Tail = NULL;
- thread->Next = NULL;
-
- // Anything to do?
- if( !thread ) {
- SHORTREL(&glThreadListLock);
- return NULL;
- }
- }
- #else
- # error "Unimplemented scheduling algorithm"
- #endif
-
- // Make the new thread non-schedulable
- thread->CurCPU = CPU;
- thread->Remaining = thread->Quantum;
-
- SHORTREL( &glThreadListLock );
-
- return thread;
-}
-
-// === EXPORTS ===
-EXPORT(Threads_GetUID);
-EXPORT(Threads_GetGID);
+++ /dev/null
-/*
- * Acess 2
- * - By John Hodge (thePowersGang)
- *
- * Timer Code
- */
-#include <acess.h>
-#include <timers.h>
-#include <events.h>
-#include <hal_proc.h> // Proc_GetCurThread
-
-// === CONSTANTS ===
-#define NUM_TIMERS 8
-
-// === TYPEDEFS ===
-struct sTimer {
- tTimer *Next;
- Sint64 FiresAfter;
- void (*Callback)(void*);
- void *Argument;
-};
-
-// === PROTOTYPES ===
-void Timer_CallTimers(void);
-
-// === GLOBALS ===
-volatile Uint64 giTicks = 0;
-volatile Sint64 giTimestamp = 0;
-volatile Uint64 giPartMiliseconds = 0;
-tTimer *gTimers; // TODO: Replace by a ring-list timer
-
-// === CODE ===
-/**
- * \fn void Timer_CallTimers()
- */
-void Timer_CallTimers()
-{
- while( gTimers && gTimers->FiresAfter < now() )
- {
- tTimer *next;
-
- if( gTimers->Callback )
- gTimers->Callback(gTimers->Argument);
- else
- Threads_PostEvent(gTimers->Argument, THREAD_EVENT_TIMER);
-
- next = gTimers->Next;
- free(gTimers);
- gTimers = next;
- }
-}
-
-/**
- * \brief Schedule an action
- */
-tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
-{
- tTimer *ret;
- tTimer *t, *p;
-
- if(Callback == NULL)
- Argument = Proc_GetCurThread();
-
- // TODO: Use a pool instead?
- ret = malloc(sizeof(tTimer));
-
- ret->Callback = Callback;
- ret->FiresAfter = now() + Delta;
- ret->Argument = Argument;
-
- // Add into list (sorted)
- for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
- {
- if( t->FiresAfter > ret->FiresAfter ) break;
- }
- ret->Next = t;
- p->Next = ret;
-
- return ret;
-}
-
-/**
- * \brief Delete a timer
- */
-void Time_RemoveTimer(tTimer *Timer)
-{
- tTimer *t, *p;
- for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
- {
- if( t == Timer )
- {
- p->Next = t->Next;
- free(Timer);
- return ;
- }
- }
-}
-
-/**
- * \fn void Time_Delay(int Delay)
- * \brief Delay for a small ammount of time
- */
-void Time_Delay(int Delay)
-{
-// tTime dest = now() + Delay;
-// while(dest > now()) Threads_Yield();
- Time_CreateTimer(Delay, NULL, NULL);
- Threads_WaitEvents(THREAD_EVENT_TIMER);
-}
-
-// === EXPORTS ===
-EXPORT(Time_CreateTimer);
-EXPORT(Time_RemoveTimer);
-EXPORT(Time_Delay);
+++ /dev/null
-/*
- * Acess Micro VFS
- */
-#include <acess.h>
-#include "vfs.h"
-#include "vfs_int.h"
-
-// === GLOBALS ===
-tVFS_ACL gVFS_ACL_EveryoneRWX = { {1,-1}, {0,VFS_PERM_ALL} };
-tVFS_ACL gVFS_ACL_EveryoneRW = { {1,-1}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE} };
-tVFS_ACL gVFS_ACL_EveryoneRX = { {1,-1}, {0,VFS_PERM_READ|VFS_PERM_EXECUTE} };
-tVFS_ACL gVFS_ACL_EveryoneRO = { {1,-1}, {0,VFS_PERM_READ} };
-
-// === CODE ===
-/**
- * \fn int VFS_CheckACL(tVFS_Node *Node, Uint Permissions)
- * \brief Checks the permissions on a file
- */
-int VFS_CheckACL(tVFS_Node *Node, Uint Permissions)
-{
- int i;
- int uid = Threads_GetUID();
- int gid = Threads_GetGID();
-
- // Root can do anything
- if(uid == 0) return 1;
-
- // Root only file?, fast return
- if( Node->NumACLs == 0 ) {
- Log("VFS_CheckACL - %p inaccesable, NumACLs = 0, uid=%i", Node, uid);
- return 0;
- }
-
- // Check Deny Permissions
- for(i=0;i<Node->NumACLs;i++)
- {
- if(!Node->ACLs[i].Inv) continue; // Ignore ALLOWs
- if(Node->ACLs[i].ID != 0x7FFFFFFF)
- {
- if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue;
- if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue;
- }
-
- //Log("Deny %x", Node->ACLs[i].Perms);
-
- if(Node->ACLs[i].Perms & Permissions) {
- Log("VFS_CheckACL - %p inaccesable, %x denied",
- Node, Node->ACLs[i].Perms & Permissions);
- return 0;
- }
- }
-
- // Check for allow permissions
- for(i=0;i<Node->NumACLs;i++)
- {
- if(Node->ACLs[i].Inv) continue; // Ignore DENYs
- if(Node->ACLs[i].ID != 0x7FFFFFFF)
- {
- if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue;
- if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue;
- }
-
- //Log("Allow %x", Node->ACLs[i].Perms);
-
- if((Node->ACLs[i].Perms & Permissions) == Permissions) return 1;
- }
-
- Log("VFS_CheckACL - %p inaccesable, %x not allowed", Node, Permissions);
- return 0;
-}
-/**
- * \fn int VFS_GetACL(int FD, tVFS_ACL *Dest)
- */
-int VFS_GetACL(int FD, tVFS_ACL *Dest)
-{
- int i;
- tVFS_Handle *h = VFS_GetHandle(FD);
-
- // Error check
- if(!h) {
- return -1;
- }
-
- // Root can do anything
- if(Dest->Group == 0 && Dest->ID == 0) {
- Dest->Inv = 0;
- Dest->Perms = -1;
- return 1;
- }
-
- // Root only file?, fast return
- if( h->Node->NumACLs == 0 ) {
- Dest->Inv = 0;
- Dest->Perms = 0;
- return 0;
- }
-
- // Check Deny Permissions
- for(i=0;i<h->Node->NumACLs;i++)
- {
- if(h->Node->ACLs[i].Group != Dest->Group) continue;
- if(h->Node->ACLs[i].ID != Dest->ID) continue;
-
- Dest->Inv = h->Node->ACLs[i].Inv;
- Dest->Perms = h->Node->ACLs[i].Perms;
- return 1;
- }
-
-
- Dest->Inv = 0;
- Dest->Perms = 0;
- return 0;
-}
-
-/**
- * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group)
- * \brief Converts UNIX permissions to three Acess ACL entries
- */
-tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group)
-{
- tVFS_ACL *ret = malloc(sizeof(tVFS_ACL)*3);
-
- // Error Check
- if(!ret) return NULL;
-
- // Owner
- ret[0].Group = 0; ret[0].ID = Owner;
- ret[0].Inv = 0; ret[0].Perms = 0;
- if(Mode & 0400) ret[0].Perms |= VFS_PERM_READ;
- if(Mode & 0200) ret[0].Perms |= VFS_PERM_WRITE;
- if(Mode & 0100) ret[0].Perms |= VFS_PERM_EXECUTE;
-
- // Group
- ret[1].Group = 1; ret[1].ID = Group;
- ret[1].Inv = 0; ret[1].Perms = 0;
- if(Mode & 0040) ret[1].Perms |= VFS_PERM_READ;
- if(Mode & 0020) ret[1].Perms |= VFS_PERM_WRITE;
- if(Mode & 0010) ret[1].Perms |= VFS_PERM_EXECUTE;
-
- // Global
- ret[2].Group = 1; ret[2].ID = -1;
- ret[2].Inv = 0; ret[2].Perms = 0;
- if(Mode & 0004) ret[2].Perms |= VFS_PERM_READ;
- if(Mode & 0002) ret[2].Perms |= VFS_PERM_WRITE;
- if(Mode & 0001) ret[2].Perms |= VFS_PERM_EXECUTE;
-
- // Return buffer
- return ret;
-}
-
-// === EXPORTS ===
-// --- Variables ---
-EXPORTV(gVFS_ACL_EveryoneRWX);
-EXPORTV(gVFS_ACL_EveryoneRW);
-EXPORTV(gVFS_ACL_EveryoneRX);
-// --- Functions ---
-EXPORT(VFS_UnixToAcessACL);
+++ /dev/null
-/*
- * Acess2 VFS
- * - Directory Management Functions
- */
-#define DEBUG 0
-#include <acess.h>
-#include <vfs.h>
-#include <vfs_int.h>
-
-// === IMPORTS ===
-extern tVFS_Mount *gRootMount;
-
-// === PROTOTYPES ===
-#if 0
- int VFS_MkDir(const char *Path);
- int VFS_MkNod(const char *Path, Uint Flags);
- int VFS_Symlink(const char *Name, const char *Link);
-#endif
-
-// === CODE ===
-/**
- * \fn int VFS_MkDir(char *Path)
- * \brief Create a new node
- * \param Path Path of directory to create
- */
-int VFS_MkDir(const char *Path)
-{
- return VFS_MkNod(Path, VFS_FFLAG_DIRECTORY);
-}
-
-/**
- * \fn int VFS_MkNod(char *Path, Uint Flags)
- * \brief Create a new node in a directory
- * \param Path Path of new node
- * \param Flags Flags to apply to the node
- */
-int VFS_MkNod(const char *Path, Uint Flags)
-{
- char *absPath, *name;
- int pos = 0, oldpos = 0;
- int next = 0;
- tVFS_Node *parent;
- int ret;
-
- ENTER("sPath xFlags", Path, Flags);
-
- absPath = VFS_GetAbsPath(Path);
- LOG("absPath = '%s'", absPath);
-
- while( (next = strpos(&absPath[pos+1], '/')) != -1 ) {
- LOG("next = %i", next);
- pos += next+1;
- LOG("pos = %i", pos);
- oldpos = pos;
- }
- absPath[oldpos] = '\0'; // Mutilate path
- name = &absPath[oldpos+1];
-
- LOG("absPath = '%s', name = '%s'", absPath, name);
-
- // Check for root
- if(absPath[0] == '\0')
- parent = VFS_ParsePath("/", NULL, NULL);
- else
- parent = VFS_ParsePath(absPath, NULL, NULL);
-
- LOG("parent = %p", parent);
-
- if(!parent) {
- LEAVE('i', -1);
- return -1; // Error Check
- }
-
- // Permissions Check
- if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) {
- _CloseNode(parent);
- free(absPath);
- LEAVE('i', -1);
- return -1;
- }
-
- LOG("parent = %p", parent);
-
- if(!parent->Type || !parent->Type->MkNod) {
- Warning("VFS_MkNod - Directory has no MkNod method");
- LEAVE('i', -1);
- return -1;
- }
-
- // Create node
- ret = parent->Type->MkNod(parent, name, Flags);
-
- // Free allocated string
- free(absPath);
-
- // Free Parent
- _CloseNode(parent);
-
- // Error Check
- if(ret == 0) {
- LEAVE('i', -1);
- return -1;
- }
-
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \fn int VFS_Symlink(const char *Name, const char *Link)
- * \brief Creates a symlink called \a Name to \a Link
- * \param Name Name of symbolic link
- * \param Link Destination of symbolic link
- */
-int VFS_Symlink(const char *Name, const char *Link)
-{
- char *realLink;
- int fp;
-
- ENTER("sName sLink", Name, Link);
-
- // Get absolue path name
- realLink = VFS_GetAbsPath( Link );
- if(!realLink) {
- Log_Warning("VFS", "Path '%s' is badly formed", Link);
- LEAVE('i', -1);
- return -1;
- }
-
- LOG("realLink = '%s'", realLink);
-
- // Make node
- if( VFS_MkNod(Name, VFS_FFLAG_SYMLINK) != 0 ) {
- Log_Warning("VFS", "Unable to create link node '%s'", Name);
- free(realLink);
- LEAVE('i', -2);
- return -2; // Make link node
- }
-
- // Write link address
- fp = VFS_Open(Name, VFS_OPENFLAG_WRITE|VFS_OPENFLAG_NOLINK);
- VFS_Write(fp, strlen(realLink), realLink);
- VFS_Close(fp);
-
- free(realLink);
-
- LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \fn int VFS_ReadDir(int FD, char *Dest)
- * \brief Read from a directory
- */
-int VFS_ReadDir(int FD, char *Dest)
-{
- tVFS_Handle *h = VFS_GetHandle(FD);
- char *tmp;
-
- //ENTER("ph pDest", h, Dest);
-
- if(!h || !h->Node->Type || !h->Node->Type->ReadDir) {
- //LEAVE('i', 0);
- return 0;
- }
-
- if(h->Node->Size != -1 && h->Position >= h->Node->Size) {
- //LEAVE('i', 0);
- return 0;
- }
-
- do {
- tmp = h->Node->Type->ReadDir(h->Node, h->Position);
- if((Uint)tmp < (Uint)VFS_MAXSKIP)
- h->Position += (Uint)tmp;
- else
- h->Position ++;
- } while(tmp != NULL && (Uint)tmp < (Uint)VFS_MAXSKIP);
-
- //LOG("tmp = '%s'", tmp);
-
- if(!tmp) {
- //LEAVE('i', 0);
- return 0;
- }
-
- strcpy(Dest, tmp);
- free(tmp);
-
- //LEAVE('i', 1);
- return 1;
-}
+++ /dev/null
-/*
- * Acess 2
- * Device Filesystem (DevFS)
- * - vfs/fs/devfs.c
- */
-#include <acess.h>
-#include <vfs.h>
-#include <fs_devfs.h>
-
-// === PROTOTYPES ===
-#if 0
- int DevFS_AddDevice(tDevFS_Driver *Device);
-void DevFS_DelDevice(tDevFS_Driver *Device);
-#endif
-tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options);
-char *DevFS_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name);
-
-// === GLOBALS ===
-tVFS_Driver gDevFS_Info = {
- "devfs", 0, DevFS_InitDevice, NULL, NULL
- };
-tVFS_NodeType gDevFS_DirType = {
- .TypeName = "DevFS-Dir",
- .ReadDir = DevFS_ReadDir,
- .FindDir = DevFS_FindDir
- };
-tVFS_Node gDevFS_RootNode = {
- .Size = 0,
- .Flags = VFS_FFLAG_DIRECTORY,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Type = &gDevFS_DirType
- };
-tDevFS_Driver *gDevFS_Drivers = NULL;
- int giDevFS_NextID = 1;
-tShortSpinlock glDevFS_ListLock;
-
-// === CODE ===
-/**
- * \fn int DevFS_AddDevice(tDevFS_Driver *Device)
- */
-int DevFS_AddDevice(tDevFS_Driver *Device)
-{
- int ret = 0;
- tDevFS_Driver *dev;
-
- SHORTLOCK( &glDevFS_ListLock );
-
- // Check if the device is already registered or the name is taken
- for( dev = gDevFS_Drivers; dev; dev = dev->Next )
- {
- if(dev == Device) break;
- if(strcmp(dev->Name, Device->Name) == 0) break;
- }
-
- if(dev) {
- if(dev == Device)
- Log_Warning("DevFS", "Device %p '%s' attempted to register itself twice",
- dev, dev->Name);
- else
- Log_Warning("DevFS", "Device %p attempted to register '%s' which was owned by %p",
- Device, dev->Name, dev);
- ret = 0; // Error
- }
- else {
- Device->Next = gDevFS_Drivers;
- gDevFS_Drivers = Device;
- gDevFS_RootNode.Size ++;
- ret = giDevFS_NextID ++;
- }
- SHORTREL( &glDevFS_ListLock );
-
- return ret;
-}
-
-/**
- * \brief Delete a device from the DevFS folder
- */
-void DevFS_DelDevice(tDevFS_Driver *Device)
-{
- tDevFS_Driver *prev = NULL, *dev;
-
- SHORTLOCK( &glDevFS_ListLock );
- // Search list for device
- for(dev = gDevFS_Drivers;
- dev && dev != Device;
- prev = dev, dev = dev->Next
- );
-
- // Check if it was found
- if(dev)
- {
- if(prev)
- prev->Next = Device->Next;
- else
- gDevFS_Drivers = Device->Next;
- }
- else
- Log_Warning("DevFS", "Attempted to unregister device %p '%s' which was not registered",
- Device, Device->Name);
-
- SHORTREL( &glDevFS_ListLock );
-}
-
-/**
- * \brief Initialise the DevFS and detect double-mounting, or just do nothing
- * \note STUB
- */
-tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options)
-{
- return &gDevFS_RootNode;
-}
-
-/**
- * \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos)
- */
-char *DevFS_ReadDir(tVFS_Node *Node, int Pos)
-{
- tDevFS_Driver *dev;
-
- if(Pos < 0) return NULL;
-
- for(dev = gDevFS_Drivers;
- dev && Pos--;
- dev = dev->Next
- );
-
- if(dev)
- return strdup(dev->Name);
- else
- return NULL;
-}
-
-/**
- * \fn tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name)
- * \brief Get an entry from the devices directory
- */
-tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name)
-{
- tDevFS_Driver *dev;
-
- //ENTER("pNode sName", Node, Name);
-
- for(dev = gDevFS_Drivers;
- dev;
- dev = dev->Next
- )
- {
- //LOG("dev = %p", dev);
- //LOG("dev->Name = '%s'", dev->Name);
- if(strcmp(dev->Name, Name) == 0) {
- //LEAVE('p', &dev->RootNode);
- return &dev->RootNode;
- }
- }
-
- //LEAVE('n');
- return NULL;
-}
-
-// --- EXPORTS ---
-EXPORT(DevFS_AddDevice);
-EXPORT(DevFS_DelDevice);
+++ /dev/null
-/*
- * AcessMicro VFS
- * - Root Filesystem Driver
- */
-#define DEBUG 0
-#include <acess.h>
-#include <vfs.h>
-#include <vfs_ramfs.h>
-
-// === CONSTANTS ===
-#define MAX_FILES 64
-#define MAX_FILE_SIZE 1024
-
-// === PROTOTYPES ===
-tVFS_Node *Root_InitDevice(const char *Device, const char **Options);
- int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
-tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name);
-char *Root_ReadDir(tVFS_Node *Node, int Pos);
-Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
-tRamFS_File *Root_int_AllocFile(void);
-
-// === GLOBALS ===
-tVFS_Driver gRootFS_Info = {
- "rootfs", 0, Root_InitDevice, NULL, NULL
- };
-tRamFS_File RootFS_Files[MAX_FILES];
-tVFS_ACL RootFS_DirACLs[3] = {
- {{0,0}, {0,VFS_PERM_ALL}}, // Owner (Root)
- {{1,0}, {0,VFS_PERM_ALL}}, // Group (Root)
- {{0,-1}, {0,VFS_PERM_ALL^VFS_PERM_WRITE}} // World (Nobody)
-};
-tVFS_ACL RootFS_FileACLs[3] = {
- {{0,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Owner (Root)
- {{1,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Group (Root)
- {{0,-1}, {0,VFS_PERM_READ}} // World (Nobody)
-};
-tVFS_NodeType gRootFS_DirType = {
- .TypeName = "RootFS-Dir",
- .ReadDir = Root_ReadDir,
- .FindDir = Root_FindDir,
- .MkNod = Root_MkNod
-};
-tVFS_NodeType gRootFS_FileType = {
- .TypeName = "RootFS-File",
- .Read = Root_Read,
- .Write = Root_Write,
-};
-
-// === CODE ===
-/**
- * \brief Initialise the root filesystem
- */
-tVFS_Node *Root_InitDevice(const char *Device, const char **Options)
-{
- tRamFS_File *root;
- if(strcmp(Device, "root") != 0) {
- return NULL;
- }
-
- // Create Root Node
- root = &RootFS_Files[0];
-
- root->Node.ImplPtr = root;
-
- root->Node.CTime
- = root->Node.MTime
- = root->Node.ATime = now();
- root->Node.NumACLs = 3;
- root->Node.ACLs = RootFS_DirACLs;
-
- root->Node.Type = &gRootFS_DirType;
-
- return &root->Node;
-}
-
-/**
- * \fn int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
- * \brief Create an entry in the root directory
- */
-int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
-{
- tRamFS_File *parent = Node->ImplPtr;
- tRamFS_File *child;
- tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild;
-
- ENTER("pNode sName xFlags", Node, Name, Flags);
-
- LOG("%i > %i", strlen(Name)+1, sizeof(child->Name));
- if(strlen(Name) + 1 > sizeof(child->Name))
- LEAVE_RET('i', 0);
-
- // Find last child, while we're at it, check for duplication
- for( child = parent->Data.FirstChild; child; prev = child, child = child->Next )
- {
- if(strcmp(child->Name, Name) == 0) {
- LEAVE('i', 0);
- return 0;
- }
- }
-
- child = Root_int_AllocFile();
- memset(child, 0, sizeof(tRamFS_File));
-
- strcpy(child->Name, Name);
-
- child->Parent = parent;
- child->Next = NULL;
- child->Data.FirstChild = NULL;
-
- child->Node.ImplPtr = child;
- child->Node.Flags = Flags;
- child->Node.NumACLs = 3;
- child->Node.Size = 0;
-
- if(Flags & VFS_FFLAG_DIRECTORY)
- {
- child->Node.ACLs = RootFS_DirACLs;
- child->Node.Type = &gRootFS_DirType;
- } else {
- if(Flags & VFS_FFLAG_SYMLINK)
- child->Node.ACLs = RootFS_DirACLs;
- else
- child->Node.ACLs = RootFS_FileACLs;
- child->Node.Type = &gRootFS_FileType;
- }
-
- prev->Next = child;
-
- parent->Node.Size ++;
-
- LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \fn tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name)
- * \brief Find an entry in the filesystem
- */
-tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name)
-{
- tRamFS_File *parent = Node->ImplPtr;
- tRamFS_File *child = parent->Data.FirstChild;
-
- //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name);
-
- for(;child;child = child->Next)
- {
- //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name);
- if(strcmp(child->Name, Name) == 0) return &child->Node;
- }
-
- return NULL;
-}
-
-/**
- * \fn char *Root_ReadDir(tVFS_Node *Node, int Pos)
- * \brief Get an entry from the filesystem
- */
-char *Root_ReadDir(tVFS_Node *Node, int Pos)
-{
- tRamFS_File *parent = Node->ImplPtr;
- tRamFS_File *child = parent->Data.FirstChild;
-
- for( ; child && Pos--; child = child->Next ) ;
-
- if(child) return strdup(child->Name);
-
- return NULL;
-}
-
-/**
- * \fn Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from a file in the root directory
- */
-Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tRamFS_File *file = Node->ImplPtr;
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- if(Offset > Node->Size) {
- LEAVE('i', 0);
- return 0;
- }
- if(Length > Node->Size) Length = Node->Size;
-
- if(Offset+Length > Node->Size)
- Length = Node->Size - Offset;
-
- memcpy(Buffer, file->Data.Bytes+Offset, Length);
- LOG("Buffer = '%.*s'", (int)Length, Buffer);
-
- LEAVE('i', Length);
- return Length;
-}
-
-/**
- * \fn Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to a file in the root directory
- */
-Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tRamFS_File *file = Node->ImplPtr;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- if(Offset > Node->Size) {
- LEAVE('i', -1);
- return -1;
- }
-
- if(Offset + Length > MAX_FILE_SIZE)
- {
- Length = MAX_FILE_SIZE - Offset;
- }
-
- LOG("Buffer = '%.*s'", (int)Length, Buffer);
-
- // Check if buffer needs to be expanded
- if(Offset + Length > Node->Size)
- {
- void *tmp = realloc( file->Data.Bytes, Offset + Length );
- if(tmp == NULL) {
- Warning("Root_Write - Increasing buffer size failed");
- LEAVE('i', -1);
- return -1;
- }
- file->Data.Bytes = tmp;
- Node->Size = Offset + Length;
- LOG("Expanded buffer to %i bytes", (int)Node->Size);
- }
-
- memcpy(file->Data.Bytes+Offset, Buffer, Length);
- LOG("File - '%.*s'", Node->Size, file->Data.Bytes);
-
- LEAVE('i', Length);
- return Length;
-}
-
-/**
- * \fn tRamFS_File *Root_int_AllocFile(void)
- * \brief Allocates a file from the pool
- */
-tRamFS_File *Root_int_AllocFile(void)
-{
- int i;
- for( i = 0; i < MAX_FILES; i ++ )
- {
- if( RootFS_Files[i].Name[0] == '\0' )
- {
- return &RootFS_Files[i];
- }
- }
- return NULL;
-}
+++ /dev/null
-/*
- * Acess2 VFS
- * - AllocHandle, GetHandle
- */
-#define DEBUG 0
-#include <acess.h>
-#include <mm_virt.h>
-#include "vfs.h"
-#include "vfs_int.h"
-#include "vfs_ext.h"
-#include <threads.h> // GetMaxFD
-#include <vfs_threads.h> // Handle maintainance
-
-// === CONSTANTS ===
-#define MAX_KERNEL_FILES 128
-
-// === PROTOTYPES ===
-#if 0
-tVFS_Handle *VFS_GetHandle(int FD);
-#endif
- int VFS_AllocHandle(int FD, tVFS_Node *Node, int Mode);
-
-// === GLOBALS ===
-tVFS_Handle *gaUserHandles = (void*)MM_PPD_HANDLES;
-tVFS_Handle *gaKernelHandles = (void*)MM_KERNEL_VFS;
-
-// === CODE ===
-/**
- * \fn tVFS_Handle *VFS_GetHandle(int FD)
- * \brief Gets a pointer to the handle information structure
- */
-tVFS_Handle *VFS_GetHandle(int FD)
-{
- tVFS_Handle *h;
-
- //Log_Debug("VFS", "VFS_GetHandle: (FD=0x%x)", FD);
-
- if(FD < 0) return NULL;
-
- if(FD & VFS_KERNEL_FLAG) {
- FD &= (VFS_KERNEL_FLAG - 1);
- if(FD >= MAX_KERNEL_FILES) return NULL;
- h = &gaKernelHandles[ FD ];
- } else {
- if(FD >= *Threads_GetMaxFD()) return NULL;
- h = &gaUserHandles[ FD ];
- }
-
- if(h->Node == NULL) return NULL;
- //Log_Debug("VFS", "VFS_GetHandle: RETURN %p", h);
- return h;
-}
-
-int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
-{
- int i;
-
- // Check for a user open
- if(bIsUser)
- {
- int max_handles = *Threads_GetMaxFD();
- // Allocate Buffer
- if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
- {
- Uint addr, size;
- size = max_handles * sizeof(tVFS_Handle);
- for(addr = 0; addr < size; addr += 0x1000)
- {
- if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) )
- {
- Warning("OOM - VFS_AllocHandle");
- Threads_Exit(0, 0xFF); // Terminate user
- }
- }
- memset( gaUserHandles, 0, size );
- }
- // Get a handle
- for( i = 0; i < max_handles; i ++ )
- {
- if(gaUserHandles[i].Node) continue;
- gaUserHandles[i].Node = Node;
- gaUserHandles[i].Position = 0;
- gaUserHandles[i].Mode = Mode;
- return i;
- }
- }
- else
- {
- // Allocate space if not already
- if( MM_GetPhysAddr( (tVAddr)gaKernelHandles ) == 0 )
- {
- Uint addr, size;
- size = MAX_KERNEL_FILES * sizeof(tVFS_Handle);
- for(addr = 0; addr < size; addr += 0x1000)
- {
- if( !MM_Allocate( (tVAddr)gaKernelHandles + addr ) )
- {
- Panic("OOM - VFS_AllocHandle");
- Threads_Exit(0, 0xFF); // Terminate application (get some space back)
- }
- }
- memset( gaKernelHandles, 0, size );
- }
- // Get a handle
- for(i=0;i<MAX_KERNEL_FILES;i++)
- {
- if(gaKernelHandles[i].Node) continue;
- gaKernelHandles[i].Node = Node;
- gaKernelHandles[i].Position = 0;
- gaKernelHandles[i].Mode = Mode;
- return i|VFS_KERNEL_FLAG;
- }
- }
-
- return -1;
-}
-
-void VFS_ReferenceUserHandles(void)
-{
- int i;
- int max_handles = *Threads_GetMaxFD();
-
- // Check if this process has any handles
- if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
- return ;
-
- for( i = 0; i < max_handles; i ++ )
- {
- tVFS_Handle *h;
- h = &gaUserHandles[i];
- if( !h->Node )
- continue ;
- if( h->Node->Type && h->Node->Type->Reference )
- h->Node->Type->Reference( h->Node );
- }
-}
-
-void VFS_CloseAllUserHandles(void)
-{
- int i;
- int max_handles = *Threads_GetMaxFD();
-
- // Check if this process has any handles
- if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
- return ;
-
- for( i = 0; i < max_handles; i ++ )
- {
- tVFS_Handle *h;
- h = &gaUserHandles[i];
- if( !h->Node )
- continue ;
- if( h->Node->Type && h->Node->Type->Close )
- h->Node->Type->Close( h->Node );
- }
-}
-
-/**
- * \brief Take a backup of a set of file descriptors
- */
-void *VFS_SaveHandles(int NumFDs, int *FDs)
-{
- tVFS_Handle *ret;
- int i;
- int max_handles = *Threads_GetMaxFD();
-
- // Check if this process has any handles
- if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
- return NULL;
-
- // Allocate
- ret = malloc( NumFDs * sizeof(tVFS_Handle) );
- if( !ret )
- return NULL;
-
- if( NumFDs > max_handles )
- NumFDs = max_handles;
-
- // Take copies of the handles
- for( i = 0; i < NumFDs; i ++ )
- {
- tVFS_Handle *h;
- if( FDs == NULL )
- h = &gaUserHandles[i];
- else {
- h = VFS_GetHandle(FDs[i] & (VFS_KERNEL_FLAG - 1));
- if(!h) {
- Log_Warning("VFS", "VFS_SaveHandles - Invalid FD %i",
- FDs[i] & (VFS_KERNEL_FLAG - 1) );
- free(ret);
- return NULL;
- }
- }
- memcpy( &ret[i], h, sizeof(tVFS_Handle) );
-
- // Reference node
- if( !h->Node )
- continue ;
- if( h->Node->Type && h->Node->Type->Reference )
- h->Node->Type->Reference( h->Node );
- }
-
- return ret;
-}
-
-void VFS_RestoreHandles(int NumFDs, void *Handles)
-{
- tVFS_Handle *handles = Handles;
- int i;
-
- // NULL = nothing to do
- if( !Handles )
- return ;
-
- // Check if there is already a set of handles
- if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) != 0 )
- return ;
-
-
- // Allocate user handle area
- {
- Uint addr, size;
- int max_handles = *Threads_GetMaxFD();
- size = max_handles * sizeof(tVFS_Handle);
- for(addr = 0; addr < size; addr += 0x1000)
- {
- if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) )
- {
- Warning("OOM - VFS_AllocHandle");
- Threads_Exit(0, 0xFF); // Terminate user
- }
- }
- memset( gaUserHandles, 0, size );
- }
-
- // Restore handles
- memcpy( gaUserHandles, handles, NumFDs * sizeof(tVFS_Handle) );
- // Reference when copied
- for( i = 0; i < NumFDs; i ++ )
- {
- tVFS_Handle *h = &handles[i];
-
- if( !h->Node )
- continue ;
- if( h->Node->Type && h->Node->Type->Reference )
- h->Node->Type->Reference( h->Node );
- }
-}
-
-void VFS_FreeSavedHandles(int NumFDs, void *Handles)
-{
- tVFS_Handle *handles = Handles;
- int i;
-
- // NULL = nothing to do
- if( !Handles )
- return ;
-
- // Dereference all saved nodes
- for( i = 0; i < NumFDs; i ++ )
- {
- tVFS_Handle *h = &handles[i];
-
- if( !h->Node )
- continue ;
- if( h->Node->Type && h->Node->Type->Close )
- h->Node->Type->Close( h->Node );
- }
- free( Handles );
-}
+++ /dev/null
-/*
- * AcessMicro VFS
- * - File IO Passthru's
- */
-#define DEBUG 0
-#include <acess.h>
-#include "vfs.h"
-#include "vfs_int.h"
-
-// === CODE ===
-/**
- * \fn Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer)
- * \brief Read data from a node (file)
- */
-Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer)
-{
- tVFS_Handle *h;
- Uint64 ret;
-
- ENTER("iFD XLength pBuffer", FD, Length, Buffer);
-
- h = VFS_GetHandle(FD);
- if(!h) LEAVE_RET('i', -1);
-
- if( !(h->Mode & VFS_OPENFLAG_READ) || h->Node->Flags & VFS_FFLAG_DIRECTORY )
- LEAVE_RET('i', -1);
-
- if(!h->Node->Type || !h->Node->Type->Read) LEAVE_RET('i', 0);
-
- ret = h->Node->Type->Read(h->Node, h->Position, Length, Buffer);
- if(ret == -1) LEAVE_RET('i', -1);
-
- h->Position += ret;
- LEAVE('X', ret);
- return ret;
-}
-
-/**
- * \fn Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read data from a given offset (atomic)
- */
-Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tVFS_Handle *h;
- Uint64 ret;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- if( !(h->Mode & VFS_OPENFLAG_READ) ) return -1;
- if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1;
-
- if( !h->Node->Type || !h->Node->Type->Read) {
- Warning("VFS_ReadAt - Node %p, does not have a read method", h->Node);
- return 0;
- }
- ret = h->Node->Type->Read(h->Node, Offset, Length, Buffer);
- if(ret == -1) return -1;
- return ret;
-}
-
-/**
- * \fn Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer)
- * \brief Read data from a node (file)
- */
-Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer)
-{
- tVFS_Handle *h;
- Uint64 ret;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1;
- if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1;
-
- if( !h->Node->Type || !h->Node->Type->Write ) return 0;
-
- ret = h->Node->Type->Write(h->Node, h->Position, Length, Buffer);
- if(ret == -1) return -1;
-
- h->Position += ret;
- return ret;
-}
-
-/**
- * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
- * \brief Write data to a file at a given offset
- */
-Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tVFS_Handle *h;
- Uint64 ret;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1;
- if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1;
-
- if(!h->Node->Type || !h->Node->Type->Write) return 0;
- ret = h->Node->Type->Write(h->Node, Offset, Length, Buffer);
-
- if(ret == -1) return -1;
- return ret;
-}
-
-/**
- * \fn Uint64 VFS_Tell(int FD)
- * \brief Returns the current file position
- */
-Uint64 VFS_Tell(int FD)
-{
- tVFS_Handle *h;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- return h->Position;
-}
-
-/**
- * \fn int VFS_Seek(int FD, Sint64 Offset, int Whence)
- * \brief Seek to a new location
- * \param FD File descriptor
- * \param Offset Where to go
- * \param Whence From where
- */
-int VFS_Seek(int FD, Sint64 Offset, int Whence)
-{
- tVFS_Handle *h;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- //Log_Debug("VFS", "VFS_Seek: (fd=0x%x, Offset=0x%llx, Whence=%i)",
- // FD, Offset, Whence);
-
- // Set relative to current position
- if(Whence == 0) {
- h->Position += Offset;
- return 0;
- }
-
- // Set relative to end of file
- if(Whence < 0) {
- if( h->Node->Size == -1 ) return -1;
-
- h->Position = h->Node->Size - Offset;
- return 0;
- }
-
- // Set relative to start of file
- h->Position = Offset;
- return 0;
-}
-
-/**
- * \fn int VFS_IOCtl(int FD, int ID, void *Buffer)
- * \brief Call an IO Control on a file
- */
-int VFS_IOCtl(int FD, int ID, void *Buffer)
-{
- tVFS_Handle *h;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- if(!h->Node->Type || !h->Node->Type->IOCtl) return -1;
- return h->Node->Type->IOCtl(h->Node, ID, Buffer);
-}
-
-/**
- * \fn int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
- * \brief Retrieve file information
- * \return Number of ACLs stored
- */
-int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
-{
- tVFS_Handle *h;
- int max;
-
- h = VFS_GetHandle(FD);
- if(!h) return -1;
-
- if( h->Mount )
- Dest->mount = h->Mount->Identifier;
- else
- Dest->mount = 0;
- Dest->inode = h->Node->Inode;
- Dest->uid = h->Node->UID;
- Dest->gid = h->Node->GID;
- Dest->size = h->Node->Size;
- Dest->atime = h->Node->ATime;
- Dest->ctime = h->Node->MTime;
- Dest->mtime = h->Node->CTime;
- Dest->numacls = h->Node->NumACLs;
-
- Dest->flags = 0;
- if(h->Node->Flags & VFS_FFLAG_DIRECTORY) Dest->flags |= 0x10;
- if(h->Node->Flags & VFS_FFLAG_SYMLINK) Dest->flags |= 0x20;
-
- max = (MaxACLs < h->Node->NumACLs) ? MaxACLs : h->Node->NumACLs;
- memcpy(&Dest->acls, h->Node->ACLs, max*sizeof(tVFS_ACL));
-
- return max;
-}
-
-// === EXPORTS ===
-EXPORT(VFS_Read);
-EXPORT(VFS_Write);
-EXPORT(VFS_ReadAt);
-EXPORT(VFS_WriteAt);
-EXPORT(VFS_IOCtl);
-EXPORT(VFS_Seek);
-EXPORT(VFS_Tell);
+++ /dev/null
-/*
- * Acess 2
- * Virtual File System
- */
-#include <acess.h>
-#include <fs_sysfs.h>
-#include <threads.h>
-#include <vfs.h>
-#include <vfs_int.h>
-#include <vfs_ext.h>
-
-// === IMPORTS ===
-extern tVFS_Driver gRootFS_Info;
-extern tVFS_Driver gDevFS_Info;
-
-// === PROTOTYPES ===
-#if 0
- int VFS_Init(void);
-char *VFS_GetTruePath(const char *Path);
-void VFS_GetMemPath(char *Dest, void *Base, Uint Length);
-tVFS_Driver *VFS_GetFSByName(const char *Name);
- int VFS_AddDriver(tVFS_Driver *Info);
-#endif
-void VFS_UpdateDriverFile(void);
-
-// === EXPORTS ===
-EXPORT(VFS_AddDriver);
-
-// === GLOBALS ===
-tVFS_Node NULLNode = {0};
-tShortSpinlock slDriverListLock;
-tVFS_Driver *gVFS_Drivers = NULL;
-char *gsVFS_DriverFile = NULL;
- int giVFS_DriverFileID = 0;
-
-char *gsVFS_MountFile = NULL;
- int giVFS_MountFileID = 0;
-
-// === CODE ===
-/**
- * \fn int VFS_Init(void)
- * \brief Initialises the VFS for use by the kernel and user
- */
-int VFS_Init(void)
-{
- // Core Drivers
- gVFS_Drivers = &gRootFS_Info;
- gVFS_Drivers->Next = &gDevFS_Info;
- VFS_UpdateDriverFile();
-
- // Register with SysFS
- giVFS_MountFileID = SysFS_RegisterFile("VFS/Mounts", NULL, 0);
- giVFS_DriverFileID = SysFS_RegisterFile("VFS/Drivers", NULL, 0);
-
- if( VFS_Mount("root", "/", "rootfs", "") != 0 ) {
- Log_KernelPanic("VFS", "Unable to mount root (Where the **** is rootfs?)");
- return -1;
- }
- VFS_MkDir("/Devices");
- VFS_MkDir("/Mount");
- VFS_Mount("dev", "/Devices", "devfs", "");
-
- Log_Debug("VFS", "Setting max files");
- *Threads_GetMaxFD() = 32;
- return 0;
-}
-
-/**
- * \fn char *VFS_GetTruePath(const char *Path)
- * \brief Gets the true path (non-symlink) of a file
- */
-char *VFS_GetTruePath(const char *Path)
-{
- tVFS_Node *node;
- char *ret, *tmp;
-
- tmp = VFS_GetAbsPath(Path);
- if(tmp == NULL) return NULL;
- //Log(" VFS_GetTruePath: tmp = '%s'", tmp);
- node = VFS_ParsePath(tmp, &ret, NULL);
- free(tmp);
- //Log(" VFS_GetTruePath: node=%p, ret='%s'", node, ret);
-
- if(!node) return NULL;
- if(node->Type->Close) node->Type->Close(node);
-
- return ret;
-}
-
-/**
- * \fn void VFS_GetMemPath(char *Dest, void *Base, Uint Length)
- * \brief Create a VFS memory pointer path
- */
-void VFS_GetMemPath(char *Dest, void *Base, Uint Length)
-{
- Dest[0] = '$';
- itoa( &Dest[1], (tVAddr)Base, 16, BITS/4, '0' );
- Dest[BITS/4+1] = ':';
- itoa( &Dest[BITS/4+2], Length, 16, BITS/4, '0' );
- Dest[BITS/2+2] = '\0';
-}
-
-/**
- * \fn tVFS_Driver *VFS_GetFSByName(const char *Name)
- * \brief Gets a filesystem structure given a name
- */
-tVFS_Driver *VFS_GetFSByName(const char *Name)
-{
- tVFS_Driver *drv = gVFS_Drivers;
-
- for(;drv;drv=drv->Next)
- {
-// Log("strcmp('%s' (%p), '%s') == 0?", drv->Name, drv->Name, Name);
- if(strcmp(drv->Name, Name) == 0)
- return drv;
- }
- return NULL;
-}
-
-/**
- * \fn int VFS_AddDriver(tVFS_Driver *Info)
- */
-int VFS_AddDriver(tVFS_Driver *Info)
-{
- if(!Info) return -1;
-
- SHORTLOCK( &slDriverListLock );
- Info->Next = gVFS_Drivers;
- gVFS_Drivers = Info;
- SHORTREL( &slDriverListLock );
-
- VFS_UpdateDriverFile();
-
- return 0;
-}
-
-/**
- * \fn void VFS_UpdateDriverFile(void)
- * \brief Updates the driver list file
- */
-void VFS_UpdateDriverFile(void)
-{
- tVFS_Driver *drv;
- int len = 0;
- char *buf;
-
- // Format:
- // <name>\n
- for( drv = gVFS_Drivers; drv; drv = drv->Next )
- {
- len += 1 + strlen(drv->Name);
- }
- buf = malloc(len+1);
- len = 0;
- for( drv = gVFS_Drivers; drv; drv = drv->Next )
- {
- strcpy( &buf[len], drv->Name );
- len += strlen(drv->Name);
- buf[len++] = '\n';
- }
- buf[len] = '\0';
-
- SysFS_UpdateFile( giVFS_DriverFileID, buf, len );
- if(gsVFS_DriverFile) free(gsVFS_DriverFile);
- gsVFS_DriverFile = buf;
-}
+++ /dev/null
-/*
- * Acess 2
- * Virtual File System
- * - Memory Pseudo Files
- */
-#include <acess.h>
-#include <vfs.h>
-
-// === PROTOTYPES ===
-tVFS_Node *VFS_MemFile_Create(const char *Path);
-void VFS_MemFile_Close(tVFS_Node *Node);
-Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
-
-// === GLOBALS ===
-tVFS_NodeType gVFS_MemFileType = {
- .Close = VFS_MemFile_Close,
- .Read = VFS_MemFile_Read,
- .Write = VFS_MemFile_Write
- };
-
-// === CODE ===
-/**
- * \fn tVFS_Node *VFS_MemFile_Create(const char *Path)
- */
-tVFS_Node *VFS_MemFile_Create(const char *Path)
-{
- Uint base, size;
- const char *str = Path;
- tVFS_Node *ret;
-
- str++; // Eat '$'
-
- // Read Base address
- base = 0;
- for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ )
- {
- base *= 16;
- if('A' <= *str && *str <= 'F')
- base += *str - 'A' + 10;
- else
- base += *str - '0';
- }
-
- // Check separator
- if(*str++ != ':') return NULL;
-
- // Read buffer size
- size = 0;
- for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ )
- {
- size *= 16;
- if('A' <= *str && *str <= 'F')
- size += *str - 'A' + 10;
- else
- size += *str - '0';
- }
-
- // Check for NULL byte
- if(*str != '\0') return NULL;
-
- // Allocate and fill node
- ret = malloc(sizeof(tVFS_Node));
- memset(ret, 0, sizeof(tVFS_Node));
-
- // State
- ret->ImplPtr = (void*)base;
- ret->Size = size;
-
- // ACLs
- ret->NumACLs = 1;
- ret->ACLs = &gVFS_ACL_EveryoneRWX;
-
- // Functions
- ret->Type = &gVFS_MemFileType;
-
- return ret;
-}
-
-/**
- * \fn void VFS_MemFile_Close(tVFS_Node *Node)
- * \brief Dereference and clean up a memory file
- */
-void VFS_MemFile_Close(tVFS_Node *Node)
-{
- Node->ReferenceCount --;
- if( Node->ReferenceCount == 0 ) {
- Node->ImplPtr = NULL;
- free(Node);
- }
-}
-
-/**
- * \fn Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from a memory file
- */
-Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- // Check for use of free'd file
- if(Node->ImplPtr == NULL) return 0;
-
- // Check for out of bounds read
- if(Offset > Node->Size) return 0;
-
- // Truncate data read if needed
- if(Offset + Length > Node->Size)
- Length = Node->Size - Offset;
-
- // Copy Data
- memcpy(Buffer, (Uint8*)Node->ImplPtr + Offset, Length);
-
- return Length;
-}
-
-/**
- * \fn Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to a memory file
- */
-Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- // Check for use of free'd file
- if(Node->ImplPtr == NULL) return 0;
-
- // Check for out of bounds read
- if(Offset > Node->Size) return 0;
-
- // Truncate data read if needed
- if(Offset + Length > Node->Size)
- Length = Node->Size - Offset;
-
- // Copy Data
- memcpy((Uint8*)Node->ImplPtr + Offset, Buffer, Length);
-
- return Length;
-}
+++ /dev/null
-/*
- * Acess2 Kernel VFS
- * - By John Hodge (thePowersGang)
- *
- * mmap.c
- * - VFS_MMap support
- */
-#define DEBUG 0
-#include <acess.h>
-#include <vfs.h>
-#include <vfs_ext.h>
-#include <vfs_int.h>
-
-#define MMAP_PAGES_PER_BLOCK 16
-
-// === STRUCTURES ===
-typedef struct sVFS_MMapPageBlock tVFS_MMapPageBlock;
-struct sVFS_MMapPageBlock
-{
- tVFS_MMapPageBlock *Next;
- Uint64 BaseOffset; // Must be a multiple of MMAP_PAGES_PER_BLOCK*PAGE_SIZE
- tPAddr PhysAddrs[MMAP_PAGES_PER_BLOCK];
-};
-
-// === CODE ===
-void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset)
-{
- tVFS_Handle *h;
- tVAddr mapping_dest, mapping_base;
- int npages, pagenum;
- tVFS_MMapPageBlock *pb, *prev;
-
- ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
-
- if( Flags & MMAP_MAP_ANONYMOUS )
- Offset = (tVAddr)DestHint & 0xFFF;
-
- npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE;
- pagenum = Offset / PAGE_SIZE;
-
- mapping_base = (tVAddr)DestHint;
- mapping_dest = mapping_base & ~(PAGE_SIZE-1);
-
- // TODO: Locate space for the allocation
-
- // Handle anonymous mappings
- if( Flags & MMAP_MAP_ANONYMOUS )
- {
- size_t ofs = 0;
- LOG("%i pages anonymous to %p", npages, mapping_dest);
- for( ; npages --; mapping_dest += PAGE_SIZE, ofs += PAGE_SIZE )
- {
- if( MM_GetPhysAddr(mapping_dest) ) {
- // TODO: Set flags to COW if needed (well, if shared)
- MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW);
- LOG("clear from %p, %i bytes", (void*)(mapping_base + ofs),
- PAGE_SIZE - (mapping_base & (PAGE_SIZE-1))
- );
- memset( (void*)(mapping_base + ofs), 0, PAGE_SIZE - (mapping_base & (PAGE_SIZE-1)));
- }
- else {
- LOG("New empty page");
- // TODO: Map a COW zero page instead
- if( !MM_Allocate(mapping_dest) ) {
- // TODO: Error
- Log_Warning("VFS", "VFS_MMap: Anon alloc to %p failed", mapping_dest);
- }
- memset((void*)mapping_dest, 0, PAGE_SIZE);
- LOG("Anon map to %p", mapping_dest);
- }
- }
- LEAVE_RET('p', (void*)mapping_base);
- }
-
- h = VFS_GetHandle(FD);
- if( !h || !h->Node ) LEAVE_RET('n', NULL);
-
- LOG("h = %p", h);
-
- Mutex_Acquire( &h->Node->Lock );
-
- // Search for existing mapping for each page
- // - Sorted list of 16 page blocks
- for(
- pb = h->Node->MMapInfo, prev = NULL;
- pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK < pagenum;
- prev = pb, pb = pb->Next
- );
-
- LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0);
-
- // - Allocate a block if needed
- if( !pb || pb->BaseOffset > pagenum )
- {
- void *old_pb = pb;
- pb = malloc( sizeof(tVFS_MMapPageBlock) );
- if(!pb) {
- Mutex_Release( &h->Node->Lock );
- LEAVE_RET('n', NULL);
- }
- pb->Next = old_pb;
- pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK;
- memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs));
- if(prev)
- prev->Next = pb;
- else
- h->Node->MMapInfo = pb;
- }
-
- // - Map (and allocate) pages
- while( npages -- )
- {
- if( MM_GetPhysAddr(mapping_dest) == 0 )
- {
- if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 )
- {
- tVFS_NodeType *nt = h->Node->Type;
- if( !nt )
- {
- // TODO: error
- }
- else if( nt->MMap )
- nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest);
- else
- {
- int read_len;
- // Allocate pages and read data
- if( MM_Allocate(mapping_dest) == 0 ) {
- // TODO: Unwrap
- Mutex_Release( &h->Node->Lock );
- LEAVE('n');
- return NULL;
- }
- // TODO: Clip read length
- read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest);
-// if( read_len != PAGE_SIZE ) {
-// memset( (void*)(mapping_dest+read_len), 0, PAGE_SIZE-read_len );
-// }
- }
- pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest );
- MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node );
- MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
- LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
- pb->PhysAddrs[pagenum - pb->BaseOffset]);
- }
- else
- {
- MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] );
- MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
- LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
- pb->PhysAddrs[pagenum - pb->BaseOffset]);
- }
- h->Node->ReferenceCount ++;
-
- // Set flags
- if( !(Protection & MMAP_PROT_WRITE) ) {
- MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO);
- }
- else {
- MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
- }
-
- if( Protection & MMAP_PROT_EXEC ) {
- MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC);
- }
- else {
- MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC);
- }
- }
- else
- {
- LOG("Flag update on %p", mapping_dest);
- if( (MM_GetFlags(mapping_dest) & MM_PFLAG_RO) && (Protection & MMAP_PROT_WRITE) )
- {
- MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
- }
- }
- if( Flags & MMAP_MAP_PRIVATE )
- MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW);
- pagenum ++;
- mapping_dest += PAGE_SIZE;
-
- // Roll on to next block if needed
- if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK)
- {
- if( pb->Next && pb->Next->BaseOffset == pagenum )
- pb = pb->Next;
- else
- {
- tVFS_MMapPageBlock *oldpb = pb;
- pb = malloc( sizeof(tVFS_MMapPageBlock) );
- pb->Next = oldpb->Next;
- pb->BaseOffset = pagenum;
- memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs));
- oldpb->Next = pb;
- }
- pagenum = 0;
- }
- }
-
- Mutex_Release( &h->Node->Lock );
-
- LEAVE('p', mapping_base);
- return (void*)mapping_base;
-}
-
-int VFS_MUnmap(void *Addr, size_t Length)
-{
- return 0;
-}
+++ /dev/null
-/*
- * Acess Micro - VFS Server version 1
- */
-#include <acess.h>
-#include <vfs.h>
-#include <vfs_int.h>
-#include <fs_sysfs.h>
-
-// === IMPORTS ===
-extern int giVFS_MountFileID;
-extern char *gsVFS_MountFile;
-
-// === PROTOTYPES ===
-#if 0
- int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
-#endif
-void VFS_UpdateMountFile(void);
-
-// === GLOBALS ===
-tMutex glVFS_MountList;
-tVFS_Mount *gVFS_Mounts;
-tVFS_Mount *gVFS_RootMount = NULL;
-Uint32 giVFS_NextMountIdent = 1;
-
-// === CODE ===
-/**
- * \brief Mount a device
- * \param Device Device string to mount
- * \param MountPoint Destination for the mount
- * \param Filesystem Filesystem to use for the mount
- * \param Options Options to be passed to the filesystem
- * \return -1 on Invalid FS, -2 on No Mem, 0 on success
- *
- * Mounts the filesystem on \a Device at \a MountPoint using the driver
- * \a Filesystem. The options in the string \a Options is passed to the
- * driver's mount.
- */
-int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options)
-{
- tVFS_Mount *mnt;
- tVFS_Driver *fs;
- int deviceLen = strlen(Device);
- int mountLen = strlen(MountPoint);
- int argLen = strlen(Options);
-
- // Get the filesystem
- fs = VFS_GetFSByName(Filesystem);
- if(!fs) {
- Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem);
- return -1;
- }
-
- // Create mount information
- mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 );
- if(!mnt) {
- return -2;
- }
-
- // HACK: Forces VFS_ParsePath to fall back on root
- if(mountLen == 1 && MountPoint[0] == '/')
- mnt->MountPointLen = 0;
- else
- mnt->MountPointLen = mountLen;
-
- // Fill Structure
- mnt->Filesystem = fs;
-
- mnt->Device = &mnt->StrData[0];
- memcpy( mnt->Device, Device, deviceLen+1 );
-
- mnt->MountPoint = &mnt->StrData[deviceLen+1];
- memcpy( mnt->MountPoint, MountPoint, mountLen+1 );
-
- mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1];
- memcpy( mnt->Options, Options, argLen+1 );
-
- // Initialise Volume
- mnt->RootNode = fs->InitDevice(Device, NULL); //&ArgString);
- if(!mnt->RootNode) {
- free(mnt);
- return -2;
- }
-
- mnt->Identifier = giVFS_NextMountIdent++;
- #if 0
- // Ensure identifiers don't repeat
- // - Only a problem if there have been 4 billion mounts
- while( giVFS_NextMountIdent == 0 || VFS_GetMountByIdent(giVFS_NextMountIdent) )
- giVFS_NextMountIdent ++;
- #endif
-
- // Set root
- if(!gVFS_RootMount) gVFS_RootMount = mnt;
-
- // Add to mount list
- Mutex_Acquire( &glVFS_MountList );
- {
- tVFS_Mount *tmp;
- mnt->Next = NULL;
- if(gVFS_Mounts) {
- for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next );
- tmp->Next = mnt;
- }
- else {
- gVFS_Mounts = mnt;
- }
- }
- Mutex_Release( &glVFS_MountList );
-
- Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem);
-
- VFS_UpdateMountFile();
-
- return 0;
-}
-
-/**
- * \brief Gets a mount point given the identifier
- */
-tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID)
-{
- tVFS_Mount *mnt;
- for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
- {
- if(mnt->Identifier == MountID)
- return mnt;
- }
- return NULL;
-}
-
-/**
- * \brief Updates the mount file buffer
- *
- * Updates the ProcFS mounts file buffer to match the current mounts list.
- */
-void VFS_UpdateMountFile(void)
-{
- int len = 0;
- char *buf;
- tVFS_Mount *mnt;
-
- // Format:
- // <device>\t<location>\t<type>\t<options>\n
-
- for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
- {
- len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint)
- + strlen(mnt->Filesystem->Name) + strlen(mnt->Options);
- }
-
- buf = malloc( len + 1 );
- len = 0;
- for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
- {
- strcpy( &buf[len], mnt->Device );
- len += strlen(mnt->Device);
- buf[len++] = '\t';
-
- strcpy( &buf[len], mnt->MountPoint );
- len += strlen(mnt->MountPoint);
- buf[len++] = '\t';
-
- strcpy( &buf[len], mnt->Filesystem->Name );
- len += strlen(mnt->Filesystem->Name);
- buf[len++] = '\t';
-
- strcpy( &buf[len], mnt->Options );
- len += strlen(mnt->Options);
- buf[len++] = '\n';
- }
- buf[len] = 0;
-
- SysFS_UpdateFile( giVFS_MountFileID, buf, len );
- if( gsVFS_MountFile ) free( gsVFS_MountFile );
- gsVFS_MountFile = buf;
-}
+++ /dev/null
-/*
- * AcessMicro VFS
- * - File IO Passthru's
- */
-#include <acess.h>
-#include "vfs.h"
-#include "vfs_int.h"
-
-// === TYPES ===
-typedef struct sCachedInode {
- struct sCachedInode *Next;
- tVFS_Node Node;
-} tCachedInode;
-typedef struct sInodeCache {
- struct sInodeCache *Next;
- int Handle;
- tCachedInode *FirstNode; // Sorted List
- Uint64 MaxCached; // Speeds up Searching
-} tInodeCache;
-
-// === PROTOTYPES ===
-tInodeCache *Inode_int_GetFSCache(int Handle);
-
-// === GLOBALS ===
- int gVFS_NextInodeHandle = 1;
-tShortSpinlock glVFS_InodeCache;
-tInodeCache *gVFS_InodeCache = NULL;
-
-// === CODE ===
-/**
- * \fn int Inode_GetHandle()
- */
-int Inode_GetHandle()
-{
- tInodeCache *ent;
-
- ent = malloc( sizeof(tInodeCache) );
- ent->MaxCached = 0;
- ent->Handle = gVFS_NextInodeHandle++;
- ent->Next = NULL; ent->FirstNode = NULL;
-
- // Add to list
- SHORTLOCK( &glVFS_InodeCache );
- ent->Next = gVFS_InodeCache;
- gVFS_InodeCache = ent;
- SHORTREL( &glVFS_InodeCache );
-
- return gVFS_NextInodeHandle-1;
-}
-
-/**
- * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode)
- * \brief Gets a node from the cache
- */
-tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode)
-{
- tInodeCache *cache;
- tCachedInode *ent;
-
- cache = Inode_int_GetFSCache(Handle);
- if(!cache) return NULL;
-
- if(Inode > cache->MaxCached) return NULL;
-
- // Search Cache
- ent = cache->FirstNode;
- for( ; ent; ent = ent->Next )
- {
- if(ent->Node.Inode < Inode) continue;
- if(ent->Node.Inode > Inode) return NULL;
- ent->Node.ReferenceCount ++;
- return &ent->Node;
- }
-
- return NULL; // Should never be reached
-}
-
-/**
- * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node)
- */
-tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node)
-{
- tInodeCache *cache;
- tCachedInode *newEnt, *ent, *prev;
-
- cache = Inode_int_GetFSCache(Handle);
- if(!cache) return NULL;
-
- if(Node->Inode > cache->MaxCached)
- cache->MaxCached = Node->Inode;
-
- // Search Cache
- ent = cache->FirstNode;
- prev = (tCachedInode*) &cache->FirstNode;
- for( ; ent; prev = ent, ent = ent->Next )
- {
- if(ent->Node.Inode < Node->Inode) continue;
- if(ent->Node.Inode == Node->Inode) {
- ent->Node.ReferenceCount ++;
- return &ent->Node;
- }
- break;
- }
-
- // Create new entity
- newEnt = malloc(sizeof(tCachedInode));
- newEnt->Next = ent;
- memcpy(&newEnt->Node, Node, sizeof(tVFS_Node));
- prev->Next = newEnt;
-
- return &newEnt->Node;
-}
-
-/**
- * \fn void Inode_UncacheNode(int Handle, Uint64 Inode)
- * \brief Dereferences/Removes a cached node
- */
-void Inode_UncacheNode(int Handle, Uint64 Inode)
-{
- tInodeCache *cache;
- tCachedInode *ent, *prev;
-
- cache = Inode_int_GetFSCache(Handle);
- if(!cache) return ;
-
- if(Inode > cache->MaxCached) return ;
-
- // Search Cache
- ent = cache->FirstNode;
- prev = (tCachedInode*) &cache->FirstNode; // Special case removal
- for( ; ent; prev = ent, ent = ent->Next )
- {
- if(ent->Node.Inode < Inode) continue;
- if(ent->Node.Inode > Inode) return;
- ent->Node.ReferenceCount --;
- // Check if node needs to be freed
- if(ent->Node.ReferenceCount == 0)
- {
- prev->Next = ent->Next;
- if(ent->Node.Inode == cache->MaxCached)
- {
- if(ent != cache->FirstNode)
- cache->MaxCached = prev->Node.Inode;
- else
- cache->MaxCached = 0;
- }
-
- free(ent);
- }
- return ;
- }
-
- return ;
-}
-
-/**
- * \fn void Inode_ClearCache(int Handle)
- * \brief Removes a cache
- */
-void Inode_ClearCache(int Handle)
-{
- tInodeCache *cache;
- tInodeCache *prev = NULL;
- tCachedInode *ent, *next;
-
- // Find the cache
- for(
- cache = gVFS_InodeCache;
- cache && cache->Handle < Handle;
- prev = cache, cache = cache->Next
- );
- if(!cache || cache->Handle != Handle) return;
-
- // Search Cache
- ent = cache->FirstNode;
- while( ent )
- {
- ent->Node.ReferenceCount = 1;
- next = ent->Next;
-
- if(ent->Node.Type && ent->Node.Type->Close)
- ent->Node.Type->Close( &ent->Node );
- free(ent);
-
- ent = next;
- }
-
- // Free Cache
- if(prev == NULL)
- gVFS_InodeCache = cache->Next;
- else
- prev->Next = cache->Next;
- free(cache);
-}
-
-/**
- * \fn tInodeCache *Inode_int_GetFSCache(int Handle)
- * \brief Gets a cache given it's handle
- */
-tInodeCache *Inode_int_GetFSCache(int Handle)
-{
- tInodeCache *cache = gVFS_InodeCache;
- // Find Cache
- for( ; cache; cache = cache->Next )
- {
- if(cache->Handle > Handle) continue;
- if(cache->Handle < Handle) {
- Warning("Inode_int_GetFSCache - Handle %i not in cache\n", Handle);
- return NULL;
- }
- break;
- }
- if(!cache) {
- Warning("Inode_int_GetFSCache - Handle %i not in cache [NULL]\n", Handle);
- return NULL;
- }
-
- return cache;
-}
+++ /dev/null
-/*
- * Acess2 VFS
- * - Open, Close and ChDir
- */
-#define DEBUG 0
-#include <acess.h>
-#include "vfs.h"
-#include "vfs_int.h"
-#include "vfs_ext.h"
-#include <threads.h>
-
-// === CONSTANTS ===
-#define OPEN_MOUNT_ROOT 1
-#define MAX_PATH_SLASHES 256
-#define MAX_NESTED_LINKS 4
-#define MAX_PATH_LEN 255
-
-// === IMPORTS ===
-extern tVFS_Mount *gVFS_RootMount;
-extern int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode);
-extern tVFS_Node *VFS_MemFile_Create(const char *Path);
-
-// === PROTOTYPES ===
- int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode );
-
-// === CODE ===
-/**
- * \fn char *VFS_GetAbsPath(const char *Path)
- * \brief Create an absolute path from a relative one
- */
-char *VFS_GetAbsPath(const char *Path)
-{
- char *ret;
- int pathLen = strlen(Path);
- char *pathComps[MAX_PATH_SLASHES];
- char *tmpStr;
- int iPos = 0;
- int iPos2 = 0;
- const char *chroot = *Threads_GetChroot();
- int chrootLen;
- const char *cwd = *Threads_GetCWD();
- int cwdLen;
-
- ENTER("sPath", Path);
-
- // Memory File
- if(Path[0] == '$') {
- ret = malloc(strlen(Path)+1);
- if(!ret) {
- Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
- return NULL;
- }
- strcpy(ret, Path);
- LEAVE('p', ret);
- return ret;
- }
-
- // - Fetch ChRoot
- if( chroot == NULL )
- chroot = "";
- chrootLen = strlen(chroot);
-
- // Check if the path is already absolute
- if(Path[0] == '/') {
- ret = malloc(chrootLen + pathLen + 1);
- if(!ret) {
- Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
- return NULL;
- }
- strcpy(ret + chrootLen, Path);
- }
- else {
- if(cwd == NULL) {
- cwd = "/";
- cwdLen = 1;
- }
- else {
- cwdLen = strlen(cwd);
- }
- // Prepend the current directory
- ret = malloc(chrootLen + cwdLen + 1 + pathLen + 1 );
- strcpy(ret+chrootLen, cwd);
- ret[cwdLen] = '/';
- strcpy(ret+chrootLen+cwdLen+1, Path);
- //Log("ret = '%s'", ret);
- }
-
- // Parse Path
- pathComps[iPos++] = tmpStr = ret+chrootLen+1;
- while(*tmpStr)
- {
- if(*tmpStr++ == '/')
- {
- pathComps[iPos++] = tmpStr;
- if(iPos == MAX_PATH_SLASHES) {
- LOG("Path '%s' has too many elements", Path);
- free(ret);
- LEAVE('n');
- return NULL;
- }
- }
- }
- pathComps[iPos] = NULL;
-
- // Cleanup
- iPos2 = iPos = 0;
- while(pathComps[iPos])
- {
- tmpStr = pathComps[iPos];
- // Always Increment iPos
- iPos++;
- // ..
- if(tmpStr[0] == '.' && tmpStr[1] == '.' && (tmpStr[2] == '/' || tmpStr[2] == '\0') )
- {
- if(iPos2 != 0)
- iPos2 --;
- continue;
- }
- // .
- if(tmpStr[0] == '.' && (tmpStr[1] == '/' || tmpStr[1] == '\0') )
- {
- continue;
- }
- // Empty
- if(tmpStr[0] == '/' || tmpStr[0] == '\0')
- {
- continue;
- }
-
- // Set New Position
- pathComps[iPos2] = tmpStr;
- iPos2++;
- }
- pathComps[iPos2] = NULL;
-
- // Build New Path
- iPos2 = chrootLen + 1; iPos = 0;
- ret[0] = '/';
- while(pathComps[iPos])
- {
- tmpStr = pathComps[iPos];
- while(*tmpStr && *tmpStr != '/')
- {
- ret[iPos2++] = *tmpStr;
- tmpStr++;
- }
- ret[iPos2++] = '/';
- iPos++;
- }
- if(iPos2 > 1)
- ret[iPos2-1] = 0;
- else
- ret[iPos2] = 0;
-
- // Prepend the chroot
- if(chrootLen)
- memcpy( ret, chroot, chrootLen );
-
- LEAVE('s', ret);
-// Log_Debug("VFS", "VFS_GetAbsPath: RETURN '%s'", ret);
- return ret;
-}
-
-/**
- * \fn char *VFS_ParsePath(const char *Path, char **TruePath)
- * \brief Parses a path, resolving sysmlinks and applying permissions
- */
-tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint)
-{
- tVFS_Mount *mnt, *longestMount;
- int cmp, retLength = 0;
- int ofs, nextSlash;
- int iNestedLinks = 0;
- tVFS_Node *curNode, *tmpNode;
- char *tmp;
- char path_buffer[MAX_PATH_LEN+1];
-
- ENTER("sPath pTruePath", Path, TruePath);
-
- // HACK: Memory File
- if(Threads_GetUID() == 0 && Path[0] == '$') {
- if(TruePath) {
- *TruePath = malloc(strlen(Path)+1);
- strcpy(*TruePath, Path);
- }
- curNode = VFS_MemFile_Create(Path);
- if(MountPoint) {
- *MountPoint = NULL;
- }
- LEAVE('p', curNode);
- return curNode;
- }
-
-restart_parse:
- // For root we always fast return
- if(Path[0] == '/' && Path[1] == '\0') {
- if(TruePath) {
- *TruePath = malloc( gVFS_RootMount->MountPointLen+1 );
- strcpy(*TruePath, gVFS_RootMount->MountPoint);
- }
- if(MountPoint) *MountPoint = gVFS_RootMount;
- LEAVE('p', gVFS_RootMount->RootNode);
- return gVFS_RootMount->RootNode;
- }
-
- // Check if there is anything mounted
- if(!gVFS_Mounts) {
- Log_Error("VFS", "VFS_ParsePath - No filesystems mounted");
- return NULL;
- }
-
- // Find Mountpoint
- longestMount = gVFS_RootMount;
- for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
- {
- // Quick Check
- if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0')
- continue;
- // Length Check - If the length is smaller than the longest match sofar
- if(mnt->MountPointLen < longestMount->MountPointLen) continue;
- // String Compare
- cmp = strcmp(Path, mnt->MountPoint);
-
- #if OPEN_MOUNT_ROOT
- // Fast Break - Request Mount Root
- if(cmp == 0) {
- if(TruePath) {
- *TruePath = malloc( mnt->MountPointLen+1 );
- strcpy(*TruePath, mnt->MountPoint);
- }
- if(MountPoint)
- *MountPoint = mnt;
- LEAVE('p', mnt->RootNode);
- return mnt->RootNode;
- }
- #endif
- // Not a match, continue
- if(cmp != '/') continue;
- longestMount = mnt;
- }
-
- // Save to shorter variable
- mnt = longestMount;
-
- LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint);
-
- // Initialise String
- if(TruePath)
- {
- // Assumes that the resultant path (here) will not be > strlen(Path) + 1
- *TruePath = malloc( strlen(Path) + 1 );
- strcpy(*TruePath, mnt->MountPoint);
- retLength = mnt->MountPointLen;
- }
-
- curNode = mnt->RootNode;
- curNode->ReferenceCount ++;
- // Parse Path
- ofs = mnt->MountPointLen+1;
- for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; ofs += nextSlash + 1)
- {
- char pathEle[nextSlash+1];
-
- // Empty String
- if(nextSlash == 0) continue;
-
- memcpy(pathEle, &Path[ofs], nextSlash);
- pathEle[nextSlash] = 0;
-
- // Check permissions on root of filesystem
- if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) {
- //Log("Permissions fail on '%s'", Path);
- goto _error;
- }
-
- // Check if the node has a FindDir method
- if( !curNode->Type->FindDir )
- {
- //Log("FindDir fail on '%s'", Path);
- goto _error;
- }
- LOG("FindDir{=%p}(%p, '%s')", curNode->Type->FindDir, curNode, pathEle);
- // Get Child Node
- tmpNode = curNode->Type->FindDir(curNode, pathEle);
- LOG("tmpNode = %p", tmpNode);
- _CloseNode( curNode );
- curNode = tmpNode;
-
- // Error Check
- if(!curNode) {
- LOG("Node '%s' not found in dir '%s'", pathEle, Path);
- goto _error;
- }
-
- // Handle Symbolic Links
- if(curNode->Flags & VFS_FFLAG_SYMLINK) {
- if(TruePath) {
- free(*TruePath);
- *TruePath = NULL;
- }
- if(!curNode->Type || !curNode->Type->Read) {
- Log_Warning("VFS", "VFS_ParsePath - Read of symlink node %p'%s' is NULL",
- curNode, Path);
- goto _error;
- }
-
- if(iNestedLinks > MAX_NESTED_LINKS) {
- Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded");
- goto _error;
- }
-
- // Parse Symlink Path
- // - Just update the path variable and restart the function
- // > Count nested symlinks and limit to some value (counteracts loops)
- {
- int remlen = strlen(Path) - (ofs + nextSlash);
- if( curNode->Size + remlen > MAX_PATH_LEN ) {
- Log_Warning("VFS", "VFS_ParsePath - Symlinked path too long");
- goto _error;
- }
- curNode->Type->Read( curNode, 0, curNode->Size, path_buffer );
- path_buffer[ curNode->Size ] = '\0';
- LOG("path_buffer = '%s'", path_buffer);
- strcat(path_buffer, Path + ofs+nextSlash);
-
- Path = path_buffer;
-// Log_Debug("VFS", "VFS_ParsePath: Symlink translated to '%s'", Path);
- iNestedLinks ++;
- }
-
- // EVIL: Goto :)
- LOG("Symlink -> '%s', restart", Path);
- goto restart_parse;
- }
-
- // Handle Non-Directories
- if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
- {
- Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory");
- goto _error;
- }
-
- // Check if path needs extending
- if(!TruePath) continue;
-
- // Increase buffer space
- tmp = realloc( *TruePath, retLength + strlen(pathEle) + 1 + 1 );
- // Check if allocation succeeded
- if(!tmp) {
- Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
- goto _error;
- }
- *TruePath = tmp;
- // Append to path
- (*TruePath)[retLength] = '/';
- strcpy(*TruePath+retLength+1, pathEle);
-
- LOG("*TruePath = '%s'", *TruePath);
-
- // - Extend Path
- retLength += nextSlash + 1;
- }
-
- // Check final finddir call
- if( !curNode->Type || !curNode->Type->FindDir ) {
- Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path);
- goto _error;
- }
-
- // Get last node
- LOG("FindDir(%p, '%s')", curNode, &Path[ofs]);
- tmpNode = curNode->Type->FindDir(curNode, &Path[ofs]);
- LOG("tmpNode = %p", tmpNode);
- // Check if file was found
- if(!tmpNode) {
- LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
- goto _error;
- }
- _CloseNode( curNode );
-
- if(TruePath)
- {
- // Increase buffer space
- tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1);
- // Check if allocation succeeded
- if(!tmp) {
- Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
- goto _error;
- }
- *TruePath = tmp;
- // Append to path
- (*TruePath)[retLength] = '/';
- strcpy(*TruePath + retLength + 1, &Path[ofs]);
- // - Extend Path
- //retLength += strlen(tmpNode->Name) + 1;
- }
-
- if( MountPoint ) {
- *MountPoint = mnt;
- }
-
- LEAVE('p', tmpNode);
- return tmpNode;
-
-_error:
- _CloseNode( curNode );
-
- if(TruePath && *TruePath) {
- free(*TruePath);
- *TruePath = NULL;
- }
- LEAVE('n');
- return NULL;
-}
-
-/**
- * \brief Create and return a handle number for the given node and mode
- */
-int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode )
-{
- int i;
-
- ENTER("pNode pMount xMode", Node, Mount, Mode);
-
- i = 0;
- i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
- i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
- i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
-
- LOG("i = 0b%b", i);
-
- // Permissions Check
- if( !VFS_CheckACL(Node, i) ) {
- _CloseNode( Node );
- Log_Log("VFS", "VFS_int_CreateHandle: Permissions Failed");
- errno = EACCES;
- LEAVE_RET('i', -1);
- }
-
- i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), Node, Mode );
- if( i < 0 ) {
- Log_Notice("VFS", "VFS_int_CreateHandle: Out of handles");
- errno = ENFILE;
- LEAVE_RET('i', -1);
- }
-
- VFS_GetHandle(i)->Mount = Mount;
-
- LEAVE_RET('x', i);
-}
-
-/**
- * \fn int VFS_Open(const char *Path, Uint Mode)
- * \brief Open a file
- */
-int VFS_Open(const char *Path, Uint Flags)
-{
- return VFS_OpenEx(Path, Flags, 0);
-}
-
-int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode)
-{
- tVFS_Node *node;
- tVFS_Mount *mnt;
- char *absPath;
-
- ENTER("sPath xFlags oMode", Path, Flags);
-
- // Get absolute path
- absPath = VFS_GetAbsPath(Path);
- if(absPath == NULL) {
- Log_Warning("VFS", "VFS_Open: Path expansion failed '%s'", Path);
- LEAVE_RET('i', -1);
- }
- LOG("absPath = \"%s\"", absPath);
-
- // Parse path and get mount point
- node = VFS_ParsePath(absPath, NULL, &mnt);
-
- // Create file if requested and it doesn't exist
- if( !node && (Flags & VFS_OPENFLAG_CREATE) )
- {
- // TODO: Translate `Mode` into ACL and node flags
- // Get parent, create node
- VFS_MkNod(absPath, 0);
- node = VFS_ParsePath(absPath, NULL, &mnt);
- }
-
- // Free generated path
- free(absPath);
-
- // Check for error
- if(!node)
- {
- LOG("Cannot find node");
- errno = ENOENT;
- LEAVE_RET('i', -1);
- }
-
- // Check for symlinks
- if( !(Flags & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) )
- {
- char tmppath[node->Size+1];
- if( node->Size > MAX_PATH_LEN ) {
- Log_Warning("VFS", "VFS_Open - Symlink is too long (%i)", node->Size);
- LEAVE_RET('i', -1);
- }
- if( !node->Type || !node->Type->Read ) {
- Log_Warning("VFS", "VFS_Open - No read method on symlink");
- LEAVE_RET('i', -1);
- }
- // Read symlink's path
- node->Type->Read( node, 0, node->Size, tmppath );
- tmppath[ node->Size ] = '\0';
- _CloseNode( node );
- // Open the target
- node = VFS_ParsePath(tmppath, NULL, &mnt);
- if(!node) {
- LOG("Cannot find symlink target node (%s)", tmppath);
- errno = ENOENT;
- LEAVE_RET('i', -1);
- }
- }
-
- LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Flags));
-}
-
-
-/**
- * \brief Open a file from an open directory
- */
-int VFS_OpenChild(int FD, const char *Name, Uint Mode)
-{
- tVFS_Handle *h;
- tVFS_Node *node;
-
- ENTER("xFD sName xMode", FD, Name, Mode);
-
- // Get handle
- h = VFS_GetHandle(FD);
- if(h == NULL) {
- Log_Warning("VFS", "VFS_OpenChild - Invalid file handle 0x%x", FD);
- errno = EINVAL;
- LEAVE_RET('i', -1);
- }
-
- // Check for directory
- if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
- Log_Warning("VFS", "VFS_OpenChild - Passed handle is not a directory");
- errno = ENOTDIR;
- LEAVE_RET('i', -1);
- }
-
- // Sanity check
- if( !h->Node->Type || !h->Node->Type->FindDir ) {
- Log_Error("VFS", "VFS_OpenChild - Node does not have a type/is missing FindDir");
- errno = ENOTDIR;
- LEAVE_RET('i', -1);
- }
-
- // Find Child
- node = h->Node->Type->FindDir(h->Node, Name);
- if(!node) {
- errno = ENOENT;
- LEAVE_RET('i', -1);
- }
-
- LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode));
-}
-
-int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode)
-{
- tVFS_Mount *mnt;
- tVFS_Node *node;
-
- ENTER("iMount XInode xMode", Mount, Inode, Mode);
-
- // Get mount point
- mnt = VFS_GetMountByIdent(Mount);
- if( !mnt ) {
- LOG("Mount point ident invalid");
- errno = ENOENT;
- LEAVE_RET('i', -1);
- }
-
- // Does the filesystem support this?
- if( !mnt->Filesystem->GetNodeFromINode ) {
- LOG("Filesystem does not support inode accesses");
- errno = ENOENT;
- LEAVE_RET('i', -1);
- }
-
- // Get node
- node = mnt->Filesystem->GetNodeFromINode(mnt->RootNode, Inode);
- if( !node ) {
- LOG("Unable to find inode");
- errno = ENOENT;
- LEAVE_RET('i', -1);
- }
-
- LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Mode));
-}
-
-/**
- * \fn void VFS_Close(int FD)
- * \brief Closes an open file handle
- */
-void VFS_Close(int FD)
-{
- tVFS_Handle *h;
-
- // Get handle
- h = VFS_GetHandle(FD);
- if(h == NULL) {
- Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD);
- return;
- }
-
- #if VALIDATE_VFS_FUNCTIPONS
- if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) {
- Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)",
- h->Node, h->Node->Close);
- return ;
- }
- #endif
-
- _CloseNode(h->Node);
-
- h->Node = NULL;
-}
-
-/**
- * \brief Change current working directory
- */
-int VFS_ChDir(const char *Dest)
-{
- char *buf;
- int fd;
- tVFS_Handle *h;
-
- // Create Absolute
- buf = VFS_GetAbsPath(Dest);
- if(buf == NULL) {
- Log_Notice("VFS", "VFS_ChDir: Path expansion failed");
- return -1;
- }
-
- // Check if path exists
- fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
- if(fd == -1) {
- Log_Notice("VFS", "VFS_ChDir: Path is invalid");
- return -1;
- }
-
- // Get node so we can check for directory
- h = VFS_GetHandle(fd);
- if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
- Log("VFS_ChDir: Path is not a directory");
- VFS_Close(fd);
- return -1;
- }
-
- // Close file
- VFS_Close(fd);
-
- {
- char **cwdptr = Threads_GetCWD();
- // Free old working directory
- if( *cwdptr ) free( *cwdptr );
- // Set new
- *cwdptr = buf;
- }
-
- Log("Updated CWD to '%s'", buf);
-
- return 1;
-}
-
-/**
- * \fn int VFS_ChRoot(char *New)
- * \brief Change current root directory
- */
-int VFS_ChRoot(const char *New)
-{
- char *buf;
- int fd;
- tVFS_Handle *h;
-
- if(New[0] == '/' && New[1] == '\0')
- return 1; // What a useless thing to ask!
-
- // Create Absolute
- buf = VFS_GetAbsPath(New);
- if(buf == NULL) {
- LOG("Path expansion failed");
- return -1;
- }
-
- // Check if path exists
- fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
- if(fd == -1) {
- LOG("Path is invalid");
- return -1;
- }
-
- // Get node so we can check for directory
- h = VFS_GetHandle(fd);
- if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
- LOG("Path is not a directory");
- VFS_Close(fd);
- return -1;
- }
-
- // Close file
- VFS_Close(fd);
-
- // Update
- {
- char **chroot_ptr = Threads_GetChroot();
- if( *chroot_ptr ) free( *chroot_ptr );
- *chroot_ptr = buf;
- }
-
- LOG("Updated Root to '%s'", buf);
-
- return 1;
-}
-
-// === EXPORTS ===
-EXPORT(VFS_Open);
-EXPORT(VFS_Close);
+++ /dev/null
-/*
- * Acess2 VFS
- * - By thePowersGang (John Hodge)
- *
- * select.c
- * - Implements the select() system call (and supporting code)
- *
- * TODO: Implment timeouts (via an alarm event?)
- * TODO: Remove malloc for read/write queues
- */
-#define DEBUG 0
-#include <acess.h>
-#include "vfs.h"
-#include "vfs_int.h"
-#include "vfs_ext.h"
-#include <semaphore.h>
-#include <threads.h>
-#include <events.h>
-
-// === CONSTANTS ===
-#define NUM_THREADS_PER_ALLOC 4
-
-// === IMPORTS ===
-extern tThread *Proc_GetCurThread(void);
-
-// === TYPES ===
-typedef struct sVFS_SelectListEnt tVFS_SelectListEnt;
-
-// === STRUCTURES ===
-struct sVFS_SelectListEnt
-{
- tVFS_SelectListEnt *Next;
- tThread *Threads[NUM_THREADS_PER_ALLOC];
-};
-
-// NOTE: Typedef is in vfs.h
-struct sVFS_SelectList
-{
- tMutex Lock;
- tVFS_SelectListEnt FirstEnt;
-};
-
-// === PROTOTYPES ===
-// int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout);
-// int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel);
-// int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
-// int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
-// int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
- int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
- int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
- int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
- int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed);
-void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread);
-void VFS_int_Select_SignalAll(tVFS_SelectList *List);
-
-// === GLOBALS ===
-
-// === FUNCTIONS ===
-int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
-{
- tThread *thisthread = Proc_GetCurThread();
- int ret, type;
-
- ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
-
- // Initialise
- for( type = 0; type < 3; type ++ )
- {
- tVFS_SelectList **list;
- int *flag, wanted, maxAllowed;
- if( !(TypeFlags & (1 << type)) ) continue;
- if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) {
- LEAVE('i', -1);
- return -1;
- }
-
- // Alloc if needed
- if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList));
-
- VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
- if( *flag == wanted )
- {
- VFS_int_Select_RemThread(*list, thisthread);
- LEAVE('i', 1);
- return 1;
- }
- }
-
- // - Fast return for polling
- if( Timeout && *Timeout == 0 ) return 0;
-
- // Wait for things
- if( !Timeout || *Timeout > 0 )
- {
- LOG("Semaphore_Wait()");
- // TODO: Actual timeout
- Threads_WaitEvents( THREAD_EVENT_VFS );
- }
-
- // Get return value
- ret = 0;
- for( type = 0; type < 3; type ++ )
- {
- tVFS_SelectList **list;
- int *flag, wanted, maxAllowed;
- if( !(TypeFlags & (1 << type)) ) continue;
- VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
- LOG("VFS_int_Select_RemThread()");
- VFS_int_Select_RemThread(*list, thisthread);
- ret = ret || *flag == wanted;
- }
-
- LEAVE('i', ret);
- return ret;
-}
-
-int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
-{
- tThread *thisthread = Proc_GetCurThread();
- int ret;
-
- ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
- MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
-
- // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
- // if we are going to be woken up (either by an event at a later time,
- // or by an event that happened while or before we were registering).
- // Hence, register must happen _before_ we check the state flag
- // (See VFS_int_Select_Register), that way either we pick up the flag,
- // or the semaphore is incremeneted (or both, but never none)
-
- // Register with nodes
- ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
- ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
- ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
-
- LOG("Register ret = %i", ret);
-
- // If there were events waiting, de-register and return
- if( ret > 0 )
- {
- ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
- ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
- ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
- LEAVE('i', ret);
- return ret;
- }
-
- // TODO: Implement timeout
- LOG("Timeout = %p", Timeout);
-
- // Wait (only if there is no timeout, or it is greater than zero
- if( !Timeout || *Timeout > 0 )
- {
- // TODO: Timeout
- // TODO: Allow extra events to be waited upon
- Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents );
- }
-
- // Fill output (modify *Handles)
- // - Also, de-register
- ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
- ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
- ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
- LEAVE('i', ret);
- return ret;
-}
-
-// Mark a node as having data ready for reading
-int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
-{
- ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
- Node->DataAvaliable = !!IsDataAvaliable;
- if( Node->DataAvaliable )
- VFS_int_Select_SignalAll(Node->ReadThreads);
- LEAVE('i', 0);
- return 0;
-}
-
-// Mark a node as having a full buffer
-int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
-{
- ENTER("pNode bIsBufferFull", Node, IsBufferFull);
- Node->BufferFull = !!IsBufferFull;
- if( !Node->BufferFull )
- VFS_int_Select_SignalAll(Node->WriteThreads);
- LEAVE('i', 0);
- return 0;
-}
-
-// Mark a node as errored
-int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
-{
- ENTER("pNode bIsErrorState", Node, IsErrorState);
- Node->ErrorOccurred = !!IsErrorState;
- if( Node->ErrorOccurred )
- VFS_int_Select_SignalAll(Node->ErrorThreads);
- LEAVE('i', 0);
- return 0;
-}
-
-// --- Internal ---
-int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
-{
- // Get the type of the listen
- switch(Type)
- {
- case 0: // Read
- if(List) *List = &Node->ReadThreads;
- if(Flag) *Flag = &Node->DataAvaliable;
- if(WantedFlag) *WantedFlag = 1;
- if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
- break;
- case 1: // Write
- if(List) *List = &Node->WriteThreads;
- if(Flag) *Flag = &Node->BufferFull;
- if(WantedFlag) *WantedFlag = 0;
- if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
- break;
- case 2: // Error
- if(List) *List = &Node->ErrorThreads;
- if(Flag) *Flag = &Node->ErrorOccurred;
- if(WantedFlag) *WantedFlag = 1;
- if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
- break;
- default:
- Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
- return 1;
- }
- return 0;
-}
-
-/**
- * \return Number of files with an action
- */
-int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
-{
- int i, numFlagged = 0;
- tVFS_SelectList **list;
- int *flag, wantedFlagValue;
- int maxAllowed;
-
- if( !Handles ) return 0;
-
- ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
-
- for( i = 0; i < MaxHandle; i ++ )
- {
- tVFS_Handle *handle;
-
- // Is the descriptor set
- if( !FD_ISSET(i, Handles) ) continue;
- LOG("FD #%i", i);
-
- handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
- // Is the handle valid?
- if( !handle || !handle->Node )
- {
- if( Type == 2 ) { // Bad FD counts as an error
- numFlagged ++;
- }
- else {
- FD_CLR(i, Handles);
- }
- continue;
- }
-
- // Get the type of the listen
- if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
- LEAVE('i', 0);
- return 0;
- }
-
- // Alloc if needed
- if( !*list ) {
- *list = calloc(1, sizeof(tVFS_SelectList));
- }
-
- // Register
- if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
- {
- // Oops, error (or just no space)
- FD_CLR(i, Handles);
- }
-
- // Check for the flag
- if( !!*flag == !!wantedFlagValue )
- numFlagged ++;
- }
-
- LEAVE('i', numFlagged);
-
- return numFlagged;
-}
-/**
- * \return Number of files with an action
- */
-int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
-{
- int i, numFlagged = 0;
- tVFS_SelectList **list;
- int *flag, wantedFlagValue;
-
- if( !Handles ) return 0;
-
- ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
-
- for( i = 0; i < MaxHandle; i ++ )
- {
- tVFS_Handle *handle;
-
- // Is the descriptor set
- if( !FD_ISSET(i, Handles) ) continue;
- LOG("FD #%i", i);
-
- handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
- // Is the handle valid?
- if( !handle || !handle->Node )
- {
- if( Type == 2 ) { // Bad FD counts as an error
- numFlagged ++;
- }
- else {
- FD_CLR(i, Handles);
- }
- continue;
- }
-
- // Get the type of the listen
-
- // Get the type of the listen
- if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
- LEAVE('i', 0);
- return 0;
- }
-
- // Remove
- VFS_int_Select_RemThread(*list, Thread );
-
- // Check for the flag
- if( !!*flag == !!wantedFlagValue ) {
- numFlagged ++;
- }
- else {
- FD_CLR(i, Handles);
- }
- }
-
- LEAVE('i', numFlagged);
-
- return numFlagged;
-}
-
-/**
- * \return Boolean failure
- */
-int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
-{
- int i, count = 0;
- tVFS_SelectListEnt *block, *prev;
-
- ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
-
- // Lock to avoid concurrency issues
- Mutex_Acquire(&List->Lock);
-
- block = &List->FirstEnt;
-
- // Look for free space
- do
- {
- for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
- {
- if( block->Threads[i] == NULL )
- {
- block->Threads[i] = Thread;
- Mutex_Release(&List->Lock);
- LEAVE('i', 0);
- return 0;
- }
- count ++;
- if( MaxAllowed && count >= MaxAllowed ) {
- LEAVE('i', 1);
- return 1;
- }
- }
-
- prev = block;
- block = block->Next;
- } while(block);
-
- LOG("New block");
-
- // Create new block
- block = malloc( sizeof(tVFS_SelectListEnt) );
- if( !block ) {
- Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
- Mutex_Release(&List->Lock);
- return -1;
- }
- block->Next = NULL;
- block->Threads[0] = Thread;
- for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
- {
- block->Threads[i] = NULL;
- }
-
- // Add to list
- prev->Next = block;
-
- // Release
- Mutex_Release(&List->Lock);
-
- LEAVE('i', 0);
- return 0;
-}
-
-void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
-{
- int i;
- tVFS_SelectListEnt *block, *prev = NULL;
-
- ENTER("pList pThread", List, Thread);
-
- // Lock to avoid concurrency issues
- Mutex_Acquire(&List->Lock);
-
- block = &List->FirstEnt;
-
- // Look for the thread
- do
- {
- for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
- {
- if( block->Threads[i] == Thread )
- {
- block->Threads[i] = NULL;
-
- // Check if this block is empty
- if( block != &List->FirstEnt )
- {
- for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
- if( block->Threads[i] )
- break;
- // If empty, free it
- if( i == NUM_THREADS_PER_ALLOC ) {
- LOG("Deleting block");
- prev->Next = block->Next;
- free(block);
- }
- //TODO: If not empty, check if it can be merged downwards
- }
-
- Mutex_Release(&List->Lock);
- LEAVE('-');
- return ;
- }
- }
-
- prev = block;
- block = block->Next;
- } while(block);
-
- // Not on list, is this an error?
-
- Mutex_Release(&List->Lock);
-
- LOG("Not on list");
- LEAVE('-');
-}
-
-/**
- * \brief Signal all threads on a list
- */
-void VFS_int_Select_SignalAll(tVFS_SelectList *List)
-{
- int i;
- tVFS_SelectListEnt *block;
-
- if( !List ) return ;
-
- ENTER("pList", List);
-
- // Lock to avoid concurrency issues
- Mutex_Acquire(&List->Lock);
-
- block = &List->FirstEnt;
-
- // Look for the thread
- do
- {
- for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
- {
- if( block->Threads[i] )
- {
- LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
- Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
- }
- }
-
- block = block->Next;
- } while(block);
-
- Mutex_Release(&List->Lock);
-
- LEAVE('-');
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge (thePowersGang)
- *
- * workqueue.c
- * - Worker FIFO Queue (Single Consumer, Interrupt Producer)
- */
-#include <acess.h>
-#include <workqueue.h>
-#include <threads_int.h>
-
-// === CODE ===
-void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset)
-{
- Queue->Name = Name;
- Queue->NextOffset = NextOfset;
-}
-
-void *Workqueue_GetWork(tWorkqueue *Queue)
-{
- tThread *us;
-
- for( ;; )
- {
- // Check for work
- SHORTLOCK(&Queue->Protector);
- if(Queue->Head)
- {
- void *ret = Queue->Head;
- Queue->Head = *( (void**)ret + Queue->NextOffset/sizeof(void*) );
- if(Queue->Tail == ret)
- Queue->Tail = NULL;
- SHORTREL(&Queue->Protector);
- return ret;
- }
-
- // Go to sleep
- SHORTLOCK(&glThreadListLock);
- us = Threads_RemActive();
- us->WaitPointer = Queue;
- us->Status = THREAD_STAT_QUEUESLEEP;
- Queue->Sleeper = us;
- SHORTREL(&Queue->Protector);
- SHORTREL(&glThreadListLock);
-
- // Yield and sleep
- Threads_Yield();
- if(us->Status == THREAD_STAT_QUEUESLEEP) {
- // Why are we awake?!
- }
-
- us->WaitPointer = NULL;
- }
-}
-
-void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr)
-{
- SHORTLOCK(&Queue->Protector);
-
- if( Queue->Tail )
- *( (void**)Queue->Tail + Queue->NextOffset/sizeof(void*) ) = Ptr;
- else
- Queue->Head = Ptr;
- Queue->Tail = Ptr;
-
- if( Queue->Sleeper )
- {
- if( Queue->Sleeper->Status != THREAD_STAT_ACTIVE )
- Threads_AddActive(Queue->Sleeper);
- Queue->Sleeper = NULL;
- }
- SHORTREL(&Queue->Protector);
-}
--- /dev/null
+*.BuildNum.*
--- /dev/null
+# Doxyfile 1.5.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Acess2
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ../SrcDoc/Kernel
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = arch/archdoc.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW = NONE
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Options related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
--- /dev/null
+# Doxyfile 1.7.5.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = Acess2
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ../APIDoc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+#INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE.
+
+#CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include/apidoc_mainpage.h \
+ include/acess.h \
+ include/hal_proc.h \
+ include/binary.h \
+ include/modules.h \
+ include/vfs.h \
+ include/vfs_ext.h \
+ include/fs_devfs.h \
+ include/fs_sysfs.h \
+ include/iocache.h \
+ arch/archdoc.h \
+ include/apidoc/arch_x86.h \
+ include/api_drv_common.h \
+ include/api_drv_video.h \
+ include/api_drv_terminal.h \
+ include/api_drv_disk.h \
+ include/api_drv_keyboard.h \
+ include/api_drv_joystick.h \
+ include/api_drv_network.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS = api_drv_*.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to directory from which doxygen is run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NONE
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+#MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+#LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+#INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
--- /dev/null
+<doxygenlayout version="1.0">
+ <!-- Navigation index tabs for HTML output -->
+ <navindex>
+ <tab type="mainpage" visible="yes" title=""/>
+ <tab type="pages" visible="yes" title="" intro=""/>
+ <tab type="modules" visible="yes" title="" intro=""/>
+ <tab type="namespaces" visible="yes" title="">
+ <tab type="namespaces" visible="yes" title="" intro=""/>
+ <tab type="namespacemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="classes" visible="yes" title="">
+ <tab type="classes" visible="yes" title="" intro=""/>
+ <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="hierarchy" visible="yes" title="" intro=""/>
+ <tab type="classmembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="files" visible="yes" title="">
+ <tab type="files" visible="yes" title="" intro=""/>
+ <tab type="globals" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="dirs" visible="yes" title="" intro=""/>
+ <tab type="examples" visible="yes" title="" intro=""/>
+ </navindex>
+
+ <!-- Layout definition for a class page -->
+ <class>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <inheritancegraph visible="$CLASS_GRAPH"/>
+ <collaborationgraph visible="$COLLABORATION_GRAPH"/>
+ <allmemberslink visible="yes"/>
+ <memberdecl>
+ <nestedclasses visible="yes" title=""/>
+ <publictypes title=""/>
+ <publicslots title=""/>
+ <signals title=""/>
+ <publicmethods title=""/>
+ <publicstaticmethods title=""/>
+ <publicattributes title=""/>
+ <publicstaticattributes title=""/>
+ <protectedtypes title=""/>
+ <protectedslots title=""/>
+ <protectedmethods title=""/>
+ <protectedstaticmethods title=""/>
+ <protectedattributes title=""/>
+ <protectedstaticattributes title=""/>
+ <packagetypes title=""/>
+ <packagemethods title=""/>
+ <packagestaticmethods title=""/>
+ <packageattributes title=""/>
+ <packagestaticattributes title=""/>
+ <properties title=""/>
+ <events title=""/>
+ <privatetypes title=""/>
+ <privateslots title=""/>
+ <privatemethods title=""/>
+ <privatestaticmethods title=""/>
+ <privateattributes title=""/>
+ <privatestaticattributes title=""/>
+ <friends title=""/>
+ <related title="" subtitle=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <typedefs title=""/>
+ <enums title=""/>
+ <constructors title=""/>
+ <functions title=""/>
+ <related title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <events title=""/>
+ </memberdef>
+ <usedfiles visible="$SHOW_USED_FILES"/>
+ <authorsection visible="yes"/>
+ </class>
+
+ <!-- Layout definition for a namespace page -->
+ <namespace>
+ <briefdescription visible="yes"/>
+ <memberdecl>
+ <nestednamespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </namespace>
+
+ <!-- Layout definition for a file page -->
+ <file>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <includegraph visible="$INCLUDE_GRAPH"/>
+ <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
+ <sourcelink visible="yes"/>
+ <memberdecl>
+ <classes visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection/>
+ </file>
+
+ <!-- Layout definition for a group page -->
+ <group>
+ <briefdescription visible="yes"/>
+ <groupgraph visible="$GROUP_GRAPHS"/>
+ <memberdecl>
+ <classes visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <dirs visible="yes" title=""/>
+ <nestedgroups visible="yes" title=""/>
+ <files visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <pagedocs/>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </group>
+
+ <!-- Layout definition for a directory page -->
+ <directory>
+ <briefdescription visible="yes"/>
+ <directorygraph visible="yes"/>
+ <memberdecl>
+ <dirs visible="yes"/>
+ <files visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ </directory>
+</doxygenlayout>
--- /dev/null
+<?php
+$gLines = file("syscalls.lst");
+
+$lSyscalls = array();
+$i = 0;
+foreach($gLines as $line)
+{
+ $line = trim($line);
+ if(empty($line)) continue;
+
+ if( intVal($line) != 0)
+ $i = $line;
+ else
+ $lSyscalls[$i++] = explode("\t", $line, 3);
+}
+$lMax = $i;
+
+$lAsmInc = "; Acess2
+; System Calls List
+;
+
+";
+$lHeader = "/*
+ * AcessOS Microkernel Version
+ * syscalls.h
+ */
+#ifndef _SYSCALLS_H
+#define _SYSCALLS_H
+
+";
+$i = 0;
+foreach($lSyscalls as $num=>$call)
+{
+ if($i != $num) {
+ $lHeader .= "\n";
+ $lAsmInc .= "\n";
+ }
+
+ $lHeader .= "#define {$call[0]}\t{$num}";
+ $lHeader .= "\t// {$num} - {$call[1]}\n";
+
+ $lAsmInc .= "%define {$call[0]}\t{$num}\t; {$call[1]}\n";
+
+
+ if($i != $num)
+ $i = $num+1;
+ else
+ $i ++;
+}
+$lHeader .= "#define NUM_SYSCALLS\t$i\n";
+$lHeader .= "#define SYS_DEBUG\t0x100 // 0x100 - Print a debug string\n";
+$lHeader .= "\n";
+$lHeader .= "#ifdef __GNUC__\n";
+$lHeader .= "static const char *cSYSCALL_NAMES[] = {\n\t";
+
+$j = 0;
+for($i=0;$i<$lMax;$i++)
+{
+ if(!isset($lSyscalls[$i]))
+ $lHeader .= "\"\",";
+ else
+ $lHeader .= "\"".$lSyscalls[$i][0]."\",";
+ $j ++;
+ if($j == 6) {
+ $lHeader .= "\n\t";
+ $j = 0;
+ }
+}
+$lHeader .= "\"\"\n};\n"
+$lHeader .= "#endif\n";
+$lHeader .= "#endif\n";
+
+$fp = fopen("include/syscalls.h", "w"); fwrite($fp, $lHeader); fclose($fp);
+$fp = fopen("include/syscalls.inc.asm", "w"); fwrite($fp, $lAsmInc); fclose($fp);
+
+?>
--- /dev/null
+#!/usr/bin/perl
+#
+#
+
+open(FILE, "syscalls.lst");
+
+$num = 0;
+@calls = ();
+while($_ = <FILE>)
+{
+ if(/(\d+)/)
+ {
+ $num = $1;
+ }
+ elsif(/([A-Z_]+)\s+(.+)/)
+ {
+ push @calls, [$num, $1, $2];
+ $num ++;
+ }
+}
+
+close(FILE);
+
+# C header
+open(HEADER, ">include/syscalls.h");
+print HEADER "/*
+ * Acess2
+ * syscalls.h
+ * - System Call List
+ *
+ * NOTE: Generated from Kernel/syscalls.lst
+ */
+#ifndef _SYSCALLS_H
+#define _SYSCALLS_H
+
+";
+
+$lastid = -1;
+$i = 0;
+foreach my $call (@calls)
+{
+ print HEADER "#define ", $call->[1], "\t", $call->[0], "\t// ", $call->[2], "\n";
+ $i = $call->[0] + 1;
+}
+print HEADER "
+#define NUM_SYSCALLS ",$i,"
+#define SYS_DEBUG 0x100
+
+#ifndef __ASSEMBLER__
+static const char *cSYSCALL_NAMES[] = {
+";
+
+$lastid = -1;
+foreach $call (@calls)
+{
+ while( $lastid + 1 < $call->[0] )
+ {
+ print HEADER "\t\"\",\n";
+ $lastid = $lastid + 1;
+ }
+ print HEADER "\t\"", $call->[1], "\",\n";
+ $lastid = $lastid + 1;
+}
+print HEADER "
+\t\"\"
+};
+#endif
+
+#endif
+";
+
+close(HEADER);
+
+# Assembly Header
+open(ASM, ">include/syscalls.inc.asm");
+print ASM "; Acess2
+; System Calls List
+;
+
+";
+foreach $call (@calls)
+{
+ print ASM "%define ", $call->[1], "\t", $call->[0], "\t ;", $call->[2], "\n";
+}
+close(ASM);
--- /dev/null
+# Acess2 Kernel
+# Root Makefile
+# NOTE: Does some voodoo to allow differing architecture builds to co-exist
+# - The built objects and dependency files are suffixed with the arch name
+# - The final binary is Acess2.<ARCH>.bin
+
+-include ../Makefile.cfg
+
+-include arch/$(ARCHDIR)/Makefile
+
+-include Makefile.BuildNum.$(ARCH)
+
+ifeq ($(BUILD_NUM),)
+BUILD_NUM = 0
+endif
+
+KERNEL_VERSION = $(ACESS_VERSION)
+MAKEDEP = $(CC) -M
+
+ifeq ($(AS_SUFFIX),)
+ AS_SUFFIX = S
+endif
+
+ASFLAGS += -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1
+CPPFLAGS += -I./include -I./arch/$(ARCHDIR)/include -D_MODULE_NAME_=\"Kernel\"
+CPPFLAGS += -D ARCH=$(ARCH) -D ARCHDIR=$(ARCHDIR) -D PLATFORM=\"$(PLATFORM)\" -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1
+CPPFLAGS += -D KERNEL_VERSION=$(KERNEL_VERSION)
+CFLAGS += -Wall -fno-stack-protector -Wstrict-prototypes -g
+CFLAGS += -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wuninitialized
+CFLAGS += -O3
+LDFLAGS += -T arch/$(ARCHDIR)/link.ld -g
+
+ifeq ($(PLATFORM),default)
+ OBJDIR := obj-$(ARCH)/
+ #OBJSUFFIX := .$(ARCH)
+ BIN := ../Acess2.$(ARCH).bin
+ GZBIN := ../Acess2.$(ARCH).gz
+else
+ OBJDIR := obj-$(ARCH)-$(PLATFORM)/
+ #OBJSUFFIX := .$(ARCH)-$(PLATFORM)
+ BIN := ../Acess2.$(ARCH)-$(PLATFORM).bin
+ GZBIN := ../Acess2.$(ARCH)-$(PLATFORM).gz
+endif
+
+ifeq ($(DEBUG_BUILD),yes)
+ LDFLAGS += -g
+ CFLAGS += -g
+endif
+
+BUILDINFO_OBJ := $(OBJDIR)buildinfo.o$(OBJSUFFIX)
+BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX)
+
+OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ))
+OBJ += heap.o drvutil.o logging.o debug.o lib.o adt.o time.o
+OBJ += messages.o modules.o syscalls.o system.o
+OBJ += threads.o mutex.o semaphore.o workqueue.o events.o
+OBJ += drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o
+OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o
+OBJ += binary.o bin/elf.o bin/pe.o
+OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o
+OBJ += vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o vfs/mmap.o
+OBJ += vfs/fs/root.o vfs/fs/devfs.o
+OBJ += $(addprefix drv/, $(addsuffix .o,$(DRIVERS)))
+
+OBJ := $(addsuffix $(OBJSUFFIX), $(OBJ))
+OBJ := $(addprefix $(OBJDIR), $(OBJ))
+
+MODS += $(addprefix ../Modules/, $(addsuffix .xo.$(ARCH),$(MODULES)))
+
+DEPFILES := $(OBJ:%$(OBJSUFFIX)=%.dep$(OBJSUFFIX))
+
+SRCFILES = $(OBJ:$(OBJDIR)%.o$(OBJSUFFIX)=%.c)
+SRCFILES := $(SRCFILES:$(OBJDIR)%.ao$(OBJSUFFIX)=%.$(AS_SUFFIX))
+
+OBJ += $(BUILDINFO_OBJ)
+
+.PHONY: all clean install apidoc
+
+all: $(BIN)
+
+clean:
+ @$(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt
+ @$(RM) -r $(OBJDIR) $(OBJ) $(DEPFILES) $(BUILDINFO_SRC)
+
+install: $(BIN)
+ @cp $(BIN) $(BIN)_
+ @$(STRIP) $(BIN)_
+ @gzip -c $(BIN)_ > $(GZBIN)
+ @$(RM) $(BIN)_
+ $(xCP) $(GZBIN) $(DISTROOT)
+
+apidoc:
+ doxygen Doxyfile.api
+
+$(BIN): $(OBJ) $(MODS) arch/$(ARCHDIR)/link.ld Makefile ../BuildConf/$(ARCH)/Makefile.cfg ../BuildConf/$(ARCH)/$(PLATFORM).mk
+ @echo --- LD -o $(BIN)
+ @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) $(MODS) --defsym __buildnum=$$(( $(BUILD_NUM) + 1 )) -Map ../Map.$(ARCH).txt
+ @$(DISASM) -S $(BIN) > $(BIN).dsm
+ @wc -l $(SRCFILES) include/*.h > LineCounts.$(ARCH).txt
+ @echo BUILD_NUM = $$(( $(BUILD_NUM) + 1 )) > Makefile.BuildNum.$(ARCH)
+ $(POSTBUILD)
+
+$(OBJDIR)%.ao$(OBJSUFFIX): %.$(AS_SUFFIX) Makefile
+ @echo --- AS -o $@
+ @mkdir -p $(dir $@)
+ @$(AS) $(ASFLAGS) $< -o $@
+ifeq ($(AS_SUFFIX),S)
+ @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.ao.dep$(OBJSUFFIX) $<
+endif
+
+$(OBJDIR)%.o$(OBJSUFFIX): %.c Makefile
+# if exists %*/Makefile
+# @make -C %*/ all
+# else
+ @echo --- CC -o $@
+ @mkdir -p $(dir $@)
+ @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+ @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.o.dep$(OBJSUFFIX) $<
+# endif
+
+%.xo.$(ARCH):
+ @BUILDTYPE=static make -C $* all
+
+include/syscalls.h include/syscalls.inc.asm: syscalls.lst Makefile GenSyscalls.pl
+ perl GenSyscalls.pl
+
+Makefile: ../Makefile.cfg arch/$(ARCHDIR)/Makefile
+
+$(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) $(MODS) arch/$(ARCHDIR)/link.ld Makefile
+ @echo "#include <acess.h>" > $@
+ @echo "const char gsGitHash[] = \""`git log -n 1 | head -n 1 | awk '{print $$2}'`"\";" >> $@
+ @echo "const int giBuildNumber = $(BUILD_NUM);" >> $@
+$(BUILDINFO_OBJ): $(BUILDINFO_SRC)
+ @echo --- CC -o $@
+ @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+
+# Dependency Files
+-include $(DEPFILES)
--- /dev/null
+/*
+ * Acess2 Kernel
+ *
+ * adt.c
+ * - Complex data type code
+ */
+#include <acess.h>
+#include <adt.h>
+
+
+// === CODE ===
+// --- Ring Buffers ---
+tRingBuffer *RingBuffer_Create(size_t Space)
+{
+ tRingBuffer *ret = malloc(sizeof(tRingBuffer)+Space);
+ ret->Start = 0;
+ ret->Length = 0;
+ ret->Space = Space;
+ return ret;
+}
+
+size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length)
+{
+ size_t tmpLen;
+
+ tmpLen = Buffer->Length; // Changed in Write, so cache it for our read
+
+ if(Length > tmpLen) Length = tmpLen;
+
+ if( Buffer->Start + Length > Buffer->Space )
+ {
+ int endData = Buffer->Space - Buffer->Start;
+ memcpy(Dest, &Buffer->Data[Buffer->Start], endData);
+ memcpy((Uint8*)Dest + endData, Buffer->Data, Length - endData);
+ }
+ else
+ {
+ memcpy(Dest, &Buffer->Data[Buffer->Start], Length);
+ }
+
+ // Lock then modify
+ SHORTLOCK( &Buffer->Lock );
+ Buffer->Start += Length;
+ if( Buffer->Start > Buffer->Space )
+ Buffer->Start -= Buffer->Space;
+ Buffer->Length -= Length;
+ SHORTREL( &Buffer->Lock );
+
+ return Length;
+}
+
+size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length)
+{
+ size_t bufEnd, endSpace;
+ size_t tmpLen, tmpStart;
+
+ // Cache Start and Length because _Read can change these
+ SHORTLOCK( &Buffer->Lock );
+ tmpStart = Buffer->Start;
+ tmpLen = Buffer->Length;
+ SHORTREL( &Buffer->Lock );
+
+ bufEnd = (tmpStart + Buffer->Length) % Buffer->Space;
+ endSpace = Buffer->Space - bufEnd;
+
+ // Force to bounds
+ if(Length > Buffer->Space - tmpLen) Length = Buffer->Space - tmpLen;
+
+ if(endSpace < Length)
+ {
+ memcpy( &Buffer->Data[bufEnd], Source, endSpace );
+ memcpy( Buffer->Data, (Uint8*)Source + endSpace, Length - endSpace );
+ }
+ else
+ {
+ memcpy( &Buffer->Data[bufEnd], Source, Length );
+ }
+
+ // Lock then modify
+ SHORTLOCK( &Buffer->Lock );
+ Buffer->Length += Length;
+ SHORTREL( &Buffer->Lock );
+
+ return Length;
+}
+
--- /dev/null
+/**
+ * \file archdoc.h
+ * \brief Documentation Definitions for the Acess 2 Architecture Interface
+ * \author John Hodge (thePowersGang)
+ *
+ * Acess 2 allows different architecture builds to be made off almost
+ * the same source tree. The difference between the trees is the inclusion
+ * of a different directory from the "Kernel/arch/" directory that
+ * contains the architecture specific intialisation and management code.
+ * Each achitecture tree must provide all the definitions from this
+ * document in some form or another (usually in the most efficient way
+ * avaliable)
+ * The values and types used in this documentation are a guide only and
+ * will most probably be incorrect for most architectures.
+ */
+#ifndef _ARCHDOC_H_
+#define _ARCHDOC_H_
+
+/**
+ * \brief Maximum number of CPUs supported by this architecture driver
+ * (in the current build)
+ */
+#define MAX_CPUS 1
+/**
+ * \brief Number of bits in a machine word (Uint)
+ */
+#define BITS 32
+/**
+ * \brief Number of valid bits in a \a tPAddr
+ */
+#define PHYS_BITS 32
+
+/**
+ * \name Syncronisation Primitives
+ * \{
+ */
+/**
+ * \brief Spinlock type
+ */
+typedef volatile int tSpinlock;
+/**
+ * \brief Acquire a spinlock
+ */
+#define LOCK(lockptr) do{while(*(tSpinlock*)lockptr)Threads_Yield();*(tSpinlock*)lockptr=1;}while(0)
+/**
+ * \brief Release a held spinlock
+ */
+#define RELEASE(lockptr) do{*(tSpinlock*)lockptr=0;}while(0)
+/**
+ * \}
+ */
+//#define HALT() __asm__ __volatile__ ("hlt")
+
+
+/**
+ * \name Atomic Types
+ * \{
+ */
+typedef unsigned int Uint; //!< Unsigned machine native integer
+typedef unsigned char Uint8; //!< Unsigned 8-bit integer
+typedef unsigned short Uint16; //!< Unsigned 16-bit integer
+typedef unsigned long Uint32; //!< Unsigned 32-bit integer
+typedef unsigned long long Uint64; //!< Unsigned 64-bit integer
+typedef signed int Sint; //!< Signed Machine Native integer
+typedef signed char Sint8; //!< Signed 8-bit integer
+typedef signed short Sint16; //!< Signed 16-bit integer
+typedef signed long Sint32; //!< Signed 32-bit integer
+typedef signed long long Sint64; //!< Signed 16-bit integer
+/**
+ * \}
+ */
+
+typedef Uint size_t; //!< Counter/Byte count type
+typedef Uint32 tVAddr; //!< Virtual address type
+typedef Uint32 tPAddr; //!< Physical Address type
+
+/**
+ * \brief Register state passed to the syscall handler
+ *
+ * The \a tSyscallRegs structure allows the system call handler to read
+ * the user state to get the arguments for the call. It also allows the
+ * handler to alter specific parts of the user state to reflect the
+ * result of the call.
+ * \note The fields shown here are need only be present in the actual
+ * structure, in any order. The implementation may also add more
+ * fields for padding purposes.
+ */
+typedef struct {
+ Uint Arg4; //!< Fourth argument
+ Uint Arg3; //!< Third argument
+ Uint Arg2; //!< Second argument
+ union {
+ Uint Arg1; //!< First arugment
+ Uint RetHi; //!< High part of the return
+ };
+ union {
+ Uint Num; //!< Call Number
+ Uint Return; //!< Low return value
+ };
+
+ Uint StackPointer; //!< User stack pointer
+} tSyscallRegs;
+
+/**
+ * \brief Opaque structure definining the MMU state for a task
+ */
+typedef struct sMemoryState tMemoryState;
+
+/**
+ * \brief Opque structure defining the CPU state for a task
+ */
+typedef struct sTaskState tTaskState;
+
+
+/**
+ * \name Memory Management
+ * \{
+ */
+/**
+ * \}
+ */
+
+
+#endif
--- /dev/null
+#
+# Acess2 Kernel
+# arm7 Architecture Makefile
+# arch/arm7/Makefile
+
+CPPFLAGS =
+CFLAGS =
+ASFLAGS =
+
+CPPFLAGS += -DMMU_PRESENT=1
+LDFLAGS += `$(CC) --print-libgcc-file-name`
+
+A_OBJ = start.ao main.o lib.o lib.ao time.o pci.o debug.o
+A_OBJ += mm_phys.o mm_virt.o proc.o proc.ao
+
+#main.c: Makefile.BuildNum.$(ARCH)
+
+ifeq ($(PLATFORM),tegra2)
+ POSTBUILD = arm-elf-objcopy $(BIN) -O binary $(BIN)
+endif
--- /dev/null
+/**
+ * Acess2
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/arm7/debug.c
+ * - ARM7 Debug output
+ * NOTE: Currently designed for the realview-pb-a8 emulated by Qemu
+ */
+#include <acess.h>
+
+// === CONSTANTS ===
+//#define UART0_BASE 0x10009000
+#define UART0_BASE 0xF1000000 // Boot time mapped
+
+// === PROTOTYPES ===
+void KernelPanic_SetMode(void);
+void KernelPanic_PutChar(char Ch);
+void StartupPrint(const char *str);
+
+// === GLOBALS ===
+ int giDebug_SerialInitialised = 0;
+
+// === CODE ===
+void Debug_PutCharDebug(char ch)
+{
+ if(ch == '\n')
+ Debug_PutCharDebug('\r');
+
+ #if PLATFORM_is_tegra2
+ // Tegra2
+ while( !(*(volatile Uint32*)(UART0_BASE + 0x14) & (1 << 5)) )
+ ;
+ #endif
+
+// *(volatile Uint32*)(SERIAL_BASE + SERIAL_REG_DATA) = ch;
+ *(volatile Uint32*)(UART0_BASE) = ch;
+}
+
+void Debug_PutStringDebug(const char *str)
+{
+ for( ; *str; str++ )
+ Debug_PutCharDebug( *str );
+}
+
+void KernelPanic_SetMode(void)
+{
+}
+
+void KernelPanic_PutChar(char ch)
+{
+// Debug_PutCharDebug(ch);
+}
+
+void StartupPrint(const char *str)
+{
+}
+
--- /dev/null
+/*
+ * Acess2
+ * ARM7 Architecture Header
+ */
+#ifndef _ARCH_H_
+#define _ARCH_H_
+
+// === CONSTANTS ===
+#define INVLPTR ((void*)-1)
+#define BITS 32
+#define PAGE_SIZE 0x1000
+#define KERNEL_BASE 0x80000000 // 2GiB
+
+// === TYPES ===
+typedef unsigned int Uint;
+typedef unsigned char Uint8;
+typedef unsigned short Uint16;
+typedef unsigned long Uint32;
+typedef unsigned long long Uint64;
+typedef signed int Sint;
+typedef signed char Sint8;
+typedef signed short Sint16;
+typedef signed long Sint32;
+typedef signed long long Sint64;
+
+typedef int size_t;
+typedef char BOOL;
+
+typedef Uint32 tVAddr;
+typedef Uint32 tPAddr;
+
+#include "lock.h"
+
+// --- Debug
+extern void Debug_PutCharDebug(char Ch);
+extern void Debug_PutStringDebug(const char *String);
+
+// This should be elsewhere, but CBF
+extern void MM_SetupPhys(void);
+extern int MM_InitialiseVirtual(void);
+
+#define NO_IO_BUS 1
+
+#endif
--- /dev/null
+/*
+ * Acess2 ARMv7
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/arm7/include/assembly.h
+ * - Assembly specific macros
+ */
+#ifndef _ASSEMBLY_H_
+#define _ASSEMBLY_H_
+
+#define PUSH_GPRS \
+ str r0, [sp,#-1*4];\
+ str r1, [sp,#-2*4];\
+ str r2, [sp,#-3*4];\
+ str r3, [sp,#-4*4];\
+ str r4, [sp,#-5*4];\
+ str r5, [sp,#-6*4];\
+ str r6, [sp,#-7*4];\
+ str r7, [sp,#-8*4];\
+ str r8, [sp,#-9*4];\
+ str r9, [sp,#-10*4];\
+ str r10, [sp,#-11*4];\
+ str r11, [sp,#-12*4];\
+ str r12, [sp,#-13*4];\
+ str sp, [sp,#-14*4];\
+ str lr, [sp,#-15*4];\
+ sub sp, #16*4
+
+#define POP_GPRS add sp, #16*4; \
+ ldr r0, [sp,#-1*4]; \
+ ldr r1, [sp,#-2*4]; \
+ ldr r2, [sp,#-3*4]; \
+ ldr r3, [sp,#-4*4]; \
+ ldr r4, [sp,#-5*4]; \
+ ldr r5, [sp,#-6*4]; \
+ ldr r6, [sp,#-7*4]; \
+ ldr r7, [sp,#-8*4]; \
+ ldr r8, [sp,#-9*4]; \
+ ldr r9, [sp,#-10*4]; \
+ ldr r10, [sp,#-11*4]; \
+ ldr r11, [sp,#-12*4]; \
+ ldr r12, [sp,#-13*4]; \
+ ldr lr, [sp,#-15*4];
+
+#endif
+
--- /dev/null
+/*
+ * Acess2
+ * ARM7 Architecture
+ *
+ * lock.h - Hardware level spinlocks
+ */
+#ifndef _LOCK_H_
+#define _LOCK_H_
+
+// === CODE ===
+struct sShortSpinlock {
+ int Lock;
+};
+
+// --- Spinlocks ---
+static inline int IS_LOCKED(struct sShortSpinlock *Lock)
+{
+ return !!Lock->Lock;
+}
+
+static inline int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
+{
+ // TODO: Handle multiple CPUs
+ return !!Lock->Lock;
+}
+
+static inline int SHORTLOCK(struct sShortSpinlock *Lock)
+{
+ #if 0
+ // Coped from linux, yes, but I know what it does now :)
+ Uint tmp;
+ __asm__ __volatile__ (
+ "1: ldrex %0, [%1]\n" // Exclusive LOAD
+ " teq %0, #0\n" // Check if zero
+ " strexeq %0, %2, [%1]\n" // Set to one if it is zero (releasing lock on the memory)
+ " teqeq %0, #0\n" // If the lock was avaliable, check if the write succeeded
+ " bne 1b" // If the lock was unavaliable, or the write failed, loop
+ : "=&r" (tmp) // Temp
+ : "r" (&Lock->Lock), "r" (1)
+ : "cc" // Condition codes clobbered
+ );
+ #elif 1
+ while( *(volatile int*)&Lock->Lock ) ;
+ Lock->Lock = 1;
+ #else
+ int v = 1;
+ while( v )
+ __asm__ __volatile__ (
+ "swp %0, %0, [%1]"
+ : "=r" (v) : "r" (&Lock->Lock)
+ : "cc"
+ );
+ #endif
+ return 1;
+}
+
+static inline void SHORTREL(struct sShortSpinlock *Lock)
+{
+ Lock->Lock = 0;
+}
+
+#endif
+
--- /dev/null
+/*
+ * Acess2
+ * ARM7 Virtual Memory Manager Header
+ */
+#ifndef _MM_VIRT_H_
+#define _MM_VIRT_H_
+
+#include "options.h"
+
+#define USER_STACK_COMM 0x04000 // Pages to allocate up front
+#define USER_STACK_SIZE 0x10000 // Stack space
+#define USER_STACK_TOP 0x78000000
+
+#define MM_USER_MIN 0x00001000
+#define USER_LIB_MAX 0x70000000
+#define MM_PPD_HANDLES 0x7F800000
+#define MM_TABLE1USER 0x7FC00000 // 2 GiB - 4 MiB
+#define MM_TABLE0USER 0x7FE00000 // 2 GiB - 2 MiB
+#define MM_KSTACK_BASE 0x7FE00000
+#define MM_KSTACK_END 0x80000000
+
+// Page Blocks are 12-bits wide (12 address bits used)
+// Hence, the table is 16KiB large (and must be so aligned)
+// and each block addresses 1MiB of data
+
+// First level table is aligned to 16KiB (restriction of TTBR reg)
+// - VMSAv6 uses two TTBR regs, determined by bit 31
+
+//#define KERNEL_BASE 0x80000000 // 2GiB
+
+#define MM_KHEAP_BASE 0x80800000 // 8MiB of kernel code
+#define MM_KHEAP_MAX 0xC0000000 // ~1GiB of kernel heap
+
+#define MM_MODULE_MIN 0xC0000000 // - 0xD0000000
+#define MM_MODULE_MAX 0xCF000000
+
+#define MM_GLOBALSTACKS 0xCF000000 // Global stacks
+#define MM_GLOBALSTACKS_END 0xD0000000
+
+// PMM Data, giving it 256MiB is overkill, but it's unused atm
+#define MM_MAXPHYSPAGE (1024*1024)
+// 2^(32-12) max pages
+// 8.125 bytes per page (for bitmap allocation)
+// = 8.125 MiB
+#define MM_PMM_BASE 0xE0000000
+#define MM_PMM_END 0xF0000000
+
+#define MM_HWMAP_BASE 0xF0000000 // Ent 0xF00
+#define MM_HWMAP_END 0xFE000000
+#define MM_TMPMAP_BASE 0xFE000000
+#define MM_TMPMAP_END 0xFF000000
+
+#define MM_KERNEL_VFS 0xFF000000 //
+#define MM_TABLE1KERN 0xFF800000 // - 0x???????? 4MiB
+//#define MM_TABLE0KERN 0xFFC00000 // - 0xFFE04000 16KiB
+
+#endif
--- /dev/null
+/*
+ * Acess2 ARMv7 Port
+ * - By John Hodge (thePowersGang)
+ *
+ * options.h
+ * - C/ASM Shared constants
+ */
+#ifndef _ARMV7_OPTIONS_H_
+#define _ARMV7_OPTIONS_H_
+
+#define KERNEL_BASE 0x80000000
+
+//#define PCI_PADDR 0x60000000 // Realview (Non-PB)
+
+#if PLATFORM_is_realview_pb
+# define UART0_PADDR 0x10009000 // Realview
+# define GICI_PADDR 0x1e000000
+# define GICD_PADDR 0x1e001000
+# define PL110_BASE 0x10020000 // Integrator
+
+#endif
+
+#if PLATFORM_is_tegra2 // Tegra2
+# define UART0_PADDR 0x70006000
+# define GICD_PADDR 0x50041000
+# define GICI_PADDR 0x50040100
+//# define PL110_BASE 0x10020000 // Integrator
+#endif
+
+#define MM_KSTACK_SIZE 0x2000 // 2 Pages
+
+#endif
+
--- /dev/null
+/*
+ * Acess2
+ * ARM7 Architecture
+ *
+ * proc.h - Arch-Dependent Process Management
+ */
+#ifndef _PROC_H_
+#define _PROC_H_
+
+#define MAX_CPUS 4
+#define USER_MAX 0x80000000
+
+// === STRUCTURES ===
+typedef struct {
+ Uint32 IP, SP;
+ Uint32 UserIP, UserSP;
+} tTaskState;
+
+typedef struct {
+ Uint32 Base;
+} tMemoryState;
+
+typedef struct {
+ union {
+ Uint32 Num;
+ Uint32 Error;
+ };
+ union {
+ Uint32 Arg1;
+ Uint32 Return;
+ };
+ union {
+ Uint32 Arg2;
+ Uint32 RetHi;
+ };
+ Uint32 Arg3;
+ Uint32 Arg4;
+ Uint32 Arg5;
+ Uint32 Arg6; // R6
+} tSyscallRegs;
+
+// === MACROS ===
+#define HALT() do{}while(0)
+
+// === PROTOTYPES ===
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 ARM
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/arm7/lib.S
+ * - Assembly editions of library functions
+ */
+#include "include/assembly.h"
+
+.globl __memcpy_byte
+__memcpy_byte:
+1:
+ tst r2, r2 @ Check counter
+ moveq pc, lr @ Return if zero
+ ldrb r3, [r1],#1 @ Read
+ strb r3, [r0],#1 @ Write
+ sub r2, #1
+ b 1b
+
+@
+@ Pre-aligned memcpy (32-bit blocks)
+@
+.globl __memcpy_align4
+__memcpy_align4:
+ push {r4}
+ mvn r3, #3 @ Mask for checking length
+
+ @ 4 byte chunk copies
+1: tst r2, r3
+ ldrne r4, [r1],#4
+ strne r4, [r0],#4
+ subne r2, #4
+ bne 1b
+
+ @ single byte copies to finish off
+2: tst r2, #3
+ beq 3f
+ ldrb r4, [r1],#1
+ strb r4, [r0],#1
+ sub r2, #1
+ b 2b
+
+3: pop {r4}
+ mov pc, lr
+
+@
+@ Division
+@
+.globl __divmod32_asm
+__divmod32_asm:
+ push {r4}
+ mov r4, #0 @ Return value
+ mov r3, #1 @ add value
+
+ @ Scan up for first larger multiple of 2
+1: cmp r0, r1 @ N < D
+ bmi 2f @ ^^
+ lsl r1, r1, #1 @ D <<= 1
+ lsls r3, r3, #1 @ add <<= 1
+ beq .err @ result is zero
+ b 1b
+
+ @ Go back down
+2: lsrs r3, r3, #1 @ add >>= 1
+ beq 3f @ Done (value is zero)
+ lsr r1, r1, #1 @ D >>= 1
+ cmp r0, r1 @ N < D
+ bmi 2b
+ sub r0, r1 @ N -= D
+ add r4, r3 @ ret += add
+ b 2b
+3:
+ tst r2, r2 @ Remainder (if wanted)
+ strne r0,[r2]
+ mov r0, r4 @ Return value
+ pop {r4}
+ mov pc, lr
+.err:
+ mov r0, #0
+ tst r2, r2
+ strne r0, [r2]
+ pop {r4}
+ mov pc, lr
+
--- /dev/null
+/*
+ * Acess2 ARM7 Port
+ *
+ * lib.c - Library Functions
+ */
+#include <acess.h>
+#include "../helpers.h"
+
+// === IMPORTS ===
+extern void __memcpy_align4(void *_dest, const void *_src, size_t _length);
+extern void __memcpy_byte(void *_dest, const void *_src, size_t _length);
+extern Uint32 __divmod32_asm(Uint32 Num, Uint32 Den, Uint32 *Rem);
+
+// === PROTOTYPES ===
+Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
+Uint32 __divmod32(Uint32 Num, Uint32 Den, Uint32 *Rem);
+Uint64 __udivdi3(Uint64 Num, Uint64 Den);
+Uint64 __umoddi3(Uint64 Num, Uint64 Den);
+Uint32 __udivsi3(Uint32 Num, Uint32 Den);
+Uint32 __umodsi3(Uint32 Num, Uint32 Den);
+Sint32 __divsi3(Sint32 Num, Sint32 Den);
+Sint32 __modsi3(Sint32 Num, Sint32 Den);
+
+// === CODE ===
+void *memcpy(void *_dest, const void *_src, size_t _length)
+{
+ Uint8 *dst8 = _dest;
+ const Uint8 *src8 = _src;
+
+ if( ((tVAddr)_dest & 3) == 0 && ((tVAddr)_src & 3) == 0 )
+ {
+ __memcpy_align4(_dest, _src, _length);
+ return _dest;
+ }
+
+ // Handle small copies / Non-aligned
+ if( _length < 4 || ((tVAddr)_dest & 3) != ((tVAddr)_src & 3) )
+ {
+ __memcpy_byte(_dest, _src, _length);
+ return _dest;
+ }
+
+ // Force alignment
+ while( (tVAddr)dst8 & 3 ) *dst8 ++ = *src8++, _length --;
+
+ __memcpy_align4(dst8, src8, _length);
+
+ return _dest;
+}
+
+int memcmp(const void *_m1, const void *_m2, size_t _length)
+{
+ const Uint32 *m1, *m2;
+ const Uint8 *m1_8 = _m1, *m2_8 = _m2;
+
+ // Handle small copies / Non-aligned
+ if( _length < 4 || ((tVAddr)_m1 & 3) != ((tVAddr)_m1 & 3) )
+ {
+ for( ; _length--; m1_8++,m2_8++ ) {
+ if(*m1_8 != *m2_8) return *m1_8 - *m2_8;
+ }
+ return 0;
+ }
+
+ // Force alignment
+ for( ; (tVAddr)m1_8 & 3; m1_8 ++, m2_8 ++) {
+ if(*m1_8 != *m2_8) return *m1_8 - *m2_8;
+ }
+ m1 = (void *)m1_8; m2 = (void *)m2_8;
+
+ // DWORD copies
+ for( ; _length > 3; _length -= 4, m1++, m2++)
+ if(*m1 != *m2) return *m1 - *m2;
+
+ // Trailing bytes
+ m1_8 = (void*)m1; m2_8 = (void*)m2;
+ for( ; _length; _length --, m1_8++, m2_8++ )
+ if(*m1_8 != *m2_8) return *m1_8 - *m2_8;
+
+ return 0;
+}
+
+void *memset(void *_dest, int _value, size_t _length)
+{
+ Uint32 *dst, val32;
+ Uint8 *dst8 = _dest;
+
+ _value = (Uint8)_value;
+
+ // Handle small copies
+ if( _length < 4 )
+ {
+ for( ; _length--; dst8++ )
+ *dst8 = _value;
+ return _dest;
+ }
+
+ val32 = _value;
+ val32 |= val32 << 8;
+ val32 |= val32 << 16;
+
+ // Force alignment
+ while( (tVAddr)dst8 & 3 ) *dst8 ++ = _value;
+ dst = (void *)dst8;
+
+ // DWORD copies
+ for( ; _length > 3; _length -= 4)
+ *dst++ = val32;
+
+ // Trailing bytes
+ dst8 = (void*)dst;
+ for( ; _length; _length -- )
+ *dst8 ++ = _value;
+
+ return _dest;
+}
+
+DEF_DIVMOD(64)
+DEF_DIVMOD(32)
+
+Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem)
+{
+ Uint64 ret;
+ if(Den == 0) return 0; // TODO: #div0
+ if(Num < Den) {
+ if(Rem) *Rem = Num;
+ return 0;
+ }
+ if(Num == 0) {
+ if(Rem) *Rem = 0;
+ return 0;
+ }
+ if(Den == 1) {
+ if(Rem) *Rem = 0;
+ return Num;
+ }
+ if(Den == 2) {
+ if(Rem) *Rem = Num & 1;
+ return Num >> 1;
+ }
+ if(Den == 16) {
+ if(Rem) *Rem = Num & 0xF;
+ return Num >> 4;
+ }
+ if(Den == 32) {
+ if(Rem) *Rem = Num & 0x1F;
+ return Num >> 5;
+ }
+ if(Den == 0x1000) {
+ if(Rem) *Rem = Num & 0xFFF;
+ return Num >> 12;
+ }
+
+ if( !(Den >> 32) && !(Num >> 32) ) {
+ if(Rem) *Rem = 0; // Clear high bits
+ return __divmod32_asm(Num, Den, (Uint32*)Rem);
+ }
+
+ ret = __divmod64(Num, Den, Rem);
+ return ret;
+}
+
+// Unsigned Divide 64-bit Integer
+Uint64 __udivdi3(Uint64 Num, Uint64 Den)
+{
+ return DivMod64U(Num, Den, NULL);
+}
+
+// Unsigned Modulus 64-bit Integer
+Uint64 __umoddi3(Uint64 Num, Uint64 Den)
+{
+ Uint64 ret = 0;
+ DivMod64U(Num, Den, &ret);
+ return ret;
+}
+
+Uint32 __udivsi3(Uint32 Num, Uint32 Den)
+{
+ return __divmod32_asm(Num, Den, NULL);
+}
+
+Uint32 __umodsi3(Uint32 Num, Uint32 Den)
+{
+ Uint32 rem;
+ __divmod32_asm(Num, Den, &rem);
+ return rem;
+}
+
+static inline Sint32 DivMod32S(Sint32 Num, Sint32 Den, Sint32 *Rem)
+{
+ Sint32 ret = 1;
+ if( Num < 0 ) {
+ ret = -ret;
+ Num = -Num;
+ }
+ if( Den < 0 ) {
+ ret = -ret;
+ Den = -Den;
+ }
+ if(ret < 0)
+ ret = -__divmod32(Num, Den, (Uint32*)Rem);
+ else
+ ret = __divmod32(Num, Den, (Uint32*)Rem);
+ return ret;
+}
+
+Sint32 __divsi3(Sint32 Num, Sint32 Den)
+{
+ return DivMod32S(Num, Den, NULL);
+}
+
+Sint32 __modsi3(Sint32 Num, Sint32 Den)
+{
+ Sint32 rem;
+ DivMod32S(Num, Den, &rem);
+ return rem;
+}
--- /dev/null
+ENTRY (_start)
+
+_kernel_base = 0x80000000;
+_usertext_vbase = 0xFFFFE000;
+
+SECTIONS
+{
+ . = 0;
+ .init :
+ {
+ *(.init)
+ }
+ . += _kernel_base;
+ .text : AT( ADDR(.text) - _kernel_base )
+ {
+ *(.text*)
+ *(.rodata*)
+ }
+
+
+ /* HACKS: User accesible .text section */
+ . = ALIGN(0x1000);
+ gUsertextPhysStart = . - _kernel_base;
+ . = _usertext_vbase;
+ .usertext : AT( gUsertextPhysStart )
+ {
+ *(.usertext)
+ }
+ . += gUsertextPhysStart + _kernel_base - _usertext_vbase;
+
+ /* 0x4000 (4 pages) alignment needed for root table */
+ .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base )
+ {
+ *(.padata)
+ *(.data*)
+
+ gKernelSymbols = .;
+ *(KEXPORT)
+ gKernelSymbolsEnd = .;
+
+ gKernelModules = .;
+ *(KMODULES)
+ gKernelModulesEnd = .;
+ }
+ .bss : AT( ADDR(.bss) - _kernel_base )
+ {
+ bss_start = .;
+ *(.bss*)
+ *(COMMON*)
+ . = ALIGN(0x1000);
+ *(.pabss)
+ bss_end = .;
+ }
+ gKernelEnd = .;
+}
--- /dev/null
+/*
+ * Acess2
+ *
+ * ARM7 Entrypoint
+ * arch/arm7/main.c
+ */
+#define DEBUG 0
+
+#include <acess.h>
+#include <modules.h>
+
+// === IMPORTS ===
+extern void Interrupts_Setup(void);
+extern void Arch_LoadBootModules(void);
+extern void Heap_Install(void);
+extern void Threads_Init(void);
+extern void System_Init(const char *Commandline);
+
+// === PROTOTYPES ===
+ int kmain(void);
+Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args);
+
+// === CODE ===
+int kmain(void)
+{
+ LogF("Acess2 ARMv7 v"EXPAND_STR(KERNEL_VERSION)"\n");
+ LogF(" Git Hash %s\n", gsGitHash);
+ LogF(" Build %i\n", BUILD_NUM);
+
+ MM_SetupPhys();
+
+ LogF("Heap Setup...\n");
+ Heap_Install();
+
+ LogF("Threads Init...\n");
+ Threads_Init();
+
+ LogF("VFS Init...\n");
+ VFS_Init();
+
+ // Boot modules?
+ Module_EnsureLoaded("armv7_GIC");
+
+ //
+ LogF("Moving to arch-independent init\n");
+ #if PLATFORM_is_tegra2
+ System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=Tegra2Vid");
+ #else
+ System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=PL110");
+ #endif
+// System_Init("Acess2.armv7.bin /Acess=initrd:");
+ //TODO:
+ LogF("End of kmain(), for(;;) Threads_Sleep();\n");
+ for(;;)
+ Threads_Sleep();
+}
+
+void Arch_LoadBootModules(void)
+{
+}
+
+Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args)
+{
+ Uint32 ret = -1, err = 0;
+ Uint32 addr;
+ ENTER("iNum xArgs[0] xArgs[1] xArgs[2] xArgs[3]",
+ Num, Args[0], Args[1], Args[2], Args[3]
+ );
+ switch(Num)
+ {
+ case 1:
+// Log_Debug("ARMv7", "__clear_cache(%p, %p)", Args[0], Args[1]);
+ // Align
+ Args[0] &= ~0xFFF;
+ Args[1] += 0xFFF; Args[1] &= ~0xFFF;
+ // Invalidate!
+ for( addr = Args[0]; addr < Args[1]; addr += 0x1000 )
+ {
+ LOG("addr = %p", addr);
+ __asm__ __volatile__ (
+ "mcrlt p15, 0, %0, c7, c5, 1;\n\t"
+ "mcrlt p15, 0, %0, c7, c6, 1;\n\t"
+ :
+ : "r" (addr)
+ );
+ }
+ ret = 0;
+ break;
+ }
+ Args[0] = ret; // RetLow
+ Args[1] = 0; // RetHi
+ Args[2] = err; // Errno
+ LEAVE('x', ret);
+ return ret;
+}
+
--- /dev/null
+/*
+ * Acess2
+ *
+ * ARM7 Physical Memory Manager
+ * arch/arm7/mm_phys.c
+ */
+#define DEBUG 0
+
+#include <acess.h>
+#include <mm_virt.h>
+
+#define MM_NUM_RANGES 1 // Single range
+#define MM_RANGE_MAX 0
+#define TRACE_ALLOCS 0
+
+#define NUM_STATIC_ALLOC 4
+
+char gStaticAllocPages[NUM_STATIC_ALLOC][PAGE_SIZE] __attribute__ ((section(".padata")));
+tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {
+ (tPAddr)(&gStaticAllocPages[0]) - KERNEL_BASE,
+ (tPAddr)(&gStaticAllocPages[1]) - KERNEL_BASE,
+ (tPAddr)(&gStaticAllocPages[2]) - KERNEL_BASE,
+ (tPAddr)(&gStaticAllocPages[3]) - KERNEL_BASE
+};
+extern char gKernelEnd[];
+
+
+#include <tpl_mm_phys_bitmap.h>
+
+//#define REALVIEW_LOWRAM_SIZE 0x10000000
+#define REALVIEW_LOWRAM_SIZE (32*1024*1024)
+
+void MM_SetupPhys(void)
+{
+ LogF("MM_SetupPhys: ()\n");
+ MM_Tpl_InitPhys( REALVIEW_LOWRAM_SIZE/0x1000, NULL );
+}
+
+int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length )
+{
+ switch(Index)
+ {
+ case 0:
+ *Start = ((tVAddr)&gKernelEnd - KERNEL_BASE + 0xFFF) & ~0xFFF;
+ *Length = REALVIEW_LOWRAM_SIZE - *Start;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * \brief Takes a physical address and returns the ID of its range
+ * \param Addr Physical address of page
+ * \return Range ID from eMMPhys_Ranges
+ */
+int MM_int_GetRangeID( tPAddr Addr )
+{
+ return MM_RANGE_MAX; // ARM doesn't need ranges
+}
--- /dev/null
+/*
+ * Acess2
+ *
+ * ARM7 Virtual Memory Manager
+ * - arch/arm7/mm_virt.c
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <mm_virt.h>
+#include <hal_proc.h>
+
+#define TRACE_MAPS 0
+
+#define AP_KRW_ONLY 1 // Kernel page
+#define AP_KRO_ONLY 5 // Kernel RO page
+#define AP_RW_BOTH 3 // Standard RW
+#define AP_RO_BOTH 7 // COW Page
+#define AP_RO_USER 2 // User RO Page
+#define PADDR_MASK_LVL1 0xFFFFFC00
+
+// === IMPORTS ===
+extern Uint32 kernel_table0[];
+
+// === TYPES ===
+typedef struct
+{
+ tPAddr PhysAddr;
+ Uint8 Size;
+ Uint8 Domain;
+ BOOL bExecutable;
+ BOOL bGlobal;
+ BOOL bShared;
+ int AP;
+} tMM_PageInfo;
+
+//#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)])
+#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)])
+#define USRFRACTAL(addr) (*((Uint32*)(0x7FDFF000) + ((addr)>>22)))
+#define TLBIALL() __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0))
+#define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" ((addr)&~0xFFF):"memory")
+#define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF))
+
+// === PROTOTYPES ===
+void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1);
+ int MM_int_AllocateCoarse(tVAddr VAddr, int Domain);
+ int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
+ int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
+tVAddr MM_NewUserStack(void);
+tPAddr MM_AllocateZero(tVAddr VAddr);
+tPAddr MM_AllocateRootTable(void);
+void MM_int_CloneTable(Uint32 *DestEnt, int Table);
+tPAddr MM_Clone(void);
+tVAddr MM_NewKStack(int bGlobal);
+void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info);
+//void MM_DumpTables(tVAddr Start, tVAddr End);
+void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch);
+
+// === GLOBALS ===
+tPAddr giMM_ZeroPage;
+
+// === CODE ===
+int MM_InitialiseVirtual(void)
+{
+ return 0;
+}
+
+void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1)
+{
+ if(VAddr & 0x80000000) {
+ *Table0 = (void*)&kernel_table0; // Level 0
+ *Table1 = (void*)MM_TABLE1KERN; // Level 1
+ }
+ else {
+ *Table0 = (void*)MM_TABLE0USER;
+ *Table1 = (void*)MM_TABLE1USER;
+ }
+}
+
+int MM_int_AllocateCoarse(tVAddr VAddr, int Domain)
+{
+ Uint32 *table0, *table1;
+ Uint32 *desc;
+ tPAddr paddr;
+
+ ENTER("xVAddr iDomain", VAddr, Domain);
+
+ MM_int_GetTables(VAddr, &table0, &table1);
+
+ VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page
+
+ desc = &table0[ VAddr>>20];
+ LOG("desc = %p", desc);
+
+ // table0: 4 bytes = 1 MiB
+
+ LOG("desc[0] = %x", desc[0]);
+ LOG("desc[1] = %x", desc[1]);
+ LOG("desc[2] = %x", desc[2]);
+ LOG("desc[3] = %x", desc[3]);
+
+ if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0
+ || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 )
+ {
+ // Error?
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ paddr = MM_AllocPhys();
+ if( !paddr )
+ {
+ // Error
+ LEAVE('i', 2);
+ return 2;
+ }
+
+ *desc = paddr | (Domain << 5) | 1;
+ desc[1] = desc[0] + 0x400;
+ desc[2] = desc[0] + 0x800;
+ desc[3] = desc[0] + 0xC00;
+
+ if( VAddr < 0x80000000 ) {
+ USRFRACTAL(VAddr) = paddr | 0x13;
+ }
+ else {
+ FRACTAL(table1, VAddr) = paddr | 0x13;
+ }
+
+ // TLBIALL
+ TLBIALL();
+
+ memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 );
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
+{
+ Uint32 *table0, *table1;
+ Uint32 *desc;
+
+ ENTER("pVAddr ppi", VAddr, pi);
+
+ MM_int_GetTables(VAddr, &table0, &table1);
+
+ desc = &table0[ VAddr >> 20 ];
+ LOG("desc = %p", desc);
+
+ switch(pi->Size)
+ {
+ case 12: // Small Page
+ case 16: // Large Page
+ LOG("Page");
+ if( (*desc & 3) == 0 ) {
+ MM_int_AllocateCoarse( VAddr, pi->Domain );
+ }
+ desc = &table1[ VAddr >> 12 ];
+ LOG("desc (2) = %p", desc);
+ if( pi->Size == 12 )
+ {
+ // Small page
+ // - Error if overwriting a large page
+ if( (*desc & 3) == 1 ) LEAVE_RET('i', 1);
+ if( pi->PhysAddr == 0 ) {
+ *desc = 0;
+ TLBIMVA( VAddr );
+ DCCMVAC( (tVAddr) desc );
+ #warning "HACK: TLBIALL"
+ TLBIALL();
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ *desc = (pi->PhysAddr & 0xFFFFF000) | 2;
+ if(!pi->bExecutable) *desc |= 1; // XN
+ if(!pi->bGlobal) *desc |= 1 << 11; // nG
+ if( pi->bShared) *desc |= 1 << 10; // S
+ *desc |= (pi->AP & 3) << 4; // AP
+ *desc |= ((pi->AP >> 2) & 1) << 9; // APX
+ TLBIMVA( VAddr );
+ #warning "HACK: TLBIALL"
+ TLBIALL();
+ DCCMVAC( (tVAddr) desc );
+ LEAVE('i', 0);
+ return 0;
+ }
+ else
+ {
+ // Large page
+ Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo");
+ }
+ break;
+ case 20: // Section or unmapped
+ Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo");
+ break;
+ case 24: // Supersection
+ // Error if not aligned
+ if( VAddr & 0xFFFFFF ) {
+ LEAVE('i', 1);
+ return 1;
+ }
+ if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18))) )
+ {
+ if( pi->PhysAddr == 0 ) {
+ *desc = 0;
+ }
+ else {
+ // Apply
+ *desc = pi->PhysAddr & 0xFF000000;
+// *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20;
+// *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5;
+ *desc |= 2 | (1 << 18);
+ }
+ // TODO: Apply to all entries
+ Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections");
+ LEAVE('i', 0);
+ return 0;
+ }
+ // TODO: What here?
+ Log_Warning("MMVirt", "TODO: 24-bit not on supersection?");
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ LEAVE('i', 1);
+ return 1;
+}
+
+int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
+{
+ Uint32 *table0, *table1;
+ Uint32 desc;
+
+// LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi);
+
+ MM_int_GetTables(VAddr, &table0, &table1);
+
+ desc = table0[ VAddr >> 20 ];
+
+// if( VAddr > 0x90000000)
+// LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc);
+
+ pi->bExecutable = 1;
+ pi->bGlobal = 0;
+ pi->bShared = 0;
+ pi->AP = 0;
+
+ switch( (desc & 3) )
+ {
+ // 0: Unmapped
+ case 0:
+ pi->PhysAddr = 0;
+ pi->Size = 20;
+ pi->Domain = 0;
+ return 1;
+
+ // 1: Coarse page table
+ case 1:
+ // Domain from top level table
+ pi->Domain = (desc >> 5) & 7;
+ // Get next level
+ desc = table1[ VAddr >> 12 ];
+// LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc);
+ switch( desc & 3 )
+ {
+ // 0: Unmapped
+ case 0:
+ pi->Size = 12;
+ return 1;
+ // 1: Large Page (64KiB)
+ case 1:
+ pi->Size = 16;
+ pi->PhysAddr = desc & 0xFFFF0000;
+ pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
+ pi->bExecutable = !(desc & 0x8000);
+ pi->bShared = (desc >> 10) & 1;
+ return 0;
+ // 2/3: Small page
+ case 2:
+ case 3:
+ pi->Size = 12;
+ pi->PhysAddr = desc & 0xFFFFF000;
+ pi->bExecutable = !(desc & 1);
+ pi->bGlobal = !(desc >> 11);
+ pi->bShared = (desc >> 10) & 1;
+ pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
+ return 0;
+ }
+ return 1;
+
+ // 2: Section (or Supersection)
+ case 2:
+ if( desc & (1 << 18) ) {
+ // Supersection
+ pi->PhysAddr = desc & 0xFF000000;
+ pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32;
+ pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36;
+ pi->Size = 24;
+ pi->Domain = 0; // Supersections default to zero
+ pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
+ return 0;
+ }
+
+ // Section
+ pi->PhysAddr = desc & 0xFFF80000;
+ pi->Size = 20;
+ pi->Domain = (desc >> 5) & 7;
+ pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
+ return 0;
+
+ // 3: Reserved (invalid)
+ case 3:
+ pi->PhysAddr = 0;
+ pi->Size = 20;
+ pi->Domain = 0;
+ return 2;
+ }
+ return 2;
+}
+
+// --- Exports ---
+tPAddr MM_GetPhysAddr(tVAddr VAddr)
+{
+ tMM_PageInfo pi;
+ if( MM_int_GetPageInfo(VAddr, &pi) )
+ return 0;
+ return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1));
+}
+
+Uint MM_GetFlags(tVAddr VAddr)
+{
+ tMM_PageInfo pi;
+ int ret;
+
+ if( MM_int_GetPageInfo(VAddr, &pi) )
+ return 0;
+
+ ret = 0;
+
+ switch(pi.AP)
+ {
+ case 0:
+ break;
+ case AP_KRW_ONLY:
+ ret |= MM_PFLAG_KERNEL;
+ break;
+ case AP_KRO_ONLY:
+ ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO;
+ break;
+ case AP_RW_BOTH:
+ break;
+ case AP_RO_BOTH:
+ ret |= MM_PFLAG_COW;
+ break;
+ case AP_RO_USER:
+ ret |= MM_PFLAG_RO;
+ break;
+ }
+
+ if( pi.bExecutable ) ret |= MM_PFLAG_EXEC;
+ return ret;
+}
+
+void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
+{
+ tMM_PageInfo pi;
+ Uint curFlags;
+
+ if( MM_int_GetPageInfo(VAddr, &pi) )
+ return ;
+
+ curFlags = MM_GetFlags(VAddr);
+ if( (curFlags & Mask) == Flags )
+ return ;
+ curFlags &= ~Mask;
+ curFlags |= Flags;
+
+ if( curFlags & MM_PFLAG_COW )
+ pi.AP = AP_RO_BOTH;
+ else
+ {
+ switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) )
+ {
+ case 0:
+ pi.AP = AP_RW_BOTH; break;
+ case MM_PFLAG_KERNEL:
+ pi.AP = AP_KRW_ONLY; break;
+ case MM_PFLAG_RO:
+ pi.AP = AP_RO_USER; break;
+ case MM_PFLAG_KERNEL|MM_PFLAG_RO:
+ pi.AP = AP_KRO_ONLY; break;
+ }
+ }
+
+ pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC);
+
+ MM_int_SetPageInfo(VAddr, &pi);
+}
+
+int MM_IsValidBuffer(tVAddr Addr, size_t Size)
+{
+ tMM_PageInfo pi;
+ int bUser = 0;
+
+ Size += Addr & (PAGE_SIZE-1);
+ Addr &= ~(PAGE_SIZE-1);
+
+ if( MM_int_GetPageInfo(Addr, &pi) ) return 0;
+ Addr += PAGE_SIZE;
+
+ if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY)
+ bUser = 1;
+
+ while( Size >= PAGE_SIZE )
+ {
+ if( MM_int_GetPageInfo(Addr, &pi) )
+ return 0;
+ if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY))
+ return 0;
+ Addr += PAGE_SIZE;
+ Size -= PAGE_SIZE;
+ }
+
+ return 1;
+}
+
+int MM_Map(tVAddr VAddr, tPAddr PAddr)
+{
+ tMM_PageInfo pi = {0};
+ #if TRACE_MAPS
+ Log("MM_Map %P=>%p", PAddr, VAddr);
+ #endif
+
+ pi.PhysAddr = PAddr;
+ pi.Size = 12;
+ if(VAddr < USER_STACK_TOP)
+ pi.AP = AP_RW_BOTH;
+ else
+ pi.AP = AP_KRW_ONLY; // Kernel Read/Write
+ pi.bExecutable = 1;
+ if( MM_int_SetPageInfo(VAddr, &pi) ) {
+// MM_DerefPhys(pi.PhysAddr);
+ return 0;
+ }
+ return pi.PhysAddr;
+}
+
+tPAddr MM_Allocate(tVAddr VAddr)
+{
+ tMM_PageInfo pi = {0};
+
+ ENTER("pVAddr", VAddr);
+
+ pi.PhysAddr = MM_AllocPhys();
+ if( pi.PhysAddr == 0 ) LEAVE_RET('i', 0);
+ pi.Size = 12;
+ if(VAddr < USER_STACK_TOP)
+ pi.AP = AP_RW_BOTH;
+ else
+ pi.AP = AP_KRW_ONLY;
+ pi.bExecutable = 0;
+ if( MM_int_SetPageInfo(VAddr, &pi) ) {
+ MM_DerefPhys(pi.PhysAddr);
+ LEAVE('i', 0);
+ return 0;
+ }
+ LEAVE('x', pi.PhysAddr);
+ return pi.PhysAddr;
+}
+
+tPAddr MM_AllocateZero(tVAddr VAddr)
+{
+ if( !giMM_ZeroPage ) {
+ giMM_ZeroPage = MM_Allocate(VAddr);
+ MM_RefPhys(giMM_ZeroPage);
+ memset((void*)VAddr, 0, PAGE_SIZE);
+ }
+ else {
+ MM_RefPhys(giMM_ZeroPage);
+ MM_Map(VAddr, giMM_ZeroPage);
+ }
+ MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
+ return giMM_ZeroPage;
+}
+
+void MM_Deallocate(tVAddr VAddr)
+{
+ tMM_PageInfo pi;
+
+ if( MM_int_GetPageInfo(VAddr, &pi) ) return ;
+ if( pi.PhysAddr == 0 ) return;
+ MM_DerefPhys(pi.PhysAddr);
+
+ pi.PhysAddr = 0;
+ pi.AP = 0;
+ pi.bExecutable = 0;
+ MM_int_SetPageInfo(VAddr, &pi);
+}
+
+tPAddr MM_AllocateRootTable(void)
+{
+ tPAddr ret;
+
+ ret = MM_AllocPhysRange(2, -1);
+ if( ret & 0x1000 ) {
+ MM_DerefPhys(ret);
+ MM_DerefPhys(ret+0x1000);
+ ret = MM_AllocPhysRange(3, -1);
+ if( ret & 0x1000 ) {
+ MM_DerefPhys(ret);
+ ret += 0x1000;
+// Log("MM_AllocateRootTable: Second try not aligned, %P", ret);
+ }
+ else {
+ MM_DerefPhys(ret + 0x2000);
+// Log("MM_AllocateRootTable: Second try aligned, %P", ret);
+ }
+ }
+// else
+// Log("MM_AllocateRootTable: Got it in one, %P", ret);
+ return ret;
+}
+
+void MM_int_CloneTable(Uint32 *DestEnt, int Table)
+{
+ tPAddr table;
+ Uint32 *tmp_map;
+ Uint32 *cur = (void*)MM_TABLE1USER;
+// Uint32 *cur = &FRACTAL(MM_TABLE1USER,0);
+ int i;
+
+ table = MM_AllocPhys();
+ if(!table) return ;
+
+ cur += 256*Table;
+
+ tmp_map = (void*)MM_MapTemp(table);
+
+ for( i = 0; i < 1024; i ++ )
+ {
+// Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]);
+ switch(cur[i] & 3)
+ {
+ case 0: tmp_map[i] = 0; break;
+ case 1:
+ tmp_map[i] = 0;
+ Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000);
+ // Large page?
+ break;
+ case 2:
+ case 3:
+ // Small page
+ // - If full RW
+// Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230);
+ if( (cur[i] & 0x230) == 0x010 )
+ {
+ void *dst, *src;
+ tPAddr newpage;
+ newpage = MM_AllocPhys();
+ src = (void*)( (Table*256+i)*0x1000 );
+ dst = (void*)MM_MapTemp(newpage);
+// Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF);
+ memcpy(dst, src, PAGE_SIZE);
+ MM_FreeTemp( (tVAddr)dst );
+ tmp_map[i] = newpage | (cur[i] & 0xFFF);
+ }
+ else
+ {
+ if( (cur[i] & 0x230) == 0x030 )
+ cur[i] |= 0x200; // Set to full RO (Full RO=COW, User RO = RO)
+ tmp_map[i] = cur[i];
+ MM_RefPhys( tmp_map[i] & ~0xFFF );
+ }
+ break;
+ }
+ }
+ MM_FreeTemp( (tVAddr) tmp_map );
+
+ DestEnt[0] = table + 0*0x400 + 1;
+ DestEnt[1] = table + 1*0x400 + 1;
+ DestEnt[2] = table + 2*0x400 + 1;
+ DestEnt[3] = table + 3*0x400 + 1;
+}
+
+tPAddr MM_Clone(void)
+{
+ tPAddr ret;
+ Uint32 *new_lvl1_1, *new_lvl1_2, *cur;
+ Uint32 *tmp_map;
+ int i;
+
+// MM_DumpTables(0, KERNEL_BASE);
+
+ ret = MM_AllocateRootTable();
+
+ cur = (void*)MM_TABLE0USER;
+ new_lvl1_1 = (void*)MM_MapTemp(ret);
+ new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000);
+ tmp_map = new_lvl1_1;
+ for( i = 0; i < 0x800-4; i ++ )
+ {
+ // HACK! Ignore the original identity mapping
+ if( i == 0 && Threads_GetTID() == 0 ) {
+ tmp_map[0] = 0;
+ continue;
+ }
+ if( i == 0x400 )
+ tmp_map = &new_lvl1_2[-0x400];
+ switch( cur[i] & 3 )
+ {
+ case 0: tmp_map[i] = 0; break;
+ case 1:
+ MM_int_CloneTable(&tmp_map[i], i);
+ i += 3; // Tables are alocated in blocks of 4
+ break;
+ case 2:
+ case 3:
+ Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i);
+ tmp_map[i] = 0;
+ break;
+ }
+ }
+
+ // Allocate Fractal table
+ {
+ int j, num;
+ tPAddr tmp = MM_AllocPhys();
+ Uint32 *table = (void*)MM_MapTemp(tmp);
+ Uint32 sp;
+ register Uint32 __SP asm("sp");
+
+ // Map table to last 4MiB of user space
+ new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1;
+ new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1;
+ new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1;
+ new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1;
+
+ tmp_map = new_lvl1_1;
+ for( j = 0; j < 512; j ++ )
+ {
+ if( j == 256 )
+ tmp_map = &new_lvl1_2[-0x400];
+ if( (tmp_map[j*4] & 3) == 1 )
+ {
+ table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00;
+ table[j] |= 0x813; // nG, Kernel Only, Small page, XN
+ }
+ else
+ table[j] = 0;
+ }
+ // Fractal
+ table[j++] = (ret + 0x0000) | 0x813;
+ table[j++] = (ret + 0x1000) | 0x813;
+ // Nuke the rest
+ for( ; j < 1024; j ++ )
+ table[j] = 0;
+
+ // Get kernel stack bottom
+ sp = __SP & ~(MM_KSTACK_SIZE-1);
+ j = (sp / 0x1000) % 1024;
+ num = MM_KSTACK_SIZE/0x1000;
+
+// Log("num = %i, sp = %p, j = %i", num, sp, j);
+
+ // Copy stack pages
+ for(; num--; j ++, sp += 0x1000)
+ {
+ tVAddr page;
+ void *tmp_page;
+
+ page = MM_AllocPhys();
+// Log("page = %P", page);
+ table[j] = page | 0x813;
+
+ tmp_page = (void*)MM_MapTemp(page);
+ memcpy(tmp_page, (void*)sp, 0x1000);
+ MM_FreeTemp( (tVAddr) tmp_page );
+ }
+
+ MM_FreeTemp( (tVAddr)table );
+ }
+
+ MM_FreeTemp( (tVAddr)new_lvl1_1 );
+ MM_FreeTemp( (tVAddr)new_lvl1_2 );
+
+// Log("MM_Clone: ret = %P", ret);
+
+ return ret;
+}
+
+void MM_ClearUser(void)
+{
+ int i, j;
+ const int user_table_count = USER_STACK_TOP / (256*0x1000);
+ Uint32 *cur = (void*)MM_TABLE0USER;
+ Uint32 *tab;
+
+// MM_DumpTables(0, 0x80000000);
+
+// Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4);
+
+ for( i = 0; i < user_table_count; i ++ )
+ {
+ switch( cur[i] & 3 )
+ {
+ case 0: break; // Already unmapped
+ case 1: // Sub pages
+ tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
+ for( j = 0; j < 1024; j ++ )
+ {
+ switch( tab[j] & 3 )
+ {
+ case 0: break; // Unmapped
+ case 1:
+ Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser");
+ break;
+ case 2:
+ case 3:
+ MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) );
+ break;
+ }
+ }
+ MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) );
+ cur[i+0] = 0;
+ cur[i+1] = 0;
+ cur[i+2] = 0;
+ i += 3;
+ break;
+ case 2:
+ case 3:
+ Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser");
+ break;
+ }
+ cur[i] = 0;
+ }
+
+ // Final block of 4 tables are KStack
+ i = 0x800 - 4;
+
+ // Clear out unused stacks
+ {
+ register Uint32 __SP asm("sp");
+ int cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024;
+
+ tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
+
+ // First 512 is the Table1 mapping + 2 for Table0 mapping
+ for( j = 512+2; j < 1024; j ++ )
+ {
+ // Skip current stack
+ if( j == cur_stack_base ) {
+ j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1;
+ continue ;
+ }
+ if( !(tab[j] & 3) ) continue;
+ ASSERT( (tab[j] & 3) == 2 );
+ MM_DerefPhys( tab[j] & ~(PAGE_SIZE) );
+ tab[j] = 0;
+ }
+ }
+
+
+// MM_DumpTables(0, 0x80000000);
+}
+
+tVAddr MM_MapTemp(tPAddr PAddr)
+{
+ tVAddr ret;
+ tMM_PageInfo pi;
+
+ for( ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE )
+ {
+ if( MM_int_GetPageInfo(ret, &pi) == 0 )
+ continue;
+
+// Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0));
+ MM_RefPhys(PAddr); // Counter the MM_Deallocate in FreeTemp
+ MM_Map(ret, PAddr);
+
+ return ret;
+ }
+ Log_Warning("MMVirt", "MM_MapTemp: All slots taken");
+ return 0;
+}
+
+void MM_FreeTemp(tVAddr VAddr)
+{
+ if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) {
+ Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr);
+ return ;
+ }
+
+ MM_Deallocate(VAddr);
+}
+
+tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages)
+{
+ tVAddr ret;
+ int i;
+ tMM_PageInfo pi;
+
+ ENTER("xPAddr iNPages", PAddr, NPages);
+
+ // Scan for a location
+ for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE )
+ {
+// LOG("checking %p", ret);
+ // Check if there is `NPages` free pages
+ for( i = 0; i < NPages; i ++ )
+ {
+ if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 )
+ break;
+ }
+ // Nope, jump to after the used page found and try again
+// LOG("i = %i, ==? %i", i, NPages);
+ if( i != NPages ) {
+ ret += i * PAGE_SIZE;
+ continue ;
+ }
+
+ // Map the pages
+ for( i = 0; i < NPages; i ++ )
+ MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE);
+ // and return
+ LEAVE('p', ret);
+ return ret;
+ }
+ Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages);
+ LEAVE('p', 0);
+ return 0;
+}
+
+tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr)
+{
+ tPAddr phys;
+ tVAddr ret;
+
+ phys = MM_AllocPhysRange(Pages, MaxBits);
+ if(!phys) {
+ Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages);
+ return 0;
+ }
+
+ ret = MM_MapHWPages(phys, Pages);
+ *PAddr = phys;
+
+ return ret;
+}
+
+void MM_UnmapHWPages(tVAddr Vaddr, Uint Number)
+{
+ Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages");
+}
+
+tVAddr MM_NewKStack(int bShared)
+{
+ tVAddr min_addr, max_addr;
+ tVAddr addr, ofs;
+
+ if( bShared ) {
+ min_addr = MM_GLOBALSTACKS;
+ max_addr = MM_GLOBALSTACKS_END;
+ }
+ else {
+ min_addr = MM_KSTACK_BASE;
+ max_addr = MM_KSTACK_END;
+ }
+
+ // Locate a free slot
+ for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE )
+ {
+ tMM_PageInfo pi;
+ if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) ) break;
+ }
+
+ // Check for an error
+ if(addr >= max_addr) {
+ return 0;
+ }
+
+ // 1 guard page
+ for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE )
+ {
+ if( MM_Allocate(addr + ofs) == 0 )
+ {
+ while(ofs)
+ {
+ ofs -= PAGE_SIZE;
+ MM_Deallocate(addr + ofs);
+ }
+ Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate");
+ return 0;
+ }
+ }
+ return addr + ofs;
+}
+
+tVAddr MM_NewUserStack(void)
+{
+ tVAddr addr, ofs;
+
+ addr = USER_STACK_TOP - USER_STACK_SIZE;
+ if( MM_GetPhysAddr(addr + PAGE_SIZE) ) {
+ Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken",
+ addr + PAGE_SIZE
+ );
+ return 0;
+ }
+
+ // 1 guard page
+ for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE )
+ {
+ tPAddr rv;
+ if(ofs >= USER_STACK_SIZE - USER_STACK_COMM)
+ rv = MM_Allocate(addr + ofs);
+ else
+ rv = MM_AllocateZero(addr + ofs);
+ if(rv == 0)
+ {
+ while(ofs)
+ {
+ ofs -= PAGE_SIZE;
+ MM_Deallocate(addr + ofs);
+ }
+ Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate");
+ return 0;
+ }
+ MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL);
+ }
+// Log("Return %p", addr + ofs);
+// MM_DumpTables(0, 0x80000000);
+ return addr + ofs;
+}
+
+void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info)
+{
+ if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage )
+ {
+ Debug("%p => %8s - 0x%7x %i %x %s",
+ Start, "ZERO", Len,
+ Info->Domain, Info->AP,
+ Info->bGlobal ? "G" : "nG"
+ );
+ }
+ else
+ {
+ Debug("%p => %8x - 0x%7x %i %x %s",
+ Start, Info->PhysAddr-Len, Len,
+ Info->Domain, Info->AP,
+ Info->bGlobal ? "G" : "nG"
+ );
+ }
+}
+
+void MM_DumpTables(tVAddr Start, tVAddr End)
+{
+ tVAddr range_start = 0, addr;
+ tMM_PageInfo pi, pi_old;
+ int i = 0, inRange=0;
+
+ memset(&pi_old, 0, sizeof(pi_old));
+
+ Debug("Page Table Dump (%p to %p):", Start, End);
+ range_start = Start;
+ for( addr = Start; i == 0 || (addr && addr < End); i = 1 )
+ {
+ int rv;
+// Log("addr = %p", addr);
+ rv = MM_int_GetPageInfo(addr, &pi);
+ if( rv
+ || pi.Size != pi_old.Size
+ || pi.Domain != pi_old.Domain
+ || pi.AP != pi_old.AP
+ || pi.bGlobal != pi_old.bGlobal
+ || pi_old.PhysAddr != pi.PhysAddr )
+ {
+ if(inRange) {
+ MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old);
+ }
+ addr &= ~((1 << pi.Size)-1);
+ range_start = addr;
+ }
+
+ pi_old = pi;
+ // Handle the zero page
+ if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage )
+ pi_old.PhysAddr += 1 << pi_old.Size;
+ addr += 1 << pi_old.Size;
+ inRange = (rv == 0);
+ }
+ if(inRange)
+ MM_int_DumpTableEnt(range_start, addr - range_start, &pi);
+ Debug("Done");
+}
+
+// NOTE: Runs in abort context, not much difference, just a smaller stack
+void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch)
+{
+ int rv;
+ tMM_PageInfo pi;
+
+ rv = MM_int_GetPageInfo(Addr, &pi);
+
+ // Check for COW
+ if( rv == 0 && pi.AP == AP_RO_BOTH )
+ {
+ pi.AP = AP_RW_BOTH;
+ if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage )
+ {
+ tPAddr newpage;
+ newpage = MM_AllocPhys();
+ if( !newpage ) {
+ Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO");
+ for(;;);
+ }
+
+ #if TRACE_COW
+ Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC,
+ newpage, MM_GetRefCount(pi.PhysAddr));
+ #endif
+
+ MM_DerefPhys(pi.PhysAddr);
+ pi.PhysAddr = newpage;
+ pi.AP = AP_RW_BOTH;
+ MM_int_SetPageInfo(Addr, &pi);
+
+ memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE );
+
+ return ;
+ }
+ else if( MM_GetRefCount(pi.PhysAddr) > 1 )
+ {
+ // Duplicate the page
+ tPAddr newpage;
+ void *dst, *src;
+
+ newpage = MM_AllocPhys();
+ if(!newpage) {
+ Log_Error("MMVirt", "Unable to allocate new page for COW");
+ for(;;);
+ }
+ dst = (void*)MM_MapTemp(newpage);
+ src = (void*)(Addr & ~(PAGE_SIZE-1));
+ memcpy( dst, src, PAGE_SIZE );
+ MM_FreeTemp( (tVAddr)dst );
+
+ #if TRACE_COW
+ Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC,
+ pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr));
+ #endif
+
+ MM_DerefPhys(pi.PhysAddr);
+ pi.PhysAddr = newpage;
+ }
+ #if TRACE_COW
+ else {
+ Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P",
+ Addr, PC, pi.PhysAddr);
+ }
+ #endif
+ // Unset COW
+ pi.AP = AP_RW_BOTH;
+ MM_int_SetPageInfo(Addr, &pi);
+ return ;
+ }
+
+
+ Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR,
+ (bPrefetch ? " - Prefetch" : "")
+ );
+ if( Addr < 0x80000000 )
+ MM_DumpTables(0, 0x80000000);
+ else
+ MM_DumpTables(0x80000000, -1);
+ for(;;);
+}
+
--- /dev/null
+/*
+ *
+ */
+#include <acess.h>
+#include <drv_pci_int.h>
+
+// Realview
+//#define PCI_BASE 0x60000000
+
+//#define PCI_BASE 0xF0400000 // VMM Mapping
+#define PCI_BASE 0
+
+// === CODE ===
+void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data)
+{
+ #if PCI_BASE
+ Uint32 address = PCI_BASE | Addr;
+ *(Uint32*)(address) = Data;
+ #else
+ #endif
+}
+
+Uint32 PCI_CfgReadDWord(Uint32 Addr)
+{
+ #if PCI_BASE
+ Uint32 address = PCI_BASE | Addr;
+ return *(Uint32*)address;
+ #else
+ return 0xFFFFFFFF;
+ #endif
+}
+
--- /dev/null
+/*
+ * Acess2 ARM
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/arm7/proc.S
+ * - Process management assembly
+ */
+
+#include "include/assembly.h"
+
+.globl KernelThreadHeader
+@ SP+12: Argument 1
+@ SP+8: Argument Count
+@ SP+4: Function
+@ SP+0: Thread Pointer
+KernelThreadHeader:
+ ldr r0, [sp],#4
+ @ TODO: Do something with the thread pointer
+
+ ldr r4, [sp],#4 @ Function
+ @ Get argument
+ ldr r0, [sp],#4
+
+ blx r4
+
+ ldr r0, =0
+ bl Threads_Exit
+ b .
+
+.globl SwitchTask
+@ R0: New stack
+@ R1: Pointer to where to save old stack
+@ R2: New IP
+@ R3: Pointer to save old IP
+@ SP+0: New address space
+SwitchTask:
+ push {r4-r12,lr}
+
+ @ Save IP
+ ldr r4, =.return
+ str r4, [r3]
+ @ Save SP
+ str sp, [r1]
+
+ @ Only update TTBR0 if the task has an explicit address space
+ ldr r1, [sp,#4*10]
+ tst r1, r1
+ mcrne p15, 0, r1, c2, c0, 0 @ Set TTBR0 to r0
+ mov r1, #0
+ mcrne p15, 0, r1, c8, c7, 0 @ TLBIALL - Invalidate all
+
+ @ Restore SP
+ mov sp, r0
+
+ bx r2
+
+.return:
+ pop {r4-r12,pc}
+
+.extern MM_Clone
+.extern MM_DumpTables
+.globl Proc_CloneInt
+Proc_CloneInt:
+ @ R0: SP Destination
+ @ R1: Mem Destination
+ push {r4-r12,lr}
+ mov r4, r1 @ Save mem destination
+ str sp, [r0] @ Save SP to SP dest
+
+ bl MM_Clone
+ str r0, [r4] @ Save clone return to Mem Dest
+
+ ldr r0, =Proc_CloneInt_new
+ pop {r4-r12,pc}
+Proc_CloneInt_new:
+ mov r0, #0
+ pop {r4-r12,pc}
+
+@ R0: New user SP
+@ Return: Old user SP
+.globl Proc_int_SwapUserSP
+Proc_int_SwapUserSP:
+ cps #31 @ Go to system mode
+ mov r1, sp
+ tst r0, r0 @ Only update if non-zero
+ movne sp, r0
+ mov r0, r1
+ cps #19
+ mov pc, lr
+
+.section .usertext, "ax"
+.globl Proc_int_DropToUser
+@ R0: User IP
+@ R1: User SP
+Proc_int_DropToUser:
+ cps #16
+ mov sp, r1
+ mov pc, r0
+
+.section .rodata
+csProc_CloneInt_NewTaskMessage:
+ .asciz "New task PC=%p, R4=%p, sp=%p"
+csProc_CloneInt_OldTaskMessage:
+ .asciz "Parent task PC=%p, R4=%p, SP=%p"
--- /dev/null
+/*
+ * Acess2
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/arm7/proc.c
+ * - ARM7 Process Switching
+ */
+#include <acess.h>
+#include <threads_int.h>
+#include <hal_proc.h>
+
+// === IMPORTS ===
+extern tThread gThreadZero;
+extern tProcess gProcessZero;
+extern void SwitchTask(Uint32 NewSP, Uint32 *OldSP, Uint32 NewIP, Uint32 *OldIP, Uint32 MemPtr);
+extern void KernelThreadHeader(void); // Actually takes args on stack
+extern void Proc_int_DropToUser(Uint32 IP, Uint32 SP) NORETURN __attribute__((long_call));
+extern Uint32 Proc_int_SwapUserSP(Uint32 NewSP);
+extern Uint32 Proc_CloneInt(Uint32 *SP, Uint32 *MemPtr);
+extern tVAddr MM_NewKStack(int bGlobal); // TODO: Move out into a header
+extern tVAddr MM_NewUserStack(void);
+extern char kernel_table0[];
+
+// === PROTOTYPES ===
+void Proc_IdleThread(void *unused);
+
+// === GLOBALS ===
+tThread *gpCurrentThread = &gThreadZero;
+tThread *gpIdleThread = NULL;
+
+// === CODE ===
+void ArchThreads_Init(void)
+{
+ gProcessZero.MemState.Base = (tPAddr)&kernel_table0 - KERNEL_BASE;
+}
+
+void Proc_IdleThread(void *unused)
+{
+ Threads_SetPriority(gpIdleThread, -1);
+ for(;;) {
+ Proc_Reschedule();
+ __asm__ __volatile__ ("wfi");
+ }
+}
+
+void Proc_Start(void)
+{
+ tTID tid;
+
+ tid = Proc_NewKThread( Proc_IdleThread, NULL );
+ gpIdleThread = Threads_GetThread(tid);
+ gpIdleThread->ThreadName = (char*)"Idle Thread";
+}
+
+int GetCPUNum(void)
+{
+ return 0;
+}
+
+tThread *Proc_GetCurThread(void)
+{
+ return gpCurrentThread;
+}
+
+void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
+{
+ Uint32 *usr_sp;
+ int i;
+ const char **envp;
+ tVAddr delta;
+
+// Log_Debug("Proc", "Proc_StartUser: (Entrypoint=%p, Base=%p, ArgC=%i, ArgV=%p, DataSize=0x%x)",
+// Entrypoint, Base, ArgC, ArgV, DataSize);
+
+ // Write data to the user's stack
+ usr_sp = (void*)MM_NewUserStack();
+ usr_sp -= (DataSize+3)/4;
+ memcpy(usr_sp, ArgV, DataSize);
+ free(ArgV);
+
+ // Adjust user's copy of the arguments
+ delta = (tVAddr)usr_sp - (tVAddr)ArgV;
+ ArgV = (void*)usr_sp;
+ for(i = 0; ArgV[i]; i ++) ArgV[i] += delta;
+ envp = &ArgV[i+1];
+ for(i = 0; envp[i]; i ++) envp[i] += delta;
+
+ *--usr_sp = (Uint32)envp;
+ *--usr_sp = (Uint32)ArgV;
+ *--usr_sp = (Uint32)ArgC;
+ *--usr_sp = Base;
+
+ // Drop to user code
+ Log_Debug("Proc", "Proc_int_DropToUser(%p, %p)", Entrypoint, usr_sp);
+ Proc_int_DropToUser(Entrypoint, (Uint32)usr_sp);
+}
+
+void Proc_ClearProcess(tProcess *Process)
+{
+ Log_Warning("Proc", "TODO: Nuke address space etc");
+}
+
+void Proc_ClearThread(tThread *Thread)
+{
+}
+
+tTID Proc_Clone(Uint Flags)
+{
+ tThread *new;
+ Uint32 pc, sp, mem;
+
+ new = Threads_CloneTCB(Flags);
+ if(!new) return -1;
+
+ // Actual clone magic
+ pc = Proc_CloneInt(&sp, &mem);
+ if(pc == 0) {
+ Log("Proc_Clone: In child");
+ return 0;
+ }
+
+ new->SavedState.IP = pc;
+ new->SavedState.SP = sp;
+ new->SavedState.UserSP = Proc_int_SwapUserSP(0);
+ new->SavedState.UserIP = Proc_GetCurThread()->SavedState.UserIP;
+ new->Process->MemState.Base = mem;
+
+ Threads_AddActive(new);
+
+ return new->TID;
+}
+
+int Proc_SpawnWorker( void (*Fnc)(void*), void *Ptr )
+{
+ tThread *new;
+ Uint32 sp;
+
+ new = Threads_CloneThreadZero();
+ if(!new) return -1;
+ if(new->ThreadName) free(new->ThreadName);
+ new->ThreadName = NULL;
+
+ new->KernelStack = MM_NewKStack(1);
+ if(!new->KernelStack) {
+ // TODO: Delete thread
+ Log_Error("Proc", "Unable to allocate kernel stack");
+ return -1;
+ }
+
+ sp = new->KernelStack;
+
+ *(Uint32*)(sp -= 4) = (Uint)Ptr;
+ *(Uint32*)(sp -= 4) = (Uint)Fnc;
+ *(Uint32*)(sp -= 4) = (Uint)new;
+
+ new->SavedState.SP = sp;
+ new->SavedState.IP = (Uint)KernelThreadHeader;
+
+ Threads_AddActive(new);
+
+ return new->TID;
+}
+
+tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr )
+{
+ tThread *new;
+ Uint32 sp;
+
+ new = Threads_CloneTCB(0);
+ if(!new) return -1;
+ free(new->ThreadName);
+ new->ThreadName = NULL;
+
+ // TODO: Non-shared stack
+ new->KernelStack = MM_NewKStack(1);
+ if(!new->KernelStack) {
+ // TODO: Delete thread
+ Log_Error("Proc", "Unable to allocate kernel stack");
+ return -1;
+ }
+
+ sp = new->KernelStack;
+
+ *(Uint32*)(sp -= 4) = (Uint)Ptr;
+ *(Uint32*)(sp -= 4) = (Uint)Fnc;
+ *(Uint32*)(sp -= 4) = (Uint)new;
+
+ new->SavedState.SP = sp;
+ new->SavedState.IP = (Uint)KernelThreadHeader;
+
+ Threads_AddActive(new);
+
+ return new->TID;
+}
+
+void Proc_CallFaultHandler(tThread *Thread)
+{
+
+}
+
+void Proc_Reschedule(void)
+{
+ tThread *cur, *next;
+
+ cur = gpCurrentThread;
+
+ next = Threads_GetNextToRun(0, cur);
+ if(!next) next = gpIdleThread;
+ if(!next || next == cur) return;
+
+ Log("Switching to %p (%i %s) IP=%p SP=%p TTBR0=%p UsrSP=%p",
+ next, next->TID, next->ThreadName,
+ next->SavedState.IP, next->SavedState.SP, next->Process->MemState.Base,
+ next->SavedState.UserSP
+ );
+
+ Log("Requested by %p", __builtin_return_address(0));
+
+ gpCurrentThread = next;
+
+ cur->SavedState.UserSP = Proc_int_SwapUserSP( next->SavedState.UserSP );
+
+ SwitchTask(
+ next->SavedState.SP, &cur->SavedState.SP,
+ next->SavedState.IP, &cur->SavedState.IP,
+ next->Process->MemState.Base
+ );
+
+}
+
+void Proc_DumpThreadCPUState(tThread *Thread)
+{
+
+}
+
--- /dev/null
+
+#include "include/assembly.h"
+#include "include/options.h"
+
+@
+@ Exception defs taken from ARM DDI 0406B
+@
+.section .init
+interrupt_vector_table:
+ivt_reset: b _start @ 0x00 Reset
+ivt_undef: b Undef_Handler @ 0x04 #UD
+ivt_svc: b SVC_Handler @ 0x08 SVC (used to be called SWI)
+ivt_prefetch: b PrefetchAbort @ 0x0C Prefetch abort
+ivt_data: b DataAbort @ 0x10 Data abort
+ivt_unused: b . @ 0x14 Not Used
+ivt_irq: b IRQHandler @ 0x18 IRQ
+ivt_fiq: b . @ 0x1C FIQ (Fast interrupt)
+
+.globl _start
+_start:
+ ldr r2, =UART0_PADDR
+ mov r1, #'A'
+ str r1, [r2]
+
+ ldr r0, =kernel_table0-KERNEL_BASE
+ mcr p15, 0, r0, c2, c0, 1 @ Set TTBR1 to r0
+ mcr p15, 0, r0, c2, c0, 0 @ Set TTBR0 to r0 too (for identity)
+
+ mov r1, #'c'
+ str r1, [r2]
+
+ mov r0, #1
+ mcr p15, 0, r0, c2, c0, 2 @ Set TTCR to 1 (50/50 split)
+
+ mov r1, #'e'
+ str r1, [r2]
+
+ mov r0, #3
+ mcr p15, 0, r0, c3, c0, 0 @ Set Domain 0 to Manager
+
+ mov r1, #'s'
+ str r1, [r2]
+
+ @ Enable VMSA
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #1
+ orr r0, r0, #1 << 23
+ mvn r1, #1 << 2
+ and r0, r0, r1
+ mcr p15, 0, r0, c1, c0, 0
+
+ @ HACK! Disable caching
+ mrc p15, 0, r1, c1, c0, 0
+
+ ldr r2, =0xF1000000
+ mov r1, #'s'
+ str r1, [r2]
+
+ @ Enable access faults on domains 0 & 1
+ mov r0, #0x55 @ 01010101b
+ mcr p15, 0, r0, c3, c0, 0
+
+ mov r1, #'2'
+ str r1, [r2]
+
+ @
+ @ Check for security extensions
+ @
+ mrc p15, 0, r0, c0, c1, 1
+ and r0, #0xF0
+ @ - Present
+ ldrne r0,=KERNEL_BASE
+ mcrne p15, 0, r0, c12, c0, 0 @ Set the VBAR (brings exceptions into high memory)
+ @ - Absent
+ mrceq p15, 0, r0, c1, c0, 0 @ Set SCTLR.V
+ orreq r0, #0x2000
+ mcreq p15, 0, r0, c1, c0, 0
+
+ mov r1, #'-'
+ str r1, [r2]
+
+ @ Prepare for interrupts
+ cps #18 @ IRQ Mode
+ ldr sp, =irqstack+0x1000 @ Set up stack
+ cps #23 @ Abort Mode
+ ldr sp, =abortstack+0x1000
+ cps #19
+
+ mov r1, #'a'
+ str r1, [r2]
+ mov r1, #'r'
+ str r1, [r2]
+ mov r1, #'m'
+ str r1, [r2]
+ mov r1, #13
+ str r1, [r2]
+ mov r1, #10
+ str r1, [r2]
+
+.extern bss_start
+.extern bss_size_div_4
+.zero_bss:
+ ldr r0, =bss_start
+ ldr r1, =bss_end
+ mov r3, #0
+.zero_bss_loop:
+ str r3, [r0],#4
+ cmp r0, r1
+ bls .zero_bss_loop
+
+.goto_c:
+ ldr sp, =0x80000000-8 @ Set up stack (top of user range)
+ ldr r0, =kmain
+ mov pc, r0
+1: b 1b @ Infinite loop
+
+.comm irqstack, 0x1000 @ ; 4KiB Stack
+.comm abortstack, 0x1000 @ ; 4KiB Stack
+
+.extern SyscallHandler
+SVC_Handler:
+@ sub lr, #4
+ srsdb sp!, #19 @ Save state to stack
+ cpsie ifa, #19 @ Ensure we're in supervisor with interrupts enabled (should already be there)
+ push {r0-r12}
+
+ ldr r4, [lr,#-4]
+ mvn r5, #0xFF000000
+ and r4, r5
+
+ tst r4, #0x1000
+ bne .arm_specifics
+
+ push {r4}
+
+ mov r0, sp
+ ldr r4, =SyscallHandler
+ blx r4
+
+@ ldr r0, =csSyscallPrintRetAddr
+@ ldr r1, [sp,#9*4+5*4]
+@ ldr r4, =Log
+@ blx r4
+
+ pop {r2} @ errno
+ pop {r0,r1} @ Ret/RetHi
+ add sp, #2*4 @ Saved r2/r3
+
+ pop {r4-r12}
+ rfeia sp! @ Pop state (actually RFEFD)
+.arm_specifics:
+ and r4, #0xFF
+ mov r0, r4 @ Number
+ mov r1, sp @ Arguments
+
+ ldr r4, =ARMv7_int_HandleSyscalls
+ blx r4
+
+ add sp, #4*4
+ pop {r4-r12}
+ rfeia sp!
+
+
+.globl gpIRQHandler
+gpIRQHandler: .long 0
+IRQ_saved_sp: .long 0
+IRQ_saved_lr: .long 0
+.globl IRQHandler
+IRQHandler:
+ sub lr, #4 @ Adjust LR to the correct value
+ srsdb sp!, #19 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
+ cps #19
+
+ PUSH_GPRS
+
+@ ldr r0, =csIRQ_Tag
+@ ldr r1, =csIRQ_Fmt
+@ ldr r4, =Log_Debug
+@ blx r4
+
+ @ Call the registered handler
+ ldr r0, gpIRQHandler
+ blx r0
+
+ @ Restore CPU state
+ POP_GPRS
+ cpsie i
+ rfeia sp! @ Pop state (actually RFEFD)
+ bx lr
+
+.globl DataAbort
+DataAbort:
+ sub lr, #8 @ Adjust LR to the correct value
+ srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
+@ cpsid ifa, #19
+ PUSH_GPRS
+
+ mov r3, #0 @ not a prefetch abort
+ mrc p15, 0, r2, c5, c0, 0 @ Read DFSR (Data Fault Status Register) to R2
+ mrc p15, 0, r1, c6, c0, 0 @ Read DFAR (Data Fault Address Register) into R1
+ mov r0, lr @ PC
+ ldr r4, =MM_PageFault
+ blx r4
+
+ POP_GPRS
+ rfeia sp! @ Pop state (actually RFEFD)
+
+.globl PrefetchAbort
+PrefetchAbort:
+ sub lr, #4 @ Adjust LR to the correct value
+ srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
+@ cpsid ifa, #19
+ PUSH_GPRS
+
+ ldr r0, =csAbort_Tag
+ ldr r1, =csPrefetchAbort_Fmt
+# mov r2, lr
+ mrc p15, 0, r2, c6, c0, 2 @ Read IFAR (Instruction Fault Address Register) into R3
+ mrc p15, 0, r3, c5, c0, 1 @ Read IFSR (Instruction Fault Status Register) into R3
+ ldr r5, =Log_Error
+ blx r5
+
+.loop:
+ wfi
+ b .loop
+.globl Undef_Handler
+Undef_Handler:
+ wfi
+ b Undef_Handler
+
+
+
+.section .rodata
+csIRQ_Tag:
+csAbort_Tag:
+ .asciz "ARMv7"
+csIRQ_Fmt:
+ .asciz "IRQ"
+csDataAbort_Fmt:
+ .asciz "Data Abort - %p accessed %p, DFSR=%x Unk:%x Unk:%x"
+csPrefetchAbort_Fmt:
+ .asciz "Prefetch Abort at %p, IFSR=%x"
+csSyscallPrintRetAddr:
+ .asciz "Syscall ret to %p"
+
+.section .padata
+.globl kernel_table0
+
+kernel_table0:
+ .long 0x00000402 @ Identity map the first 1 MiB
+ .rept 0x7FC - 1
+ .long 0
+ .endr
+ .long user_table1_map + 0x000 - KERNEL_BASE + 1 @ 0x7FC00000
+ .long user_table1_map + 0x400 - KERNEL_BASE + 1 @ 0x7FD00000
+ .long user_table1_map + 0x800 - KERNEL_BASE + 1 @ KStacks
+ .long user_table1_map + 0xC00 - KERNEL_BASE + 1
+ @ 0x80000000 - User/Kernel split
+ .long 0x00000402 @ Map first 4 MiB to 2GiB (KRW only)
+ .long 0x00100402 @
+ .long 0x00200402 @
+ .long 0x00300402 @
+ .rept 0xF00 - 0x800 - 4
+ .long 0
+ .endr
+#if PCI_PADDR
+ .long PCI_PADDR + 0*(1 << 20) + 0x402 @ Map PCI config space
+ .long PCI_PADDR + 1*(1 << 20) + 0x402
+ .long PCI_PADDR + 2*(1 << 20) + 0x402
+ .long PCI_PADDR + 3*(1 << 20) + 0x402
+ .long PCI_PADDR + 4*(1 << 20) + 0x402
+ .long PCI_PADDR + 5*(1 << 20) + 0x402
+ .long PCI_PADDR + 6*(1 << 20) + 0x402
+ .long PCI_PADDR + 7*(1 << 20) + 0x402
+ .long PCI_PADDR + 8*(1 << 20) + 0x402
+ .long PCI_PADDR + 9*(1 << 20) + 0x402
+ .long PCI_PADDR + 10*(1 << 20) + 0x402
+ .long PCI_PADDR + 11*(1 << 20) + 0x402
+ .long PCI_PADDR + 12*(1 << 20) + 0x402
+ .long PCI_PADDR + 13*(1 << 20) + 0x402
+ .long PCI_PADDR + 14*(1 << 20) + 0x402
+ .long PCI_PADDR + 15*(1 << 20) + 0x402
+#else
+ .rept 16
+ .long 0
+ .endr
+#endif
+ .long hwmap_table_0 + 0x000 - KERNEL_BASE + 1
+ .long hwmap_table_0 + 0x400 - KERNEL_BASE + 1
+ .long hwmap_table_0 + 0x800 - KERNEL_BASE + 1
+ .long hwmap_table_0 + 0xC00 - KERNEL_BASE + 1
+ .rept 0xFF8 - 0xF00 - 16 - 4
+ .long 0
+ .endr
+ @ Page fractals
+ .long kernel_table1_map + 0x000 - KERNEL_BASE + 1
+ .long kernel_table1_map + 0x400 - KERNEL_BASE + 1
+ .long kernel_table1_map + 0x800 - KERNEL_BASE + 1
+ .long kernel_table1_map + 0xC00 - KERNEL_BASE + 1
+ .long kernel_exception_map + 0x000 - KERNEL_BASE + 1
+ .long kernel_exception_map + 0x400 - KERNEL_BASE + 1
+ .long kernel_exception_map + 0x800 - KERNEL_BASE + 1
+ .long kernel_exception_map + 0xC00 - KERNEL_BASE + 1
+
+@ PID0 user table
+.globl user_table1_map
+@ User table1 data table (only the first half is needed)
+@ - Abused to provide kernel stacks in the unused half of the table
+user_table1_map: @ Size = 4KiB (only 2KiB used)
+ .rept 0x800/4-1
+ .long 0
+ .endr
+ .long user_table1_map - KERNEL_BASE + 0x13 @ ...1FF000 = 0x7FDFF000
+ @ Kernel stack zone
+ .long kernel_table0 + 0x0000 - KERNEL_BASE + 0x13 @ ...200000 = 0x7FE00000
+ .long kernel_table0 + 0x1000 - KERNEL_BASE + 0x13 @ ...201000 = 0x7FE01000
+ .rept (0x800/4)-(MM_KSTACK_SIZE/0x1000)-2
+ .long 0
+ .endr
+ #if MM_KSTACK_SIZE != 0x2000
+ #error Kernel stack size not changed in start.S
+ #endif
+ .long stack + 0x0000 - KERNEL_BASE + 0x13 @ Kernel Stack
+ .long stack + 0x1000 - KERNEL_BASE + 0x13 @
+
+.globl kernel_table1_map
+kernel_table1_map: @ Size = 4KiB
+ .rept (0xF00+16)/4
+ .long 0
+ .endr
+ .long hwmap_table_0 - KERNEL_BASE + 0x13
+ .rept 0xFF8/4 - (0xF00+16)/4 - 1
+ .long 0
+ .endr
+ .long kernel_table1_map - KERNEL_BASE + 0x13
+ .long kernel_exception_map - KERNEL_BASE + 0x13
+
+@ Hardware mappings
+.globl hwmap_table_0
+hwmap_table_0:
+ .long UART0_PADDR + 0x13 @ UART0
+ .rept 1024 - 1
+ .long 0
+ .endr
+.globl kernel_exception_map
+kernel_exception_map:
+ @ Padding
+ .rept 1024-256
+ .long 0
+ .endr
+ @ Align to nearly the end
+ .rept 256-16
+ .long 0
+ .endr
+ .long 0x212 @ Map first page for exceptions (Kernel RO, Execute)
+ .rept 16-1-2
+ .long 0
+ .endr
+ .long gUsertextPhysStart + 0x22 @ User .text (User RO, Kernel RW, because both is COW)
+ .long 0
+
+.section .padata
+stack:
+ .space MM_KSTACK_SIZE, 0 @ Original kernel stack
+
+// vim: ts=8 ft=armv7
+
--- /dev/null
+/*
+ * Acess2
+ *
+ * ARM7 Time code
+ * arch/arm7/time.c
+ */
+#include <acess.h>
+
+// === GLOBALS ===
+tTime giTimestamp;
+
+// === CODE ===
+tTime now(void)
+{
+ return giTimestamp;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/helpers.h
+ * - Misc helper functions for the arch code
+ */
+#ifndef _ARCH_HELPERS_H_
+#define _ARCH_HELPERS_H_
+
+// Divide
+// - Find what power of two times Den is > Num
+// - Iterate down in bit significance
+// > If the `N` value is greater than `D`, we can set this bit
+#define DEF_DIVMOD(s) Uint##s __divmod##s(Uint##s N, Uint##s D, Uint##s*Rem){\
+ Uint##s ret=0,add=1;\
+ while(N>=D&&add) {D<<=1;add<<=1;}\
+ while(add>1){\
+ add>>=1;D>>=1;\
+ if(N>=D){ret+=add;N-=D;}\
+ }\
+ if(Rem)*Rem = N;\
+ return ret;\
+}
+
+#endif
+
--- /dev/null
+#
+# Acess2 Kernel
+# m68k Architecture Makefile
+# arch/m68k/Makefile
+
+CFLAGS =
+
+A_OBJ = main.o debug.o lib.o time.o proc.o mm_virt.o mm_phys.o
+
+LDFLAGS += `$(CC) --print-libgcc-file-name`
+
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/debug.c
+ * - Debugging output
+ */
+#include <acess.h>
+
+// === PROTOTYPES ===
+void StartupPrint(const char *Str);
+void KernelPanic_SetMode(void);
+void KernelPanic_PutChar(char ch);
+
+// === CODE ===
+void StartupPrint(const char *Str)
+{
+}
+
+void KernelPanic_SetMode(void)
+{
+}
+
+void KernelPanic_PutChar(char ch)
+{
+}
+
--- /dev/null
+/*
+ * Acess2 M68000 port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/include/arch.h
+ * - Architectre config
+ */
+#ifndef _M68K_ARCH_H_
+#define _M68K_ARCH_H_
+
+#define INVLPTR ((void*)-1)
+#define BITS 32
+
+typedef unsigned long long Uint64;
+typedef unsigned long Uint32;
+typedef unsigned short Uint16;
+typedef unsigned char Uint8;
+
+typedef signed long long Sint64;
+typedef signed long Sint32;
+typedef signed short Sint16;
+typedef signed char Sint8;
+
+typedef unsigned int Uint;
+typedef unsigned int size_t;
+
+typedef char BOOL;
+
+typedef Uint32 tVAddr;
+typedef Uint32 tPAddr;
+
+struct sShortSpinlock
+{
+ int v;
+};
+
+// Non preemptive and no SMP, no need for these
+#define SHORTLOCK(lock) do{}while(0)
+#define SHORTREL(lock) do{}while(0)
+#define IS_LOCKED(lock) (0)
+#define CPU_HAS_LOCK(lock) (0)
+
+#define Debug_PutCharDebug(ch) do{}while(0)
+#define Debug_PutStringDebug(ch) do{}while(0)
+
+#define HALT() do{}while(0)
+
+#define USER_MAX 0
+
+#define PAGE_SIZE 0x1000
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 M68000 port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/include/mm_virt.h
+ * - Virtual memory addresses
+ */
+#ifndef _M68K_MM_VIRT_H_
+#define _M68K_MM_VIRT_H_
+
+#define MM_KHEAP_BASE 0
+#define MM_KHEAP_MAX 0
+
+#define MM_USER_MIN 0
+#define USER_LIB_MAX 0
+#define MM_MODULE_MIN 0
+#define MM_MODULE_MAX 0
+
+#define MM_PPD_HANDLES 0
+#define MM_KERNEL_VFS 0
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 M68000 port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/include/proc.h
+ * - Task management defs
+ */
+#ifndef _M68K_PROC_H_
+#define _M68K_PROC_H_
+
+#define MAX_CPUS 1
+
+typedef int tMemoryState; // Unused
+
+typedef struct {
+ Uint32 IP;
+ Uint32 SP;
+} tTaskState;
+
+typedef struct {
+ Uint32 Num;
+ union {
+ Uint32 Arg1;
+ Uint32 Return;
+ };
+ union {
+ Uint32 Arg2;
+ Uint32 RetHi;
+ };
+ union {
+ Uint32 Arg3;
+ Uint32 Error;
+ };
+ Uint32 Arg4;
+ Uint32 Arg5;
+ Uint32 Arg6;
+} tSyscallRegs;
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/lib.c
+ * - Library functions
+ */
+#include <acess.h>
+#include "../helpers.h"
+#include <drv_pci_int.h>
+
+Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
+Uint64 __udivdi3(Uint64 Num, Uint64 Den);
+Uint64 __umoddi3(Uint64 Num, Uint64 Den);
+
+// === CODE ===
+void *memcpy(void *__dest, const void *__src, size_t __len)
+{
+ register Uint8 *dest = __dest;
+ register const Uint8 *src = __src;
+
+ while(__len --)
+ *dest++ = *src++;
+
+ return __dest;
+}
+
+void *memset(void *__dest, int __val, size_t __count)
+{
+ register Uint8 *dest = __dest;
+
+ while(__count --)
+ *dest = __val;
+
+ return __dest;
+}
+
+int memcmp(const void *__p1, const void *__p2, size_t __maxlen)
+{
+ const char *m1 = __p1, *m2 = __p2;
+ while( __maxlen-- )
+ {
+ if(*m1 != *m2) return *m1 - *m2;
+ }
+ return 0;
+}
+
+DEF_DIVMOD(64)
+
+Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem)
+{
+ Uint64 ret;
+ if(Den == 0) return 0; // TODO: #div0
+ if(Num < Den) {
+ if(Rem) *Rem = Num;
+ return 0;
+ }
+ if(Num == 0) {
+ if(Rem) *Rem = 0;
+ return 0;
+ }
+ if(Den == 1) {
+ if(Rem) *Rem = 0;
+ return Num;
+ }
+ if(Den == 2) {
+ if(Rem) *Rem = Num & 1;
+ return Num >> 1;
+ }
+ if(Den == 16) {
+ if(Rem) *Rem = Num & 0xF;
+ return Num >> 4;
+ }
+ if(Den == 32) {
+ if(Rem) *Rem = Num & 0x1F;
+ return Num >> 5;
+ }
+ if(Den == 0x1000) {
+ if(Rem) *Rem = Num & 0xFFF;
+ return Num >> 12;
+ }
+
+ if( !(Den >> 32) && !(Num >> 32) ) {
+ if(Rem) *Rem = (Uint32)Num % (Uint32)Den; // Clear high bits
+ return (Uint32)Num / (Uint32)Den;
+ }
+
+ ret = __divmod64(Num, Den, Rem);
+ return ret;
+}
+
+// Unsigned Divide 64-bit Integer
+Uint64 __udivdi3(Uint64 Num, Uint64 Den)
+{
+ return DivMod64U(Num, Den, NULL);
+}
+
+// Unsigned Modulus 64-bit Integer
+Uint64 __umoddi3(Uint64 Num, Uint64 Den)
+{
+ Uint64 ret = 0;
+ DivMod64U(Num, Den, &ret);
+ return ret;
+}
+
+// ---- PCI (stubbed)
+Uint32 PCI_CfgReadDWord(Uint32 Addr)
+{
+ return 0xFFFFFFFF;
+}
+void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data)
+{
+}
+
--- /dev/null
+ENTRY (_start)
+
+_kernel_base = 0x0;
+
+SECTIONS
+{
+ . = 0;
+ .init :
+ {
+ *(.init)
+ }
+ . += _kernel_base;
+ .text : AT( ADDR(.text) - _kernel_base )
+ {
+ *(.text*)
+ *(.rodata*)
+ }
+ /* 0x4000 (4 pages) alignment needed for root table */
+ .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base )
+ {
+ *(.padata)
+ *(.data*)
+
+ gKernelSymbols = .;
+ *(KEXPORT)
+ gKernelSymbolsEnd = .;
+
+ gKernelModules = .;
+ *(KMODULES)
+ gKernelModulesEnd = .;
+ }
+ .bss : AT( ADDR(.bss) - _kernel_base )
+ {
+ *(.bss*)
+ *(COMMON*)
+ . = ALIGN(0x1000);
+ *(.pabss)
+ }
+ gKernelEnd = .;
+}
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/main.c
+ * - C entrypoint
+ */
+#include <acess.h>
+#include <init.h>
+
+// === PROTOTYPES ===
+void kmain(void);
+
+// === CODE ===
+void kmain(void)
+{
+ LogF("Acess2 m68k v"EXPAND_STR(KERNEL_VERSION)"\n");
+ LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash);
+
+}
+
+void Arch_LoadBootModules(void)
+{
+
+}
+
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/mm_phys.c
+ * - Stubbed physical memory management
+ */
+#include <acess.h>
+
+// === CODE ===
+void MM_RefPhys(tPAddr Page)
+{
+ // TODO: Refcount pages
+ Log_Warning("MMPhys", "TODO: Implement MM_RefPhys");
+}
+
+int MM_SetPageNode(tPAddr Page, void *Node)
+{
+ Log_Warning("MMPhys", "TODO: Implement MM_SetPageNode");
+ return -1;
+}
+
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/mm_virt.c
+ * - Stubbed virtual memory management (no MMU)
+ */
+#include <acess.h>
+#include <mm_virt.h>
+#include <hal_proc.h>
+
+// === CODE ===
+tPAddr MM_GetPhysAddr(tVAddr Addr)
+{
+ return Addr;
+}
+
+void MM_SetFlags(tVAddr Addr, Uint Val, Uint Mask)
+{
+ return ;
+}
+
+Uint MM_GetFlags(tVAddr Addr)
+{
+ return 0;
+}
+
+int MM_Map(tVAddr Dest, tPAddr Src)
+{
+ Dest &= (PAGE_SIZE-1);
+ Src &= (PAGE_SIZE-1);
+ if(Dest != Src)
+ memcpy((void*)Dest, (void*)Src, PAGE_SIZE);
+ return 0;
+}
+
+tPAddr MM_Allocate(tVAddr Dest)
+{
+ return Dest;
+}
+
+void MM_Deallocate(tVAddr Addr)
+{
+}
+
+void MM_ClearUser(void)
+{
+}
+
+void MM_DumpTables(tVAddr Start, tVAddr End)
+{
+
+}
+
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/proc.c
+ * - Multithreading
+ */
+#include <acess.h>
+#include <threads_int.h>
+#include <hal_proc.h>
+
+// === IMPORTS ===
+extern tThread gThreadZero;
+
+// === GLOBALS ===
+tThread *gpCurrentThread = &gThreadZero;
+
+// === CODE ===
+void ArchThreads_Init(void)
+{
+}
+
+void Proc_Start(void)
+{
+}
+
+tThread *Proc_GetCurThread(void)
+{
+ return gpCurrentThread;
+}
+
+int GetCPUNum(void)
+{
+ return 0;
+}
+
+tTID Proc_Clone(Uint Flags)
+{
+ UNIMPLEMENTED();
+ return -1;
+}
+
+tTID Proc_NewKThread(tThreadFunction Fcn, void *Arg)
+{
+ UNIMPLEMENTED();
+ return -1;
+}
+
+tTID Proc_SpawnWorker(tThreadFunction Fcn, void *Arg)
+{
+ UNIMPLEMENTED();
+ return -1;
+}
+
+void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize)
+{
+ Log_KernelPanic("Proc", "TODO: Implement Proc_StartUser");
+ for(;;);
+}
+
+void Proc_CallFaultHandler(tThread *Thread)
+{
+
+}
+
+void Proc_DumpThreadCPUState(tThread *Thread)
+{
+
+}
+
+void Proc_Reschedule(void)
+{
+ Log_Notice("Proc", "TODO: Implement Proc_Reschedule");
+}
+
--- /dev/null
+/*
+ * Acess2 M68K port
+ * - By John Hodge (thePowersGang)
+ *
+ * arch/m68k/time.c
+ * - Timekeeping
+ */
+#include <acess.h>
+
+// === CODE ===
+Sint64 now(void)
+{
+ return 0;
+}
--- /dev/null
+#
+# Acess2 Kernel
+# i386 Architecture Makefile
+# arch/i386/Makefile
+
+AS_SUFFIX = asm
+
+CPPFLAGS =
+CFLAGS =
+ASFLAGS = -f elf
+
+USE_MP=0
+
+ifeq ($(PLATFORM),default)
+ USE_MP=0
+else ifeq ($(PLATFORM),smp)
+ USE_MP=1
+endif
+
+ASFLAGS += -D USE_MP=$(USE_MP)
+CPPFLAGS += -DUSE_MP=$(USE_MP)
+
+A_OBJ = start.ao main.o lib.o desctab.ao errors.o irq.o
+A_OBJ += mm_phys.o mm_virt.o
+A_OBJ += proc.o proc.ao time.o vm8086.o
+A_OBJ += kpanic.o pci.o
--- /dev/null
+; AcessOS Microkernel Version
+;
+; desctab.asm
+[BITS 32]
+
+
+[section .data]
+; IDT
+ALIGN 8
+[global gIDT]
+gIDT:
+ ; CS = 0x08, Type = 32-bit Interrupt (0xE = 1 110)
+ times 256 dd 0x00080000,0x00000E00
+[global gIDTPtr]
+gIDTPtr:
+ dw 256 * 16 - 1 ; Limit
+ dd gIDT ; Base
+
+[section .text]
+
+[global Desctab_Install]
+Desctab_Install:
+ ; Set up IDT
+ ; Helper Macros
+ ; - Set an IDT entry to an ISR
+%macro SETISR 1
+ mov eax, Isr%1
+ mov WORD [gIDT + %1*8], ax
+ shr eax, 16
+ mov WORD [gIDT + %1*8+6], ax
+ ; Enable
+ mov ax, WORD [gIDT + %1*8 + 4]
+ or ax, 0x8000
+ mov WORD [gIDT + %1*8 + 4], ax
+%endmacro
+ ; Enable user calling of an ISR
+%macro SET_USER 1
+ or WORD [gIDT + %1*8 + 4], 0x6000
+%endmacro
+ ; Set an ISR as a trap (leaves interrupts enabled when invoked)
+%macro SET_TRAP 1
+ or WORD [gIDT + %1*8 + 4], 0x0100
+%endmacro
+
+ ; Error handlers
+ %assign i 0
+ %rep 32
+ SETISR i
+ %assign i i+1
+ %endrep
+
+ ; User Syscall
+ SETISR 0xAC
+ SET_USER 0xAC
+ SET_TRAP 0xAC ; Interruptable
+
+ ; MP ISRs
+ %if USE_MP
+ SETISR 0xED ; 0xED Inter-processor HALT
+ SETISR 0xEE ; 0xEE Timer
+ SETISR 0xEF ; 0xEF Spurious Interrupt
+ %endif
+
+ ; IRQs
+ %assign i 0xF0
+ %rep 16
+ SETISR i
+ %assign i i+1
+ %endrep
+
+ ; Load IDT
+ lidt [gIDTPtr]
+
+ ; Remap PIC
+ push edx ; Save EDX
+ mov dx, 0x20
+ mov al, 0x11
+ out dx, al ; Init Command
+ mov dx, 0x21
+ mov al, 0xF0
+ out dx, al ; Offset (Start of IDT Range)
+ mov al, 0x04
+ out dx, al ; IRQ connected to Slave (00000100b) = IRQ2
+ mov al, 0x01
+ out dx, al ; Set Mode
+ mov al, 0x00
+ out dx, al ; Set Mode
+
+ mov dx, 0xA0
+ mov al, 0x11
+ out dx, al ; Init Command
+ mov dx, 0xA1
+ mov al, 0xF8
+ out dx, al ; Offset (Start of IDT Range)
+ mov al, 0x02
+ out dx, al ; IRQ Line connected to master
+ mov al, 0x01
+ out dx, al ; Set Mode
+ mov dl, 0x00
+ out dx, al ; Set Mode
+ pop edx
+
+ ret
+
+
+; ===============
+; = Define ISRs =
+; ===============
+%macro ISR_ERRNO 1
+[global Isr%1]
+Isr%1:
+ ;xchg bx, bx
+ push %1
+ jmp ErrorCommon
+%endmacro
+%macro ISR_NOERR 1
+[global Isr%1]
+Isr%1:
+ ;xchg bx, bx
+ push 0
+ push %1
+ jmp ErrorCommon
+%endmacro
+
+%macro DEF_SYSCALL 1
+[global Isr%1]
+Isr%1:
+ push 0
+ push %1
+ jmp SyscallCommon
+%endmacro
+
+%macro DEF_IRQ 1
+[global Isr%1]
+Isr%1:
+ push 0
+ push %1
+ jmp IRQCommon
+%endmacro
+
+ISR_NOERR 0; 0: Divide By Zero Exception
+ISR_NOERR 1; 1: Debug Exception
+ISR_NOERR 2; 2: Non Maskable Interrupt Exception
+ISR_NOERR 3; 3: Int 3 Exception
+ISR_NOERR 4; 4: INTO Exception
+ISR_NOERR 5; 5: Out of Bounds Exception
+ISR_NOERR 6; 6: Invalid Opcode Exception
+ISR_NOERR 7; 7: Coprocessor Not Available Exception
+ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!)
+ISR_NOERR 9; 9: Coprocessor Segment Overrun Exception
+ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!)
+ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!)
+ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!)
+ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!)
+ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!)
+ISR_NOERR 15; 15: Reserved Exception
+ISR_NOERR 16; 16: Floating Point Exception
+ISR_NOERR 17; 17: Alignment Check Exception
+ISR_NOERR 18; 18: Machine Check Exception
+ISR_NOERR 19; 19: Reserved
+ISR_NOERR 20; 20: Reserved
+ISR_NOERR 21; 21: Reserved
+ISR_NOERR 22; 22: Reserved
+ISR_NOERR 23; 23: Reserved
+ISR_NOERR 24; 24: Reserved
+ISR_NOERR 25; 25: Reserved
+ISR_NOERR 26; 26: Reserved
+ISR_NOERR 27; 27: Reserved
+ISR_NOERR 28; 28: Reserved
+ISR_NOERR 29; 29: Reserved
+ISR_NOERR 30; 30: Reserved
+ISR_NOERR 31; 31: Reserved
+
+DEF_SYSCALL 0xAC ; Acess System Call
+
+%if USE_MP
+[global Isr0xED]
+; 0xED - Interprocessor HALT
+Isr0xED:
+ cli
+.jmp: hlt
+ jmp .jmp
+
+[global Isr0xEE]
+[extern SchedulerBase]
+; AP's Timer Interrupt
+Isr0xEE:
+ push eax ; Line up with interrupt number
+ mov eax, dr1 ; CPU Number
+ push eax
+ mov eax, [esp+4] ; Load EAX back
+ jmp SchedulerBase
+; Spurious Interrupt
+[global Isr0xEF]
+Isr0xEF:
+ xchg bx, bx ; MAGIC BREAK
+ iret
+%endif
+
+; IRQs
+; - Timer
+[global Isr240]
+[global Isr240.jmp]
+[extern SchedulerBase]
+[extern SetAPICTimerCount]
+Isr240:
+ push 0 ; Line up with Argument in errors
+ push 0 ; CPU Number
+ ;xchg bx, bx ; MAGIC BREAK
+Isr240.jmp:
+ %if USE_MP
+ jmp SetAPICTimerCount ; This is reset once the bus speed has been calculated
+ %else
+ jmp SchedulerBase
+ %endif
+; - Assignable
+%assign i 0xF1
+%rep 16
+ DEF_IRQ i
+%assign i i+1
+%endrep
+
+; ---------------------
+; Common error handling
+; ---------------------
+[extern ErrorHandler]
+ErrorCommon:
+ ;xchg bx, bx ; MAGIC BREAK
+
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ ; Clear TF
+; pushf
+; and WORD [esp], 0xFEFF
+; popf
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ push esp
+ call ErrorHandler
+ add esp, 4
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ add esp, 8 ; Error Code and ID
+ iret
+
+; --------------------------
+; Common System Call Handler
+; --------------------------
+[extern SyscallHandler]
+SyscallCommon:
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ push esp
+ call SyscallHandler
+ add esp, 4
+
+ ; Pass changes to TF on to the user
+ ; EFLAGS is stored at ESP[4+8+2+2]
+ ; 4 Segment Registers
+ ; 8 GPRs
+ ; 2 Error Code / Interrupt ID
+ ; 2 CS/EIP
+ pushf
+ pop eax
+ and eax, 0x100 ; 0x100 = Trace Flag
+ and WORD [esp+(4+8+2+2)*4], ~0x100 ; Clear
+ or DWORD [esp+(4+8+2+2)*4], eax ; Set for user
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ add esp, 8 ; Error Code and ID
+ iret
+
+; ------------
+; IRQ Handling
+; ------------
+[extern IRQ_Handler]
+[global IRQCommon]
+[global IRQCommon_handled]
+IRQCommon_handled equ IRQCommon.handled
+IRQCommon:
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ push esp
+ call IRQ_Handler
+.handled:
+ add esp, 4
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ add esp, 8 ; Error Code and ID
+ iret
+
+; vim: ft=nasm ts=8
--- /dev/null
+/*
+ * Acess2 - x86 Architecture
+ * arch/x86/errors.c
+ * - CPU Error Handler
+ */
+#include <acess.h>
+#include <proc.h>
+#include <mm_virt.h>
+
+// === CONSTANTS ===
+#define MAX_BACKTRACE 8 //!< Maximum distance to trace the stack backwards
+
+// === IMPORTS ===
+extern void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs);
+extern void VM8086_GPF(tRegs *Regs);
+extern void Threads_Dump(void);
+extern void Threads_Fault(int Num);
+extern int GetCPUNum(void);
+extern void MM_DumpTables(tVAddr, tVAddr);
+extern void Proc_EnableSSE(void);
+extern void Proc_RestoreSSE(Uint32 Data);
+
+// === PROTOTYPES ===
+void __stack_chk_fail(void);
+void ErrorHandler(tRegs *Regs);
+void Proc_PrintBacktrace(void);
+void Error_Backtrace(Uint eip, Uint ebp);
+void StartupPrint(char *Str);
+
+// === GLOBALS ===
+const char *csaERROR_NAMES[] = {
+ "Divide By Zero", "Debug", "NMI Exception", "INT3",
+ "INTO Instr - Overflow", "BOUND Instr - Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable",
+ "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present",
+ "Stack Fault Exception", "GPF", "#PF", "Reserved",
+ "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved"
+ };
+
+// === CODE ===
+/**
+ * \brief Keeps GCC happy
+ */
+void __stack_chk_fail(void)
+{
+ Panic("FATAL ERROR: Stack Check Failed\n");
+ for(;;);
+}
+
+/**
+ * \fn void ErrorHandler(tRegs *Regs)
+ * \brief General Error Handler
+ * \param Regs Register state at error
+ */
+void ErrorHandler(tRegs *Regs)
+{
+ Uint cr;
+
+ //if( Regs && !(Regs->int_num == 13 && Regs->eflags & 0x20000) )
+ // __asm__ __volatile__ ("xchg %bx, %bx");
+ //Log_Debug("X86", "Regs = %p", Regs);
+ //Log_Debug("X86", "Error %i at 0x%08x", Regs->int_num, Regs->eip);
+
+ __asm__ __volatile__ ("cli");
+
+ // Debug exception (used for single-stepping)
+ if(Regs->int_num == 1)
+ {
+ static Uint32 lastEIP = 0;
+ tThread *thread = Proc_GetCurThread();
+ if( Regs->eip == lastEIP )
+ return;
+ Log("%p(%i %s) IP=%08x", thread, thread->TID, thread->ThreadName, Regs->eip);
+ lastEIP = Regs->eip;
+ return ;
+ }
+
+ // Page Fault
+ if(Regs->int_num == 14)
+ {
+ __asm__ __volatile__ ("sti"); // Should be OK, TODO: Test
+ __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
+ MM_PageFault( cr, Regs->err_code, Regs );
+ return ;
+ }
+
+ // #NM - Coprocessor unavaliable
+ if(Regs->int_num == 7)
+ {
+ tThread *thread = Proc_GetCurThread();
+ if(!thread->SavedState.bSSEModified)
+ {
+ Proc_EnableSSE();
+ if(!thread->SavedState.SSE)
+ thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF);
+ else
+ Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF );
+ thread->SavedState.bSSEModified = 1;
+ __asm__ __volatile__ ("sti");
+ return ;
+ }
+ // oops, SSE enabled but a #NM, bad news
+ }
+
+ // VM8086 GPF
+ if(Regs->int_num == 13 && Regs->eflags & 0x20000)
+ {
+ VM8086_GPF(Regs);
+ return ;
+ }
+
+ // Check if it's a user mode fault
+ if( (Regs->cs & 3) == 3 ) {
+ Log_Warning("Arch", "User Fault - %s, Code: 0x%x",
+ csaERROR_NAMES[Regs->int_num], Regs->err_code);
+ Log_Warning("Arch", "at CS:EIP %04x:%08x",
+ Regs->cs, Regs->eip);
+ MM_DumpTables(0, KERNEL_BASE);
+ switch( Regs->int_num )
+ {
+ // Division by Zero
+ case 0: Threads_Fault(FAULT_DIV0); break;
+ // Invalid opcode
+ case 6: Threads_Fault(FAULT_OPCODE); break;
+ // GPF
+ case 13: Threads_Fault(FAULT_ACCESS); break;
+ // Floating Point Exception
+ case 16: Threads_Fault(FAULT_FLOAT); break;
+
+ default: Threads_Fault(FAULT_MISC); break;
+ }
+ return ;
+ }
+
+ Debug_KernelPanic();
+
+ LogF("CPU %i Error %i - %s, Code: 0x%x - At %08x\n",
+ GetCPUNum(),
+ Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code,
+ Regs->eip);
+
+ //Warning("CPU Error %i - %s, Code: 0x%x",
+ // Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code);
+ //Warning(" CS:EIP = 0x%04x:%08x", Regs->cs, Regs->eip);
+ __ASM__ ("xchg %bx, %bx");
+ if(Regs->cs == 0x08)
+ Warning(" SS:ESP = 0x0010:%08x", (Uint)Regs+sizeof(tRegs));
+ else
+ Warning(" SS:ESP = 0x%04x:%08x", Regs->ss, Regs->esp);
+ Warning(" EFLAGS = 0x%08x", Regs->eflags);
+ Warning(" EAX %08x ECX %08x EDX %08x EBX %08x",
+ Regs->eax, Regs->ecx, Regs->edx, Regs->ebx);
+ Warning(" ESP %08x EBP %08x ESI %08x EDI %08x",
+ Regs->esp, Regs->ebp, Regs->esi, Regs->edi);
+ Warning(" DS %04x ES %04x FS %04x GS %04x",
+ Regs->ds, Regs->es, Regs->fs, Regs->gs);
+
+ // Control Registers
+ __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr));
+ Warning(" CR0 0x%08x", cr);
+ __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
+ Warning(" CR2 0x%08x", cr);
+ __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr));
+ Warning(" CR3 0x%08x", cr);
+
+ switch( Regs->int_num )
+ {
+ case 6: // #UD
+ Warning(" Offending bytes: %02x %02x %02x %02x",
+ *(Uint8*)(Regs->eip+0), *(Uint8*)(Regs->eip+1),
+ *(Uint8*)(Regs->eip+2), *(Uint8*)(Regs->eip+3));
+ break;
+ }
+
+ // Print Stack Backtrace
+ Error_Backtrace(Regs->eip, Regs->ebp);
+
+ // Dump running threads
+ Threads_Dump();
+
+ for(;;) __asm__ __volatile__ ("hlt");
+}
+
+void Proc_PrintBacktrace(void)
+{
+ Uint32 ebp;
+ __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp));
+ Error_Backtrace( *(Uint32*)(ebp+4), *(Uint32*)ebp );
+}
+
+/**
+ * \fn void Error_Backtrace(Uint eip, Uint ebp)
+ * \brief Unrolls the stack to trace execution
+ * \param eip Current Instruction Pointer
+ * \param ebp Current Base Pointer (Stack Frame)
+ */
+void Error_Backtrace(Uint eip, Uint ebp)
+{
+ int i = 0;
+// Uint delta = 0;
+// char *str = NULL;
+
+ //if(eip < 0xC0000000 && eip > 0x1000)
+ //{
+ // LogF("Backtrace: User - 0x%x\n", eip);
+ // return;
+ //}
+
+ #if 0
+ if(eip > 0xE0000000)
+ {
+ LogF("Backtrace: Data Area - 0x%x\n", eip);
+ return;
+ }
+
+ if(eip > 0xC8000000)
+ {
+ LogF("Backtrace: Kernel Module - 0x%x\n", eip);
+ return;
+ }
+ #endif
+
+ //str = Debug_GetSymbol(eip, &delta);
+// if(str == NULL)
+ LogF("Backtrace: 0x%x", eip);
+// else
+// LogF("Backtrace: %s+0x%x", str, delta);
+ if(!MM_GetPhysAddr(ebp))
+ {
+ LogF("\nBacktrace: Invalid EBP, stopping\n");
+ return;
+ }
+
+
+ while( MM_GetPhysAddr(ebp) && i < MAX_BACKTRACE )
+ {
+ if( ebp >= MM_KERNEL_STACKS_END ) break;
+ //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta);
+// if(str == NULL)
+ LogF(" >> 0x%x", *(Uint*)(ebp+4));
+// else
+// LogF(" >> %s+0x%x", str, delta);
+ ebp = *(Uint*)ebp;
+ i++;
+ }
+ LogF("\n");
+}
+
+/**
+ * \fn void StartupPrint(char *Str)
+ * \brief Str String to print
+ * \note WHY IS THIS HERE?!?!
+ */
+void StartupPrint(char *Str)
+{
+ Uint16 *buf = (void*)0xC00B8000;
+ int i = 0;
+ static int line = 0;
+ while(*Str)
+ {
+ buf[line*80 + i++] = *Str | 0x0700;
+ Str ++;
+ }
+
+ // Clear the rest of the line
+ while(i < 80)
+ buf[line*80 + i++] = 0x0720;
+
+ line ++;
+ if(line == 25)
+ {
+ line --;
+ memcpy(buf, &buf[80], 80*24*2);
+ memset(&buf[80*24], 0, 80*2);
+ }
+}
+
+// === EXPORTS ===
+EXPORT(__stack_chk_fail);
--- /dev/null
+/*
+ * Acess2
+ * - x86 Architecture
+ * arch/x86/include/arch.h
+ */
+#ifndef _ARCH_H_
+#define _ARCH_H_
+
+// - Base Defintions
+#define KERNEL_BASE 0xC0000000
+#define BITS 32
+#define PAGE_SIZE 0x1000
+
+#define INVLPTR ((void*)-1)
+
+// Allow nested spinlocks?
+#define LOCK_DISABLE_INTS 1
+
+// - Processor/Machine Specific Features
+#if ARCH != x86 && ARCH != x86_smp
+# error "Unknown architecture '" #ARCH "'"
+#endif
+
+#if USE_MP
+# define MAX_CPUS 8
+#else
+# define MAX_CPUS 1
+#endif
+
+#if USE_PAE
+# define PHYS_BITS 48
+#else
+# define PHYS_BITS 32
+#endif
+
+#define __ASM__ __asm__ __volatile__
+
+// === Spinlocks ===
+/**
+ * \brief Short Spinlock structure
+ */
+struct sShortSpinlock {
+ volatile int Lock; //!< Lock value
+
+ #if LOCK_DISABLE_INTS
+ int IF; //!< Interrupt state on call to SHORTLOCK
+ #endif
+};
+
+// === MACROS ===
+/**
+ * \brief Halt the CPU (shorter version of yield)
+ */
+#if 1
+#define HALT() do { \
+ Uint32 flags; \
+ __asm__ __volatile__ ("pushf;pop %0" : "=a"(flags)); \
+ if( !(flags & 0x200) ) Panic("HALT called with interrupts disabled"); \
+ __asm__ __volatile__ ("hlt"); \
+} while(0)
+#else
+#define HALT() __asm__ __volatile__ ("hlt")
+#endif
+/**
+ * \brief Fire a magic breakpoint (bochs)
+ */
+#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx")
+
+// === TYPES ===
+typedef unsigned int Uint; // Unsigned machine native integer
+typedef unsigned char Uint8;
+typedef unsigned short Uint16;
+typedef unsigned long Uint32;
+typedef unsigned long long Uint64;
+typedef signed int Sint; // Signed Machine Native integer
+typedef signed char Sint8;
+typedef signed short Sint16;
+typedef signed long Sint32;
+typedef signed long long Sint64;
+typedef Uint size_t;
+typedef char BOOL;
+
+typedef Uint32 tPAddr;
+typedef Uint32 tVAddr;
+
+typedef struct {
+ Uint32 gs, fs, es, ds;
+ Uint32 edi, esi, ebp, kesp;
+ Uint32 ebx, edx, ecx, eax;
+ Uint32 int_num, err_code;
+ Uint32 eip, cs;
+ Uint32 eflags, esp, ss;
+} tRegs;
+
+typedef struct {
+ Uint Resvd1[4]; // GS, FS, ES, DS
+ Uint Arg4, Arg5; // EDI, ESI
+ Uint Arg6; // EBP
+ Uint Resvd2[1]; // Kernel ESP
+ union {
+ Uint Arg1;
+ Uint Error;
+ }; // EBX
+ union {
+ Uint Arg3;
+ Uint RetHi; // High 32 bits of ret
+ }; // EDX
+ Uint Arg2; // ECX
+ union {
+ Uint Num;
+ Uint Return;
+ }; // EAX
+ Uint Resvd3[5]; // Int, Err, Eip, CS, ...
+ Uint StackPointer; // ESP
+ Uint Resvd4[1]; // SS
+} tSyscallRegs;
+
+// === FUNCTIONS ===
+extern void Debug_PutCharDebug(char ch);
+extern void Debug_PutStringDebug(const char *String);
+
+extern int IS_LOCKED(struct sShortSpinlock *Lock);
+extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock);
+extern void SHORTLOCK(struct sShortSpinlock *Lock);
+extern void SHORTREL(struct sShortSpinlock *Lock);
+
+#endif // !defined(_ARCH_H_)
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * x86 Arch - Internal Definitions
+ * - arch/x86/include/arch_int.h
+ */
+#ifndef _ARCH_INT_H_
+#define _ARCH_INT_H_
+
+/**
+ * \brief Spinlock primative atomic set-if-zero loop
+ */
+extern void __AtomicTestSetLoop(Uint *Ptr, Uint Value);
+
+/**
+ * \brief Clear and free an address space
+ */
+extern void MM_ClearSpace(Uint32 CR3);
+
+#endif
+
--- /dev/null
+/**
+ */
+#ifndef _DESCTAB_H_
+#define _DESCTAB_H_
+
+typedef struct {
+ Uint16 LimitLow;
+ Uint16 BaseLow;
+ Uint8 BaseMid;
+ Uint8 Access;
+ struct {
+ unsigned LimitHi: 4;
+ unsigned Flags: 4;
+ } __attribute__ ((packed));
+ Uint8 BaseHi;
+} __attribute__ ((packed)) tGDT;
+
+typedef struct {
+ Uint16 OffsetLo;
+ Uint16 CS;
+ Uint16 Flags;
+ Uint16 OffsetHi;
+} __attribute__ ((packed)) tIDT;
+
+#endif
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * mm_phys.h
+ */
+#ifndef _MM_PHYS_H
+#define _MM_PHYS_H
+
+// === FUNCTIONS ===
+//extern tPAddr MM_AllocPhys(void);
+//extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
+//extern void MM_RefPhys(tPAddr PAddr);
+//extern void MM_DerefPhys(tPAddr PAddr);
+//extern int MM_GetRefCount(tPAddr Addr);
+
+#endif
--- /dev/null
+/*
+ * Acess2
+ * - Virtual Memory Manager (Header)
+ */
+#ifndef _MM_VIRT_H
+#define _MM_VIRT_H
+
+// NOTES:
+// - 1PD is 0x400000
+
+// - Memory Layout
+#define MM_USER_MIN 0x00200000
+#define USER_STACK_SZ 0x00020000 // 128 KiB
+#define USER_STACK_TOP 0x00800000
+#define USER_LIB_MAX 0xBC000000
+#define MM_USER_MAX 0xBC000000 // Top load address for user libraries
+#define MM_PPD_MIN 0xBC000000 // Per-Process Data base
+#define MM_PPD_HANDLES 0xBC000000 // - VFS Handles (Practically unlimited)
+#define MM_PPD_MMAP 0xBD000000 // - MMap Entries (24b each = 0x2AAAA max)
+#define MM_PPD_UNALLOC 0xBE000000 //
+#define MM_PPD_CFG 0xBFFFF000 // - Per-process config entries
+#define MM_PPD_MAX 0xC0000000 //
+
+#define MM_KHEAP_BASE 0xC0400000 // C+4MiB
+#define MM_KHEAP_MAX 0xCF000000 //
+#define MM_KERNEL_VFS 0xCF000000 //
+#define MM_KUSER_CODE 0xCFFF0000 // 16 Pages
+#define MM_MODULE_MIN 0xD0000000 // Lowest Module Address
+#define MM_MODULE_MAX 0xE0000000 // 128 MiB
+
+// Page Info (Which VFS node owns each physical page)
+// 2^32/2^12*16
+// = 2^24 = 16 MiB = 0x4000000
+// 256 items per page
+#define MM_PAGENODE_BASE 0xE0000000
+
+// Needs (2^32/2^12*4 bytes)
+// - 2^22 bytes max = 4 MiB = 0x1000000
+// 1024 items per page
+#define MM_REFCOUNT_BASE 0xE4000000
+
+#define MM_KERNEL_STACKS 0xF0000000
+#define MM_KERNEL_STACK_SIZE 0x00008000
+#define MM_KERNEL_STACKS_END 0xFC000000
+
+// === FUNCTIONS ===
+extern void MM_FinishVirtualInit(void);
+extern void MM_SetCR3(Uint CR3);
+extern tPAddr MM_Clone(int bCloneUser);
+extern tVAddr MM_NewKStack(void);
+extern tVAddr MM_NewWorkerStack(Uint *InitialStack, size_t StackSize);
+
+#endif
--- /dev/null
+/*
+ */
+#ifndef _MP_H
+#define _MP_H
+
+#define MPPTR_IDENT ('_'|('M'<<8)|('P'<<16)|('_'<<24))
+#define MPTABLE_IDENT ('P'|('C'<<8)|('M'<<16)|('P'<<24))
+
+typedef struct sMPInfo {
+ Uint32 Sig; // '_MP_'
+ Uint32 MPConfig;
+ Uint8 Length;
+ Uint8 Version;
+ Uint8 Checksum;
+ Uint8 Features[5]; // 2-4 are unused
+} tMPInfo;
+
+typedef union uMPTable_Ent {
+ Uint8 Type;
+ struct {
+ Uint8 Type;
+ Uint8 APICID;
+ Uint8 APICVer;
+ Uint8 CPUFlags; // bit 0: Enabled, bit 1: Boot Processor
+ Uint32 CPUSignature; // Stepping, Model, Family
+ Uint32 FeatureFlags;
+ Uint32 Reserved[2];
+ } __attribute__((packed)) Proc; // 0x00
+ struct {
+ Uint8 Type;
+ Uint8 ID;
+ char TypeString[6];
+ } __attribute__((packed)) Bus; // 0x01
+ struct {
+ Uint8 Type;
+ Uint8 ID;
+ Uint8 Version;
+ Uint8 Flags; // bit 0: Enabled
+ Uint32 Addr;
+ } __attribute__((packed)) IOAPIC; // 0x02
+ struct {
+ Uint8 Type;
+ Uint8 IntType;
+ Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode
+ Uint8 SourceBusID;
+ Uint8 SourceBusIRQ;
+ Uint8 DestAPICID;
+ Uint8 DestAPICIRQ;
+ } __attribute__((packed)) IOInt;
+ struct {
+ Uint8 Type;
+ Uint8 IntType;
+ Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode
+ Uint8 SourceBusID;
+ Uint8 SourceBusIRQ;
+ Uint8 DestLocalAPICID;
+ Uint8 DestLocalAPICIRQ;
+ } __attribute__((packed)) LocalInt;
+} __attribute__((packed)) tMPTable_Ent;
+
+typedef struct sMPTable {
+ Uint32 Sig;
+ Uint16 BaseTableLength;
+ Uint8 SpecRev;
+ Uint8 Checksum;
+
+ char OemID[8];
+ char ProductID[12];
+
+ Uint32 OEMTablePtr;
+ Uint16 OEMTableSize;
+ Uint16 EntryCount;
+
+ Uint32 LocalAPICMemMap; //!< Address used to access the local APIC
+ Uint16 ExtendedTableLen;
+ Uint8 ExtendedTableChecksum;
+ Uint8 Reserved;
+
+ tMPTable_Ent Entries[];
+} tMPTable;
+
+typedef volatile struct {
+ Uint32 Addr;
+ Uint32 Resvd1[3];
+ union {
+ Uint8 Byte;
+ Uint16 Word;
+ Uint32 DWord;
+ } Value;
+ Uint32 Resvd2[3];
+} tIOAPIC;
+
+typedef struct {
+ Uint32 Val;
+ Uint32 Padding[3];
+} volatile tReg;
+
+typedef volatile struct {
+ tReg _unused1[2];
+ tReg ID;
+ tReg Version;
+ tReg _unused2[4];
+ tReg TPR; // Task Priority Register
+ tReg APR; // Arbitration Priority Register (RO)
+ tReg PPR; // Processor Priority Register (RO)
+ tReg EOI; // EOI Register (Write Only)
+ tReg _unused3[1];
+ tReg LogDest; // Logical Destination Register
+ tReg DestFmt; // Destination Format Register (0-27: RO, 28-31: RW)
+ tReg SIV; // Spurious Interrupt Vector Register (0-8: RW, 9-31: RO)
+ tReg ISR[8]; // In-Service Register - Total 256 Bits (RO)
+ tReg TMR[8]; // Trigger Mode Register - Total 256 Bits (RO)
+ tReg IRR[8]; // Interrupt Request Register - Total 256 Bits (RO)
+ tReg ErrorStatus; // Error Status Register (RO)
+ tReg _unused4[6];
+ tReg LVTCMI; // LVT CMI Registers
+ // 0x300
+ tReg ICR[2]; // Interrupt Command Register (RW)
+ // LVT Registers (Controls Local Vector Table)
+ // Structure:
+ // 0-7: Vector - IDT Vector for the interrupt
+ // 12: Delivery Status (0: Idle, 1: Send Pending)
+ // 16: Mask (0: Enabled, 1: Disabled)
+ // 0x320
+ tReg LVTTimer; // LVT Timer Register (RW)
+ tReg LVTThermalSensor; // LVT Thermal Sensor Register (RW)
+ tReg LVTPerfMonCounters; // LVT Performance Monitor Counters Register (RW)
+ tReg LVTLInt0; // LVT Local Interrupt (LINT) #0 Register (RW);
+ tReg LVTLInt1; // LVT Local Interrupt (LINT) #1 Register (RW);
+ tReg LVTError; // LVT Error Register (RW);
+ // 0x380
+ tReg InitialCount; // Initial Count Register (Used for the timer) (RW)
+ tReg CurrentCount; // Current Count Register (Used for the timer) (RW)
+ tReg _unused5[4];
+ // 0x3E0
+ tReg DivideConifg; // Divide Configuration Register (RW)
+ tReg _unused6[1];
+} volatile tAPIC;
+
+#endif
--- /dev/null
+/*
+ * Acess 2
+ * By John Hodge (thePowersGang)
+ *
+ * multiboot2.h
+ * - Multiboot 2 Header
+ */
+#ifndef _MULTIBOOT2_H_
+#define _MULTIBOOT2_H_
+
+#define MULTIBOOT2_MAGIC 0x36D76289
+
+typedef struct sMultiboot2Info
+{
+ Uint32 TotalSize;
+ Uint32 Reserved; // SBZ
+} tMultiboot2Info;
+
+#endif
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * proc.h
+ */
+#ifndef _PROC_H
+#define _PROC_H
+
+// === TYPES ==
+typedef struct sTSS {
+ Uint32 Link;
+ Uint32 ESP0, SS0;
+ Uint32 ESP1, SS1;
+ Uint32 ESP2, SS2;
+ Uint32 CR3;
+ Uint32 EIP;
+ Uint32 EFLAGS;
+ Uint32 EAX, ECX, EDX, EBX;
+ Uint32 ESP, EBP, ESI, EDI;
+ Uint32 ES, CS, DS, SS, FS, GS;
+ Uint32 LDTR;
+ Uint16 Resvd, IOPB; // IO Permissions Bitmap
+} __attribute__((packed)) tTSS;
+
+typedef struct {
+ #if USE_PAE
+ Uint PDPT[4];
+ #else
+ Uint32 CR3;
+ #endif
+} tMemoryState;
+
+// 512 bytes, 16 byte aligned
+typedef struct sSSEState
+{
+ char data[512];
+} tSSEState;
+
+typedef struct {
+ Uint EIP, ESP;
+ Uint32 UserCS, UserEIP;
+ tSSEState *SSE;
+ int bSSEModified;
+} tTaskState;
+
+#include <threads_int.h>
+
+#define USER_MAX KERNEL_BASE
+
+#endif
--- /dev/null
+/*
+ * Acess2 VM8086 BIOS Interface
+ * - By John Hodge (thePowersGang)
+ *
+ * vm8086.h
+ * - Core Header
+ */
+#ifndef _VM80806_H_
+#define _VM80806_H_
+
+// === TYPES ===
+/**
+ * \note Semi-opaque - Past \a .IP, the implementation may add any data
+ * it needs to the state.
+ */
+typedef struct sVM8086
+{
+ Uint16 AX, CX, DX, BX;
+ Uint16 BP, SP, SI, DI;
+
+ Uint16 SS, DS, ES;
+
+ Uint16 CS, IP;
+
+ struct sVM8086_InternalData *Internal;
+} tVM8086;
+
+// === FUNCTIONS ===
+/**
+ * \brief Create an instance of the VM8086 Emulator
+ * \note Do not free this pointer with ::free, instead use ::VM8086_Free
+ * \return Pointer to a tVM8086 structure, this structure may be larger than
+ * tVM8086 due to internal data.
+ */
+extern tVM8086 *VM8086_Init(void);
+/**
+ * \brief Free an allocated tVM8086 structure
+ * \param State Emulator state to free
+ */
+extern void VM8086_Free(tVM8086 *State);
+/**
+ * \brief Allocate a piece of memory in the emulated address space and
+ * return a host and emulated pointer to it.
+ * \param State Emulator state
+ * \param Size Size of memory block
+ * \param Segment Pointer to location to store the allocated memory's segment
+ * \param Offset Pointet to location to store the allocated memory's offset
+ * \return Host pointer to the allocated memory
+ */
+extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset);
+/**
+ * \brief Gets a pointer to a piece of emulated memory
+ * \todo Only 1 machine page is garenteed to be contiguous
+ * \param State Emulator State
+ * \param Segment Source Segment
+ * \param Offset Source Offset
+ * \return Host pointer to the emulated memory
+ */
+extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset);
+/**
+ * \brief Calls a real-mode interrupt described by the current state of the IVT.
+ * \param State Emulator State
+ * \param Interrupt BIOS Interrupt to call
+ */
+extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt);
+
+#endif
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * irq.c
+ */
+#include <acess.h>
+
+// === CONSTANTS ===
+#define MAX_CALLBACKS_PER_IRQ 4
+#define TRACE_IRQS 0
+
+// === TYPES ===
+typedef void (*tIRQ_Callback)(int, void *);
+
+// === PROTOTYPES ===
+void IRQ_Handler(tRegs *Regs);
+
+// === GLOBALS ===
+tIRQ_Callback gIRQ_Handlers[16][MAX_CALLBACKS_PER_IRQ];
+void *gaIRQ_DataPointers[16][MAX_CALLBACKS_PER_IRQ];
+
+// === CODE ===
+/**
+ * \fn void IRQ_Handler(tRegs *Regs)
+ * \brief Handle an IRQ
+ */
+void IRQ_Handler(tRegs *Regs)
+{
+ int i, irq = Regs->int_num - 0xF0;
+
+ //Log("IRQ_Handler: (Regs={int_num:%i})", Regs->int_num);
+
+ for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ )
+ {
+ if( gIRQ_Handlers[irq][i] ) {
+ gIRQ_Handlers[irq][i](irq, gaIRQ_DataPointers[irq][i]);
+ #if TRACE_IRQS
+ if( irq != 8 )
+ Log("IRQ %i: Call %p", Regs->int_num, gIRQ_Handlers[Regs->int_num][i]);
+ #endif
+ }
+ }
+
+ //Log(" IRQ_Handler: Resetting");
+ if(irq >= 8)
+ outb(0xA0, 0x20); // ACK IRQ (Secondary PIC)
+ outb(0x20, 0x20); // ACK IRQ
+ //Log("IRQ_Handler: RETURN");
+}
+
+/**
+ * \fn int IRQ_AddHandler( int Num, void (*Callback)(int) )
+ */
+int IRQ_AddHandler( int Num, void (*Callback)(int, void*), void *Ptr )
+{
+ int i;
+ for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ )
+ {
+ if( gIRQ_Handlers[Num][i] == NULL ) {
+ Log_Log("IRQ", "Added IRQ%i Cb#%i %p", Num, i, Callback);
+ gIRQ_Handlers[Num][i] = Callback;
+ gaIRQ_DataPointers[Num][i] = Ptr;
+ return 1;
+ }
+ }
+
+ Log_Warning("IRQ", "No free callbacks on IRQ%i", Num);
+ return 0;
+}
--- /dev/null
+/*
+ * Acess 2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * kpanic.c
+ * - x86 Kernel Panic Handler
+ */
+
+#include <acess.h>
+#include <proc.h>
+
+// === CONSTANTS ===
+#define FB ((Uint16 *)(KERNEL_BASE|0xB8000))
+#define BGC 0x4F00 // White on Red
+//#define BGC 0xC000 // Black on Bright Red
+//#define BGC 0x1F00 // White on Blue (BSOD!)
+
+// === IMPORTS ===
+extern Uint32 GetEIP(void);
+extern void Error_Backtrace(Uint32 eip, Uint32 ebp);
+#if USE_MP
+extern void MP_SendIPIVector(int CPU, Uint8 Vector);
+extern int giNumCPUs;
+extern int GetCPUNum(void);
+#endif
+
+// === PROTOTYPES ===
+void KernelPanic_SetMode(void);
+void KernelPanic_PutChar(char Ch);
+
+// === CONSTANTS ===
+const struct {
+ Uint16 IdxPort;
+ Uint16 DatPort;
+ Uint8 Index;
+ Uint8 Value;
+} caRegValues[] = {
+ //{0x3C0, 0x3C0, 0x10, 0x0C}, // Mode Control (Blink Enabled)
+ {0x3C0, 0x3C0, 0x10, 0x04}, // Mode Control (Blink Disabled)
+ {0x3C0, 0x3C0, 0x11, 0x00}, // Overscan Register
+ {0x3C0, 0x3C0, 0x12, 0x0F}, // Color Plane Enable
+ {0x3C0, 0x3C0, 0x13, 0x08}, // Horizontal Panning
+ {0x3C0, 0x3C0, 0x14, 0x00}, // Color Select
+ {0 , 0x3C2, 0 , 0x67}, // Miscellaneous Output Register
+ {0x3C4, 0x3C5, 0x01, 0x00}, // Clock Mode Register
+ {0x3C4, 0x3C5, 0x03, 0x00}, // Character select
+ {0x3C4, 0x3C5, 0x04, 0x07}, // Memory Mode Register
+ {0x3CE, 0x3CF, 0x05, 0x10}, // Mode Register
+ {0x3CE, 0x3CF, 0x06, 0x0E}, // Miscellaneous Register
+ {0x3D4, 0x3D5, 0x00, 0x5F}, // Horizontal Total
+ {0x3D4, 0x3D5, 0x01, 0x4F}, // Horizontal Display Enable End
+ {0x3D4, 0x3D5, 0x02, 0x50}, // Horizontal Blank Start
+ {0x3D4, 0x3D5, 0x03, 0x82}, // Horizontal Blank End
+ {0x3D4, 0x3D5, 0x04, 0x55}, // Horizontal Retrace Start
+ {0x3D4, 0x3D5, 0x05, 0x81}, // Horizontal Retrace End
+ {0x3D4, 0x3D5, 0x06, 0xBF}, // Vertical Total
+ {0x3D4, 0x3D5, 0x07, 0x1F}, // Overflow Register
+ {0x3D4, 0x3D5, 0x08, 0x00}, // Preset row scan
+ {0x3D4, 0x3D5, 0x09, 0x4F}, // Maximum Scan Line
+ {0x3D4, 0x3D5, 0x10, 0x9C}, // Vertical Retrace Start
+ {0x3D4, 0x3D5, 0x11, 0x8E}, // Vertical Retrace End
+ {0x3D4, 0x3D5, 0x12, 0x8F}, // Vertical Display Enable End
+ {0x3D4, 0x3D5, 0x13, 0x28}, // Logical Width
+ {0x3D4, 0x3D5, 0x14, 0x1F}, // Underline Location
+ {0x3D4, 0x3D5, 0x15, 0x96}, // Vertical Blank Start
+ {0x3D4, 0x3D5, 0x16, 0xB9}, // Vertical Blank End
+ {0x3D4, 0x3D5, 0x17, 0xA3} // CRTC Mode Control
+};
+#define NUM_REGVALUES (sizeof(caRegValues)/sizeof(caRegValues[0]))
+
+// === GLOBALS ===
+ int giKP_Pos = 0;
+
+// === CODE ===
+/**
+ * \brief Sets the screen mode for a kernel panic
+ */
+void KernelPanic_SetMode(void)
+{
+ int i;
+
+ // This function is called by Panic(), but MM_PageFault and the
+ // CPU exception handers also call it, so let's not clear the screen
+ // twice
+ if( giKP_Pos ) return ;
+
+ // Restore VGA 0xB8000 text mode
+ #if 1
+ for( i = 0; i < NUM_REGVALUES; i++ )
+ {
+ // Reset Flip-Flop
+ if( caRegValues[i].IdxPort == 0x3C0 ) inb(0x3DA);
+
+ if( caRegValues[i].IdxPort )
+ outb(caRegValues[i].IdxPort, caRegValues[i].Index);
+ outb(caRegValues[i].DatPort, caRegValues[i].Value);
+ }
+
+ inb(0x3DA);
+ outb(0x3C0, 0x20);
+ #endif
+
+ #if USE_MP
+ // Send halt to all processors
+ for( i = 0; i < giNumCPUs; i ++ )
+ {
+ if(i == GetCPUNum()) continue ;
+ FB[i] = BGC|('A'+i);
+ MP_SendIPIVector(i, 0xED);
+ }
+ #endif
+
+ // Clear Screen
+ for( i = 0; i < 80*25; i++ )
+ {
+ FB[i] = BGC;
+ }
+
+ {
+ Uint32 eip = GetEIP();
+ Uint32 ebp;
+ __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp));
+ Error_Backtrace(eip, ebp);
+ }
+}
+
+void KernelPanic_PutChar(char Ch)
+{
+ if( giKP_Pos > 80*25 ) return ;
+ switch(Ch)
+ {
+ case '\t':
+ do {
+ FB[giKP_Pos] &= 0xFF00;
+ FB[giKP_Pos++] |= ' ';
+ } while(giKP_Pos & 7);
+ break;
+
+ case '\n':
+ giKP_Pos += 80;
+ case '\r':
+ giKP_Pos -= giKP_Pos % 80;
+ break;
+
+ default:
+ if(' ' <= Ch && Ch < 0x7F)
+ {
+ FB[giKP_Pos] &= 0xFF00;
+ FB[giKP_Pos] |= Ch;
+ }
+ giKP_Pos ++;
+ break;
+ }
+}
--- /dev/null
+/*
+ * Acess2
+ *
+ * arch/x86/lib.c
+ * - General arch-specific stuff
+ */
+#include <acess.h>
+#include <threads_int.h>
+#include <arch_int.h>
+#include <hal_proc.h> // GetCPUNum
+
+#define TRACE_LOCKS 0
+
+#define DEBUG_TO_E9 1
+#define DEBUG_TO_SERIAL 1
+#define SERIAL_PORT 0x3F8
+#define GDB_SERIAL_PORT 0x2F8
+
+// === IMPRORTS ===
+#if TRACE_LOCKS
+extern struct sShortSpinlock glDebug_Lock;
+extern tMutex glPhysAlloc;
+#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glThreadListLock && Lock != &glPhysAlloc.Protector)
+//#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glPhysAlloc.Protector)
+#endif
+
+// === PROTOTYPES ==
+Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
+Uint64 __udivdi3(Uint64 Num, Uint64 Den);
+Uint64 __umoddi3(Uint64 Num, Uint64 Den);
+
+// === GLOBALS ===
+ int gbDebug_SerialSetup = 0;
+ int gbGDB_SerialSetup = 0;
+
+// === CODE ===
+/**
+ * \brief Determine if a short spinlock is locked
+ * \param Lock Lock pointer
+ */
+int IS_LOCKED(struct sShortSpinlock *Lock)
+{
+ return !!Lock->Lock;
+}
+
+/**
+ * \brief Check if the current CPU has the lock
+ * \param Lock Lock pointer
+ */
+int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
+{
+ return Lock->Lock == GetCPUNum() + 1;
+}
+
+void __AtomicTestSetLoop(Uint *Ptr, Uint Value)
+{
+ __ASM__(
+ "1:\n\t"
+ "xor %%eax, %%eax;\n\t"
+ "lock cmpxchgl %0, (%1);\n\t"
+ "jnz 1b;\n\t"
+ :: "r"(Value), "r"(Ptr)
+ : "eax" // EAX clobbered
+ );
+}
+/**
+ * \brief Acquire a Short Spinlock
+ * \param Lock Lock pointer
+ *
+ * This type of mutex should only be used for very short sections of code,
+ * or in places where a Mutex_* would be overkill, such as appending
+ * an element to linked list (usually two assignement lines in C)
+ *
+ * \note This type of lock halts interrupts, so ensure that no timing
+ * functions are called while it is held. As a matter of fact, spend as
+ * little time as possible with this lock held
+ * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested
+ */
+void SHORTLOCK(struct sShortSpinlock *Lock)
+{
+ int IF;
+ int cpu = GetCPUNum() + 1;
+
+ // Save interrupt state
+ __ASM__ ("pushf;\n\tpop %0" : "=r"(IF));
+ IF &= 0x200; // AND out all but the interrupt flag
+
+ #if TRACE_LOCKS
+ if( TRACE_LOCK_COND )
+ {
+ //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0));
+ Debug("%i %p obtaining %p (Called by %p)", cpu-1, __builtin_return_address(0), Lock, __builtin_return_address(1));
+ }
+ #endif
+
+ __ASM__("cli");
+
+ // Wait for another CPU to release
+ __AtomicTestSetLoop( (Uint*)&Lock->Lock, cpu );
+ Lock->IF = IF;
+
+ #if TRACE_LOCKS
+ if( TRACE_LOCK_COND )
+ {
+ //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0));
+ Debug("%i %p locked by %p\t%p", cpu-1, Lock, __builtin_return_address(0), __builtin_return_address(1));
+// Debug("got it");
+ }
+ #endif
+}
+/**
+ * \brief Release a short lock
+ * \param Lock Lock pointer
+ */
+void SHORTREL(struct sShortSpinlock *Lock)
+{
+ #if TRACE_LOCKS
+ if( TRACE_LOCK_COND )
+ {
+ //Log_Log("LOCK", "%p released by %p", Lock, __builtin_return_address(0));
+ Debug("Lock %p released by %p\t%p", Lock, __builtin_return_address(0), __builtin_return_address(1));
+ }
+ #endif
+
+ // Lock->IF can change anytime once Lock->Lock is zeroed
+ if(Lock->IF) {
+ Lock->Lock = 0;
+ __ASM__ ("sti");
+ }
+ else {
+ Lock->Lock = 0;
+ }
+}
+
+// === DEBUG IO ===
+#if USE_GDB_STUB
+int putDebugChar(char ch)
+{
+ if(!gbGDB_SerialSetup) {
+ outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
+ outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
+ outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte)
+ outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1)
+ outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
+ outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ gbGDB_SerialSetup = 1;
+ }
+ while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 );
+ outb(GDB_SERIAL_PORT, ch);
+ return 0;
+}
+int getDebugChar(void)
+{
+ if(!gbGDB_SerialSetup) {
+ outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
+ outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
+ outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte)
+ outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
+ outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ gbGDB_SerialSetup = 1;
+ }
+ while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ;
+ return inb(GDB_SERIAL_PORT);
+}
+#endif /* USE_GDB_STUB */
+
+void Debug_PutCharDebug(char ch)
+{
+ #if DEBUG_TO_E9
+ __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) );
+ #endif
+
+ #if DEBUG_TO_SERIAL
+ if(!gbDebug_SerialSetup) {
+ outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts
+ outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
+ outb(SERIAL_PORT + 1, 0x00); // (hi byte)
+ outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
+ outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ gbDebug_SerialSetup = 1;
+ }
+ while( (inb(SERIAL_PORT + 5) & 0x20) == 0 );
+ outb(SERIAL_PORT, ch);
+ #endif
+}
+
+void Debug_PutStringDebug(const char *String)
+{
+ while(*String)
+ Debug_PutCharDebug(*String++);
+}
+
+// === IO Commands ===
+void outb(Uint16 Port, Uint8 Data)
+{
+ __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data));
+}
+void outw(Uint16 Port, Uint16 Data)
+{
+ __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data));
+}
+void outd(Uint16 Port, Uint32 Data)
+{
+ __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data));
+}
+Uint8 inb(Uint16 Port)
+{
+ Uint8 ret;
+ __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port));
+ return ret;
+}
+Uint16 inw(Uint16 Port)
+{
+ Uint16 ret;
+ __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port));
+ return ret;
+}
+Uint32 ind(Uint16 Port)
+{
+ Uint32 ret;
+ __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port));
+ return ret;
+}
+
+/**
+ * \fn void *memset(void *Dest, int Val, size_t Num)
+ * \brief Do a byte granuality set of Dest
+ */
+void *memset(void *Dest, int Val, size_t Num)
+{
+ Uint32 val = Val&0xFF;
+ val |= val << 8;
+ val |= val << 16;
+ __asm__ __volatile__ (
+ "rep stosl;\n\t"
+ "mov %3, %%ecx;\n\t"
+ "rep stosb"
+ :: "D" (Dest), "a" (val), "c" (Num/4), "r" (Num&3));
+ return Dest;
+}
+/**
+ * \brief Set double words
+ */
+void *memsetd(void *Dest, Uint32 Val, size_t Num)
+{
+ __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num));
+ return Dest;
+}
+
+/**
+ * \fn int memcmp(const void *m1, const void *m2, size_t Num)
+ * \brief Compare two pieces of memory
+ */
+int memcmp(const void *m1, const void *m2, size_t Num)
+{
+ const Uint8 *d1 = m1;
+ const Uint8 *d2 = m2;
+ if( Num == 0 ) return 0; // No bytes are always identical
+
+ while(Num--)
+ {
+ if(*d1 != *d2)
+ return *d1 - *d2;
+ d1 ++;
+ d2 ++;
+ }
+ return 0;
+}
+
+/**
+ * \fn void *memcpy(void *Dest, const void *Src, size_t Num)
+ * \brief Copy \a Num bytes from \a Src to \a Dest
+ */
+void *memcpy(void *Dest, const void *Src, size_t Num)
+{
+ tVAddr dst = (tVAddr)Dest;
+ tVAddr src = (tVAddr)Src;
+ if( (dst & 3) != (src & 3) )
+ {
+ __asm__ __volatile__ ("rep movsb" :: "D" (dst), "S" (src), "c" (Num));
+// Debug("\nmemcpy:Num=0x%x by %p (UA)", Num, __builtin_return_address(0));
+ }
+ #if 1
+ else if( Num > 128 && (dst & 15) == (src & 15) )
+ {
+ char tmp[16+15]; // Note, this is a hack to save/restor xmm0
+ int count = 16 - (dst & 15);
+// Debug("\nmemcpy:Num=0x%x by %p (SSE)", Num, __builtin_return_address(0));
+ if( count < 16 )
+ {
+ Num -= count;
+ __asm__ __volatile__ ("rep movsb" : "=D"(dst),"=S"(src): "0"(dst), "1"(src), "c"(count));
+ }
+
+ count = Num / 16;
+ __asm__ __volatile__ (
+ "movdqa 0(%5), %%xmm0;\n\t"
+ "1:\n\t"
+ "movdqa 0(%1), %%xmm0;\n\t"
+ "movdqa %%xmm0, 0(%0);\n\t"
+ "add $16,%0;\n\t"
+ "add $16,%1;\n\t"
+ "loop 1b;\n\t"
+ "movdqa %%xmm0, 0(%5);\n\t"
+ : "=r"(dst),"=r"(src)
+ : "0"(dst), "1"(src), "c"(count), "r" (((tVAddr)tmp+15)&~15)
+ );
+
+ count = Num & 15;
+ if(count)
+ __asm__ __volatile__ ("rep movsb" :: "D"(dst), "S"(src), "c"(count));
+ }
+ #endif
+ else
+ {
+// Debug("\nmemcpy:Num=0x%x by %p", Num, __builtin_return_address(0));
+ __asm__ __volatile__ (
+ "rep movsl;\n\t"
+ "mov %3, %%ecx;\n\t"
+ "rep movsb"
+ :: "D" (Dest), "S" (Src), "c" (Num/4), "r" (Num&3));
+ }
+ return Dest;
+}
+
+/**
+ * \fn void *memcpyd(void *Dest, const void *Src, size_t Num)
+ * \brief Copy \a Num DWORDs from \a Src to \a Dest
+ */
+void *memcpyd(void *Dest, const void *Src, size_t Num)
+{
+ __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num));
+ return Dest;
+}
+
+#include "../helpers.h"
+
+DEF_DIVMOD(64);
+
+Uint64 DivMod64U(Uint64 Num, Uint64 Div, Uint64 *Rem)
+{
+ if( Div < 0x100000000ULL && Num < 0xFFFFFFFF * Div ) {
+ Uint32 rem, ret_32;
+ __asm__ __volatile__(
+ "div %4"
+ : "=a" (ret_32), "=d" (rem)
+ : "a" ( (Uint32)(Num & 0xFFFFFFFF) ), "d" ((Uint32)(Num >> 32)), "r" (Div)
+ );
+ if(Rem) *Rem = rem;
+ return ret_32;
+ }
+
+ return __divmod64(Num, Div, Rem);
+}
+
+/**
+ * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den)
+ * \brief Divide two 64-bit integers
+ */
+Uint64 __udivdi3(Uint64 Num, Uint64 Den)
+{
+ if(Den == 0) {
+ __asm__ __volatile__ ("int $0x0");
+ return -1;
+ }
+ // Common speedups
+ if(Num <= 0xFFFFFFFF && Den <= 0xFFFFFFFF)
+ return (Uint32)Num / (Uint32)Den;
+ if(Den == 1) return Num;
+ if(Den == 2) return Num >> 1; // Speed Hacks
+ if(Den == 4) return Num >> 2; // Speed Hacks
+ if(Den == 8) return Num >> 3; // Speed Hacks
+ if(Den == 16) return Num >> 4; // Speed Hacks
+ if(Den == 32) return Num >> 5; // Speed Hacks
+ if(Den == 1024) return Num >> 10; // Speed Hacks
+ if(Den == 2048) return Num >> 11; // Speed Hacks
+ if(Den == 4096) return Num >> 12;
+ if(Num < Den) return 0;
+ if(Num < Den*2) return 1;
+ if(Num == Den*2) return 2;
+
+ return __divmod64(Num, Den, NULL);
+}
+
+/**
+ * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den)
+ * \brief Get the modulus of two 64-bit integers
+ */
+Uint64 __umoddi3(Uint64 Num, Uint64 Den)
+{
+ Uint64 ret = 0;
+ if(Den == 0) {
+ __asm__ __volatile__ ("int $0x0"); // Call Div by Zero Error
+ return -1;
+ }
+ if(Den == 1) return 0; // Speed Hacks
+ if(Den == 2) return Num & 1; // Speed Hacks
+ if(Den == 4) return Num & 3; // Speed Hacks
+ if(Den == 8) return Num & 7; // Speed Hacks
+ if(Den == 16) return Num & 15; // Speed Hacks
+ if(Den == 32) return Num & 31; // Speed Hacks
+ if(Den == 1024) return Num & 1023; // Speed Hacks
+ if(Den == 2048) return Num & 2047; // Speed Hacks
+ if(Den == 4096) return Num & 4095; // Speed Hacks
+
+ if(Num >> 32 == 0 && Den >> 32 == 0)
+ return (Uint32)Num % (Uint32)Den;
+
+ __divmod64(Num, Den, &ret);
+ return ret;
+}
+
+
+// --- EXPORTS ---
+EXPORT(memcpy); EXPORT(memset);
+EXPORT(memcmp);
+//EXPORT(memcpyw); EXPORT(memsetw);
+EXPORT(memcpyd); EXPORT(memsetd);
+EXPORT(inb); EXPORT(inw); EXPORT(ind);
+EXPORT(outb); EXPORT(outw); EXPORT(outd);
+EXPORT(__udivdi3); EXPORT(__umoddi3);
+
+EXPORT(SHORTLOCK);
+EXPORT(SHORTREL);
+EXPORT(IS_LOCKED);
--- /dev/null
+/*
+ * AcessMicro Kernel
+ * Linker Script
+ */
+
+lowStart = start - 0xC0000000;
+ENTRY(lowStart)
+OUTPUT_FORMAT(elf32-i386)
+
+SECTIONS {
+ . = 0x100000;
+ __load_addr = .;
+ .multiboot : AT(ADDR(.multiboot)) {
+ *(.multiboot)
+ }
+
+ . += 0xC0000000;
+
+ .text ALIGN(0x1000): AT(ADDR(.text) - 0xC0000000) {
+ *(.text)
+ }
+
+ .usertext ALIGN(0x1000): AT(ADDR(.usertext) - 0xC0000000) {
+ _UsertextBase = .;
+ *(.usertext)
+ }
+ _UsertextEnd = .;
+
+ .rodata ALIGN(0x1000): AT(ADDR(.rodata) - 0xC0000000) {
+ *(.initpd)
+ *(.rodata)
+ *(.rdata)
+ gKernelModules = .;
+ *(KMODULES)
+ gKernelModulesEnd = .;
+ . = ALIGN(4);
+ gKernelSymbols = .;
+ *(KEXPORT)
+ gKernelSymbolsEnd = .;
+
+
+ }
+ /*
+ .debug_abbrev : { *(.debug_abbrev) }
+ .debug_info : { *(.debug_info) }
+ .debug_line : { *(.debug_line) }
+ .debug_loc : { *(.debug_loc) }
+ .debug_pubnames : { *(.debug_pubnames) }
+ .debug_aranges : { *(.debug_aranges) }
+ .debug_ranges : { *(.debug_ranges) }
+ .debug_str : { *(.debug_str) }
+ .debug_frame : { *(.debug_frame) }
+ */
+
+ .padata ALIGN (0x1000) : AT(ADDR(.padata) - 0xC0000000) {
+ *(.padata)
+ }
+
+ .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
+ *(.data)
+ }
+
+ __bss_start = .;
+ .bss : AT(ADDR(.bss) - 0xC0000000) {
+ _sbss = .;
+ *(COMMON)
+ *(.bss)
+ _ebss = .;
+ }
+ gKernelEnd = (. + 0xFFF)&0xFFFFF000;
+}
--- /dev/null
+/*
+ * Acess 2
+ * x86 Kernel Main
+ * arch/x86/main.c
+ */
+#include <acess.h>
+#include <mboot.h>
+#include <multiboot2.h>
+#include <init.h>
+#include <mm_virt.h>
+#include <mp.h>
+
+#define VGA_ERRORS 0
+
+#define MAX_ARGSTR_POS (0x400000-0x2000)
+
+// === IMPORTS ===
+extern void Heap_Install(void);
+extern void Desctab_Install(void);
+extern void MM_PreinitVirtual(void);
+extern void MM_Install(tMBoot_Info *MBoot);
+extern void MM_InstallVirtual(void);
+extern void Threads_Init(void);
+extern int Time_Setup(void);
+// --- Core ---
+extern void System_Init(char *Commandline);
+
+// === PROTOTYPES ===
+ int kmain(Uint MbMagic, void *MbInfoPtr);
+
+// === GLOBALS ===
+char *gsBootCmdLine = NULL;
+struct {
+ Uint32 PBase;
+ void *Base;
+ Uint Size;
+ char *ArgString;
+} *gaArch_BootModules;
+ int giArch_NumBootModules = 0;
+
+// === CODE ===
+int kmain(Uint MbMagic, void *MbInfoPtr)
+{
+ int i;
+ tMBoot_Module *mods;
+ tMBoot_Info *mbInfo;
+
+ LogF("Acess2 x86-"PLATFORM" v"EXPAND_STR(KERNEL_VERSION)"\n");
+ LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash);
+
+ Log("MbMagic = %08x, MbInfoPtr = %p", MbMagic, MbInfoPtr);
+
+ // Set up non-boot info dependent stuff
+ Desctab_Install(); // Set up GDT and IDT
+ MM_PreinitVirtual(); // Initialise virtual mappings
+
+ switch(MbMagic)
+ {
+ // Multiboot 1
+ case MULTIBOOT_MAGIC:
+ // Adjust Multiboot structure address
+ mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE );
+ gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE);
+
+ MM_Install( mbInfo ); // Set up physical memory manager
+ break;
+
+ // Multiboot 2
+ case MULTIBOOT2_MAGIC:
+ Panic("Multiboot 2 not yet supported");
+ //MM_InstallMBoot2( MbInfo ); // Set up physical memory manager
+ return 0;
+ break;
+
+ default:
+ Panic("Multiboot magic invalid %08x, expected %08x or %08x\n",
+ MbMagic, MULTIBOOT_MAGIC, MULTIBOOT2_MAGIC);
+ return 0;
+ }
+
+ MM_InstallVirtual(); // Clean up virtual address space
+ Heap_Install(); // Create initial heap
+
+ // Start Multitasking
+ Threads_Init();
+
+ // Start Timers
+ Time_Setup();
+
+ Log_Log("Arch", "Starting VFS...");
+ // Load Virtual Filesystem
+ VFS_Init();
+
+ // Load initial modules
+ mods = (void*)( mbInfo->Modules + KERNEL_BASE );
+ giArch_NumBootModules = mbInfo->ModuleCount;
+ gaArch_BootModules = malloc( giArch_NumBootModules * sizeof(*gaArch_BootModules) );
+ for( i = 0; i < mbInfo->ModuleCount; i ++ )
+ {
+ int ofs;
+
+ Log_Log("Arch", "Multiboot Module at 0x%08x, 0x%08x bytes (String at 0x%08x)",
+ mods[i].Start, mods[i].End-mods[i].Start, mods[i].String);
+
+ gaArch_BootModules[i].PBase = mods[i].Start;
+ gaArch_BootModules[i].Size = mods[i].End - mods[i].Start;
+
+ // Always HW map the module data
+ ofs = mods[i].Start&0xFFF;
+ gaArch_BootModules[i].Base = (void*)( MM_MapHWPages(mods[i].Start,
+ (gaArch_BootModules[i].Size+ofs+0xFFF) / 0x1000
+ ) + ofs );
+
+ // Only map the string if needed
+ if( (tVAddr)mods[i].String > MAX_ARGSTR_POS )
+ {
+ // Assumes the string is < 4096 bytes long)
+ gaArch_BootModules[i].ArgString = (void*)(
+ MM_MapHWPages(mods[i].String, 2) + (mods[i].String&0xFFF)
+ );
+ }
+ else
+ gaArch_BootModules[i].ArgString = (char *)mods[i].String + KERNEL_BASE;
+ }
+
+ // Pass on to Independent Loader
+ Log_Log("Arch", "Starting system");
+ System_Init(gsBootCmdLine);
+
+ // Sleep forever (sleeping beauty)
+ for(;;)
+ Threads_Sleep();
+ return 0;
+}
+
+void Arch_LoadBootModules(void)
+{
+ int i, j, numPages;
+ for( i = 0; i < giArch_NumBootModules; i ++ )
+ {
+ Log_Log("Arch", "Loading '%s'", gaArch_BootModules[i].ArgString);
+
+ if( !Module_LoadMem( gaArch_BootModules[i].Base, gaArch_BootModules[i].Size, gaArch_BootModules[i].ArgString ) )
+ {
+ Log_Warning("Arch", "Unable to load module");
+ }
+
+ // Unmap and free
+ numPages = (gaArch_BootModules[i].Size + ((Uint)gaArch_BootModules[i].Base&0xFFF) + 0xFFF) >> 12;
+ MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].Base, numPages );
+
+ for( j = 0; j < numPages; j++ )
+ MM_DerefPhys( gaArch_BootModules[i].PBase + (j << 12) );
+
+ if( (tVAddr) gaArch_BootModules[i].ArgString > MAX_ARGSTR_POS )
+ MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].ArgString, 2 );
+ }
+ Log_Log("Arch", "Boot modules loaded");
+ free( gaArch_BootModules );
+}
--- /dev/null
+/*
+ * Acess2
+ * - Physical memory manager
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <mboot.h>
+#include <mm_virt.h>
+
+//#define USE_STACK 1
+#define TRACE_ALLOCS 0 // Print trace messages on AllocPhys/DerefPhys
+
+
+// === IMPORTS ===
+extern char gKernelEnd[];
+extern void Proc_PrintBacktrace(void);
+
+// === PROTOTYPES ===
+void MM_Install(tMBoot_Info *MBoot);
+//tPAddr MM_AllocPhys(void);
+//tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
+//void MM_RefPhys(tPAddr PAddr);
+//void MM_DerefPhys(tPAddr PAddr);
+// int MM_GetRefCount(tPAddr PAddr);
+
+// === GLOBALS ===
+tMutex glPhysAlloc;
+Uint64 giPhysAlloc = 0; // Number of allocated pages
+Uint64 giPageCount = 0; // Total number of pages
+Uint64 giLastPossibleFree = 0; // Last possible free page (before all pages are used)
+
+Uint32 gaSuperBitmap[1024]; // Blocks of 1024 Pages
+Uint32 gaPageBitmap[1024*1024/32]; // Individual pages
+ int *gaPageReferences;
+void **gaPageNodes = (void*)MM_PAGENODE_BASE;
+#define REFENT_PER_PAGE (0x1000/sizeof(gaPageReferences[0]))
+
+// === CODE ===
+void MM_Install(tMBoot_Info *MBoot)
+{
+ Uint kernelPages, num;
+ Uint i;
+ Uint64 maxAddr = 0;
+ tMBoot_Module *mods;
+ tMBoot_MMapEnt *ent;
+
+ // --- Find largest address
+ MBoot->MMapAddr |= KERNEL_BASE;
+ ent = (void *)( MBoot->MMapAddr );
+ while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
+ {
+ // Adjust for size
+ ent->Size += 4;
+
+ // If entry is RAM and is above `maxAddr`, change `maxAddr`
+ if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
+ maxAddr = ent->Base + ent->Length;
+ // Go to next entry
+ ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
+ }
+
+ if(maxAddr == 0) {
+ giPageCount = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
+ }
+ else {
+ giPageCount = maxAddr >> 12;
+ }
+ giLastPossibleFree = giPageCount - 1;
+
+ memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32);
+
+ // Set up allocateable space
+ ent = (void *)( MBoot->MMapAddr );
+ while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
+ {
+ memsetd( &gaPageBitmap[ent->Base/(4096*32)], 0, ent->Length/(4096*32) );
+ ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
+ }
+
+ // Get used page count (Kernel)
+ kernelPages = (Uint)&gKernelEnd - KERNEL_BASE - 0x100000;
+ kernelPages += 0xFFF; // Page Align
+ kernelPages >>= 12;
+
+ // Fill page bitmap
+ num = kernelPages/32;
+ memsetd( &gaPageBitmap[0x100000/(4096*32)], -1, num );
+ gaPageBitmap[ 0x100000/(4096*32) + num ] = (1 << (kernelPages & 31)) - 1;
+
+ // Fill Superpage bitmap
+ num = kernelPages/(32*32);
+ memsetd( &gaSuperBitmap[0x100000/(4096*32*32)], -1, num );
+ gaSuperBitmap[ 0x100000/(4096*32*32) + num ] = (1 << ((kernelPages / 32) & 31)) - 1;
+
+ // Mark Multiboot's pages as taken
+ // - Structure
+ MM_RefPhys( (Uint)MBoot - KERNEL_BASE );
+ // - Module List
+ for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; )
+ MM_RefPhys( MBoot->Modules + (i << 12) );
+ // - Modules
+ mods = (void*)(MBoot->Modules + KERNEL_BASE);
+ for(i = 0; i < MBoot->ModuleCount; i++)
+ {
+ num = (mods[i].End - mods[i].Start + 0xFFF) >> 12;
+ while(num--)
+ MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) );
+ }
+
+ gaPageReferences = (void*)MM_REFCOUNT_BASE;
+
+ Log_Log("PMem", "Physical memory set up");
+}
+
+/**
+ * \fn tPAddr MM_AllocPhys(void)
+ * \brief Allocates a physical page from the general pool
+ */
+tPAddr MM_AllocPhys(void)
+{
+ // int a, b, c;
+ int indx = -1;
+ tPAddr ret;
+
+ ENTER("");
+
+ Mutex_Acquire( &glPhysAlloc );
+
+ // Classful scan
+ #if 1
+ {
+ const int addrClasses[] = {0,16,20,24,32,64};
+ const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]);
+ int i;
+ int first, last;
+ for( i = numAddrClasses; i -- > 1; )
+ {
+ first = 1UL << (addrClasses[i-1] - 12);
+ last = (1UL << (addrClasses[i] - 12)) - 1;
+ // Range is above the last free page
+ if( first > giLastPossibleFree )
+ continue;
+ // Last possible free page is in the range
+ if( last > giLastPossibleFree )
+ last = giLastPossibleFree;
+
+ // Scan the range
+ for( indx = first; indx < last; )
+ {
+ if( gaSuperBitmap[indx>>10] == -1 ) {
+ indx += 1024;
+ continue;
+ }
+
+ if( gaPageBitmap[indx>>5] == -1 ) {
+ indx += 32;
+ continue;
+ }
+
+ if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
+ indx ++;
+ continue;
+ }
+ break;
+ }
+ if( indx < last ) break;
+
+ giLastPossibleFree = first; // Well, we couldn't find any in this range
+ }
+ // Out of memory?
+ if( i <= 1 ) indx = -1;
+ }
+ #elif 0
+ // Find free page
+ // Scan downwards
+ LOG("giLastPossibleFree = %i", giLastPossibleFree);
+ for( indx = giLastPossibleFree; indx >= 0; )
+ {
+ if( gaSuperBitmap[indx>>10] == -1 ) {
+ indx -= 1024;
+ continue;
+ }
+
+ if( gaPageBitmap[indx>>5] == -1 ) {
+ indx -= 32;
+ continue;
+ }
+
+ if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
+ indx --;
+ continue;
+ }
+ break;
+ }
+ if( indx >= 0 )
+ giLastPossibleFree = indx;
+ LOG("indx = %i", indx);
+ #else
+ c = giLastPossibleFree % 32;
+ b = (giLastPossibleFree / 32) % 32;
+ a = giLastPossibleFree / 1024;
+
+ LOG("a=%i,b=%i,c=%i", a, b, c);
+ for( ; gaSuperBitmap[a] == -1 && a >= 0; a-- );
+ if(a < 0) {
+ Mutex_Release( &glPhysAlloc );
+ Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used",
+ __builtin_return_address(0), giPhysAlloc, giPageCount);
+ LEAVE('i', 0);
+ return 0;
+ }
+ for( ; gaSuperBitmap[a] & (1<<b); b-- );
+ for( ; gaPageBitmap[a*32+b] & (1<<c); c-- );
+ LOG("a=%i,b=%i,c=%i", a, b, c);
+ indx = (a << 10) | (b << 5) | c;
+ if( indx >= 0 )
+ giLastPossibleFree = indx;
+ #endif
+
+ if( indx < 0 ) {
+ Mutex_Release( &glPhysAlloc );
+ Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)",
+ __builtin_return_address(0), giPhysAlloc, giPageCount, indx);
+ Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree);
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ if( indx > 0xFFFFF ) {
+ Panic("The fuck? Too many pages! (indx = 0x%x)", indx);
+ }
+
+ if( indx >= giPageCount ) {
+ Mutex_Release( &glPhysAlloc );
+ Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount);
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Mark page used
+ if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[indx] ) )
+ gaPageReferences[indx] = 1;
+ gaPageBitmap[ indx>>5 ] |= 1 << (indx&31);
+
+ giPhysAlloc ++;
+
+ // Get address
+ ret = indx << 12;
+
+ // Mark used block
+ if(gaPageBitmap[ indx>>5 ] == -1) {
+ gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31);
+ }
+
+ // Release Spinlock
+ Mutex_Release( &glPhysAlloc );
+
+ LEAVE('X', ret);
+ #if TRACE_ALLOCS
+ if( now() > 4000 ) {
+ Log_Debug("PMem", "MM_AllocPhys: RETURN %P (%i free)", ret, giPageCount-giPhysAlloc);
+ Proc_PrintBacktrace();
+ }
+ #endif
+ return ret;
+}
+
+/**
+ * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
+ * \brief Allocate a range of physical pages
+ * \param Pages Number of pages to allocate
+ * \param MaxBits Maximum number of address bits to use
+ */
+tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
+{
+ int a, b;
+ int i, idx, sidx;
+ tPAddr ret;
+
+ ENTER("iPages iMaxBits", Pages, MaxBits);
+
+ // Sanity Checks
+ if(MaxBits < 0) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS;
+
+ // Lock
+ Mutex_Acquire( &glPhysAlloc );
+
+ // Set up search state
+ if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) {
+ sidx = (tPAddr)1 << (MaxBits-12);
+ }
+ else {
+ sidx = giLastPossibleFree;
+ }
+ idx = sidx / 32;
+ sidx %= 32;
+ b = idx % 32;
+ a = idx / 32;
+
+ #if 0
+ LOG("a=%i, b=%i, idx=%i, sidx=%i", a, b, idx, sidx);
+
+ // Find free page
+ for( ; gaSuperBitmap[a] == -1 && a --; ) b = 31;
+ if(a < 0) {
+ Mutex_Release( &glPhysAlloc );
+ Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
+ LEAVE('i', 0);
+ return 0;
+ }
+ LOG("a = %i", a);
+ for( ; gaSuperBitmap[a] & (1 << b); b-- ) sidx = 31;
+ LOG("b = %i", b);
+ idx = a * 32 + b;
+ for( ; gaPageBitmap[idx] & (1 << sidx); sidx-- )
+ LOG("gaPageBitmap[%i] = 0x%08x", idx, gaPageBitmap[idx]);
+
+ LOG("idx = %i, sidx = %i", idx, sidx);
+ #else
+
+ #endif
+
+ // Check if the gap is large enough
+ while( idx >= 0 )
+ {
+ // Find a free page
+ for( ; ; )
+ {
+ // Bulk Skip
+ if( gaPageBitmap[idx] == -1 ) {
+ idx --;
+ sidx = 31;
+ continue;
+ }
+
+ if( gaPageBitmap[idx] & (1 << sidx) ) {
+ sidx --;
+ if(sidx < 0) { sidx = 31; idx --; }
+ if(idx < 0) break;
+ continue;
+ }
+ break;
+ }
+ if( idx < 0 ) break;
+
+ // Check if it is a free range
+ for( i = 0; i < Pages; i++ )
+ {
+ // Used page? break
+ if( gaPageBitmap[idx] & (1 << sidx) )
+ break;
+
+ sidx --;
+ if(sidx < 0) { sidx = 31; idx --; }
+ if(idx < 0) break;
+ }
+
+ if( i == Pages )
+ break;
+ }
+
+ // Check if an address was found
+ if( idx < 0 ) {
+ Mutex_Release( &glPhysAlloc );
+ Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Mark pages used
+ for( i = 0; i < Pages; i++ )
+ {
+ if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[idx*32+sidx] ) )
+ gaPageReferences[idx*32+sidx] = 1;
+ gaPageBitmap[ idx ] |= 1 << sidx;
+ sidx ++;
+ giPhysAlloc ++;
+ if(sidx == 32) { sidx = 0; idx ++; }
+ }
+
+ // Get address
+ ret = (idx << 17) | (sidx << 12);
+
+ // Mark used block
+ if(gaPageBitmap[ idx ] == -1) gaSuperBitmap[idx/32] |= 1 << (idx%32);
+
+ // Release Spinlock
+ Mutex_Release( &glPhysAlloc );
+
+ LEAVE('X', ret);
+ #if TRACE_ALLOCS
+ Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)",
+ ret, ret + (1<<Pages)-1, giPageCount-giPhysAlloc);
+ #endif
+ return ret;
+}
+
+/**
+ * \fn void MM_RefPhys(tPAddr PAddr)
+ */
+void MM_RefPhys(tPAddr PAddr)
+{
+ // Get page number
+ PAddr >>= 12;
+
+ // We don't care about non-ram pages
+ if(PAddr >= giPageCount) return;
+
+ // Lock Structures
+ Mutex_Acquire( &glPhysAlloc );
+
+ // Reference the page
+ if( gaPageReferences )
+ {
+ if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 )
+ {
+ int i, base;
+ tVAddr addr = ((tVAddr)&gaPageReferences[PAddr]) & ~0xFFF;
+// Log_Debug("PMem", "MM_RefPhys: Allocating info for %X", PAddr);
+ Mutex_Release( &glPhysAlloc );
+ if( MM_Allocate( addr ) == 0 ) {
+ Log_KernelPanic("PMem", "MM_RefPhys: Out of physical memory allocating info for %X", PAddr*PAGE_SIZE);
+ }
+ Mutex_Acquire( &glPhysAlloc );
+
+ base = PAddr & ~(1024-1);
+ for( i = 0; i < 1024; i ++ ) {
+ gaPageReferences[base + i] = (gaPageBitmap[(base+i)/32] & (1 << (base+i)%32)) ? 1 : 0;
+ }
+ }
+ gaPageReferences[ PAddr ] ++;
+ }
+
+ // Mark as used
+ gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31);
+
+ // Mark used block
+ if(gaPageBitmap[ PAddr / 32 ] == -1)
+ gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31);
+
+ // Release Spinlock
+ Mutex_Release( &glPhysAlloc );
+}
+
+/**
+ * \fn void MM_DerefPhys(tPAddr PAddr)
+ * \brief Dereferences a physical page
+ */
+void MM_DerefPhys(tPAddr PAddr)
+{
+ // Get page number
+ PAddr >>= 12;
+
+ // We don't care about non-ram pages
+ if(PAddr >= giPageCount) return;
+
+ // Check if it is freed
+ if( !(gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ) {
+ Log_Warning("MMVirt", "MM_DerefPhys - Non-referenced memory dereferenced");
+ return;
+ }
+
+ // Lock Structures
+ Mutex_Acquire( &glPhysAlloc );
+
+ if( giLastPossibleFree < PAddr )
+ giLastPossibleFree = PAddr;
+
+ // Dereference
+ if( !MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) || (-- gaPageReferences[PAddr]) == 0 )
+ {
+ #if TRACE_ALLOCS
+ Log_Debug("PMem", "MM_DerefPhys: Free'd %P (%i free)", PAddr<<12, giPageCount-giPhysAlloc);
+ Proc_PrintBacktrace();
+ #endif
+ //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0));
+ giPhysAlloc --;
+ gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31));
+ if(gaPageBitmap[ PAddr / 32 ] == 0)
+ gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31));
+
+ if( MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) )
+ {
+ gaPageNodes[PAddr] = NULL;
+ // TODO: Free Node Page when fully unused
+ }
+ }
+
+ // Release spinlock
+ Mutex_Release( &glPhysAlloc );
+}
+
+/**
+ * \fn int MM_GetRefCount(tPAddr Addr)
+ */
+int MM_GetRefCount(tPAddr PAddr)
+{
+ // Get page number
+ PAddr >>= 12;
+
+ // We don't care about non-ram pages
+ if(PAddr >= giPageCount) return -1;
+
+ if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 )
+ return (gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ? 1 : 0;
+
+ // Check if it is freed
+ return gaPageReferences[ PAddr ];
+}
+
+int MM_SetPageNode(tPAddr PAddr, void *Node)
+{
+ tVAddr block_addr;
+
+ if( MM_GetRefCount(PAddr) == 0 ) return 1;
+
+ PAddr /= PAGE_SIZE;
+
+ block_addr = (tVAddr) &gaPageNodes[PAddr];
+ block_addr &= ~(PAGE_SIZE-1);
+
+ if( !MM_GetPhysAddr( block_addr ) )
+ {
+ if( !MM_Allocate( block_addr ) ) {
+ Log_Warning("PMem", "Unable to allocate Node page");
+ return -1;
+ }
+ memset( (void*)block_addr, 0, PAGE_SIZE );
+ }
+
+ gaPageNodes[PAddr] = Node;
+// Log("gaPageNodes[0x%x] = %p", PAddr, Node);
+ return 0;
+}
+
+int MM_GetPageNode(tPAddr PAddr, void **Node)
+{
+ if( MM_GetRefCount(PAddr) == 0 ) return 1;
+
+ PAddr /= PAGE_SIZE;
+ if( !MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) {
+ *Node = NULL;
+ return 0;
+ }
+ *Node = gaPageNodes[PAddr];
+ return 0;
+}
+
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * mm_virt.c
+ *
+ * Memory Map
+ * 0xE0 - Kernel Base
+ * 0xF0 - Kernel Stacks
+ * 0xFD - Fractals
+ * 0xFE - Unused
+ * 0xFF - System Calls / Kernel's User Code
+ */
+#define DEBUG 0
+#define SANITY 1
+#include <acess.h>
+#include <mm_virt.h>
+#include <mm_phys.h>
+#include <proc.h>
+#include <hal_proc.h>
+#include <arch_int.h>
+
+#define TAB 22
+
+#define WORKER_STACKS 0x00100000 // Thread0 Only!
+#define WORKER_STACK_SIZE MM_KERNEL_STACK_SIZE
+#define WORKER_STACKS_END 0xB0000000
+#define NUM_WORKER_STACKS ((WORKER_STACKS_END-WORKER_STACKS)/WORKER_STACK_SIZE)
+
+#define PAE_PAGE_TABLE_ADDR 0xFC000000 // 16 MiB
+#define PAE_PAGE_DIR_ADDR 0xFCFC0000 // 16 KiB
+#define PAE_PAGE_PDPT_ADDR 0xFCFC3F00 // 32 bytes
+#define PAE_TMP_PDPT_ADDR 0xFCFC3F20 // 32 bytes
+#define PAE_TMP_DIR_ADDR 0xFCFE0000 // 16 KiB
+#define PAE_TMP_TABLE_ADDR 0xFD000000 // 16 MiB
+
+#define PAGE_TABLE_ADDR 0xFC000000
+#define PAGE_DIR_ADDR 0xFC3F0000
+#define PAGE_CR3_ADDR 0xFC3F0FC0
+#define TMP_CR3_ADDR 0xFC3F0FC4 // Part of core instead of temp
+#define TMP_DIR_ADDR 0xFC3F1000 // Same
+#define TMP_TABLE_ADDR 0xFC400000
+
+#define HW_MAP_ADDR 0xFE000000
+#define HW_MAP_MAX 0xFFEF0000
+#define NUM_HW_PAGES ((HW_MAP_MAX-HW_MAP_ADDR)/0x1000)
+#define TEMP_MAP_ADDR 0xFFEF0000 // Allows 16 "temp" pages
+#define NUM_TEMP_PAGES 16
+#define LAST_BLOCK_ADDR 0xFFFF0000 // Free space for kernel provided user code/ *(-1) protection
+
+#define PF_PRESENT 0x1
+#define PF_WRITE 0x2
+#define PF_USER 0x4
+#define PF_GLOBAL 0x80
+#define PF_COW 0x200
+#define PF_NOPAGE 0x400
+
+#define INVLPG(addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(addr))
+
+#define GET_TEMP_MAPPING(cr3) do { \
+ __ASM__("cli"); \
+ __AtomicTestSetLoop( (Uint *)gpTmpCR3, cr3 | 3 ); \
+} while(0)
+#define REL_TEMP_MAPPING() do { \
+ *gpTmpCR3 = 0; \
+ __ASM__("sti"); \
+} while(0)
+
+typedef Uint32 tTabEnt;
+
+// === IMPORTS ===
+extern char _UsertextEnd[], _UsertextBase[];
+extern Uint32 gaInitPageDir[1024];
+extern Uint32 gaInitPageTable[1024];
+extern void Threads_SegFault(tVAddr Addr);
+extern void Error_Backtrace(Uint eip, Uint ebp);
+
+// === PROTOTYPES ===
+void MM_PreinitVirtual(void);
+void MM_InstallVirtual(void);
+void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
+//void MM_DumpTables(tVAddr Start, tVAddr End);
+//void MM_ClearUser(void);
+tPAddr MM_DuplicatePage(tVAddr VAddr);
+
+// === GLOBALS ===
+#define gaPageTable ((tTabEnt*)PAGE_TABLE_ADDR)
+#define gaPageDir ((tTabEnt*)PAGE_DIR_ADDR)
+#define gaTmpTable ((tTabEnt*)TMP_TABLE_ADDR)
+#define gaTmpDir ((tTabEnt*)TMP_DIR_ADDR)
+#define gpPageCR3 ((tTabEnt*)PAGE_CR3_ADDR)
+#define gpTmpCR3 ((tTabEnt*)TMP_CR3_ADDR)
+
+#define gaPAE_PageTable ((tTabEnt*)PAE_PAGE_TABLE_ADDR)
+#define gaPAE_PageDir ((tTabEnt*)PAE_PAGE_DIR_ADDR)
+#define gaPAE_MainPDPT ((tTabEnt*)PAE_PAGE_PDPT_ADDR)
+#define gaPAE_TmpTable ((tTabEnt*)PAE_TMP_DIR_ADDR)
+#define gaPAE_TmpDir ((tTabEnt*)PAE_TMP_DIR_ADDR)
+#define gaPAE_TmpPDPT ((tTabEnt*)PAE_TMP_PDPT_ADDR)
+ int gbUsePAE = 0;
+tMutex glTempMappings;
+tMutex glTempFractal;
+Uint32 gWorkerStacks[(NUM_WORKER_STACKS+31)/32];
+ int giLastUsedWorker = 0;
+struct sPageInfo {
+ void *Node;
+ tVAddr Base;
+ Uint64 Offset;
+ int Length;
+ int Flags;
+} *gaMappedRegions; // sizeof = 24 bytes
+
+// === CODE ===
+/**
+ * \fn void MM_PreinitVirtual(void)
+ * \brief Maps the fractal mappings
+ */
+void MM_PreinitVirtual(void)
+{
+ gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((tTabEnt)&gaInitPageDir - KERNEL_BASE) | 3;
+ INVLPG( PAGE_TABLE_ADDR );
+}
+
+/**
+ * \fn void MM_InstallVirtual(void)
+ * \brief Sets up the constant page mappings
+ */
+void MM_InstallVirtual(void)
+{
+ int i;
+
+ // --- Pre-Allocate kernel tables
+ for( i = KERNEL_BASE>>22; i < 1024; i ++ )
+ {
+ if( gaPageDir[ i ] ) continue;
+ // Skip stack tables, they are process unique
+ if( i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) {
+ gaPageDir[ i ] = 0;
+ continue;
+ }
+ // Preallocate table
+ gaPageDir[ i ] = MM_AllocPhys() | 3;
+ INVLPG( &gaPageTable[i*1024] );
+ memset( &gaPageTable[i*1024], 0, 0x1000 );
+ }
+
+ // Unset kernel on the User Text pages
+ for( i = ((tVAddr)&_UsertextEnd-(tVAddr)&_UsertextBase+0xFFF)/4096; i--; ) {
+ MM_SetFlags( (tVAddr)&_UsertextBase + i*4096, 0, MM_PFLAG_KERNEL );
+ }
+
+ *gpTmpCR3 = 0;
+}
+
+/**
+ * \brief Cleans up the SMP required mappings
+ */
+void MM_FinishVirtualInit(void)
+{
+ gaInitPageDir[ 0 ] = 0;
+}
+
+/**
+ * \fn void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
+ * \brief Called on a page fault
+ */
+void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
+{
+ //ENTER("xAddr bErrorCode", Addr, ErrorCode);
+
+ // -- Check for COW --
+ if( gaPageDir [Addr>>22] & PF_PRESENT && gaPageTable[Addr>>12] & PF_PRESENT
+ && gaPageTable[Addr>>12] & PF_COW )
+ {
+ tPAddr paddr;
+ if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
+ {
+ gaPageTable[Addr>>12] &= ~PF_COW;
+ gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
+ }
+ else
+ {
+ //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr);
+ paddr = MM_DuplicatePage( Addr );
+ MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
+ gaPageTable[Addr>>12] &= PF_USER;
+ gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
+ }
+
+// Log_Debug("MMVirt", "COW for %p (%P)", Addr, gaPageTable[Addr>>12]);
+
+ INVLPG( Addr & ~0xFFF );
+ return;
+ }
+
+ // Disable instruction tracing
+ __ASM__("pushf; andw $0xFEFF, 0(%esp); popf");
+ Proc_GetCurThread()->bInstrTrace = 0;
+
+ // If it was a user, tell the thread handler
+ if(ErrorCode & 4) {
+ Log_Warning("MMVirt", "User %s %s memory%s",
+ (ErrorCode&2?"write to":"read from"),
+ (ErrorCode&1?"bad/locked":"non-present"),
+ (ErrorCode&16?" (Instruction Fetch)":"")
+ );
+ Log_Warning("MMVirt", "Instruction %04x:%08x accessed %p", Regs->cs, Regs->eip, Addr);
+ __ASM__("sti"); // Restart IRQs
+ #if 1
+ Error_Backtrace(Regs->eip, Regs->ebp);
+ #endif
+ Threads_SegFault(Addr);
+ return ;
+ }
+
+ Debug_KernelPanic();
+
+ // -- Check Error Code --
+ if(ErrorCode & 8)
+ Warning("Reserved Bits Trashed!");
+ else
+ {
+ Warning("Kernel %s %s memory%s",
+ (ErrorCode&2?"write to":"read from"),
+ (ErrorCode&1?"bad/locked":"non-present"),
+ (ErrorCode&16?" (Instruction Fetch)":"")
+ );
+ }
+
+ Log("CPU %i - Code at %p accessed %p", GetCPUNum(), Regs->eip, Addr);
+ // Print Stack Backtrace
+ Error_Backtrace(Regs->eip, Regs->ebp);
+
+ #if 0
+ Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]);
+ if( gaPageDir[Addr>>22] & PF_PRESENT )
+ Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]);
+ #endif
+ //MM_DumpTables(0, -1);
+
+ // Register Dump
+ Log("EAX %08x ECX %08x EDX %08x EBX %08x", Regs->eax, Regs->ecx, Regs->edx, Regs->ebx);
+ Log("ESP %08x EBP %08x ESI %08x EDI %08x", Regs->esp, Regs->ebp, Regs->esi, Regs->edi);
+ //Log("SS:ESP %04x:%08x", Regs->ss, Regs->esp);
+ Log("CS:EIP %04x:%08x", Regs->cs, Regs->eip);
+ Log("DS %04x ES %04x FS %04x GS %04x", Regs->ds, Regs->es, Regs->fs, Regs->gs);
+ {
+ Uint dr0, dr1;
+ __ASM__ ("mov %%dr0, %0":"=r"(dr0):);
+ __ASM__ ("mov %%dr1, %0":"=r"(dr1):);
+ Log("DR0 %08x DR1 %08x", dr0, dr1);
+ }
+
+ Panic("Page Fault at 0x%x (Accessed 0x%x)", Regs->eip, Addr);
+}
+
+/**
+ * \fn void MM_DumpTables(tVAddr Start, tVAddr End)
+ * \brief Dumps the layout of the page tables
+ */
+void MM_DumpTables(tVAddr Start, tVAddr End)
+{
+ tVAddr rangeStart = 0;
+ tPAddr expected = 0;
+ void *expected_node = NULL, *tmpnode = NULL;
+ tVAddr curPos;
+ Uint page;
+ const tPAddr MASK = ~0xF78;
+
+ Start >>= 12; End >>= 12;
+
+ #if 0
+ Log("Directory Entries:");
+ for(page = Start >> 10;
+ page < (End >> 10)+1;
+ page ++)
+ {
+ if(gaPageDir[page])
+ {
+ Log(" 0x%08x-0x%08x :: 0x%08x",
+ page<<22, ((page+1)<<22)-1,
+ gaPageDir[page]&~0xFFF
+ );
+ }
+ }
+ #endif
+
+ Log("Table Entries:");
+ for(page = Start, curPos = Start<<12;
+ page < End;
+ curPos += 0x1000, page++)
+ {
+ if( !(gaPageDir[curPos>>22] & PF_PRESENT)
+ || !(gaPageTable[page] & PF_PRESENT)
+ || (gaPageTable[page] & MASK) != expected
+ || (tmpnode=NULL,MM_GetPageNode(expected, &tmpnode), tmpnode != expected_node))
+ {
+ if(expected) {
+ tPAddr orig = gaPageTable[rangeStart>>12];
+ Log(" 0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p",
+ rangeStart,
+ orig & ~0xFFF,
+ curPos - rangeStart,
+ (orig & PF_NOPAGE ? "P" : "-"),
+ (orig & PF_COW ? "C" : "-"),
+ (orig & PF_GLOBAL ? "G" : "-"),
+ (orig & PF_USER ? "U" : "-"),
+ (orig & PF_WRITE ? "W" : "-"),
+ expected_node
+ );
+ expected = 0;
+ }
+ if( !(gaPageDir[curPos>>22] & PF_PRESENT) ) continue;
+ if( !(gaPageTable[curPos>>12] & PF_PRESENT) ) continue;
+
+ expected = (gaPageTable[page] & MASK);
+ MM_GetPageNode(expected, &expected_node);
+ rangeStart = curPos;
+ }
+ if(expected) expected += 0x1000;
+ }
+
+ if(expected) {
+ tPAddr orig = gaPageTable[rangeStart>>12];
+ Log("0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p",
+ rangeStart,
+ orig & ~0xFFF,
+ curPos - rangeStart,
+ (orig & PF_NOPAGE ? "p" : "-"),
+ (orig & PF_COW ? "C" : "-"),
+ (orig & PF_GLOBAL ? "G" : "-"),
+ (orig & PF_USER ? "U" : "-"),
+ (orig & PF_WRITE ? "W" : "-"),
+ expected_node
+ );
+ expected = 0;
+ }
+}
+
+/**
+ * \fn tPAddr MM_Allocate(tVAddr VAddr)
+ */
+tPAddr MM_Allocate(tVAddr VAddr)
+{
+ tPAddr paddr;
+ //ENTER("xVAddr", VAddr);
+ //__ASM__("xchg %bx,%bx");
+ // Check if the directory is mapped
+ if( gaPageDir[ VAddr >> 22 ] == 0 )
+ {
+ // Allocate directory
+ paddr = MM_AllocPhys();
+ if( paddr == 0 ) {
+ Warning("MM_Allocate - Out of Memory (Called by %p)", __builtin_return_address(0));
+ //LEAVE('i',0);
+ return 0;
+ }
+ // Map and mark as user (if needed)
+ gaPageDir[ VAddr >> 22 ] = paddr | 3;
+ if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER;
+
+ INVLPG( &gaPageDir[ VAddr >> 22 ] );
+ memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 );
+ }
+ // Check if the page is already allocated
+ else if( gaPageTable[ VAddr >> 12 ] != 0 ) {
+ Warning("MM_Allocate - Allocating to used address (%p)", VAddr);
+ //LEAVE('X', gaPageTable[ VAddr >> 12 ] & ~0xFFF);
+ return gaPageTable[ VAddr >> 12 ] & ~0xFFF;
+ }
+
+ // Allocate
+ paddr = MM_AllocPhys();
+ //LOG("paddr = 0x%llx", paddr);
+ if( paddr == 0 ) {
+ Warning("MM_Allocate - Out of Memory when allocating at %p (Called by %p)",
+ VAddr, __builtin_return_address(0));
+ //LEAVE('i',0);
+ return 0;
+ }
+ // Map
+ gaPageTable[ VAddr >> 12 ] = paddr | 3;
+ // Mark as user
+ if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER;
+ // Invalidate Cache for address
+ INVLPG( VAddr & ~0xFFF );
+
+ //LEAVE('X', paddr);
+ return paddr;
+}
+
+/**
+ * \fn void MM_Deallocate(tVAddr VAddr)
+ */
+void MM_Deallocate(tVAddr VAddr)
+{
+ if( gaPageDir[ VAddr >> 22 ] == 0 ) {
+ Warning("MM_Deallocate - Directory not mapped");
+ return;
+ }
+
+ if(gaPageTable[ VAddr >> 12 ] == 0) {
+ Warning("MM_Deallocate - Page is not allocated");
+ return;
+ }
+
+ // Dereference page
+ MM_DerefPhys( gaPageTable[ VAddr >> 12 ] & ~0xFFF );
+ // Clear page
+ gaPageTable[ VAddr >> 12 ] = 0;
+}
+
+/**
+ * \fn tPAddr MM_GetPhysAddr(tVAddr Addr)
+ * \brief Checks if the passed address is accesable
+ */
+tPAddr MM_GetPhysAddr(tVAddr Addr)
+{
+ if( !(gaPageDir[Addr >> 22] & 1) )
+ return 0;
+ if( !(gaPageTable[Addr >> 12] & 1) )
+ return 0;
+ return (gaPageTable[Addr >> 12] & ~0xFFF) | (Addr & 0xFFF);
+}
+
+/**
+ * \fn void MM_SetCR3(Uint CR3)
+ * \brief Sets the current process space
+ */
+void MM_SetCR3(Uint CR3)
+{
+ __ASM__("mov %0, %%cr3"::"r"(CR3));
+}
+
+/**
+ * \fn int MM_Map(tVAddr VAddr, tPAddr PAddr)
+ * \brief Map a physical page to a virtual one
+ */
+int MM_Map(tVAddr VAddr, tPAddr PAddr)
+{
+ //ENTER("xVAddr xPAddr", VAddr, PAddr);
+ // Sanity check
+ if( PAddr & 0xFFF || VAddr & 0xFFF ) {
+ Log_Warning("MM_Virt", "MM_Map - Physical or Virtual Addresses are not aligned");
+ //LEAVE('i', 0);
+ return 0;
+ }
+
+ // Align addresses
+ PAddr &= ~0xFFF; VAddr &= ~0xFFF;
+
+ // Check if the directory is mapped
+ if( gaPageDir[ VAddr >> 22 ] == 0 )
+ {
+ tPAddr tmp = MM_AllocPhys();
+ if( tmp == 0 )
+ return 0;
+ gaPageDir[ VAddr >> 22 ] = tmp | 3;
+
+ // Mark as user
+ if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER;
+
+ INVLPG( &gaPageTable[ (VAddr >> 12) & ~0x3FF ] );
+ memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 );
+ }
+ // Check if the page is already allocated
+ else if( gaPageTable[ VAddr >> 12 ] != 0 ) {
+ Warning("MM_Map - Allocating to used address");
+ //LEAVE('i', 0);
+ return 0;
+ }
+
+ // Map
+ gaPageTable[ VAddr >> 12 ] = PAddr | 3;
+ // Mark as user
+ if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER;
+
+ //LOG("gaPageTable[ 0x%x ] = (Uint)%p = 0x%x",
+ // VAddr >> 12, &gaPageTable[ VAddr >> 12 ], gaPageTable[ VAddr >> 12 ]);
+
+ // Reference
+ MM_RefPhys( PAddr );
+
+ //LOG("INVLPG( 0x%x )", VAddr);
+ INVLPG( VAddr );
+
+ //LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \brief Clear user's address space
+ */
+void MM_ClearUser(void)
+{
+ Uint i, j;
+
+ for( i = 0; i < (MM_USER_MAX>>22); i ++ )
+ {
+ // Check if directory is not allocated
+ if( !(gaPageDir[i] & PF_PRESENT) ) {
+ gaPageDir[i] = 0;
+ continue;
+ }
+
+ // Deallocate tables
+ for( j = 0; j < 1024; j ++ )
+ {
+ if( gaPageTable[i*1024+j] & 1 )
+ MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF );
+ gaPageTable[i*1024+j] = 0;
+ }
+
+ // Deallocate directory
+ MM_DerefPhys( gaPageDir[i] & ~0xFFF );
+ gaPageDir[i] = 0;
+ INVLPG( &gaPageTable[i*1024] );
+ }
+ INVLPG( gaPageDir );
+}
+
+/**
+ * \brief Deallocate an address space
+ */
+void MM_ClearSpace(Uint32 CR3)
+{
+ int i, j;
+
+ if(CR3 == (*gpPageCR3 & ~0xFFF)) {
+ Log_Error("MMVirt", "Can't clear current address space");
+ return ;
+ }
+
+ if( MM_GetRefCount(CR3) > 1 ) {
+ MM_DerefPhys(CR3);
+ Log_Log("MMVirt", "CR3 %P is still referenced, not cleaning (but dereferenced)", CR3);
+ return ;
+ }
+
+ Log_Debug("MMVirt", "Clearing out address space 0x%x from 0x%x", CR3, *gpPageCR3);
+
+ GET_TEMP_MAPPING(CR3);
+ INVLPG( gaTmpDir );
+
+ for( i = 0; i < 1024; i ++ )
+ {
+ Uint32 *table = &gaTmpTable[i*1024];
+ if( !(gaTmpDir[i] & PF_PRESENT) )
+ continue ;
+
+ INVLPG( table );
+
+ if( i < 768 || (i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) )
+ {
+ for( j = 0; j < 1024; j ++ )
+ {
+ if( !(table[j] & 1) )
+ continue;
+ MM_DerefPhys( table[j] & ~0xFFF );
+ }
+ }
+
+ if( i != (PAGE_TABLE_ADDR >> 22) )
+ {
+ MM_DerefPhys( gaTmpDir[i] & ~0xFFF );
+ }
+ }
+
+
+ MM_DerefPhys( CR3 );
+
+ REL_TEMP_MAPPING();
+}
+
+/**
+ * \fn tPAddr MM_Clone(void)
+ * \brief Clone the current address space
+ */
+tPAddr MM_Clone(int bNoUserCopy)
+{
+ Uint i, j;
+ tPAddr ret;
+ Uint page = 0;
+ tVAddr kStackBase = Proc_GetCurThread()->KernelStack - MM_KERNEL_STACK_SIZE;
+ void *tmp;
+
+ // Create Directory Table
+ ret = MM_AllocPhys();
+ if( ret == 0 ) {
+ return 0;
+ }
+
+ // Map
+ GET_TEMP_MAPPING( ret );
+ INVLPG( gaTmpDir );
+ memsetd( gaTmpDir, 0, 1024 );
+
+ if( Threads_GetPID() != 0 && !bNoUserCopy )
+ {
+ // Copy Tables
+ for( i = 0; i < 768; i ++)
+ {
+ // Check if table is allocated
+ if( !(gaPageDir[i] & PF_PRESENT) ) {
+ gaTmpDir[i] = 0;
+ page += 1024;
+ continue;
+ }
+
+ // Allocate new table
+ gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7);
+ INVLPG( &gaTmpTable[page] );
+ // Fill
+ for( j = 0; j < 1024; j ++, page++ )
+ {
+ if( !(gaPageTable[page] & PF_PRESENT) ) {
+ gaTmpTable[page] = 0;
+ continue;
+ }
+
+ // Refrence old page
+ MM_RefPhys( gaPageTable[page] & ~0xFFF );
+ // Add to new table
+ if(gaPageTable[page] & PF_WRITE) {
+ gaTmpTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
+ gaPageTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
+ INVLPG( page << 12 );
+ }
+ else
+ gaTmpTable[page] = gaPageTable[page];
+ }
+ }
+ }
+
+ // Map in kernel tables (and make fractal mapping)
+ for( i = 768; i < 1024; i ++ )
+ {
+ // Fractal
+ if( i == (PAGE_TABLE_ADDR >> 22) ) {
+ gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gpTmpCR3;
+ continue;
+ }
+ if( i == (TMP_TABLE_ADDR >> 22) ) {
+ gaTmpDir[ TMP_TABLE_ADDR >> 22 ] = 0;
+ continue ;
+ }
+
+ if( gaPageDir[i] == 0 ) {
+ gaTmpDir[i] = 0;
+ continue;
+ }
+
+ //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]);
+ MM_RefPhys( gaPageDir[i] & ~0xFFF );
+ gaTmpDir[i] = gaPageDir[i];
+ }
+
+ // Allocate kernel stack
+ for(i = MM_KERNEL_STACKS >> 22; i < MM_KERNEL_STACKS_END >> 22; i ++ )
+ {
+ // Check if directory is allocated
+ if( (gaPageDir[i] & 1) == 0 ) {
+ gaTmpDir[i] = 0;
+ continue;
+ }
+
+ // We don't care about other kernel stacks, just the current one
+ if( i != kStackBase >> 22 ) {
+ MM_DerefPhys( gaPageDir[i] & ~0xFFF );
+ gaTmpDir[i] = 0;
+ continue;
+ }
+
+ // Create a copy
+ gaTmpDir[i] = MM_AllocPhys() | 3;
+ INVLPG( &gaTmpTable[i*1024] );
+ for( j = 0; j < 1024; j ++ )
+ {
+ // Is the page allocated? If not, skip
+ if( !(gaPageTable[i*1024+j] & 1) ) {
+ gaTmpTable[i*1024+j] = 0;
+ continue;
+ }
+
+ // We don't care about other kernel stacks
+ if( ((i*1024+j)*4096 & ~(MM_KERNEL_STACK_SIZE-1)) != kStackBase ) {
+ gaTmpTable[i*1024+j] = 0;
+ continue;
+ }
+
+ // Allocate page
+ gaTmpTable[i*1024+j] = MM_AllocPhys() | 3;
+
+ MM_RefPhys( gaTmpTable[i*1024+j] & ~0xFFF );
+
+ tmp = (void *) MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF );
+ memcpy( tmp, (void *)( (i*1024+j)*0x1000 ), 0x1000 );
+ MM_FreeTemp( (Uint)tmp );
+ }
+ }
+
+ REL_TEMP_MAPPING();
+
+ //LEAVE('x', ret);
+ return ret;
+}
+
+/**
+ * \fn tVAddr MM_NewKStack(void)
+ * \brief Create a new kernel stack
+ */
+tVAddr MM_NewKStack(void)
+{
+ tVAddr base;
+ Uint i;
+ for(base = MM_KERNEL_STACKS; base < MM_KERNEL_STACKS_END; base += MM_KERNEL_STACK_SIZE)
+ {
+ // Check if space is free
+ if(MM_GetPhysAddr(base) != 0) continue;
+ // Allocate
+ //for(i = MM_KERNEL_STACK_SIZE; i -= 0x1000 ; )
+ for(i = 0; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
+ {
+ if( MM_Allocate(base+i) == 0 )
+ {
+ // On error, print a warning and return error
+ Warning("MM_NewKStack - Out of memory");
+ // - Clean up
+ //for( i += 0x1000 ; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
+ // MM_Deallocate(base+i);
+ return 0;
+ }
+ }
+ // Success
+// Log("MM_NewKStack - Allocated %p", base + MM_KERNEL_STACK_SIZE);
+ return base+MM_KERNEL_STACK_SIZE;
+ }
+ // No stacks left
+ Log_Warning("MMVirt", "MM_NewKStack - No address space left");
+ return 0;
+}
+
+/**
+ * \fn tVAddr MM_NewWorkerStack()
+ * \brief Creates a new worker stack
+ */
+tVAddr MM_NewWorkerStack(Uint *StackContents, size_t ContentsSize)
+{
+ Uint base, addr;
+ tVAddr tmpPage;
+ tPAddr page;
+
+ // TODO: Thread safety
+ // Find a free worker stack address
+ for(base = giLastUsedWorker; base < NUM_WORKER_STACKS; base++)
+ {
+ // Used block
+ if( gWorkerStacks[base/32] == -1 ) {
+ base += 31; base &= ~31;
+ base --; // Counteracted by the base++
+ continue;
+ }
+ // Used stack
+ if( gWorkerStacks[base/32] & (1 << base) ) {
+ continue;
+ }
+ break;
+ }
+ if(base >= NUM_WORKER_STACKS) {
+ Warning("Uh-oh! Out of worker stacks");
+ return 0;
+ }
+
+ // It's ours now!
+ gWorkerStacks[base/32] |= (1 << base);
+ // Make life easier for later calls
+ giLastUsedWorker = base;
+ // We have one
+ base = WORKER_STACKS + base * WORKER_STACK_SIZE;
+ //Log(" MM_NewWorkerStack: base = 0x%x", base);
+
+ // Set the temp fractals to TID0's address space
+ GET_TEMP_MAPPING( ((Uint)gaInitPageDir - KERNEL_BASE) );
+ INVLPG( gaTmpDir );
+
+ // Check if the directory is mapped (we are assuming that the stacks
+ // will fit neatly in a directory)
+ //Log(" MM_NewWorkerStack: gaTmpDir[ 0x%x ] = 0x%x", base>>22, gaTmpDir[ base >> 22 ]);
+ if(gaTmpDir[ base >> 22 ] == 0) {
+ gaTmpDir[ base >> 22 ] = MM_AllocPhys() | 3;
+ INVLPG( &gaTmpTable[ (base>>12) & ~0x3FF ] );
+ }
+
+ // Mapping Time!
+ for( addr = 0; addr < WORKER_STACK_SIZE; addr += 0x1000 )
+ {
+ page = MM_AllocPhys();
+ gaTmpTable[ (base + addr) >> 12 ] = page | 3;
+ }
+
+ // Release temporary fractal
+ REL_TEMP_MAPPING();
+
+ // NOTE: Max of 1 page
+ // `page` is the last allocated page from the previious for loop
+ tmpPage = MM_MapTemp( page );
+ memcpy( (void*)( tmpPage + (0x1000 - ContentsSize) ), StackContents, ContentsSize);
+ MM_FreeTemp(tmpPage);
+
+ //Log("MM_NewWorkerStack: RETURN 0x%x", base);
+ return base + WORKER_STACK_SIZE;
+}
+
+/**
+ * \fn void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
+ * \brief Sets the flags on a page
+ */
+void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
+{
+ tTabEnt *ent;
+ if( !(gaPageDir[VAddr >> 22] & 1) ) return ;
+ if( !(gaPageTable[VAddr >> 12] & 1) ) return ;
+
+ ent = &gaPageTable[VAddr >> 12];
+
+ // Read-Only
+ if( Mask & MM_PFLAG_RO )
+ {
+ if( Flags & MM_PFLAG_RO ) {
+ *ent &= ~PF_WRITE;
+ }
+ else {
+ gaPageDir[VAddr >> 22] |= PF_WRITE;
+ *ent |= PF_WRITE;
+ }
+ }
+
+ // Kernel
+ if( Mask & MM_PFLAG_KERNEL )
+ {
+ if( Flags & MM_PFLAG_KERNEL ) {
+ *ent &= ~PF_USER;
+ }
+ else {
+ gaPageDir[VAddr >> 22] |= PF_USER;
+ *ent |= PF_USER;
+ }
+ }
+
+ // Copy-On-Write
+ if( Mask & MM_PFLAG_COW )
+ {
+ if( Flags & MM_PFLAG_COW ) {
+ *ent &= ~PF_WRITE;
+ *ent |= PF_COW;
+ }
+ else {
+ *ent &= ~PF_COW;
+ *ent |= PF_WRITE;
+ }
+ }
+
+ //Log("MM_SetFlags: *ent = 0x%08x, gaPageDir[%i] = 0x%08x",
+ // *ent, VAddr >> 22, gaPageDir[VAddr >> 22]);
+}
+
+/**
+ * \brief Get the flags on a page
+ */
+Uint MM_GetFlags(tVAddr VAddr)
+{
+ tTabEnt *ent;
+ Uint ret = 0;
+
+ // Validity Check
+ if( !(gaPageDir[VAddr >> 22] & 1) ) return 0;
+ if( !(gaPageTable[VAddr >> 12] & 1) ) return 0;
+
+ ent = &gaPageTable[VAddr >> 12];
+
+ // Read-Only
+ if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO;
+ // Kernel
+ if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
+ // Copy-On-Write
+ if( *ent & PF_COW ) ret |= MM_PFLAG_COW;
+
+ return ret;
+}
+
+/**
+ * \brief Check if the provided buffer is valid
+ * \return Boolean valid
+ */
+int MM_IsValidBuffer(tVAddr Addr, size_t Size)
+{
+ int bIsUser;
+ int dir, tab;
+
+ Size += Addr & (PAGE_SIZE-1);
+ Addr &= ~(PAGE_SIZE-1);
+
+ dir = Addr >> 22;
+ tab = Addr >> 12;
+
+// Debug("Addr = %p, Size = 0x%x, dir = %i, tab = %i", Addr, Size, dir, tab);
+
+ if( !(gaPageDir[dir] & 1) ) return 0;
+ if( !(gaPageTable[tab] & 1) ) return 0;
+
+ bIsUser = !!(gaPageTable[tab] & PF_USER);
+
+ while( Size >= PAGE_SIZE )
+ {
+ if( (tab & 1023) == 0 )
+ {
+ dir ++;
+ if( !(gaPageDir[dir] & 1) ) return 0;
+ }
+
+ if( !(gaPageTable[tab] & 1) ) return 0;
+ if( bIsUser && !(gaPageTable[tab] & PF_USER) ) return 0;
+
+ tab ++;
+ Size -= PAGE_SIZE;
+ }
+ return 1;
+}
+
+/**
+ * \fn tPAddr MM_DuplicatePage(tVAddr VAddr)
+ * \brief Duplicates a virtual page to a physical one
+ */
+tPAddr MM_DuplicatePage(tVAddr VAddr)
+{
+ tPAddr ret;
+ Uint temp;
+ int wasRO = 0;
+
+ //ENTER("xVAddr", VAddr);
+
+ // Check if mapped
+ if( !(gaPageDir [VAddr >> 22] & PF_PRESENT) ) return 0;
+ if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) ) return 0;
+
+ // Page Align
+ VAddr &= ~0xFFF;
+
+ // Allocate new page
+ ret = MM_AllocPhys();
+ if( !ret ) {
+ return 0;
+ }
+
+ // Write-lock the page (to keep data constistent), saving its R/W state
+ wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1);
+ gaPageTable[VAddr >> 12] &= ~PF_WRITE;
+ INVLPG( VAddr );
+
+ // Copy Data
+ temp = MM_MapTemp(ret);
+ memcpy( (void*)temp, (void*)VAddr, 0x1000 );
+ MM_FreeTemp(temp);
+
+ // Restore Writeable status
+ if(!wasRO) gaPageTable[VAddr >> 12] |= PF_WRITE;
+ INVLPG(VAddr);
+
+ //LEAVE('X', ret);
+ return ret;
+}
+
+/**
+ * \fn Uint MM_MapTemp(tPAddr PAddr)
+ * \brief Create a temporary memory mapping
+ * \todo Show Luigi Barone (C Lecturer) and see what he thinks
+ */
+tVAddr MM_MapTemp(tPAddr PAddr)
+{
+ int i;
+
+ //ENTER("XPAddr", PAddr);
+
+ PAddr &= ~0xFFF;
+
+ //LOG("glTempMappings = %i", glTempMappings);
+
+ for(;;)
+ {
+ Mutex_Acquire( &glTempMappings );
+
+ for( i = 0; i < NUM_TEMP_PAGES; i ++ )
+ {
+ // Check if page used
+ if(gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] & 1) continue;
+ // Mark as used
+ gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] = PAddr | 3;
+ INVLPG( TEMP_MAP_ADDR + (i << 12) );
+ //LEAVE('p', TEMP_MAP_ADDR + (i << 12));
+ Mutex_Release( &glTempMappings );
+ return TEMP_MAP_ADDR + (i << 12);
+ }
+ Mutex_Release( &glTempMappings );
+ Threads_Yield(); // TODO: Use a sleep queue here instead
+ }
+}
+
+/**
+ * \fn void MM_FreeTemp(tVAddr PAddr)
+ * \brief Free's a temp mapping
+ */
+void MM_FreeTemp(tVAddr VAddr)
+{
+ int i = VAddr >> 12;
+ //ENTER("xVAddr", VAddr);
+
+ if(i >= (TEMP_MAP_ADDR >> 12))
+ gaPageTable[ i ] = 0;
+
+ //LEAVE('-');
+}
+
+/**
+ * \fn tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
+ * \brief Allocates a contigous number of pages
+ */
+tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
+{
+ int i, j;
+
+ PAddr &= ~0xFFF;
+
+ // Scan List
+ for( i = 0; i < NUM_HW_PAGES; i ++ )
+ {
+ // Check if addr used
+ if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 )
+ continue;
+
+ // Check possible region
+ for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ )
+ {
+ // If there is an allocated page in the region we are testing, break
+ if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 ) break;
+ }
+ // Is it all free?
+ if( j == Number )
+ {
+ // Allocate
+ for( j = 0; j < Number; j++ ) {
+ MM_RefPhys( PAddr + (j<<12) );
+ gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3;
+ }
+ return HW_MAP_ADDR + (i<<12);
+ }
+ }
+ // If we don't find any, return NULL
+ return 0;
+}
+
+/**
+ * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
+ * \brief Allocates DMA physical memory
+ * \param Pages Number of pages required
+ * \param MaxBits Maximum number of bits the physical address can have
+ * \param PhysAddr Pointer to the location to place the physical address allocated
+ * \return Virtual address allocate
+ */
+tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
+{
+ tPAddr maxCheck = (1 << MaxBits);
+ tPAddr phys;
+ tVAddr ret;
+
+ ENTER("iPages iMaxBits pPhysAddr", Pages, MaxBits, PhysAddr);
+
+ // Sanity Check
+ if(MaxBits < 12 || !PhysAddr) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Bound
+ if(MaxBits >= PHYS_BITS) maxCheck = -1;
+
+ // Fast Allocate
+ if(Pages == 1 && MaxBits >= PHYS_BITS)
+ {
+ phys = MM_AllocPhys();
+ if( !phys ) {
+ *PhysAddr = 0;
+ LEAVE_RET('i', 0);
+ }
+ *PhysAddr = phys;
+ ret = MM_MapHWPages(phys, 1);
+ if(ret == 0) {
+ MM_DerefPhys(phys);
+ LEAVE('i', 0);
+ return 0;
+ }
+ LEAVE('x', ret);
+ return ret;
+ }
+
+ // Slow Allocate
+ phys = MM_AllocPhysRange(Pages, MaxBits);
+ // - Was it allocated?
+ if(phys == 0) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Allocated successfully, now map
+ ret = MM_MapHWPages(phys, Pages);
+ if( ret == 0 ) {
+ // If it didn't map, free then return 0
+ for(;Pages--;phys+=0x1000)
+ MM_DerefPhys(phys);
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ *PhysAddr = phys;
+ LEAVE('x', ret);
+ return ret;
+}
+
+/**
+ * \fn void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
+ * \brief Unmap a hardware page
+ */
+void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
+{
+ int i, j;
+
+ //Log_Debug("VirtMem", "MM_UnmapHWPages: (VAddr=0x%08x, Number=%i)", VAddr, Number);
+
+ // Sanity Check
+ if(VAddr < HW_MAP_ADDR || VAddr+Number*0x1000 > HW_MAP_MAX) return;
+
+ i = VAddr >> 12;
+
+ Mutex_Acquire( &glTempMappings ); // Temp and HW share a directory, so they share a lock
+
+ for( j = 0; j < Number; j++ )
+ {
+ MM_DerefPhys( gaPageTable[ i + j ] & ~0xFFF );
+ gaPageTable[ i + j ] = 0;
+ }
+
+ Mutex_Release( &glTempMappings );
+}
+
--- /dev/null
+/*\r
+ * Acess2\r
+ * arch/x86/pci.h - x86 PCI Bus Access\r
+ */\r
+#define DEBUG 0\r
+#include <acess.h>\r
+#include <drv_pci_int.h>\r
+\r
+// === CODE ===\r
+Uint32 PCI_CfgReadDWord(Uint32 Address)\r
+{\r
+ Address |= 0x80000000;\r
+ outd(0xCF8, Address);\r
+ return ind(0xCFC);\r
+}\r
+\r
+void PCI_CfgWriteDWord(Uint32 Address, Uint32 Data)\r
+{\r
+ Address |= 0x80000000;\r
+ outd(0xCF8, Address);\r
+ outd(0xCFC, Data);\r
+}\r
--- /dev/null
+; AcessOS Microkernel Version
+; Start.asm
+
+[bits 32]
+
+%define SAVEFLAG_FPU 0x1
+
+KERNEL_BASE equ 0xC0000000
+
+KSTACK_USERSTATE_SIZE equ (4+8+1+5)*4 ; SRegs, GPRegs, CPU, IRET
+
+[section .text]
+
+[global NewTaskHeader]
+NewTaskHeader:
+ mov eax, [esp]
+ mov dr0, eax
+
+ mov eax, [esp+4]
+ add esp, 12 ; Thread, Function, Arg Count
+ call eax
+
+ push eax ; Ret val
+ push 0 ; 0 = This Thread
+ call Threads_Exit
+
+[extern MM_Clone]
+[global Proc_CloneInt]
+Proc_CloneInt:
+ pusha
+ ; Save RSP
+ mov eax, [esp+0x20+4]
+ mov [eax], esp
+ push DWORD [esp+0x20+12]
+ call MM_Clone
+ add esp, 4
+ ; Save CR3
+ mov esi, [esp+0x20+8]
+ mov [esi], eax
+ ; Undo the pusha
+ add esp, 0x20
+ mov eax, .newTask
+ ret
+.newTask:
+ popa
+ xor eax, eax
+ ret
+
+[global SwitchTasks]
+; + 4 = New RSP
+; + 8 = Old RSP save loc
+; +12 = New RIP
+; +16 = Old RIP save loc
+; +20 = CR3
+SwitchTasks:
+ pusha
+
+ ; Old IP
+ mov eax, [esp+0x20+16]
+ test eax, eax
+ jz .nosave
+ mov DWORD [eax], .restore
+ ; Old SP
+ mov eax, [esp+0x20+8]
+ mov [eax], esp
+
+.nosave:
+ mov ecx, [esp+0x20+12] ; New IP
+ mov eax, [esp+0x20+20] ; New CR3
+ mov esp, [esp+0x20+ 4] ; New SP
+
+ test eax, eax
+ jz .setState
+ mov cr3, eax
+ invlpg [esp]
+ invlpg [esp+0x1000]
+.setState:
+ jmp ecx
+
+.restore:
+ popa
+ xor eax, eax
+ ret
+
+[global Proc_InitialiseSSE]
+Proc_InitialiseSSE:
+ mov eax, cr4
+ or eax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT
+ mov cr4, eax
+ mov eax, cr0
+ and ax, ~(1 << 2) ; Clear EM
+ or eax, (1 << 1) ; Set MP
+ mov eax, cr0
+ ret
+[global Proc_DisableSSE]
+Proc_DisableSSE:
+ mov eax, cr0
+ or ax, 1 << 3 ; Set TS
+ mov cr0, eax
+ ret
+[global Proc_EnableSSE]
+Proc_EnableSSE:
+ mov eax, cr0
+ and ax, ~(1 << 3) ; Clear TS
+ mov cr0, eax
+ ret
+
+[global Proc_SaveSSE]
+Proc_SaveSSE:
+ mov eax, [esp+4]
+ fxsave [eax]
+ ret
+[global Proc_RestoreSSE]
+Proc_RestoreSSE:
+ mov eax, [esp+4]
+ fxrstor [eax]
+ ret
+
+%if USE_MP
+[extern giMP_TimerCount]
+[extern gpMP_LocalAPIC]
+[extern Isr240.jmp]
+[global SetAPICTimerCount]
+SetAPICTimerCount:
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ mov eax, [gpMP_LocalAPIC]
+ mov ecx, [eax+0x320]
+ test ecx, 0x00010000
+ jz .setTime
+ mov DWORD [eax+0x380], 0xFFFFFFFF ; Set Initial Count
+ mov DWORD [eax+0x320], 0x000000F0 ; Enable the timer on IVT#0xEF (One Shot)
+ jmp .ret
+
+.setTime:
+ ; Get Timer Count
+ mov ecx, 0xFFFFFFFF
+ sub ecx, [eax+0x390]
+ mov DWORD [giMP_TimerCount], ecx
+ ; Disable APIC Timer
+ mov DWORD [eax+0x320], 0x000100EF
+ mov DWORD [eax+0x380], 0
+
+ ; Update Timer IRQ to the IRQ code
+ mov eax, SchedulerBase
+ sub eax, Isr240.jmp+5
+ mov DWORD [Isr240.jmp+1], eax
+
+ ;xchg bx, bx ; MAGIC BREAK
+.ret:
+ mov dx, 0x20
+ mov al, 0x20
+ out dx, al ; ACK IRQ
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ add esp, 8 ; CPU ID / Error Code
+ iret
+%endif
+; --------------
+; Task Scheduler
+; --------------
+[extern Proc_Scheduler]
+[global SchedulerBase]
+SchedulerBase:
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ pushf
+ and BYTE [esp+1], 0xFE ; Clear Trap Flag
+ popf
+
+ mov eax, dr0
+ push eax ; Debug Register 0, Current Thread
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ %if USE_MP
+ call GetCPUNum
+ mov ebx, eax
+ push eax ; Push as argument
+ %else
+ push 0
+ %endif
+
+ call Proc_Scheduler
+[global scheduler_return]
+scheduler_return: ; Used by some hackery in Proc_DumpThreadCPUState
+
+ add esp, 4 ; Remove CPU Number (thread is poped later)
+
+ %if USE_MP
+ test ebx, ebx
+ jnz .sendEOI
+ %endif
+
+ mov al, 0x20
+ out 0x20, al ; ACK IRQ
+ %if USE_MP
+ jmp .ret
+
+.sendEOI:
+ mov eax, DWORD [gpMP_LocalAPIC]
+ mov DWORD [eax+0x0B0], 0
+ %endif
+.ret:
+ pop eax ; Debug Register 0, Current Thread
+ mov dr0, eax
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+
+ popa
+ add esp, 4*2 ; CPU ID + Dummy error code
+ ; No Error code / int num
+ iret
+
+[extern Proc_Clone]
+[extern Threads_Exit]
+[global SpawnTask]
+SpawnTask:
+ ; Call Proc_Clone with Flags=0
+ xor eax, eax
+; push eax
+ push eax
+ call Proc_Clone
+ add esp, 8 ; Remove arguments from stack
+
+ test eax, eax
+ jnz .parent
+
+ ; In child, so now set up stack frame
+ mov ebx, [esp+4] ; Child Function
+ mov edx, [esp+8] ; Argument
+ ; Child Function
+ push edx ; Argument
+ call ebx ; Function
+ ; Kill thread once done
+ push eax ; Exit Code
+ push 0 ; Kill this thread
+ call Threads_Exit ; Kill Thread
+
+.parent:
+ ret
+
+; void Proc_ReturnToUser(void *Method, Uint Parameter, tVAddr KernelStack)
+; Calls a user fault handler
+;
+[global Proc_ReturnToUser]
+[extern Proc_GetCurThread]
+Proc_ReturnToUser:
+ push ebp
+ mov ebp, esp
+ ; [EBP+8]: handler to use
+ ; [EBP+12]: parameter
+ ; [EBP+16]: kernel stack top
+
+ ; Get kernel stack
+ mov eax, [ebp+16]
+ sub eax, KSTACK_USERSTATE_SIZE
+
+ ;
+ ; NOTE: This can cause corruption if the signal happens while the user
+ ; has called a kernel operation.
+ ; Good thing this can only be called on a user fault.
+ ;
+
+ ; Validate user ESP
+ ; - Page Table
+ mov edx, [eax+KSTACK_USERSTATE_SIZE-12] ; User ESP is at top of kstack - 3*4
+ mov ecx, edx
+ shr ecx, 22
+ test BYTE [0xFC3F0000+ecx*4], 1
+ jnz .justKillIt
+ ; - Page
+ mov ecx, edx
+ shr ecx, 12
+ test BYTE [0xFC000000+ecx*4], 1
+ jnz .justKillIt
+ ; Adjust
+ sub edx, 8
+ ; - Page Table
+ mov ecx, edx
+ shr ecx, 22
+ test BYTE [0xFC3F0000+ecx*4], 1
+ jnz .justKillIt
+ ; - Page
+ mov ecx, edx
+ shr ecx, 12
+ test BYTE [0xFC000000+ecx*4], 1
+ jnz .justKillIt
+
+ ; Get and alter User SP
+ mov edi, edx
+ mov edx, [ebp+12] ; Get parameter
+ mov [edi+4], edx ; save to user stack
+ mov [edi], DWORD User_Syscall_RetAndExit ; Return Address
+
+ ; Restore Segment Registers
+ mov ax, 0x23
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ push 0x23 ; SS
+ push edi ; ESP
+ push 0x202 ; EFLAGS (IP and Rsvd)
+ push 0x1B ; CS
+ mov eax, [ebp+8] ; Method to call
+ push eax ; EIP
+
+ iret
+
+ ; Just kill the bleeding thing
+ ; (I know it calls int 0xAC in kernel mode, but meh)
+.justKillIt:
+ xor eax, eax
+ xor ebx, ebx
+ dec ebx ; EBX = -1
+ int 0xAC
+
+[global GetCPUNum]
+GetCPUNum: ; TODO: Store in debug registers
+ mov eax, dr1
+ ret
+
+[extern GetEIP]
+[global GetEIP_Sched]
+[global GetEIP_Sched_ret]
+GetEIP_Sched_ret equ GetEIP_Sched.ret
+GetEIP_Sched:
+ call GetEIP
+GetEIP_Sched.ret:
+ ret
+
+; Usermode code exported by the kernel
+[section .usertext]
+; Export a place for the user to jump to to call a syscall
+; - Allows the kernel to change the method easily
+User_Syscall:
+ xchg bx, bx ; MAGIC BREAKPOINT
+ int 0xAC
+
+; A place to return to and exit
+User_Syscall_RetAndExit:
+ push eax
+ call User_Syscall_Exit
+User_Syscall_Exit:
+ xor eax, eax
+ mov ebx, [esp+4]
+ int 0xAC
+
+; vim: ft=nasm ts=8
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * proc.c
+ */
+#include <acess.h>
+#include <threads.h>
+#include <proc.h>
+#include <desctab.h>
+#include <mm_virt.h>
+#include <errno.h>
+#if USE_MP
+# include <mp.h>
+#endif
+#include <hal_proc.h>
+#include <arch_int.h>
+
+// === FLAGS ===
+#define DEBUG_TRACE_SWITCH 0
+#define DEBUG_DISABLE_DOUBLEFAULT 1
+#define DEBUG_VERY_SLOW_PERIOD 0
+
+// === CONSTANTS ===
+// Base is 1193182
+#define TIMER_BASE 1193182
+#if DEBUG_VERY_SLOW_PERIOD
+# define TIMER_DIVISOR 1193 //~10Hz switch, with 10 quantum = 1s per thread
+#else
+# define TIMER_DIVISOR 11932 //~100Hz
+#endif
+
+// === TYPES ===
+typedef struct sCPU
+{
+ Uint8 APICID;
+ Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
+ Uint16 Resvd;
+ tThread *Current;
+} tCPU;
+
+// === IMPORTS ===
+extern tGDT gGDT[];
+extern tIDT gIDT[];
+extern void APWait(void); // 16-bit AP pause code
+extern void APStartup(void); // 16-bit AP startup code
+extern Uint GetEIP(void); // start.asm
+extern Uint GetEIP_Sched(void); // proc.asm
+extern void NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...); // Actually takes cdecl args
+extern Uint Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone);
+extern Uint32 gaInitPageDir[1024]; // start.asm
+extern char Kernel_Stack_Top[];
+extern int giNumCPUs;
+extern int giNextTID;
+extern tThread gThreadZero;
+extern tProcess gProcessZero;
+extern void Isr8(void); // Double Fault
+extern void Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack);
+extern char scheduler_return[]; // Return address in SchedulerBase
+extern char IRQCommon[]; // Common IRQ handler code
+extern char IRQCommon_handled[]; // IRQCommon call return location
+extern char GetEIP_Sched_ret[]; // GetEIP call return location
+extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3);
+extern void Proc_InitialiseSSE(void);
+extern void Proc_SaveSSE(Uint DestPtr);
+extern void Proc_DisableSSE(void);
+
+// === PROTOTYPES ===
+//void ArchThreads_Init(void);
+#if USE_MP
+void MP_StartAP(int CPU);
+void MP_SendIPIVector(int CPU, Uint8 Vector);
+void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
+#endif
+void Proc_IdleThread(void *Ptr);
+//void Proc_Start(void);
+//tThread *Proc_GetCurThread(void);
+void Proc_ChangeStack(void);
+// int Proc_NewKThread(void (*Fcn)(void*), void *Data);
+// int Proc_Clone(Uint *Err, Uint Flags);
+Uint Proc_MakeUserStack(void);
+//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
+void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN;
+ int Proc_Demote(Uint *Err, int Dest, tRegs *Regs);
+//void Proc_CallFaultHandler(tThread *Thread);
+//void Proc_DumpThreadCPUState(tThread *Thread);
+void Proc_Scheduler(int CPU);
+
+// === GLOBALS ===
+// --- Multiprocessing ---
+#if USE_MP
+volatile int giNumInitingCPUs = 0;
+tMPInfo *gMPFloatPtr = NULL;
+volatile Uint32 giMP_TimerCount; // Start Count for Local APIC Timer
+tAPIC *gpMP_LocalAPIC = NULL;
+Uint8 gaAPIC_to_CPU[256] = {0};
+ int giProc_BootProcessorID = 0;
+tTSS gaTSSs[MAX_CPUS]; // TSS Array
+#endif
+tCPU gaCPUs[MAX_CPUS] = {
+ {.Current = &gThreadZero}
+ };
+tTSS *gTSSs = NULL; // Pointer to TSS array
+tTSS gTSS0 = {0};
+// --- Error Recovery ---
+char gaDoubleFaultStack[1024] __attribute__ ((section(".padata")));
+tTSS gDoubleFault_TSS = {
+ .ESP0 = (Uint)&gaDoubleFaultStack[1024],
+ .SS0 = 0x10,
+ .CR3 = (Uint)gaInitPageDir - KERNEL_BASE,
+ .EIP = (Uint)Isr8,
+ .ESP = (Uint)&gaDoubleFaultStack[1024],
+ .CS = 0x08, .SS = 0x10,
+ .DS = 0x10, .ES = 0x10,
+ .FS = 0x10, .GS = 0x10,
+};
+
+// === CODE ===
+/**
+ * \fn void ArchThreads_Init(void)
+ * \brief Starts the process scheduler
+ */
+void ArchThreads_Init(void)
+{
+ Uint pos = 0;
+
+ #if USE_MP
+ tMPTable *mptable;
+
+ // Mark BSP as active
+ gaCPUs[0].State = 2;
+
+ // -- Initialise Multiprocessing
+ // Find MP Floating Table
+ // - EBDA/Last 1Kib (640KiB)
+ for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) {
+ if( *(Uint*)(pos) == MPPTR_IDENT ) {
+ Log("Possible %p", pos);
+ if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
+ gMPFloatPtr = (void*)pos;
+ break;
+ }
+ }
+ // - Last KiB (512KiB base mem)
+ if(!gMPFloatPtr) {
+ for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) {
+ if( *(Uint*)(pos) == MPPTR_IDENT ) {
+ Log("Possible %p", pos);
+ if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
+ gMPFloatPtr = (void*)pos;
+ break;
+ }
+ }
+ }
+ // - BIOS ROM
+ if(!gMPFloatPtr) {
+ for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) {
+ if( *(Uint*)(pos) == MPPTR_IDENT ) {
+ Log("Possible %p", pos);
+ if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
+ gMPFloatPtr = (void*)pos;
+ break;
+ }
+ }
+ }
+
+ // If the MP Table Exists, parse it
+ if(gMPFloatPtr)
+ {
+ int i;
+ tMPTable_Ent *ents;
+ #if DUMP_MP_TABLE
+ Log("gMPFloatPtr = %p", gMPFloatPtr);
+ Log("*gMPFloatPtr = {");
+ Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig);
+ Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig);
+ Log("\t.Length = 0x%02x", gMPFloatPtr->Length);
+ Log("\t.Version = 0x%02x", gMPFloatPtr->Version);
+ Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum);
+ Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]",
+ gMPFloatPtr->Features[0], gMPFloatPtr->Features[1],
+ gMPFloatPtr->Features[2], gMPFloatPtr->Features[3],
+ gMPFloatPtr->Features[4]
+ );
+ Log("}");
+ #endif
+
+ mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig );
+ #if DUMP_MP_TABLE
+ Log("mptable = %p", mptable);
+ Log("*mptable = {");
+ Log("\t.Sig = 0x%08x", mptable->Sig);
+ Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength);
+ Log("\t.SpecRev = 0x%02x", mptable->SpecRev);
+ Log("\t.Checksum = 0x%02x", mptable->Checksum);
+ Log("\t.OEMID = '%8c'", mptable->OemID);
+ Log("\t.ProductID = '%8c'", mptable->ProductID);
+ Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr);
+ Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize);
+ Log("\t.EntryCount = 0x%04x", mptable->EntryCount);
+ Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap);
+ Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen);
+ Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum);
+ Log("}");
+ #endif
+
+ gpMP_LocalAPIC = (void*)MM_MapHWPages(mptable->LocalAPICMemMap, 1);
+
+ ents = mptable->Entries;
+ giNumCPUs = 0;
+
+ for( i = 0; i < mptable->EntryCount; i ++ )
+ {
+ int entSize = 0;
+ switch( ents->Type )
+ {
+ case 0: // Processor
+ entSize = 20;
+ #if DUMP_MP_TABLE
+ Log("%i: Processor", i);
+ Log("\t.APICID = %i", ents->Proc.APICID);
+ Log("\t.APICVer = 0x%02x", ents->Proc.APICVer);
+ Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags);
+ Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature);
+ Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags);
+ #endif
+
+ if( !(ents->Proc.CPUFlags & 1) ) {
+ Log("DISABLED");
+ break;
+ }
+
+ // Check if there is too many processors
+ if(giNumCPUs >= MAX_CPUS) {
+ giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped
+ break;
+ }
+
+ // Initialise CPU Info
+ gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs;
+ gaCPUs[giNumCPUs].APICID = ents->Proc.APICID;
+ gaCPUs[giNumCPUs].State = 0;
+ giNumCPUs ++;
+
+ // Set BSP Variable
+ if( ents->Proc.CPUFlags & 2 ) {
+ giProc_BootProcessorID = giNumCPUs-1;
+ }
+
+ break;
+
+ #if DUMP_MP_TABLE >= 2
+ case 1: // Bus
+ entSize = 8;
+ Log("%i: Bus", i);
+ Log("\t.ID = %i", ents->Bus.ID);
+ Log("\t.TypeString = '%6C'", ents->Bus.TypeString);
+ break;
+ case 2: // I/O APIC
+ entSize = 8;
+ Log("%i: I/O APIC", i);
+ Log("\t.ID = %i", ents->IOAPIC.ID);
+ Log("\t.Version = 0x%02x", ents->IOAPIC.Version);
+ Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags);
+ Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr);
+ break;
+ case 3: // I/O Interrupt Assignment
+ entSize = 8;
+ Log("%i: I/O Interrupt Assignment", i);
+ Log("\t.IntType = %i", ents->IOInt.IntType);
+ Log("\t.Flags = 0x%04x", ents->IOInt.Flags);
+ Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID);
+ Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ);
+ Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID);
+ Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ);
+ break;
+ case 4: // Local Interrupt Assignment
+ entSize = 8;
+ Log("%i: Local Interrupt Assignment", i);
+ Log("\t.IntType = %i", ents->LocalInt.IntType);
+ Log("\t.Flags = 0x%04x", ents->LocalInt.Flags);
+ Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID);
+ Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ);
+ Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID);
+ Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ);
+ break;
+ default:
+ Log("%i: Unknown (%i)", i, ents->Type);
+ break;
+ #endif
+ }
+ ents = (void*)( (Uint)ents + entSize );
+ }
+
+ if( giNumCPUs > MAX_CPUS ) {
+ Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS);
+ giNumCPUs = MAX_CPUS;
+ }
+ gTSSs = gaTSSs;
+ }
+ else {
+ Log("No MP Table was found, assuming uniprocessor\n");
+ giNumCPUs = 1;
+ gTSSs = &gTSS0;
+ }
+ #else
+ giNumCPUs = 1;
+ gTSSs = &gTSS0;
+ #endif
+
+ #if !DEBUG_DISABLE_DOUBLEFAULT
+ // Initialise Double Fault TSS
+ gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF;
+ gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16;
+ gGDT[5].BaseHi = (Uint)&gDoubleFault_TSS >> 24;
+
+ // Set double fault IDT to use the new TSS
+ gIDT[8].OffsetLo = 0;
+ gIDT[8].CS = 5<<3;
+ gIDT[8].Flags = 0x8500;
+ gIDT[8].OffsetHi = 0;
+ #endif
+
+ // Set timer frequency
+ outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
+ outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
+ outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte
+
+ Log_Debug("Proc", "PIT Frequency %i.%03i Hz",
+ TIMER_BASE/TIMER_DIVISOR,
+ ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000
+ );
+
+ #if USE_MP
+ // Get the count setting for APIC timer
+ Log("Determining APIC Count");
+ __asm__ __volatile__ ("sti");
+ while( giMP_TimerCount == 0 ) __asm__ __volatile__ ("hlt");
+ __asm__ __volatile__ ("cli");
+ Log("APIC Count %i", giMP_TimerCount);
+ {
+ Uint64 freq = giMP_TimerCount;
+ freq *= TIMER_BASE;
+ freq /= TIMER_DIVISOR;
+ if( (freq /= 1000) < 2*1000)
+ Log("Bus Frequency %i KHz", freq);
+ else if( (freq /= 1000) < 2*1000)
+ Log("Bus Frequency %i MHz", freq);
+ else if( (freq /= 1000) < 2*1000)
+ Log("Bus Frequency %i GHz", freq);
+ else
+ Log("Bus Frequency %i THz", freq);
+ }
+
+ // Initialise Normal TSS(s)
+ for(pos=0;pos<giNumCPUs;pos++)
+ {
+ #else
+ pos = 0;
+ #endif
+ gTSSs[pos].SS0 = 0x10;
+ gTSSs[pos].ESP0 = 0; // Set properly by scheduler
+ gGDT[6+pos].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
+ gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos]) >> 16) & 0xFFFF;
+ gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
+ #if USE_MP
+ }
+ #endif
+
+ // Load the BSP's TSS
+ __asm__ __volatile__ ("ltr %%ax"::"a"(0x30));
+ // Set Current Thread and CPU Number in DR0 and DR1
+ __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero));
+ __asm__ __volatile__ ("mov %0, %%db1"::"r"(0));
+
+ gaCPUs[0].Current = &gThreadZero;
+ gThreadZero.CurCPU = 0;
+
+ gProcessZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE;
+
+ // Create Per-Process Data Block
+ if( !MM_Allocate(MM_PPD_CFG) )
+ {
+ Panic("OOM - No space for initial Per-Process Config");
+ }
+
+ // Initialise SSE support
+ Proc_InitialiseSSE();
+
+ // Change Stacks
+ Proc_ChangeStack();
+}
+
+#if USE_MP
+/**
+ * \brief Start an AP
+ */
+void MP_StartAP(int CPU)
+{
+ Log_Log("Proc", "Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
+
+ // Set location of AP startup code and mark for a warm restart
+ *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
+ *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
+ outb(0x70, 0x0F); outb(0x71, 0x0A); // Set warm reset flag
+ MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); // Init IPI
+
+ // Delay
+ inb(0x80); inb(0x80); inb(0x80); inb(0x80);
+
+ // TODO: Use a better address, preferably registered with the MM
+ // - MM_AllocDMA mabye?
+ // Create a far jump
+ *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA; // Far JMP
+ *(Uint16*)(KERNEL_BASE|0x11001) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0); // IP
+ *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF; // CS
+ // Send a Startup-IPI to make the CPU execute at 0x11000 (which we
+ // just filled)
+ MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6); // StartupIPI
+
+ giNumInitingCPUs ++;
+}
+
+void MP_SendIPIVector(int CPU, Uint8 Vector)
+{
+ MP_SendIPI(gaCPUs[CPU].APICID, Vector, 0);
+}
+
+/**
+ * \brief Send an Inter-Processor Interrupt
+ * \param APICID Processor's Local APIC ID
+ * \param Vector Argument of some kind
+ * \param DeliveryMode Type of signal
+ * \note 3A 10.5 "APIC/Handling Local Interrupts"
+ */
+void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
+{
+ Uint32 val;
+
+ // Hi
+ val = (Uint)APICID << 24;
+// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val);
+ gpMP_LocalAPIC->ICR[1].Val = val;
+ // Low (and send)
+ val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
+// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val);
+ gpMP_LocalAPIC->ICR[0].Val = val;
+}
+#endif
+
+void Proc_IdleThread(void *Ptr)
+{
+ tCPU *cpu = &gaCPUs[GetCPUNum()];
+ cpu->Current->ThreadName = strdup("Idle Thread");
+ Threads_SetPriority( cpu->Current, -1 ); // Never called randomly
+ cpu->Current->Quantum = 1; // 1 slice quantum
+ for(;;) {
+ __asm__ __volatile__ ("sti"); // Make sure interrupts are enabled
+ __asm__ __volatile__ ("hlt"); // Make sure interrupts are enabled
+ Proc_Reschedule();
+ }
+}
+
+/**
+ * \fn void Proc_Start(void)
+ * \brief Start process scheduler
+ */
+void Proc_Start(void)
+{
+ int tid;
+ #if USE_MP
+ int i;
+ #endif
+
+ #if USE_MP
+ // Start APs
+ for( i = 0; i < giNumCPUs; i ++ )
+ {
+ if(i) gaCPUs[i].Current = NULL;
+
+ // Create Idle Task
+ tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[i]);
+
+ // Start the AP
+ if( i != giProc_BootProcessorID ) {
+ MP_StartAP( i );
+ }
+ }
+
+ // BSP still should run the current task
+ gaCPUs[0].Current = &gThreadZero;
+
+ // Start interrupts and wait for APs to come up
+ Log_Debug("Proc", "Waiting for APs to come up");
+ __asm__ __volatile__ ("sti");
+ while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
+ #else
+ // Create Idle Task
+ tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[0]);
+// gaCPUs[0].IdleThread = Threads_GetThread(tid);
+
+ // Set current task
+ gaCPUs[0].Current = &gThreadZero;
+
+ // Start Interrupts (and hence scheduler)
+ __asm__ __volatile__("sti");
+ #endif
+ MM_FinishVirtualInit();
+}
+
+/**
+ * \fn tThread *Proc_GetCurThread(void)
+ * \brief Gets the current thread
+ */
+tThread *Proc_GetCurThread(void)
+{
+ #if USE_MP
+ return gaCPUs[ GetCPUNum() ].Current;
+ #else
+ return gaCPUs[ 0 ].Current;
+ #endif
+}
+
+/**
+ * \fn void Proc_ChangeStack(void)
+ * \brief Swaps the current stack for a new one (in the proper stack reigon)
+ */
+void Proc_ChangeStack(void)
+{
+ Uint esp, ebp;
+ Uint tmpEbp, oldEsp;
+ Uint curBase, newBase;
+
+ __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
+ __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
+
+ oldEsp = esp;
+
+ // Create new KStack
+ newBase = MM_NewKStack();
+ // Check for errors
+ if(newBase == 0) {
+ Panic("What the?? Unable to allocate space for initial kernel stack");
+ return;
+ }
+
+ curBase = (Uint)&Kernel_Stack_Top;
+
+ LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
+
+ // Get ESP as a used size
+ esp = curBase - esp;
+ LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp );
+ // Copy used stack
+ memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp );
+ // Get ESP as an offset in the new stack
+ esp = newBase - esp;
+ // Adjust EBP
+ ebp = newBase - (curBase - ebp);
+
+ // Repair EBPs & Stack Addresses
+ // Catches arguments also, but may trash stack-address-like values
+ for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4)
+ {
+ if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase)
+ *(Uint*)tmpEbp += newBase - curBase;
+ }
+
+ Proc_GetCurThread()->KernelStack = newBase;
+
+ __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp));
+ __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp));
+}
+
+void Proc_ClearProcess(tProcess *Process)
+{
+ MM_ClearSpace(Process->MemState.CR3);
+}
+
+void Proc_ClearThread(tThread *Thread)
+{
+ if(Thread->SavedState.SSE) {
+ free(Thread->SavedState.SSE);
+ Thread->SavedState.SSE = NULL;
+ }
+}
+
+tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
+{
+ Uint esp;
+ tThread *newThread, *cur;
+
+ cur = Proc_GetCurThread();
+ newThread = Threads_CloneTCB(0);
+ if(!newThread) return -1;
+
+ // Create new KStack
+ newThread->KernelStack = MM_NewKStack();
+ // Check for errors
+ if(newThread->KernelStack == 0) {
+ free(newThread);
+ return -1;
+ }
+
+ esp = newThread->KernelStack;
+ *(Uint*)(esp-=4) = (Uint)Data; // Data (shadowed)
+ *(Uint*)(esp-=4) = 1; // Number of params
+ *(Uint*)(esp-=4) = (Uint)Fcn; // Function to call
+ *(Uint*)(esp-=4) = (Uint)newThread; // Thread ID
+
+ newThread->SavedState.ESP = esp;
+ newThread->SavedState.EIP = (Uint)&NewTaskHeader;
+ newThread->SavedState.SSE = NULL;
+// Log("New (KThread) %p, esp = %p", newThread->SavedState.EIP, newThread->SavedState.ESP);
+
+// MAGIC_BREAK();
+ Threads_AddActive(newThread);
+
+ return newThread->TID;
+}
+
+/**
+ * \fn int Proc_Clone(Uint *Err, Uint Flags)
+ * \brief Clone the current process
+ */
+tPID Proc_Clone(Uint Flags)
+{
+ tThread *newThread;
+ tThread *cur = Proc_GetCurThread();
+ Uint eip;
+
+ // Sanity, please
+ if( !(Flags & CLONE_VM) ) {
+ Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead");
+ return -1;
+ }
+
+ // New thread
+ newThread = Threads_CloneTCB(Flags);
+ if(!newThread) return -1;
+
+ newThread->KernelStack = cur->KernelStack;
+
+ // Clone state
+ eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER);
+ if( eip == 0 ) {
+ return 0;
+ }
+ newThread->SavedState.EIP = eip;
+ newThread->SavedState.SSE = NULL;
+ newThread->SavedState.bSSEModified = 0;
+
+ // Check for errors
+ if( newThread->Process->MemState.CR3 == 0 ) {
+ Log_Error("Proc", "Proc_Clone: MM_Clone failed");
+ Threads_Delete(newThread);
+ return -1;
+ }
+
+ // Add the new thread to the run queue
+ Threads_AddActive(newThread);
+ return newThread->TID;
+}
+
+/**
+ * \fn int Proc_SpawnWorker(void)
+ * \brief Spawns a new worker thread
+ */
+int Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
+{
+ tThread *new;
+ Uint stack_contents[4];
+
+ // Create new thread
+ new = Threads_CloneThreadZero();
+ if(!new) {
+ Warning("Proc_SpawnWorker - Out of heap space!\n");
+ return -1;
+ }
+
+ // Create the stack contents
+ stack_contents[3] = (Uint)Data;
+ stack_contents[2] = 1;
+ stack_contents[1] = (Uint)Fcn;
+ stack_contents[0] = (Uint)new;
+
+ // Create a new worker stack (in PID0's address space)
+ new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents));
+
+ // Save core machine state
+ new->SavedState.ESP = new->KernelStack - sizeof(stack_contents);
+ new->SavedState.EIP = (Uint)NewTaskHeader;
+ new->SavedState.SSE = NULL;
+ new->SavedState.bSSEModified = 0;
+
+ // Mark as active
+ new->Status = THREAD_STAT_PREINIT;
+ Threads_AddActive( new );
+
+ return new->TID;
+}
+
+/**
+ * \fn Uint Proc_MakeUserStack(void)
+ * \brief Creates a new user stack
+ */
+Uint Proc_MakeUserStack(void)
+{
+ int i;
+ Uint base = USER_STACK_TOP - USER_STACK_SZ;
+
+ // Check Prospective Space
+ for( i = USER_STACK_SZ >> 12; i--; )
+ if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
+ break;
+
+ if(i != -1) return 0;
+
+ // Allocate Stack - Allocate incrementally to clean up MM_Dump output
+ for( i = 0; i < USER_STACK_SZ/0x1000; i++ )
+ {
+ if( !MM_Allocate( base + (i<<12) ) )
+ {
+ Warning("OOM: Proc_MakeUserStack");
+ return 0;
+ }
+ }
+
+ return base + USER_STACK_SZ;
+}
+
+void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
+{
+ Uint *stack;
+ int i;
+ const char **envp = NULL;
+ Uint16 ss, cs;
+
+ // Copy data to the user stack and free original buffer
+ stack = (void*)Proc_MakeUserStack();
+ stack -= (DataSize+sizeof(*stack)-1)/sizeof(*stack);
+ memcpy( stack, ArgV, DataSize );
+ free(ArgV);
+
+ // Adjust Arguments and environment
+ if( DataSize )
+ {
+ Uint delta = (Uint)stack - (Uint)ArgV;
+ ArgV = (const char**)stack;
+ for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta;
+ envp = &ArgV[i+1];
+ for( i = 0; envp[i]; i++ ) envp[i] += delta;
+ }
+
+ // User Mode Segments
+ ss = 0x23; cs = 0x1B;
+
+ // Arguments
+ *--stack = (Uint)envp;
+ *--stack = (Uint)ArgV;
+ *--stack = (Uint)ArgC;
+ *--stack = Base;
+
+ Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
+}
+
+void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
+{
+ Uint *stack = (void*)Stack;
+ *--stack = SS; //Stack Segment
+ *--stack = Stack; //Stack Pointer
+ *--stack = Flags; //EFLAGS (Resvd (0x2) and IF (0x20))
+ *--stack = CS; //Code Segment
+ *--stack = IP; //EIP
+ //PUSHAD
+ *--stack = 0xAAAAAAAA; // eax
+ *--stack = 0xCCCCCCCC; // ecx
+ *--stack = 0xDDDDDDDD; // edx
+ *--stack = 0xBBBBBBBB; // ebx
+ *--stack = 0xD1D1D1D1; // edi
+ *--stack = 0x54545454; // esp - NOT POPED
+ *--stack = 0x51515151; // esi
+ *--stack = 0xB4B4B4B4; // ebp
+ //Individual PUSHs
+ *--stack = SS; // ds
+ *--stack = SS; // es
+ *--stack = SS; // fs
+ *--stack = SS; // gs
+
+ __asm__ __volatile__ (
+ "mov %%eax,%%esp;\n\t" // Set stack pointer
+ "pop %%gs;\n\t"
+ "pop %%fs;\n\t"
+ "pop %%es;\n\t"
+ "pop %%ds;\n\t"
+ "popa;\n\t"
+ "iret;\n\t" : : "a" (stack));
+ for(;;);
+}
+
+/**
+ * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
+ * \brief Demotes a process to a lower permission level
+ * \param Err Pointer to user's errno
+ * \param Dest New Permission Level
+ * \param Regs Pointer to user's register structure
+ */
+int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
+{
+ int cpl = Regs->cs & 3;
+ // Sanity Check
+ if(Dest > 3 || Dest < 0) {
+ *Err = -EINVAL;
+ return -1;
+ }
+
+ // Permission Check
+ if(cpl > Dest) {
+ *Err = -EACCES;
+ return -1;
+ }
+
+ // Change the Segment Registers
+ Regs->cs = (((Dest+1)<<4) | Dest) - 8;
+ Regs->ss = ((Dest+1)<<4) | Dest;
+ // Check if the GP Segs are GDT, then change them
+ if(!(Regs->ds & 4)) Regs->ds = ((Dest+1)<<4) | Dest;
+ if(!(Regs->es & 4)) Regs->es = ((Dest+1)<<4) | Dest;
+ if(!(Regs->fs & 4)) Regs->fs = ((Dest+1)<<4) | Dest;
+ if(!(Regs->gs & 4)) Regs->gs = ((Dest+1)<<4) | Dest;
+
+ return 0;
+}
+
+/**
+ * \brief Calls a signal handler in user mode
+ * \note Used for signals
+ */
+void Proc_CallFaultHandler(tThread *Thread)
+{
+ // Rewinds the stack and calls the user function
+ // Never returns
+ Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack );
+ for(;;);
+}
+
+void Proc_DumpThreadCPUState(tThread *Thread)
+{
+ if( Thread->CurCPU > -1 )
+ {
+ int maxBacktraceDistance = 6;
+ tRegs *regs = NULL;
+ Uint32 *stack;
+
+ if( Thread->CurCPU != GetCPUNum() ) {
+ Log(" Currently running");
+ return ;
+ }
+
+ // Backtrace to find the IRQ entrypoint
+ // - This will usually only be called by an IRQ, so this should
+ // work
+ __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (stack));
+ while( maxBacktraceDistance -- )
+ {
+ // [ebp] = oldEbp
+ // [ebp+4] = retaddr
+
+ if( stack[1] == (tVAddr)&IRQCommon_handled ) {
+ regs = (void*)stack[2];
+ break;
+ }
+
+ stack = (void*)stack[0];
+ }
+
+ if( !regs ) {
+ Log(" Unable to find IRQ Entry");
+ return ;
+ }
+
+ Log(" at %04x:%08x", regs->cs, regs->eip);
+ return ;
+ }
+
+ tVAddr diffFromScheduler = Thread->SavedState.EIP - (tVAddr)SwitchTasks;
+ tVAddr diffFromClone = Thread->SavedState.EIP - (tVAddr)Proc_CloneInt;
+ tVAddr diffFromSpawn = Thread->SavedState.EIP - (tVAddr)NewTaskHeader;
+
+ if( diffFromClone > 0 && diffFromClone < 40 ) // When I last checked, .newTask was at .+27
+ {
+ Log(" Creating process");
+ return ;
+ }
+
+ if( diffFromSpawn == 0 )
+ {
+ Log(" Creating thread");
+ return ;
+ }
+
+ if( diffFromScheduler > 0 && diffFromScheduler < 128 ) // When I last checked, GetEIP was at .+0x30
+ {
+ // Scheduled out
+ Log(" At %04x:%08x", Thread->SavedState.UserCS, Thread->SavedState.UserEIP);
+ return ;
+ }
+
+ Log(" Just created (unknown %p)", Thread->SavedState.EIP);
+}
+
+void Proc_Reschedule(void)
+{
+ tThread *nextthread, *curthread;
+ int cpu = GetCPUNum();
+
+ // TODO: Wait for the lock?
+ if(IS_LOCKED(&glThreadListLock)) return;
+
+ curthread = Proc_GetCurThread();
+
+ nextthread = Threads_GetNextToRun(cpu, curthread);
+
+ if(!nextthread || nextthread == curthread)
+ return ;
+
+ #if DEBUG_TRACE_SWITCH
+ // HACK: Ignores switches to the idle threads
+ if( nextthread->TID == 0 || nextthread->TID > giNumCPUs )
+ {
+ LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
+ GetCPUNum(),
+ nextthread, nextthread->TID, nextthread->ThreadName,
+ nextthread->Process->MemState.CR3,
+ nextthread->SavedState.EIP,
+ nextthread->SavedState.ESP
+ );
+ LogF("OldCR3 = %P\n", curthread->Process->MemState.CR3);
+ }
+ #endif
+
+ // Update CPU state
+ gaCPUs[cpu].Current = nextthread;
+ gTSSs[cpu].ESP0 = nextthread->KernelStack-4;
+ __asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
+
+ // Save FPU/MMX/XMM/SSE state
+ if( curthread && curthread->SavedState.SSE )
+ {
+ Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
+ curthread->SavedState.bSSEModified = 0;
+ Proc_DisableSSE();
+ }
+
+ if( curthread )
+ {
+ SwitchTasks(
+ nextthread->SavedState.ESP, &curthread->SavedState.ESP,
+ nextthread->SavedState.EIP, &curthread->SavedState.EIP,
+ nextthread->Process->MemState.CR3
+ );
+ }
+ else
+ {
+ SwitchTasks(
+ nextthread->SavedState.ESP, 0,
+ nextthread->SavedState.EIP, 0,
+ nextthread->Process->MemState.CR3
+ );
+ }
+
+ return ;
+}
+
+/**
+ * \fn void Proc_Scheduler(int CPU)
+ * \brief Swap current thread and clears dead threads
+ */
+void Proc_Scheduler(int CPU)
+{
+#if 0
+ tThread *thread;
+
+ // If the spinlock is set, let it complete
+ if(IS_LOCKED(&glThreadListLock)) return;
+
+ // Get current thread
+ thread = gaCPUs[CPU].Current;
+
+ if( thread )
+ {
+ tRegs *regs;
+ Uint ebp;
+ // Reduce remaining quantum and continue timeslice if non-zero
+ if( thread->Remaining-- )
+ return;
+ // Reset quantum for next call
+ thread->Remaining = thread->Quantum;
+
+ // TODO: Make this more stable somehow
+ __asm__ __volatile__("mov %%ebp, %0" : "=r" (ebp));
+ regs = (tRegs*)(ebp+(2+2)*4); // EBP,Ret + CPU,CurThread
+ thread->SavedState.UserCS = regs->cs;
+ thread->SavedState.UserEIP = regs->eip;
+
+ if(thread->bInstrTrace) {
+ regs->eflags |= 0x100; // Set TF
+ Log("%p De-scheduled", thread);
+ }
+ else
+ regs->eflags &= ~0x100; // Clear TF
+ }
+
+ // TODO: Ack timer?
+ #if USE_MP
+ if( GetCPUNum() )
+ gpMP_LocalAPIC->EOI.Val = 0;
+ else
+ #endif
+ outb(0x20, 0x20);
+ __asm__ __volatile__ ("sti");
+ Proc_Reschedule();
+#endif
+}
+
+// === EXPORTS ===
+EXPORT(Proc_SpawnWorker);
--- /dev/null
+; AcessOS Microkernel Version
+; Start.asm
+
+[bits 32]
+
+KERNEL_BASE equ 0xC0000000
+%define MAX_CPUS 16
+
+[extern __load_addr]
+[extern __bss_start]
+[extern gKernelEnd]
+[section .multiboot]
+mboot:
+ ; Multiboot macros to make a few lines later more readable
+ MULTIBOOT_PAGE_ALIGN equ 1<<0
+ MULTIBOOT_MEMORY_INFO equ 1<<1
+ MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
+ MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
+ MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
+
+ ; This is the GRUB Multiboot header. A boot signature
+ dd MULTIBOOT_HEADER_MAGIC
+ dd MULTIBOOT_HEADER_FLAGS
+ dd MULTIBOOT_CHECKSUM
+ dd mboot; - KERNEL_BASE ;Location of Multiboot Header
+
+; Multiboot 2 Header
+;mboot2:
+; MULTIBOOT2_HEADER_MAGIC equ 0xE85250D6
+; MULTIBOOT2_HEADER_ARCH equ 0
+; MULTIBOOT2_HEADER_LENGTH equ (mboot2_end-mboot2)
+; MULTIBOOT2_CHECKSUM equ -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_HEADER_ARCH + MULTIBOOT2_HEADER_LENGTH)
+;
+; dd MULTIBOOT2_HEADER_MAGIC
+; dd MULTIBOOT2_HEADER_ARCH
+; dd MULTIBOOT2_HEADER_LENGTH
+; dd MULTIBOOT2_CHECKSUM
+; ; MBoot2 Address Header
+; dw 2, 0
+; dd 8 + 16
+; dd mboot2 ; Location of Multiboot Header
+; dd __load_addr - KERNEL_BASE ; Kernel Load base
+; dd __bss_start - KERNEL_BASE ; Kernel Data End
+; dd gKernelEnd - KERNEL_BASE ; Kernel BSS End
+; ; MBoot2 Entry Point Tag
+; dw 3, 0
+; dd 8 + 4
+; dd start - KERNEL_BASE
+; ; MBoot2 Module Alignment Tag
+; dw 6, 0
+; dd 12 ; ???
+; dd 0 ; Search me, seems it wants padding
+; ; Terminator
+; dw 0, 0
+; dd 8
+;mboot2_end:
+
+[section .text]
+[extern kmain]
+[global start]
+start:
+ ; Just show we're here
+ mov WORD [0xB8000], 0x0741 ; 'A'
+
+ ; Set up stack
+ mov esp, Kernel_Stack_Top
+
+ ; Start Paging
+ mov ecx, gaInitPageDir - KERNEL_BASE
+ mov cr3, ecx
+ mov ecx, cr0
+ or ecx, 0x80010000 ; PG and WP
+ mov cr0, ecx
+
+ mov WORD [0xB8002], 0x0763 ; 'c'
+
+ ; Set GDT
+ lgdt [gGDTPtr]
+ mov cx, 0x10 ; PL0 Data
+ mov ss, cx
+ mov ds, cx
+ mov es, cx
+ mov gs, cx
+ mov fs, cx
+ mov WORD [0xB8004], 0x0765 ; 'e'
+ jmp 0x08:.higher_half
+.higher_half:
+
+ mov WORD [0xB8006], 0x0773 ; 's'
+ mov WORD [0xB8008], 0x0773 ; 's'
+
+ ; Call the kernel
+ push ebx ; Multiboot Info
+ push eax ; Multiboot Magic Value
+ mov WORD [0xB800A], 0x0732 ; '2'
+ call kmain
+
+ ; Halt the Machine
+ cli
+.hlt:
+ hlt
+ jmp .hlt
+
+;
+; Multiprocessing AP Startup Code (Must be within 0 - 0x10FFF0)
+;
+%if USE_MP
+[extern gIDTPtr]
+[extern gpMP_LocalAPIC]
+[extern giMP_TimerCount]
+[extern gaAPIC_to_CPU]
+[extern gaCPUs]
+[extern giNumInitingCPUs]
+[extern MM_NewKStack]
+[extern Proc_InitialiseSSE]
+
+lGDTPtr: ; Local GDT Pointer
+ dw 3*8-1
+ dd gGDT-KERNEL_BASE
+
+[bits 16]
+[global APWait]
+APWait:
+ ;xchg bx, bx
+.hlt:
+ ;hlt
+ jmp .hlt
+[extern Proc_Reschedule]
+[global APStartup]
+APStartup:
+ ;xchg bx, bx ; MAGIC BREAK!
+ ; Load initial GDT
+ mov ax, 0xFFFF
+ mov ds, ax
+ lgdt [DWORD ds:lGDTPtr-KERNEL_BASE-0xFFFF0]
+ ; Enable PMode in CR0
+ mov eax, cr0
+ or al, 1
+ mov cr0, eax
+ ; Jump into PMode
+ jmp 08h:DWORD .ProtectedMode-KERNEL_BASE
+[bits 32]
+.ProtectedMode:
+ ; Load segment registers
+ mov ax, 0x10
+ mov ss, ax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ ; Start Paging
+ mov eax, gaInitPageDir - KERNEL_BASE
+ mov cr3, eax
+ mov eax, cr0
+ or eax, 0x80010000 ; PG and WP
+ mov cr0, eax
+ ; Jump to higher half
+ lea eax, [.higherHalf]
+ jmp eax
+.higherHalf:
+ ; Load True GDT & IDT
+ lgdt [gGDTPtr]
+ lidt [gIDTPtr]
+
+ mov ebp, [gpMP_LocalAPIC]
+ mov ebx, [ebp+0x20] ; Read ID
+ shr ebx, 24
+ ;xchg bx, bx ; MAGIC BREAK
+ ; BL is now local APIC ID
+ mov cl, BYTE [gaAPIC_to_CPU+ebx]
+ xor ebx, ebx
+ mov bl, cl
+ ; BL is now the CPU ID
+ mov dr1, ebx ; Save the CPU number to a debug register
+ ; Mark the CPU as up
+ mov BYTE [gaCPUs+ebx*8+1], 1
+ ; Decrement the remaining CPU count
+ dec DWORD [giNumInitingCPUs]
+
+ ; Create a stack
+ lea edx, [ebx+1]
+ shl edx, 5+2 ; *32 *4
+ lea esp, [gInitAPStacks+edx]
+ call MM_NewKStack
+ mov esp, eax
+
+ ; Set TSS
+ lea ecx, [ebx*8+0x30]
+ ltr cx
+
+ ;xchg bx, bx ; MAGIC_BREAK
+ ; Enable Local APIC
+ mov DWORD [ebp+0x0F0], 0x000001EF ; Spurious Interrupt Vector (0xEF)
+ mov ecx, DWORD [giMP_TimerCount]
+ mov DWORD [ebp+0x380], ecx ; Set Initial Count
+ mov DWORD [ebp+0x320], 0x000200EE ; Enable timer on IVT#0xEE
+ mov DWORD [ebp+0x330], 0x000100E0 ; ##Enable thermal sensor on IVT#0xE0
+ mov DWORD [ebp+0x340], 0x000100D0 ; ##Enable performance counters on IVT#0xD0
+ mov DWORD [ebp+0x350], 0x000100D1 ; ##Enable LINT0 on IVT#0xD1
+ mov DWORD [ebp+0x360], 0x000100D2 ; ##Enable LINT1 on IVT#0xD2
+ mov DWORD [ebp+0x370], 0x000100E1 ; ##Enable Error on IVT#0xE1
+ mov DWORD [ebp+0x0B0], 0 ; Send an EOI (just in case)
+
+ ; Initialise SSE support
+ call Proc_InitialiseSSE
+
+ ; CPU is now marked as initialised
+
+.hlt:
+ sti
+ call Proc_Reschedule
+ hlt
+ jmp .hlt
+%endif
+
+[global GetEIP]
+GetEIP:
+ mov eax, [esp]
+ ret
+
+; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args)
+; Call a function passing the array as arguments
+[global CallWithArgArray]
+CallWithArgArray:
+ push ebp
+ mov ebp, esp
+ mov ecx, [ebp+12] ; Get NArgs
+ mov edx, [ebp+16]
+
+.top:
+ mov eax, [edx+ecx*4-4]
+ push eax
+ loop .top
+
+ mov eax, [ebp+8]
+ call eax
+ lea esp, [ebp]
+ pop ebp
+ ret
+
+[section .data]
+; GDT
+GDT_SIZE equ (1+2*2+1+MAX_CPUS)*8
+[global gGDT]
+gGDT:
+ ; PL0 - Kernel
+ ; PL3 - User
+ dd 0x00000000, 0x00000000 ; 00 NULL Entry
+ dd 0x0000FFFF, 0x00CF9A00 ; 08 PL0 Code
+ dd 0x0000FFFF, 0x00CF9200 ; 10 PL0 Data
+ dd 0x0000FFFF, 0x00CFFA00 ; 18 PL3 Code
+ dd 0x0000FFFF, 0x00CFF200 ; 20 PL3 Data
+ dd 26*4-1, 0x00408900 ; 28 Double Fault TSS
+ times MAX_CPUS dd 26*4-1, 0x00408900 ; 30+ TSSes
+[global gGDTPtr]
+gGDTPtr:
+ dw GDT_SIZE-1
+ dd gGDT
+
+[section .initpd]
+[global gaInitPageDir]
+[global gaInitPageTable]
+align 4096
+gaInitPageDir:
+ dd gaInitPageTable-KERNEL_BASE+3 ; 0x000 - Low kernel
+ times 0x300-0x000-1 dd 0
+ dd gaInitPageTable-KERNEL_BASE+3 ; 0xC00 - High kernel
+ times 0x3F0-0x300-1 dd 0
+ dd gaInitPageDir-KERNEL_BASE+3 ; 0xFC0 - Fractal
+ times 0x400-0x3F0-1 dd 0
+align 4096
+gaInitPageTable:
+ %assign i 0
+ %rep 1024
+ dd i*0x1000+3
+ %assign i i+1
+ %endrep
+[global Kernel_Stack_Top]
+ALIGN 4096
+ times 1024 dd 0
+Kernel_Stack_Top:
+gInitAPStacks:
+ times 32*MAX_CPUS dd 0
+
+; vim: ft=nasm ts=8
--- /dev/null
+/*
+ * Acess2 Kernel
+ * Timekeeping
+ * arch/x86/time.c
+ */
+#include <acess.h>
+
+// === MACROS ===
+#define TIMER_QUANTUM 100
+// 2^(15-rate), 14: 2Hz, 5: 1024Hz, 2: 8192Hz
+// (Max: 14, Min: 2) - 14 = 2Hz, 13 = 4Hz, 12 = 8Hz, 11 = 16Hz 10 = 32Hz, 2 = 8192Hz
+//#define TIMER_RATE 10 // 32 Hz
+//#define TIMER_RATE 12 // 8 Hz
+#define TIMER_RATE 14 // 2 Hz - Lowest
+#define TIMER_FREQ (0x8000>>TIMER_RATE) //Hz
+#define MS_PER_TICK_WHOLE (1000/(TIMER_FREQ))
+#define MS_PER_TICK_FRACT ((0x80000000*(1000%TIMER_FREQ))/TIMER_FREQ)
+
+// === IMPORTS ===
+extern volatile Sint64 giTimestamp;
+extern volatile Uint64 giTicks;
+extern volatile Uint64 giPartMiliseconds;
+extern void Timer_CallTimers(void);
+
+// === GLOBALS ===
+volatile Uint64 giTime_TSCAtLastTick = 0;
+volatile Uint64 giTime_TSCPerTick = 0;
+
+// === PROTOTYPES ===
+//Sint64 now(void);
+ int Time_Setup(void);
+void Time_Interrupt(int IRQ, void *Ptr);
+Uint64 Time_ReadTSC(void);
+
+// === CODE ===
+/**
+ * \fn Sint64 now()
+ * \brief Return the current timestamp
+ */
+Sint64 now(void)
+{
+ Uint64 tsc = Time_ReadTSC();
+ tsc -= giTime_TSCAtLastTick;
+ tsc *= MS_PER_TICK_WHOLE;
+ if( giTime_TSCPerTick ) {
+ tsc /= giTime_TSCPerTick;
+ }
+ else
+ tsc = 0;
+ return giTimestamp + tsc;
+}
+
+/**
+ * \fn int Time_Setup(void)
+ * \brief Sets the system time from the Realtime-Clock
+ */
+int Time_Setup(void)
+{
+ Uint8 val;
+
+ Log_Log("Timer", "RTC Timer firing at %iHz (%i divisor), %i.0x%08x",
+ TIMER_FREQ, TIMER_RATE, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT);
+
+ outb(0x70, inb(0x70)&0x7F); // Disable NMIs
+ __asm__ __volatile__ ("cli"); // Disable normal interrupts
+
+ // Set IRQ8 firing rate
+ outb(0x70, 0x0A); // Set the index to register A
+ val = inb(0x71); // Get the current value of register A
+ val &= 0xF0;
+ val |= TIMER_RATE+1;
+ outb(0x70, 0x0A); // Reset index to A
+ outb(0x71, val); // Update the timer rate
+
+ // Enable IRQ8
+ outb(0x70, 0x0B); // Set the index to register B
+ val = inb(0x71); // Read the current value of register B
+ outb(0x70, 0x0B); // Set the index again (a read will reset the index to register D)
+ outb(0x71, val | 0x40); // Write the previous value or'd with 0x40. This turns on bit 6 of register D
+
+ __asm__ __volatile__ ("sti"); // Re-enable normal interrupts
+ outb(0x70, inb(0x70)|0x80); // Re-enable NMIs
+
+ // Install IRQ Handler
+ IRQ_AddHandler(8, Time_Interrupt, NULL);
+
+ // Make sure the RTC actually fires
+ outb(0x70, 0x0C); // Select register C
+ inb(0x71); // Just throw away contents.
+
+ return 0;
+}
+
+/**
+ * \brief Called on the timekeeping IRQ
+ */
+void Time_Interrupt(int IRQ, void *Ptr)
+{
+ Uint64 curTSC = Time_ReadTSC();
+
+ if( giTime_TSCAtLastTick )
+ {
+ giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick;
+ }
+ giTime_TSCAtLastTick = curTSC;
+
+ giTicks ++;
+ giTimestamp += MS_PER_TICK_WHOLE;
+ giPartMiliseconds += MS_PER_TICK_FRACT;
+ if(giPartMiliseconds > 0x80000000) {
+ giTimestamp ++;
+ giPartMiliseconds -= 0x80000000;
+ }
+
+ Timer_CallTimers();
+
+ // Make sure the RTC Fires again
+ outb(0x70, 0x0C); // Select register C
+ inb(0x71); // Just throw away contents.
+}
+
+Uint64 Time_ReadTSC(void)
+{
+ Uint32 a, d;
+ __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d));
+ return ((Uint64)d << 32) | a;
+}
--- /dev/null
+/*
+ * Acess2 VM8086 Driver
+ * - By John Hodge (thePowersGang)
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <vm8086.h>
+#include <modules.h>
+#include <hal_proc.h>
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define VM8086_MAGIC_CS 0xFFFF
+#define VM8086_MAGIC_IP 0x0010
+#define VM8086_STACK_SEG 0x9F00
+#define VM8086_STACK_OFS 0x0AFE
+enum eVM8086_Opcodes
+{
+ VM8086_OP_PUSHF = 0x9C,
+ VM8086_OP_POPF = 0x9D,
+ VM8086_OP_INT_I = 0xCD,
+ VM8086_OP_IRET = 0xCF,
+ VM8086_OP_IN_AD = 0xEC,
+ VM8086_OP_IN_ADX = 0xED,
+ VM8086_OP_OUT_AD = 0xEE,
+ VM8086_OP_OUT_ADX = 0xEF
+};
+#define VM8086_PAGES_PER_INST 4
+
+#define VM8086_BLOCKSIZE 128
+#define VM8086_BLOCKCOUNT ((0x9F000-0x10000)/VM8086_BLOCKSIZE)
+
+// === TYPES ===
+struct sVM8086_InternalData
+{
+ struct {
+ Uint32 Bitmap; // 32 sections = 128 byte blocks
+ tVAddr VirtBase;
+ tPAddr PhysAddr;
+ } AllocatedPages[VM8086_PAGES_PER_INST];
+};
+
+// === PROTOTYPES ===
+ int VM8086_Install(char **Arguments);
+void VM8086_GPF(tRegs *Regs);
+//tVM8086 *VM8086_Init(void);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL);
+tMutex glVM8086_Process;
+tSemaphore gVM8086_TaskComplete;
+tSemaphore gVM8086_TasksToDo;
+tPID gVM8086_WorkerPID;
+tTID gVM8086_CallingThread;
+tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions
+Uint32 gaVM8086_MemBitmap[VM8086_BLOCKCOUNT/32];
+
+// === FUNCTIONS ===
+int VM8086_Install(char **Arguments)
+{
+ tPID pid;
+
+ Semaphore_Init(&gVM8086_TasksToDo, 0, 10, "VM8086", "TasksToDo");
+
+ // Lock to avoid race conditions
+ Mutex_Acquire( &glVM8086_Process );
+
+ // Create BIOS Call process
+ pid = Proc_Clone(CLONE_VM);
+ Log_Debug("VM8086", "pid = %i", pid);
+ if(pid == -1)
+ {
+ Log_Error("VM8086", "Unable to clone kernel into VM8086 worker");
+ return MODULE_ERR_MISC;
+ }
+ if(pid == 0)
+ {
+ Uint * volatile stacksetup; // Initialising Stack
+ Uint16 * volatile rmstack; // Real Mode Stack
+ int i;
+
+ Log_Debug("VM8086", "Initialising worker");
+
+ // Set Image Name
+ Threads_SetName("VM8086");
+
+ // Map ROM Area
+ for(i=0xA0;i<0x100;i++) {
+ MM_Map( i * 0x1000, i * 0x1000 );
+ }
+ MM_Map( 0, 0 ); // IVT / BDA
+ // Map (but allow allocation) of 0x1000 - 0x9F000
+ // - So much hack, it isn't funny
+ for(i=1;i<0x9F;i++) {
+ MM_Map( i * 0x1000, i * 0x1000 );
+ MM_DerefPhys( i * 0x1000 ); // Above
+ while(MM_GetRefCount(i*0x1000))
+ MM_DerefPhys( i * 0x1000 ); // Phys setup
+ }
+ MM_Map( 0x9F000, 0x9F000 ); // Stack / EBDA
+ // System Stack / Stub
+ if( MM_Allocate( 0x100000 ) == 0 ) {
+ Log_Error("VM8086", "Unable to allocate memory for stack/stub");
+ gVM8086_WorkerPID = 0;
+ Threads_Exit(0, 1);
+ }
+
+ *(Uint8*)(0x100000) = VM8086_OP_IRET;
+ *(Uint8*)(0x100001) = 0x07; // POP ES
+ *(Uint8*)(0x100002) = 0x1F; // POP DS
+ *(Uint8*)(0x100003) = 0xCB; // RET FAR
+
+ rmstack = (Uint16*)(VM8086_STACK_SEG*16 + VM8086_STACK_OFS);
+ rmstack--; *rmstack = 0xFFFF; //CS
+ rmstack--; *rmstack = 0x0010; //IP
+
+ // Setup Stack
+ stacksetup = (Uint*)0x101000;
+ stacksetup--; *stacksetup = VM8086_STACK_SEG; // GS
+ stacksetup--; *stacksetup = VM8086_STACK_SEG; // FS
+ stacksetup--; *stacksetup = VM8086_STACK_SEG; // DS
+ stacksetup--; *stacksetup = VM8086_STACK_SEG; // ES
+ stacksetup--; *stacksetup = VM8086_STACK_SEG; // SS
+ stacksetup--; *stacksetup = VM8086_STACK_OFS-2; // SP
+ stacksetup--; *stacksetup = 0x20202; // FLAGS
+ stacksetup--; *stacksetup = 0xFFFF; // CS
+ stacksetup--; *stacksetup = 0x10; // IP
+ stacksetup--; *stacksetup = 0xAAAA; // AX
+ stacksetup--; *stacksetup = 0xCCCC; // CX
+ stacksetup--; *stacksetup = 0xDDDD; // DX
+ stacksetup--; *stacksetup = 0xBBBB; // BX
+ stacksetup--; *stacksetup = 0x5454; // SP
+ stacksetup--; *stacksetup = 0xB4B4; // BP
+ stacksetup--; *stacksetup = 0x5151; // SI
+ stacksetup--; *stacksetup = 0xD1D1; // DI
+ stacksetup--; *stacksetup = 0x20|3; // DS - Kernel
+ stacksetup--; *stacksetup = 0x20|3; // ES - Kernel
+ stacksetup--; *stacksetup = 0x20|3; // FS
+ stacksetup--; *stacksetup = 0x20|3; // GS
+ __asm__ __volatile__ (
+ "mov %%eax,%%esp;\n\t" // Set stack pointer
+ "pop %%gs;\n\t"
+ "pop %%fs;\n\t"
+ "pop %%es;\n\t"
+ "pop %%ds;\n\t"
+ "popa;\n\t"
+ "iret;\n\t" : : "a" (stacksetup));
+ for(;;); // Shouldn't be reached
+ }
+
+ gVM8086_WorkerPID = pid;
+
+ // It's released when the GPF fires
+ Mutex_Acquire( &glVM8086_Process );
+ Mutex_Release( &glVM8086_Process );
+
+ // Worker killed itself
+ if( gVM8086_WorkerPID != pid ) {
+ return MODULE_ERR_MISC;
+ }
+
+ return MODULE_ERR_OK;
+}
+
+void VM8086_GPF(tRegs *Regs)
+{
+ Uint8 opcode;
+
+// Log_Log("VM8086", "GPF - %04x:%04x", Regs->cs, Regs->eip);
+
+ if(Regs->eip == VM8086_MAGIC_IP && Regs->cs == VM8086_MAGIC_CS
+ && Threads_GetPID() == gVM8086_WorkerPID)
+ {
+ if( gpVM8086_State == (void*)-1 ) {
+ Log_Log("VM8086", "Worker thread ready and waiting");
+ gpVM8086_State = NULL;
+ Mutex_Release( &glVM8086_Process ); // Release lock obtained in VM8086_Install
+ }
+// Log_Log("VM8086", "gpVM8086_State = %p, gVM8086_CallingThread = %i",
+// gpVM8086_State, gVM8086_CallingThread);
+ if( gpVM8086_State ) {
+ gpVM8086_State->AX = Regs->eax; gpVM8086_State->CX = Regs->ecx;
+ gpVM8086_State->DX = Regs->edx; gpVM8086_State->BX = Regs->ebx;
+ gpVM8086_State->BP = Regs->ebp;
+ gpVM8086_State->SI = Regs->esi; gpVM8086_State->DI = Regs->edi;
+ gpVM8086_State->DS = Regs->ds; gpVM8086_State->ES = Regs->es;
+ gpVM8086_State = NULL;
+ // Wake the caller
+ Semaphore_Signal(&gVM8086_TaskComplete, 1);
+ }
+
+ //Log_Log("VM8086", "Waiting for something to do");
+ __asm__ __volatile__ ("sti");
+ Semaphore_Wait(&gVM8086_TasksToDo, 1);
+
+ //Log_Log("VM8086", "We have a task (%p)", gpVM8086_State);
+ Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_CS;
+ Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_IP;
+ Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->CS;
+ Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->IP;
+ Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->DS;
+ Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->ES;
+
+ // Set Registers
+ Regs->eip = 0x11; Regs->cs = 0xFFFF;
+ Regs->eax = gpVM8086_State->AX; Regs->ecx = gpVM8086_State->CX;
+ Regs->edx = gpVM8086_State->DX; Regs->ebx = gpVM8086_State->BX;
+ Regs->esi = gpVM8086_State->SI; Regs->edi = gpVM8086_State->DI;
+ Regs->ebp = gpVM8086_State->BP;
+ Regs->ds = 0x23; Regs->es = 0x23;
+ Regs->fs = 0x23; Regs->gs = 0x23;
+ return ;
+ }
+
+ opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip) );
+ Regs->eip ++;
+ switch(opcode)
+ {
+ case VM8086_OP_PUSHF: //PUSHF
+ Regs->esp -= 2;
+ *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eflags & 0xFFFF;
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated PUSHF");
+ #endif
+ break;
+ case VM8086_OP_POPF: //POPF
+ // Changing IF is not allowed
+ Regs->eflags &= 0xFFFF0202;
+ Regs->eflags |= *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) );
+ Regs->esp += 2;
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated POPF");
+ #endif
+ break;
+
+ case VM8086_OP_INT_I: //INT imm8
+ {
+ int id;
+ id = *(Uint8*)( Regs->cs*16 +(Regs->eip&0xFFFF));
+ Regs->eip ++;
+
+ Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->cs;
+ Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eip;
+
+ Regs->cs = *(Uint16*)(4*id + 2);
+ Regs->eip = *(Uint16*)(4*id);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated INT 0x%x", id);
+ #endif
+ }
+ break;
+
+ case VM8086_OP_IRET: //IRET
+ Regs->eip = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2;
+ Regs->cs = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2;
+ #if TRACE_EMU
+ Log_Debug("VM8086", "IRET to %04x:%04x", Regs->cs, Regs->eip);
+ #endif
+ break;
+
+
+ case VM8086_OP_IN_AD: //IN AL, DX
+ Regs->eax &= 0xFFFFFF00;
+ Regs->eax |= inb(Regs->edx&0xFFFF);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated IN AL, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
+ #endif
+ break;
+ case VM8086_OP_IN_ADX: //IN AX, DX
+ Regs->eax &= 0xFFFF0000;
+ Regs->eax |= inw(Regs->edx&0xFFFF);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated IN AX, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
+ #endif
+ break;
+
+ case VM8086_OP_OUT_AD: //OUT DX, AL
+ outb(Regs->edx&0xFFFF, Regs->eax&0xFF);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated OUT DX, AL (*0x%04x = 0x%02x)\n", Regs->edx&0xFFFF, Regs->eax&0xFF);
+ #endif
+ break;
+ case VM8086_OP_OUT_ADX: //OUT DX, AX
+ outw(Regs->edx&0xFFFF, Regs->eax&0xFFFF);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated OUT DX, AX (*0x%04x = 0x%04x)\n", Regs->edx&0xFFFF, Regs->eax&0xFFFF);
+ #endif
+ break;
+
+ // TODO: Decide on allowing VM8086 Apps to enable/disable interrupts
+ case 0xFA: //CLI
+ break;
+ case 0xFB: //STI
+ break;
+
+ case 0x66:
+ opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip&0xFFFF));
+ switch( opcode )
+ {
+ case VM8086_OP_IN_ADX: //IN AX, DX
+ Regs->eax = ind(Regs->edx&0xFFFF);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated IN EAX, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
+ #endif
+ break;
+ case VM8086_OP_OUT_ADX: //OUT DX, AX
+ outd(Regs->edx&0xFFFF, Regs->eax);
+ #if TRACE_EMU
+ Log_Debug("VM8086", "Emulated OUT DX, EAX (*0x%04x = 0x%08x)\n", Regs->edx&0xFFFF, Regs->eax);
+ #endif
+ break;
+ default:
+ Log_Error("VM8086", "Error - Unknown opcode 66 %02x caused a GPF at %04x:%04x",
+ Regs->cs, Regs->eip,
+ opcode
+ );
+ // Force an end to the call
+ Regs->cs = VM8086_MAGIC_CS;
+ Regs->eip = VM8086_MAGIC_IP;
+ break;
+ }
+ break;
+
+ default:
+ Log_Error("VM8086", "Error - Unknown opcode %02x caused a GPF at %04x:%04x",
+ opcode, Regs->cs, Regs->eip);
+ // Force an end to the call
+ Regs->cs = VM8086_MAGIC_CS;
+ Regs->eip = VM8086_MAGIC_IP;
+ break;
+ }
+}
+
+/**
+ * \brief Create an instance of the VM8086 Emulator
+ */
+tVM8086 *VM8086_Init(void)
+{
+ tVM8086 *ret;
+ ret = calloc( 1, sizeof(tVM8086) + sizeof(struct sVM8086_InternalData) );
+ ret->Internal = (void*)((tVAddr)ret + sizeof(tVM8086));
+ return ret;
+}
+
+void VM8086_Free(tVM8086 *State)
+{
+ int i;
+ for( i = VM8086_PAGES_PER_INST; i --; )
+ MM_UnmapHWPages( State->Internal->AllocatedPages[i].VirtBase, 1);
+ free(State);
+}
+
+void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset)
+{
+ int i, j, base = 0;
+ int nBlocks, rem;
+
+ Size = (Size + 127) & ~127;
+ nBlocks = Size / 128;
+
+ if(Size > 4096) return NULL;
+
+ for( i = 0; i < VM8086_PAGES_PER_INST; i++ )
+ {
+ if( State->Internal->AllocatedPages[i].VirtBase == 0 ) continue;
+
+
+ //Log_Debug("VM8086", "AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap);
+
+ rem = nBlocks;
+ base = 0;
+ // Scan the bitmap for a free block
+ for( j = 0; j < 32; j++ ) {
+ if( State->Internal->AllocatedPages[i].Bitmap & (1 << j) ) {
+ base = j+1;
+ rem = nBlocks;
+ }
+
+ rem --;
+ if(rem == 0) // Goodie, there's a gap
+ {
+ for( j = 0; j < nBlocks; j++ )
+ State->Internal->AllocatedPages[i].Bitmap |= 1 << (base + j);
+ *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16 + base * 8;
+ *Offset = 0;
+ LOG("Allocated at #%i,%04x", i, base*128);
+ LOG(" - %x:%x", *Segment, *Offset);
+ return (void*)( State->Internal->AllocatedPages[i].VirtBase + base * 128 );
+ }
+ }
+ }
+
+ // No pages with free space?, allocate a new one
+ for( i = 0; i < VM8086_PAGES_PER_INST; i++ )
+ {
+ if( State->Internal->AllocatedPages[i].VirtBase == 0 ) break;
+ }
+ // Darn, we can't allocate any more
+ if( i == VM8086_PAGES_PER_INST ) {
+ Log_Warning("VM8086", "Out of pages in %p", State);
+ return NULL;
+ }
+
+ State->Internal->AllocatedPages[i].VirtBase = MM_AllocDMA(
+ 1, 20, &State->Internal->AllocatedPages[i].PhysAddr);
+ State->Internal->AllocatedPages[i].Bitmap = 0;
+
+ for( j = 0; j < nBlocks; j++ )
+ State->Internal->AllocatedPages[i].Bitmap |= 1 << j;
+ LOG("AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap);
+ *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16;
+ *Offset = 0;
+ LOG(" - %x:%x", *Segment, *Offset);
+ return (void*) State->Internal->AllocatedPages[i].VirtBase;
+}
+
+void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset)
+{
+ return (void*)( KERNEL_BASE + Segment*16 + Offset );
+}
+
+void VM8086_Int(tVM8086 *State, Uint8 Interrupt)
+{
+ State->IP = *(Uint16*)(KERNEL_BASE+4*Interrupt);
+ State->CS = *(Uint16*)(KERNEL_BASE+4*Interrupt+2);
+
+// Log_Debug("VM8086", "Software interrupt %i to %04x:%04x", Interrupt, State->CS, State->IP);
+
+ Mutex_Acquire( &glVM8086_Process );
+
+ gpVM8086_State = State;
+ gVM8086_CallingThread = Threads_GetTID();
+ Semaphore_Signal(&gVM8086_TasksToDo, 1);
+
+ Semaphore_Wait(&gVM8086_TaskComplete, 1);
+
+ Mutex_Release( &glVM8086_Process );
+}
--- /dev/null
+#
+# Acess2 Kernel
+# i386 Architecture Makefile
+# arch/i386/Makefile
+
+MAX_CPUS := 4
+
+AS_SUFFIX = asm
+
+CPPFLAGS := -DMAX_CPUS=$(MAX_CPUS) -D USE_MP=0
+CFLAGS := $(KERNEL_CFLAGS) -mno-sse -mno-mmx
+ASFLAGS := -f elf64 -D MAX_CPUS=$(MAX_CPUS) -D USE_MP=0
+LDFLAGS := -nostdlib -nodefaultlibs
+
+ifeq ($(ARCH),amd64)
+ ASFLAGS += -D AMD64=1
+ CPPFLAGS += -DAMD64=1
+else
+ ifeq ($(ARCH),x86_64)
+ ASFLAGS += -D AMD64=0 -D X86_64=1
+ CPPFLAGS += -DAMD64=0 -DX86_64=1
+ endif
+endif
+
+
+A_OBJ := start32.ao start64.ao desctab.ao proc.ao
+A_OBJ += main.o lib.o proc.o mm_virt.o mm_phys.o
+A_OBJ += kernelpanic.o errors.o time.o pci.o
+A_OBJ += vm8086.o
+# rme.o
+
+POSTBUILD = objcopy $(BIN) -F elf32-i386 $(BIN)
--- /dev/null
+;
+;
+;
+%include "arch/x86_64/include/common.inc.asm"
+[BITS 64]
+
+[extern Log]
+[extern gGDTPtr]
+[extern gGDT]
+
+%define NUM_IRQ_CALLBACKS 4
+
+MM_LOCALAPIC equ 0xFFFFFD0000000000
+
+[section .text]
+[global Desctab_Init]
+Desctab_Init:
+ ; Save to make following instructions smaller
+ mov rdi, gIDT
+
+ ; Set an IDT entry to a callback
+ %macro SETIDT 2
+ mov rax, %2
+ mov WORD [rdi + %1*16], ax
+ shr rax, 16
+ mov WORD [rdi + %1*16 + 6], ax
+ shr rax, 16
+ mov DWORD [rdi + %1*16 + 8], eax
+ ; Enable
+ mov ax, WORD [rdi + %1*16 + 4]
+ or ax, 0x8000
+ mov WORD [rdi + %1*16 + 4], ax
+ %endmacro
+
+ ; Install error handlers
+ %macro SETISR 1
+ SETIDT %1, Isr%1
+ %endmacro
+
+ %assign i 0
+ %rep 32
+ SETISR i
+ %assign i i+1
+ %endrep
+
+ ; Install IRQs
+ SETIDT 0xF0, PIT_IRQ
+ SETIDT 0xF1, Irq1
+ SETIDT 0xF2, Irq2
+ SETIDT 0xF3, Irq3
+ SETIDT 0xF4, Irq4
+ SETIDT 0xF5, Irq5
+ SETIDT 0xF6, Irq6
+ SETIDT 0xF7, Irq7
+ SETIDT 0xF8, Irq8
+ SETIDT 0xF9, Irq9
+ SETIDT 0xFA, Irq10
+ SETIDT 0xFB, Irq11
+ SETIDT 0xFC, Irq12
+ SETIDT 0xFD, Irq13
+ SETIDT 0xFE, Irq14
+ SETIDT 0xFF, Irq15
+
+ ; Remap PIC
+ push rdx ; Save RDX
+ mov dx, 0x20
+ mov al, 0x11
+ out dx, al ; Init Command
+ mov dx, 0x21
+ mov al, 0xF0
+ out dx, al ; Offset (Start of IDT Range)
+ mov al, 0x04
+ out dx, al ; IRQ connected to Slave (00000100b) = IRQ2
+ mov al, 0x01
+ out dx, al ; Set Mode
+ mov al, 0x00
+ out dx, al ; Set Mode
+
+ mov dx, 0xA0
+ mov al, 0x11
+ out dx, al ; Init Command
+ mov dx, 0xA1
+ mov al, 0xF8
+ out dx, al ; Offset (Start of IDT Range)
+ mov al, 0x02
+ out dx, al ; IRQ Line connected to master
+ mov al, 0x01
+ out dx, al ; Set Mode
+ mov dl, 0x00
+ out dx, al ; Set Mode
+ pop rdx
+
+
+ ; Install IDT
+ mov rax, gIDTPtr
+ lidt [rax]
+
+ ; Re-install GDT (in higher address space)
+ mov rax, gGDTPtr
+ mov rcx, gGDT
+ mov QWORD [rax+2], rcx
+ lgdt [rax]
+
+ ; Start interrupts
+ sti
+
+ ; Set IA32_LSTAR (RIP of handler)
+ mov ecx, 0xC0000082 ; IA32_LSTAR
+ mov eax, SyscallStub - 0xFFFFFFFF00000000
+ mov edx, 0xFFFFFFFF
+ wrmsr
+ ; Set IA32_FMASK (flags mask)
+ mov ecx, 0xC0000084
+ rdmsr
+ mov eax, ~0x202
+ wrmsr
+ ; Set IA32_STAR (Kernel/User CS)
+ mov ecx, 0xC0000081
+ rdmsr
+ mov edx, 0x8 | (0x1B << 16) ; Kernel CS (and Kernel DS/SS - 8), User CS
+ wrmsr
+
+ ret
+
+; int IRQ_AddHandler(int IRQ, void (*Handler)(int IRQ), void *Ptr)
+; Return Values:
+; 0 on Success
+; -1 on an invalid IRQ Number
+; -2 when no slots are avaliable
+[global IRQ_AddHandler]
+IRQ_AddHandler:
+ ; RDI - IRQ Number
+ ; RSI - Callback
+ ; RDX - Ptr
+
+ ; Check for RDI >= 16
+ cmp rdi, 16
+ jb .numOK
+ xor rax, rax
+ dec rax
+ jmp .ret
+.numOK:
+
+ ; Get handler base into RAX
+ lea rax, [rdi*4]
+ mov rcx, gaIRQ_Handlers
+ lea rax, [rcx+rax*8]
+
+ ; Find a free callback slot
+ %rep NUM_IRQ_CALLBACKS
+ mov rcx, [rax]
+ test rcx, rcx
+ jz .assign
+ add rax, 8
+ %endrep
+ ; None found, return -2
+ xor rax, rax
+ dec rax
+ dec rax
+ jmp .ret
+
+ ; Assign the IRQ Callback
+.assign:
+ ; A little bit of debug
+ push rdi
+ push rsi
+ push rax
+ push rdx
+ sub rsp, 8
+ mov rcx, rdi ; IRQ Number
+ mov rdx, rsi ; Callback
+ mov rsi, rax ; Pointer
+ mov rdi, csIRQ_Assigned
+ call Log
+ add rsp, 8
+ pop rdx
+ pop rax
+ pop rsi
+ pop rdi
+
+ ; Assign and return
+ mov [rax], rsi
+ add rax, gaIRQ_DataPtrs - gaIRQ_Handlers
+ mov [rax], rdx
+ xor rax, rax
+
+.ret:
+ ret
+
+[section .rodata]
+csIRQ_Assigned:
+ db "IRQ %p := %p (IRQ %i)",0
+csIRQ_Fired:
+ db "IRQ %i fired",0
+[section .text]
+
+%macro ISR_NOERRNO 1
+Isr%1:
+ push QWORD 0
+ push QWORD %1
+ jmp ErrorCommon
+%endmacro
+%macro ISR_ERRNO 1
+Isr%1:
+ push QWORD %1
+ jmp ErrorCommon
+%endmacro
+
+ISR_NOERRNO 0; 0: Divide By Zero Exception
+ISR_NOERRNO 1; 1: Debug Exception
+ISR_NOERRNO 2; 2: Non Maskable Interrupt Exception
+ISR_NOERRNO 3; 3: Int 3 Exception
+ISR_NOERRNO 4; 4: INTO Exception
+ISR_NOERRNO 5; 5: Out of Bounds Exception
+ISR_NOERRNO 6; 6: Invalid Opcode Exception
+ISR_NOERRNO 7; 7: Coprocessor Not Available Exception
+ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!)
+ISR_NOERRNO 9; 9: Coprocessor Segment Overrun Exception
+ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!)
+ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!)
+ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!)
+ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!)
+ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!)
+ISR_NOERRNO 15; 15: Reserved Exception
+ISR_NOERRNO 16; 16: Floating Point Exception
+ISR_NOERRNO 17; 17: Alignment Check Exception
+ISR_NOERRNO 18; 18: Machine Check Exception
+ISR_NOERRNO 19; 19: Reserved
+ISR_NOERRNO 20; 20: Reserved
+ISR_NOERRNO 21; 21: Reserved
+ISR_NOERRNO 22; 22: Reserved
+ISR_NOERRNO 23; 23: Reserved
+ISR_NOERRNO 24; 24: Reserved
+ISR_NOERRNO 25; 25: Reserved
+ISR_NOERRNO 26; 26: Reserved
+ISR_NOERRNO 27; 27: Reserved
+ISR_NOERRNO 28; 28: Reserved
+ISR_NOERRNO 29; 29: Reserved
+ISR_NOERRNO 30; 30: Reserved
+ISR_NOERRNO 31; 31: Reserved
+
+[extern Error_Handler]
+[global ErrorCommon]
+ErrorCommon:
+ PUSH_GPR
+ push gs
+ push fs
+ ;PUSH_FPU
+ ;PUSH_XMM
+
+ mov rdi, rsp
+; xchg bx, bx
+ call Error_Handler
+
+ ;POP_XMM
+ ;POP_FPU
+ pop fs
+ pop gs
+ POP_GPR
+ add rsp, 2*8
+ iretq
+
+%macro DEFIRQ 1
+Irq%1:
+ push 0
+ push %1
+ jmp IrqCommon
+%endmacro
+
+%assign i 0
+%rep 16
+DEFIRQ i
+%assign i i+1
+%endrep
+
+[global IrqCommon]
+IrqCommon:
+ PUSH_GPR
+ push gs
+ push fs
+
+; mov rdi, csIRQ_Fired
+; mov rsi, [rsp+(16+2)*8]
+; call Log
+
+ mov ebx, [rsp+(16+2)*8] ; Get interrupt number (16 GPRS + 2 SRs)
+ shl ebx, 2 ; *4
+ mov rax, gaIRQ_Handlers
+ lea rbx, [rax+rbx*8]
+
+ ; Check all callbacks
+ sub rsp, 8 ; Shadow of argument
+ %assign i 0
+ %rep NUM_IRQ_CALLBACKS
+ ; Get callback address
+ mov rax, [rbx]
+ test rax, rax ; Check if it exists
+ jz .skip.%[i]
+ ; Set RDI to IRQ number
+ mov rdi, [rsp+(16+2+1)*8] ; Get IRQ number
+ mov rsi, [rbx-gaIRQ_Handlers+gaIRQ_DataPtrs]
+ call rax ; Call
+.skip.%[i]:
+ add rbx, 8 ; Next!
+ %assign i i+1
+ %endrep
+ add rsp, 8
+
+ ; ACK
+ mov al, 0x20
+ mov rdi, [rsp+(16+2)*8] ; Get IRQ number
+ cmp rdi, 8
+ jb .skipAckSecondary
+ out 0xA0, al
+.skipAckSecondary:
+ out 0x20, al
+
+ pop fs
+ pop gs
+ POP_GPR
+ add rsp, 8*2
+ iretq
+
+[extern Time_UpdateTimestamp]
+
+%if USE_MP
+[global APIC_Timer_IRQ]
+APIC_Timer_IRQ:
+ PUSH_GPR
+ push gs
+ push fs
+
+ ; TODO: What to do?
+
+ mov eax, DWORD [gpMP_LocalAPIC]
+ mov DWORD [eax+0x0B0], 0
+
+ pop fs
+ pop gs
+ POP_GPR
+ iretq
+%endif
+
+[global PIT_IRQ]
+PIT_IRQ:
+ PUSH_GPR
+ ;PUSH_FPU
+ ;PUSH_XMM
+
+ call Time_UpdateTimestamp
+
+ %if 0
+[section .rodata]
+csUserSS: db "User SS: 0x%x",0
+[section .text]
+ mov rdi, csUserSS
+ mov rsi, [rsp+0x80+0x20]
+ call Log
+ %endif
+
+ ; Send EOI
+ mov al, 0x20
+ out 0x20, al ; ACK IRQ
+
+ ;POP_XMM
+ ;POP_FPU
+ POP_GPR
+ iretq
+
+[extern ci_offsetof_tThread_KernelStack]
+[extern SyscallHandler]
+[global SyscallStub]
+SyscallStub:
+ mov rbp, dr0
+ mov ebx, [rel ci_offsetof_tThread_KernelStack]
+ mov rbp, [rbp+rbx] ; Get kernel stack
+ xchg rbp, rsp ; Swap stacks
+
+ push rbp ; Save User RSP
+ push rcx ; RIP
+ push r11 ; RFLAGS
+
+ ; RDI
+ ; RSI
+ ; RDX
+ ; R10 (RCX for non syscall)
+ ; R8
+ ; R9
+ sub rsp, (6+2)*8
+ mov [rsp+0x00], rax ; Number
+; mov [rsp+0x08], rax ; Errno (output only)
+ mov [rsp+0x10], rdi ; Arg1
+ mov [rsp+0x18], rsi ; Arg2
+ mov [rsp+0x20], rdx ; Arg3
+ mov [rsp+0x28], r10 ; Arg4
+ mov [rsp+0x30], r8 ; Arg5
+ mov [rsp+0x38], r9 ; Arg6
+
+ mov rdi, rsp
+ sub rsp, 8
+ call SyscallHandler
+
+ %if 0
+[section .rodata]
+csSyscallReturn: db "Syscall Return: 0x%x",0
+[section .text]
+ mov rdi, csSyscallReturn
+ mov rsi, [rsp+0+8]
+ call Log
+ %endif
+
+ add rsp, 8
+ mov ebx, [rsp+8] ; Get errno
+ mov rax, [rsp+0] ; Get return
+ add rsp, (6+2)*8
+
+ pop r11
+ pop rcx
+ pop rsp ; Change back to user stack
+ ; TODO: Determine if user is 64 or 32 bit
+
+ db 0x48 ; REX, nasm doesn't have a sysretq opcode
+ sysret
+
+[section .data]
+gIDT:
+ ; 64-bit Interrupt Gate, CS = 0x8, IST0 (Disabled)
+ times 256 dd 0x00080000, 0x00000E00, 0, 0
+gIDTPtr:
+ dw 256*16-1
+ dq gIDT
+
+gaIRQ_Handlers:
+ times 16*NUM_IRQ_CALLBACKS dq 0
+gaIRQ_DataPtrs:
+ times 16*NUM_IRQ_CALLBACKS dq 0
+
+; vim: ft=nasm
--- /dev/null
+/*
+ * Acess2 x86_64 Project
+ * - Error Handling
+ */
+#include <acess.h>
+#include <proc.h>
+#include <mm_virt.h>
+#include <threads_int.h> // Needed for SSE handling
+
+#define MAX_BACKTRACE 6
+
+// === IMPORTS ===
+extern int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
+extern void Error_Backtrace(Uint IP, Uint BP);
+extern void Proc_EnableSSE(void);
+extern void Proc_RestoreSSE(Uint32 Data);
+
+// === PROTOTYPES ===
+void Error_Handler(tRegs *Regs);
+
+// === GLOBALS ==
+const char * const csaERROR_NAMES[] = {
+ "Divide By Zero", "Debug", "NMI Exception", "INT3",
+ "INTO", "Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable",
+ "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present",
+ "Stack Fault Exception", "GPF", "#PF", "Reserved",
+ "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved"
+ };
+
+// === CODE ===
+void Error_Handler(tRegs *Regs)
+{
+ Uint cr;
+
+ if( Regs->IntNum == 7 )
+ {
+ tThread *thread = Proc_GetCurThread();
+ if(!thread->SavedState.bSSEModified)
+ {
+ Proc_EnableSSE();
+ if(!thread->SavedState.SSE)
+ thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF);
+ else
+ Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF );
+ thread->SavedState.bSSEModified = 1;
+// __asm__ __volatile__ ("sti");
+ return ;
+ }
+ // oops, SSE enabled but a #NM, bad news
+ }
+
+ if( Regs->IntNum == 14 ) {
+ __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
+ if( MM_PageFault(cr, Regs->ErrorCode, Regs) == 0 )
+ return ;
+ }
+ else {
+ Debug_KernelPanic();
+
+ Error_Backtrace(Regs->RIP, Regs->RBP);
+ }
+
+ Log("CPU Error %x, Code: 0x%x", Regs->IntNum, Regs->ErrorCode);
+ Log(" - %s", csaERROR_NAMES[Regs->IntNum]);
+ Log(" CS:RIP = 0x%04x:%016llx", Regs->CS, Regs->RIP);
+ Log(" SS:RSP = 0x%04x:%016llx", Regs->SS, Regs->RSP);
+ Log(" RFLAGS = 0x%016llx", Regs->RFlags);
+
+ Log(" RAX %016llx RCX %016llx RDX %016llx RBX %016llx",
+ Regs->RAX, Regs->RCX, Regs->RDX, Regs->RBX);
+ Log(" RSP %016llx RBP %016llx RSI %016llx RDI %016llx",
+ Regs->RSP, Regs->RBP, Regs->RSP, Regs->RDI);
+ Log(" R8 %016llx R9 %016llx R10 %016llx R11 %016llx",
+ Regs->R8, Regs->R9, Regs->R10, Regs->R11);
+ Log(" R12 %016llx R13 %016llx R14 %016llx R15 %016llx",
+ Regs->R12, Regs->R13, Regs->R14, Regs->R15);
+ Log(" FS %04x GS %04x", Regs->FS, Regs->GS);
+
+
+ // Control Registers
+ __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr));
+ Warning(" CR0 0x%08x", cr);
+ __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr));
+ Warning(" CR2 0x%016llx", cr);
+ __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr));
+ Warning(" CR3 0x%016llx", cr);
+ __asm__ __volatile__ ("mov %%cr4, %0":"=r"(cr));
+ Warning(" CR4 0x%08x", cr);
+
+ switch( Regs->IntNum )
+ {
+ case 6: // #UD
+ Warning(" Offending bytes: %02x %02x %02x %02x",
+ *(Uint8*)(Regs->RIP+0), *(Uint8*)(Regs->RIP+1),
+ *(Uint8*)(Regs->RIP+2), *(Uint8*)(Regs->RIP+3)
+ );
+ break;
+ }
+
+ __asm__ __volatile__ ("cli");
+ for(;;)
+ __asm__ __volatile__ ("hlt");
+}
+
+/**
+ * \fn void Error_Backtrace(Uint eip, Uint ebp)
+ * \brief Unrolls the stack to trace execution
+ * \param eip Current Instruction Pointer
+ * \param ebp Current Base Pointer (Stack Frame)
+ */
+void Error_Backtrace(Uint IP, Uint BP)
+{
+ int i = 0;
+
+ //if(eip < 0xC0000000 && eip > 0x1000)
+ //{
+ // LogF("Backtrace: User - 0x%x\n", eip);
+ // return;
+ //}
+
+ if( IP > USER_MAX && IP < MM_KERNEL_CODE
+ && (MM_MODULE_MIN > IP || IP > MM_MODULE_MAX)
+ )
+ {
+ LogF("Backtrace: Data Area - %p\n", IP);
+ return;
+ }
+
+ //str = Debug_GetSymbol(eip, &delta);
+ //if(str == NULL)
+ LogF("Backtrace: %p", IP);
+ //else
+ // LogF("Backtrace: %s+0x%x", str, delta);
+ if( !MM_GetPhysAddr(BP) )
+ {
+ LogF("\nBacktrace: Invalid BP, stopping\n");
+ return;
+ }
+
+
+ while( MM_GetPhysAddr(BP) && MM_GetPhysAddr(BP+8+7) && i < MAX_BACKTRACE )
+ {
+ //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta);
+ //if(str == NULL)
+ LogF(" >> 0x%llx", ((Uint*)BP)[1]);
+ //else
+ // LogF(" >> %s+0x%x", str, delta);
+ BP = ((Uint*)BP)[0];
+ i++;
+ }
+ LogF("\n");
+}
--- /dev/null
+/*
+ * Acess2 x86-64 Architecure Module
+ * - By John Hodge (thePowersGang)
+ */
+#ifndef _ARCH_H_
+#define _ARCH_H_
+
+//#include <stdint.h>
+#define USER_MAX 0x00007FFF##FFFFF000
+#define KERNEL_BASE 0xFFFFFFFF##80000000
+#define BITS 64
+#define PAGE_SIZE 0x1000
+
+#define STACKED_LOCKS 2 // 0: No, 1: Per-CPU, 2: Per-Thread
+#define LOCK_DISABLE_INTS 0
+
+#define INVLPTR ((void*)0x0FFFFFFFFFFFFFFFULL)
+
+//#define INT_MAX 0x7FFFFFFF
+//#define UINT_MAX 0xFFFFFFFF
+
+// === Core Types ===
+typedef signed char Sint8;
+typedef unsigned char Uint8;
+typedef signed short Sint16;
+typedef unsigned short Uint16;
+typedef signed int Sint32;
+typedef unsigned int Uint32;
+#if __WORDSIZE == 64
+typedef signed long int Sint64;
+typedef unsigned long int Uint64;
+#else
+typedef signed long long int Sint64;
+typedef unsigned long long int Uint64;
+#endif
+
+typedef Sint64 Sint;
+typedef Uint64 Uint;
+typedef Uint64 tPAddr;
+typedef Uint64 tVAddr;
+
+typedef Uint64 size_t;
+typedef char BOOL;
+
+#define __ASM__ __asm__ __volatile__
+
+// === MACROS ===
+/**
+ * \brief Halt the CPU
+ */
+#define HALT() __asm__ __volatile__ ("sti;\n\thlt")
+/**
+ * \brief Fire a magic breakpoint (bochs)
+ */
+#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx")
+
+// Systemcall Registers
+// TODO: Fix this structure
+typedef struct sSyscallRegs
+{
+ union {
+ Uint Num;
+ Uint Return;
+ }; // RAX
+ Uint Error; // RBX
+ Uint Arg1; // RDI
+ Uint Arg2; // RSI
+ Uint Arg3; // RDX
+ Uint Arg4; // RCX
+ Uint Arg5; // R8
+ Uint Arg6; // R9
+ Uint _Flags;
+ Uint _IP;
+ Uint StackPointer; // RSP
+
+} tSyscallRegs;
+
+/**
+ * \brief Short Spinlock structure
+ */
+struct sShortSpinlock {
+ #if STACKED_LOCKS == 2
+ volatile void *Lock; //!< Lock value
+ #else
+ volatile int Lock; //!< Lock value
+ #endif
+
+ #if LOCK_DISABLE_INTS
+ int IF; //!< Interrupt state on call to SHORTLOCK
+ #endif
+ #if STACKED_LOCKS
+ int Depth;
+ #endif
+};
+
+// === FUNCTIONS ===
+extern int IS_LOCKED(struct sShortSpinlock *Lock);
+extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock);
+extern void SHORTLOCK(struct sShortSpinlock *Lock);
+extern void SHORTREL(struct sShortSpinlock *Lock);
+
+extern void Debug_PutCharDebug(char ch);
+extern void Debug_PutStringDebug(const char *Str);
+
+// TODO: Move this to acess.h
+extern tPAddr MM_AllocateZero(tVAddr VAddr);
+
+#endif
+
--- /dev/null
+/*
+ */
+#ifndef _ARCH_CONFIG_H_
+#define _ARCH_CONFIG_H_
+
+
+#define PIT_TIMER_BASE_N 3579545
+#define PIT_TIMER_BASE_D 3
+// PIT Ticks at 1.1931816666666 MHz
+// Base is 1193182 HZ
+#define PIT_TIMER_DIVISOR 11931 //~100Hz
+
+#endif
--- /dev/null
+
+%define INITIAL_KSTACK_SIZE 8
+
+%macro SAVE_GPR 1
+ mov [%1-0x08], r15
+ mov [%1-0x10], r14
+ mov [%1-0x18], r13
+ mov [%1-0x20], r12
+ mov [%1-0x28], r11
+ mov [%1-0x30], r10
+ mov [%1-0x38], r9
+ mov [%1-0x40], r8
+ mov [%1-0x48], rdi
+ mov [%1-0x50], rsi
+ mov [%1-0x58], rbp
+ mov [%1-0x60], rsp
+ mov [%1-0x68], rbx
+ mov [%1-0x70], rdx
+ mov [%1-0x78], rcx
+ mov [%1-0x80], rax
+%endmacro
+
+%macro PUSH_GPR 0
+ SAVE_GPR rsp
+ sub rsp, 0x80
+%endmacro
+
+%macro RESTORE_GPR 1
+ mov r15, [%1-0x08]
+ mov r14, [%1-0x10]
+ mov r13, [%1-0x18]
+ mov r12, [%1-0x20]
+ mov r11, [%1-0x28]
+ mov r10, [%1-0x30]
+ mov r9, [%1-0x38]
+ mov r8, [%1-0x40]
+ mov rdi, [%1-0x48]
+ mov rsi, [%1-0x50]
+ mov rbp, [%1-0x58]
+ ;mov rsp, [%1-0x60]
+ mov rbx, [%1-0x68]
+ mov rdx, [%1-0x70]
+ mov rcx, [%1-0x78]
+ mov rax, [%1-0x80]
+%endmacro
+
+%macro POP_GPR 0
+ add rsp, 0x80
+ RESTORE_GPR rsp
+%endmacro
--- /dev/null
+/**
+ */
+#ifndef _DESCTAB_H_
+#define _DESCTAB_H_
+
+typedef struct {
+ union
+ {
+ struct
+ {
+ Uint16 LimitLow;
+ Uint16 BaseLow;
+ Uint8 BaseMid;
+ Uint8 Access;
+ struct {
+ unsigned LimitHi: 4;
+ unsigned Flags: 4;
+ } __attribute__ ((packed));
+ Uint8 BaseHi;
+ };
+ Uint32 DWord[2];
+ };
+} __attribute__ ((packed)) tGDT;
+
+typedef struct {
+ Uint16 OffsetLo;
+ Uint16 CS;
+ Uint16 Flags; // 0-2: IST, 3-7: 0, 8-11: Type, 12: 0, 13-14: DPL, 15: Present
+ Uint16 OffsetMid;
+ Uint32 OffsetHi;
+ Uint32 Reserved;
+} __attribute__ ((packed)) tIDT;
+
+typedef struct {
+ Uint32 Rsvd1;
+
+ Uint64 RSP0;
+ Uint64 RSP1;
+ Uint64 RSP2;
+
+ Uint32 Rsvd2[2];
+
+ // Interrupt Stack Table Pointers
+ Uint64 IST1;
+ Uint64 IST2;
+ Uint64 IST3;
+ Uint64 IST4;
+ Uint64 IST5;
+ Uint64 IST6;
+ Uint64 IST7;
+
+ Uint32 Rsvd3[2];
+ Uint16 Rsvd4;
+ Uint16 IOMapBase;
+} __attribute__ ((packed)) tTSS;
+
+#endif
--- /dev/null
+/*
+ * Acess2 x86_64 Architecture Code
+ *
+ * This file is published under the terms of the Acess Licence.
+ * See the file COPYING for more details
+ *
+ * vmem.h - Virtual Memory Functions & Definitions
+ */
+#ifndef _VMEM_H_
+#define _VMEM_H_
+
+#include <arch.h>
+
+#define PAGE_SIZE 0x1000
+
+// === Memory Location Definitions ===
+/*
+ * Userland - Lower Half
+ * Kernel land - Upper Half
+ *
+ * START ADDRESS END ADDRESS BITS SIZE NAME
+ * 0x00000000 00000000 - 0x00007FFF FFFFFFFF 47 128 TiB User Space
+ * 0x00008000 00000000 - 0xFFFF7FFF FFFFFFFF --- SIGN EXTENSION NULL ZONE
+ * 0xFFFF8000 00000000 - 0xFFFFFFFF FFFFFFFF 47 128 TiB Kernel Range
+ * 8000 00000000 - 9000 00000000 42 16 TiB Kernel Heap
+ * 9000 00000000 - 9800 00000000 43 8 TiB Module Space
+ * 9800 00000000 - 9A00 00000000 41 2 TiB Kernel VFS
+ * ---- GAP ---- 6 TiB
+ * A000 00000000 - B000 00000000 44 16 TiB Kernel Stacks
+ * C000 00000000 - D000 00000000 44 16 TiB Hardware Mappings
+ * D000 00000000 - D080 00000000 39 512 GiB Per-Process Data
+ * D080 00000000 - D100 00000000 39 512 GiB Kernel Supplied User Code
+ * ---- GAP ---- 15 TiB
+ * E000 00000000 - E800 00000000 43 8 TiB Physical Page Nodes (2**40 pages * 8 bytes)
+ * E800 00000000 - EC00 00000000 42 4 TiB Physical Page Reference Counts (2**40 pg * 4 bytes)
+ * EC00 00000000 - EC80 00000000 39 512 GiB Physical Page Bitmap (1 page per bit)
+ * EC80 00000000 - ED00 00000000 39 512 GiB Physical Page DblAlloc Bitmap (1 page per bit)
+ * ED00 00000000 - ED00 80000000 31 2 GiB Physical Page Super Bitmap (64 pages per bit)
+ * ---- GAP ---- 9 TiB
+ * FE00 00000000 - FE80 00000000 39 512 GiB Fractal Mapping (PML4 508)
+ * FE80 00000000 - FF00 00000000 39 512 GiB Temp Fractal Mapping
+ * FF00 00000000 - FF80 00000000 39 512 GiB Temporary page mappings
+ * FF80 00000000 - FF80 80000000 31 2 GiB Local APIC
+ * ---- GAP ---- 506 GiB
+ * FFFF 00000000 - FFFF 80000000 31 2 GiB User Code
+ * FFFF 80000000 - FFFF FFFFFFFF 31 2 GiB Kernel code / data
+ */
+
+#define MM_USER_MIN 0x00000000##00010000
+#define USER_LIB_MAX 0x00007000##00000000
+#define USER_STACK_PREALLOC 0x00000000##00002000 // 8 KiB
+#define USER_STACK_SZ 0x00000000##00020000 // 64 KiB
+#define USER_STACK_TOP 0x00008000##00000000
+#define MM_KERNEL_RANGE 0xFFFF8000##00000000
+#define MM_KHEAP_BASE (MM_KERNEL_RANGE|(0x8000##00000000))
+#define MM_KHEAP_MAX (MM_KERNEL_RANGE|(0x9000##00000000))
+#define MM_MODULE_MIN (MM_KERNEL_RANGE|(0x9000##00000000))
+#define MM_MODULE_MAX (MM_KERNEL_RANGE|(0x9800##00000000))
+#define MM_KERNEL_VFS (MM_KERNEL_RANGE|(0x9800##00000000))
+#define MM_KSTACK_BASE (MM_KERNEL_RANGE|(0xA000##00000000))
+#define MM_KSTACK_TOP (MM_KERNEL_RANGE|(0xB000##00000000))
+
+#define MM_HWMAP_BASE (MM_KERNEL_RANGE|(0xC000##00000000))
+#define MM_HWMAP_TOP (MM_KERNEL_RANGE|(0xD000##00000000))
+#define MM_PPD_BASE (MM_KERNEL_RANGE|(0xD000##00000000))
+#define MM_PPD_CFG MM_PPD_BASE
+#define MM_PPD_HANDLES (MM_KERNEL_RANGE|(0xD008##00000000))
+#define MM_USER_CODE (MM_KERNEL_RANGE|(0xD080##00000000))
+
+#define MM_PAGE_NODES (MM_KERNEL_RANGE|(0xE000##00000000))
+#define MM_PAGE_COUNTS (MM_KERNEL_RANGE|(0xE800##00000000))
+#define MM_PAGE_BITMAP (MM_KERNEL_RANGE|(0xEC00##00000000))
+#define MM_PAGE_DBLBMP (MM_KERNEL_RANGE|(0xEC00##00000000))
+#define MM_PAGE_SUPBMP (MM_KERNEL_RANGE|(0xED00##00000000))
+
+#define MM_FRACTAL_BASE (MM_KERNEL_RANGE|(0xFE00##00000000))
+#define MM_TMPFRAC_BASE (MM_KERNEL_RANGE|(0xFE80##00000000))
+#define MM_TMPMAP_BASE (MM_KERNEL_RANGE|(0xFF00##00000000))
+#define MM_TMPMAP_END (MM_KERNEL_RANGE|(0xFF80##00000000))
+#define MM_LOCALAPIC (MM_KERNEL_RANGE|(0xFF80##00000000))
+#define MM_KERNEL_CODE (MM_KERNEL_RANGE|(0xFFFF##80000000))
+
+
+// === FUNCTIONS ===
+void MM_FinishVirtualInit(void);
+tVAddr MM_NewKStack(void);
+tVAddr MM_Clone(void);
+tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize);
+
+#endif
--- /dev/null
+/*
+ * Acess2 x86_64 Port
+ *
+ * proc.h - Process/Thread management code
+ */
+#ifndef _PROC_H_
+#define _PROC_H_
+
+#include <arch.h>
+
+// Register Structure
+// TODO: Rebuild once IDT code is done
+typedef struct {
+ // MMX
+ // FPU
+ Uint FS, GS;
+
+ Uint RAX, RCX, RDX, RBX;
+ Uint KernelRSP, RBP, RSI, RDI;
+ Uint R8, R9, R10, R11;
+ Uint R12, R13, R14, R15;
+
+ Uint IntNum, ErrorCode;
+ Uint RIP, CS;
+ Uint RFlags, RSP, SS;
+} tRegs;
+
+/**
+ * \brief Memory State for thread handler
+ */
+typedef struct sMemoryState
+{
+ tPAddr CR3;
+} tMemoryState;
+
+// 512 bytes, 16 byte aligned
+typedef struct sSSEState
+{
+ char data[512];
+} tSSEState;
+
+/**
+ * \brief Task state for thread handler
+ */
+typedef struct sTaskState
+{
+ Uint RIP, RSP;
+ Uint64 UserRIP, UserCS;
+ tSSEState *SSE;
+ int bSSEModified;
+} tTaskState;
+
+// === CONSTANTS ===
+#define KERNEL_STACK_SIZE 0x8000 // 32 KiB
+//#define KERNEL_STACK_SIZE 0x10000 // 64 KiB
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 VM8086 BIOS Interface
+ * - By John Hodge (thePowersGang)
+ *
+ * vm8086.h
+ * - Core Header
+ */
+#ifndef _VM80806_H_
+#define _VM80806_H_
+
+// === TYPES ===
+/**
+ * \note Semi-opaque - Past \a .IP, the implementation may add any data
+ * it needs to the state.
+ */
+typedef struct sVM8086
+{
+ Uint16 AX, CX, DX, BX;
+ Uint16 BP, SP, SI, DI;
+
+ Uint16 SS, DS, ES;
+
+ Uint16 CS, IP;
+
+ struct sVM8086_InternalData *Internal;
+} tVM8086;
+
+// === FUNCTIONS ===
+/**
+ * \brief Create an instance of the VM8086 Emulator
+ * \note Do not free this pointer with ::free, instead use ::VM8086_Free
+ * \return Pointer to a tVM8086 structure, this structure may be larger than
+ * tVM8086 due to internal data.
+ */
+extern tVM8086 *VM8086_Init(void);
+/**
+ * \brief Free an allocated tVM8086 structure
+ * \param State Emulator state to free
+ */
+extern void VM8086_Free(tVM8086 *State);
+/**
+ * \brief Allocate a piece of memory in the emulated address space and
+ * return a host and emulated pointer to it.
+ * \param State Emulator state
+ * \param Size Size of memory block
+ * \param Segment Pointer to location to store the allocated memory's segment
+ * \param Offset Pointet to location to store the allocated memory's offset
+ * \return Host pointer to the allocated memory
+ */
+extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset);
+/**
+ * \brief Gets a pointer to a piece of emulated memory
+ * \todo Only 1 machine page is garenteed to be contiguous
+ * \param State Emulator State
+ * \param Segment Source Segment
+ * \param Offset Source Offset
+ * \return Host pointer to the emulated memory
+ */
+extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset);
+/**
+ * \brief Calls a real-mode interrupt described by the current state of the IVT.
+ * \param State Emulator State
+ * \param Interrupt BIOS Interrupt to call
+ */
+extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt);
+
+#endif
--- /dev/null
+/*
+ * Acess2 x86_64 port
+ * - Kernel Panic output
+ */
+#include <acess.h>
+
+// === PROTOTYPES ===
+void KernelPanic_SetMode(void);
+void KernelPanic_PutChar(char ch);
+
+// === GLOBALS ===
+Uint16 *gpKernelPanic_Buffer = (void*)( KERNEL_BASE|0xB8000 );
+ int giKernelPanic_CurPos = 0;
+
+// === CODE ===
+void KernelPanic_SetMode(void)
+{
+ giKernelPanic_CurPos = 0;
+}
+
+void KernelPanic_PutChar(char ch)
+{
+ switch(ch)
+ {
+ case '\n':
+ giKernelPanic_CurPos += 80;
+ case '\r':
+ giKernelPanic_CurPos /= 80;
+ giKernelPanic_CurPos *= 80;
+ break;
+
+ default:
+ if(' ' <= ch && ch <= 0x7F)
+ gpKernelPanic_Buffer[giKernelPanic_CurPos] = 0x4F00|ch;
+ giKernelPanic_CurPos ++;
+ break;
+ }
+}
--- /dev/null
+/*
+ */
+#include <acess.h>
+#include <arch.h>
+
+#define DEBUG_TO_E9 1
+#define DEBUG_TO_SERIAL 1
+#define SERIAL_PORT 0x3F8
+#define GDB_SERIAL_PORT 0x2F8
+
+
+// === IMPORTS ===
+extern int GetCPUNum(void);
+extern void *Proc_GetCurThread(void);
+
+// === GLOBALS ===
+ int gbDebug_SerialSetup = 0;
+ int gbGDB_SerialSetup = 0;
+
+// === PROTOTYPEs ===
+ int putDebugChar(char ch);
+
+// === CODE ===
+/**
+ * \brief Determine if a short spinlock is locked
+ * \param Lock Lock pointer
+ */
+int IS_LOCKED(struct sShortSpinlock *Lock)
+{
+ return !!Lock->Lock;
+}
+
+/**
+ * \brief Check if the current CPU has the lock
+ * \param Lock Lock pointer
+ */
+int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
+{
+ #if STACKED_LOCKS == 1
+ return Lock->Lock == GetCPUNum() + 1;
+ #elif STACKED_LOCKS == 2
+ return Lock->Lock == Proc_GetCurThread();
+ #else
+ return 0;
+ #endif
+}
+
+/**
+ * \brief Acquire a Short Spinlock
+ * \param Lock Lock pointer
+ *
+ * This type of mutex should only be used for very short sections of code,
+ * or in places where a Mutex_* would be overkill, such as appending
+ * an element to linked list (usually two assignement lines in C)
+ *
+ * \note This type of lock halts interrupts, so ensure that no timing
+ * functions are called while it is held. As a matter of fact, spend as
+ * little time as possible with this lock held
+ * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested
+ */
+void SHORTLOCK(struct sShortSpinlock *Lock)
+{
+ int v = 1;
+ #if LOCK_DISABLE_INTS
+ int IF;
+ #endif
+ #if STACKED_LOCKS == 1
+ int cpu = GetCPUNum() + 1;
+ #elif STACKED_LOCKS == 2
+ void *thread = Proc_GetCurThread();
+ #endif
+
+ #if LOCK_DISABLE_INTS
+ // Save interrupt state and clear interrupts
+ __ASM__ ("pushf;\n\tpop %0" : "=r"(IF));
+ IF &= 0x200; // AND out all but the interrupt flag
+ #endif
+
+ #if STACKED_LOCKS == 1
+ if( Lock->Lock == cpu ) {
+ Lock->Depth ++;
+ return ;
+ }
+ #elif STACKED_LOCKS == 2
+ if( Lock->Lock == thread ) {
+ Lock->Depth ++;
+ return ;
+ }
+ #endif
+
+ // Wait for another CPU to release
+ while(v) {
+ // CMPXCHG:
+ // If r/m32 == EAX, set ZF and set r/m32 = r32
+ // Else, clear ZF and set EAX = r/m32
+ #if STACKED_LOCKS == 1
+ __ASM__("lock cmpxchgl %2, (%3)"
+ : "=a"(v)
+ : "a"(0), "r"(cpu), "r"(&Lock->Lock)
+ );
+ #elif STACKED_LOCKS == 2
+ __ASM__("lock cmpxchgq %2, (%3)"
+ : "=a"(v)
+ : "a"(0), "r"(thread), "r"(&Lock->Lock)
+ );
+ #else
+ __ASM__("xchgl %0, (%2)":"=a"(v):"a"(1),"D"(&Lock->Lock));
+ #endif
+
+ #if LOCK_DISABLE_INTS
+ if( v ) __ASM__("sti"); // Re-enable interrupts
+ #endif
+ }
+
+ #if LOCK_DISABLE_INTS
+ __ASM__("cli");
+ Lock->IF = IF;
+ #endif
+}
+/**
+ * \brief Release a short lock
+ * \param Lock Lock pointer
+ */
+void SHORTREL(struct sShortSpinlock *Lock)
+{
+ #if STACKED_LOCKS
+ if( Lock->Depth ) {
+ Lock->Depth --;
+ return ;
+ }
+ #endif
+
+ #if LOCK_DISABLE_INTS
+ // Lock->IF can change anytime once Lock->Lock is zeroed
+ if(Lock->IF) {
+ Lock->Lock = 0;
+ __ASM__ ("sti");
+ }
+ else {
+ Lock->Lock = 0;
+ }
+ #else
+ Lock->Lock = 0;
+ #endif
+}
+
+// === DEBUG IO ===
+#if USE_GDB_STUB
+int putDebugChar(char ch)
+{
+ if(!gbGDB_SerialSetup) {
+ outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
+ outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
+ outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte)
+ outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1)
+ outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
+ outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ gbDebug_SerialSetup = 1;
+ }
+ while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 );
+ outb(GDB_SERIAL_PORT, ch);
+ return 0;
+}
+int getDebugChar(void)
+{
+ if(!gbGDB_SerialSetup) {
+ outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts
+ outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
+ outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte)
+ outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
+ outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ gbDebug_SerialSetup = 1;
+ }
+ while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ;
+ return inb(GDB_SERIAL_PORT);
+}
+#endif
+
+void Debug_PutCharDebug(char ch)
+{
+ #if DEBUG_TO_E9
+ __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) );
+ #endif
+
+ #if DEBUG_TO_SERIAL
+ if(!gbDebug_SerialSetup) {
+ outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts
+ outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud
+ outb(SERIAL_PORT + 1, 0x00); // (hi byte)
+ outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it
+ outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ gbDebug_SerialSetup = 1;
+ }
+ while( (inb(SERIAL_PORT + 5) & 0x20) == 0 );
+ outb(SERIAL_PORT, ch);
+ #endif
+}
+
+void Debug_PutStringDebug(const char *String)
+{
+ while(*String)
+ Debug_PutCharDebug(*String++);
+}
+
+// === PORT IO ===
+void outb(Uint16 Port, Uint8 Data)
+{
+ __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data));
+}
+void outw(Uint16 Port, Uint16 Data)
+{
+ __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data));
+}
+void outd(Uint16 Port, Uint32 Data)
+{
+ __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data));
+}
+Uint8 inb(Uint16 Port)
+{
+ Uint8 ret;
+ __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port));
+ return ret;
+}
+Uint16 inw(Uint16 Port)
+{
+ Uint16 ret;
+ __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port));
+ return ret;
+}
+Uint32 ind(Uint16 Port)
+{
+ Uint32 ret;
+ __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port));
+ return ret;
+}
+
+// === Endianness ===
+/*
+Uint32 BigEndian32(Uint32 Value)
+{
+ Uint32 ret;
+ ret = (Value >> 24);
+ ret |= ((Value >> 16) & 0xFF) << 8;
+ ret |= ((Value >> 8) & 0xFF) << 16;
+ ret |= ((Value >> 0) & 0xFF) << 24;
+ return ret;
+}
+
+Uint16 BigEndian16(Uint16 Value)
+{
+ return (Value>>8)|(Value<<8);
+}
+*/
+
+// === Memory Manipulation ===
+int memcmp(const void *__dest, const void *__src, size_t __count)
+{
+ if( ((tVAddr)__dest & 7) != ((tVAddr)__src & 7) ) {
+ const Uint8 *src = __src, *dst = __dest;
+ while(__count)
+ {
+ if( *src != *dst )
+ return *dst - *src;
+ src ++; dst ++; __count --;
+ }
+ return 0;
+ }
+ else {
+ const Uint8 *src = __src;
+ const Uint8 *dst = __dest;
+ const Uint64 *src64, *dst64;
+
+ while( (tVAddr)src & 7 && __count ) {
+ if( *src != *dst )
+ return *dst - *src;
+ dst ++; src ++; __count --;
+ }
+
+ src64 = (void*)src;
+ dst64 = (void*)dst;
+
+ while( __count >= 8 )
+ {
+ if( *src64 != *dst64 )
+ {
+ src = (void*)src64;
+ dst = (void*)dst64;
+ if(src[0] != dst[0]) return dst[0]-src[0];
+ if(src[1] != dst[1]) return dst[1]-src[1];
+ if(src[2] != dst[2]) return dst[2]-src[2];
+ if(src[3] != dst[3]) return dst[3]-src[3];
+ if(src[4] != dst[4]) return dst[4]-src[4];
+ if(src[5] != dst[5]) return dst[5]-src[5];
+ if(src[6] != dst[6]) return dst[6]-src[6];
+ if(src[7] != dst[7]) return dst[7]-src[7];
+ return -1; // This should never happen
+ }
+ __count -= 8;
+ src64 ++;
+ dst64 ++;
+ }
+
+ src = (void*)src64;
+ dst = (void*)dst64;
+ while( __count-- )
+ {
+ if(*dst != *src) return *dst - *src;
+ dst ++;
+ src ++;
+ }
+ }
+ return 0;
+}
+
+void *memcpy(void *__dest, const void *__src, size_t __count)
+{
+ tVAddr dst = (tVAddr)__dest, src = (tVAddr)__src;
+ if( (dst & 7) != (src & 7) )
+ {
+ __asm__ __volatile__ ("rep movsb" : : "D"(dst),"S"(src),"c"(__count));
+ }
+ else
+ {
+ while( (src & 7) && __count ) {
+ *(char*)dst++ = *(char*)src++;
+ __count --;
+ }
+
+ __asm__ __volatile__ ("rep movsq" : "=D"(dst),"=S"(src) : "0"(dst),"1"(src),"c"(__count/8));
+ __count = __count & 7;
+ while( __count-- )
+ *(char*)dst++ = *(char*)src++;
+ }
+ return __dest;
+}
+
+void *memset(void *__dest, int __val, size_t __count)
+{
+ if( __val != 0 || ((tVAddr)__dest & 7) != 0 )
+ __asm__ __volatile__ ("rep stosb" : : "D"(__dest),"a"(__val),"c"(__count));
+ else {
+ Uint8 *dst = __dest;
+
+ __asm__ __volatile__ ("rep stosq" : : "D"(dst),"a"(0),"c"(__count/8));
+ dst += __count & ~7;
+ __count = __count & 7;
+ while( __count-- )
+ *dst++ = 0;
+ }
+ return __dest;
+}
+
+void *memsetd(void *__dest, Uint32 __val, size_t __count)
+{
+ __asm__ __volatile__ ("rep stosl" : : "D"(__dest),"a"(__val),"c"(__count));
+ return __dest;
+}
+
+Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem)
+{
+ Uint64 ret, rem;
+ __asm__ __volatile__(
+ "div %4"
+ : "=a" (ret), "=d" (rem)
+ : "a" ( Num ), "d" (0), "r" (Den)
+ );
+ if(Rem) *Rem = rem;
+ return ret;
+}
+
--- /dev/null
+/*
+ * Acess2 x86_64 Kernel
+ * Linker Script
+ */
+
+/* _kernel_base = 0xFFFF800000000000; */
+/* -2 GiB */
+_kernel_base = 0xFFFFFFFF80000000;
+
+/*
+OUTPUT_FORMAT(elf32-i386)
+OUTPUT_ARCH(i386:x86-64)
+*/
+OUTPUT_FORMAT(elf64-x86-64)
+ENTRY(start)
+
+SECTIONS {
+ . = 0x100000;
+ gKernelBase = .;
+ . += SIZEOF_HEADERS;
+ __load_addr = .;
+ .multiboot : AT(ADDR(.multiboot)) {
+ *(.multiboot)
+ }
+
+ . += _kernel_base;
+
+ .text ALIGN(0x1000): AT(ADDR(.text) - _kernel_base) {
+ *(.text)
+ }
+
+ .usertext ALIGN(0x1000): AT(ADDR(.usertext) - _kernel_base) {
+ _UsertextBase = .;
+ *(.usertext)
+ _UsertextEnd = .;
+ }
+
+ .rodata ALIGN(0x1000): AT(ADDR(.rodata) - _kernel_base) {
+ *(.initpd)
+ *(.rodata .rodata.*)
+ *(.rdata)
+
+ . = ALIGN(0x10);
+ gKernelModules = .;
+ *(KMODULES)
+ gKernelModulesEnd = .;
+ . = ALIGN(0x10);
+ gKernelSymbols = .;
+ *(KEXPORT)
+ gKernelSymbolsEnd = .;
+ }
+
+ .data ALIGN (0x1000) : AT(ADDR(.data) - _kernel_base) {
+ *(.padata)
+ *(.data)
+ }
+
+ __bss_start = .;
+ .bss : AT(ADDR(.bss) - _kernel_base) {
+ *(COMMON)
+ *(.bss)
+ }
+ gKernelEnd = (. + 0xFFF)&0xFFFFFFFFFFFFF000;
+}
--- /dev/null
+/*
+ * Acess2 x86_64 Project
+ */
+#include <acess.h>
+#include <mboot.h>
+#include <init.h>
+
+// === IMPORTS ===
+extern void Desctab_Init(void);
+extern void MM_InitVirt(void);
+extern void Heap_Install(void);
+extern void Threads_Init(void);
+extern int Time_Setup(void);
+extern void System_Init(char *Commandline);
+
+extern void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
+
+// === PROTOTYPES ===
+void kmain(Uint MbMagic, void *MbInfoPtr);
+
+// === GLOBALS ==
+char *gsBootCmdLine = NULL;
+
+// === CODE ===
+void kmain(Uint MbMagic, void *MbInfoPtr)
+{
+ tMBoot_Info *mbInfo;
+
+ LogF("Acess2 x86_64 v"EXPAND_STR(KERNEL_VERSION)"\n");
+ LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash);
+
+ Desctab_Init();
+
+ MM_InitVirt();
+ *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'C';
+
+ switch(MbMagic)
+ {
+ // Multiboot 1
+ case MULTIBOOT_MAGIC:
+ // Adjust Multiboot structure address
+ mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE );
+ gsBootCmdLine = (char*)( (Uint)mbInfo->CommandLine + KERNEL_BASE);
+ MM_InitPhys_Multiboot( mbInfo ); // Set up physical memory manager
+ break;
+ default:
+ Panic("Multiboot magic invalid %08x, expected %08x\n",
+ MbMagic, MULTIBOOT_MAGIC);
+ return ;
+ }
+
+ Log("gsBootCmdLine = '%s'", gsBootCmdLine);
+
+ *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'D';
+ Heap_Install();
+
+ *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'E';
+ Threads_Init();
+
+ Time_Setup();
+ *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'F';
+
+ // Load Virtual Filesystem
+ Log_Log("Arch", "Starting VFS...");
+ VFS_Init();
+
+ *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'Z';
+
+ // Pass on to Independent Loader
+ Log_Log("Arch", "Starting system");
+ System_Init(gsBootCmdLine);
+
+ // Sleep forever (sleeping beauty)
+ for(;;)
+ Threads_Sleep();
+}
+
+void Arch_LoadBootModules(void)
+{
+
+}
+
+void StartupPrint(const char *String)
+{
+
+}
--- /dev/null
+/*
+ * Acess2 x86_64 Port
+ *
+ * Physical Memory Manager
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <mboot.h>
+#include <mm_virt.h>
+
+#define TRACE_REF 0
+
+enum eMMPhys_Ranges
+{
+ MM_PHYS_16BIT, // Does anything need this?
+ MM_PHYS_20BIT, // Real-Mode
+ MM_PHYS_24BIT, // ISA DMA
+ MM_PHYS_32BIT, // x86 Hardware
+ MM_PHYS_MAX, // Doesn't care
+ NUM_MM_PHYS_RANGES
+};
+
+// === IMPORTS ===
+extern char gKernelBase[];
+extern char gKernelEnd[];
+
+// === PROTOTYPES ===
+void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
+//tPAddr MM_AllocPhysRange(int Num, int Bits);
+//tPAddr MM_AllocPhys(void);
+//void MM_RefPhys(tPAddr PAddr);
+//void MM_DerefPhys(tPAddr PAddr);
+ int MM_int_GetRangeID( tPAddr Addr );
+
+// === MACROS ===
+#define PAGE_ALLOC_TEST(__page) (gaMainBitmap[(__page)>>6] & (1ULL << ((__page)&63)))
+#define PAGE_ALLOC_SET(__page) do{gaMainBitmap[(__page)>>6] |= (1ULL << ((__page)&63));}while(0)
+#define PAGE_ALLOC_CLEAR(__page) do{gaMainBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0)
+//#define PAGE_MULTIREF_TEST(__page) (gaMultiBitmap[(__page)>>6] & (1ULL << ((__page)&63)))
+//#define PAGE_MULTIREF_SET(__page) do{gaMultiBitmap[(__page)>>6] |= 1ULL << ((__page)&63);}while(0)
+//#define PAGE_MULTIREF_CLEAR(__page) do{gaMultiBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0)
+
+// === GLOBALS ===
+tMutex glPhysicalPages;
+Uint64 *gaSuperBitmap = (void*)MM_PAGE_SUPBMP; // 1 bit = 64 Pages, 16 MiB per Word
+Uint64 *gaMainBitmap = (void*)MM_PAGE_BITMAP; // 1 bit = 1 Page, 256 KiB per Word
+Uint64 *gaMultiBitmap = (void*)MM_PAGE_DBLBMP; // Each bit means that the page is being used multiple times
+Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
+void **gapPageNodes = (void*)MM_PAGE_NODES; // Reference Counts
+tPAddr giFirstFreePage; // First possibly free page
+Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range
+Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range
+Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range
+Uint64 giMaxPhysPage = 0; // Maximum Physical page
+// Only used in init, allows the init code to provide pages for use by
+// the allocator before the bitmaps exist.
+// 3 entries because the are three calls to MM_AllocPhys in MM_Map
+#define NUM_STATIC_ALLOC 3
+tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0};
+
+// === CODE ===
+/**
+ * \brief Initialise the physical memory map using a Multiboot 1 map
+ */
+void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
+{
+ tMBoot_MMapEnt *mmapStart;
+ tMBoot_MMapEnt *ent;
+ Uint64 maxAddr = 0;
+ int numPages, superPages;
+ int i;
+ Uint64 base, size;
+ tVAddr vaddr;
+ tPAddr paddr, firstFreePage;
+
+ ENTER("pMBoot=%p", MBoot);
+
+ // Scan the physical memory map
+ // Looking for the top of physical memory
+ mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
+ LOG("mmapStart = %p", mmapStart);
+ ent = mmapStart;
+ while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
+ {
+ // Adjust for the size of the entry
+ ent->Size += 4;
+ LOG("ent={Type:%i,Base:0x%x,Length:%x",
+ ent->Type, ent->Base, ent->Length);
+
+ // If entry is RAM and is above `maxAddr`, change `maxAddr`
+ if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
+ maxAddr = ent->Base + ent->Length;
+
+ // Go to next entry
+ ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
+ }
+
+ // Did we find a valid end?
+ if(maxAddr == 0) {
+ // No, darn, let's just use the HighMem hack
+ giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
+ }
+ else {
+ // Goodie, goodie gumdrops
+ giMaxPhysPage = maxAddr >> 12;
+ }
+ LOG("giMaxPhysPage = 0x%x", giMaxPhysPage);
+
+ // Find a contigous section of memory to hold it in
+ // - Starting from the end of the kernel
+ // - We also need a region for the super bitmap
+ superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
+ numPages = (giMaxPhysPage + 7) / 8;
+ numPages = (numPages + 0xFFF) >> 12;
+ LOG("numPages = %i, superPages = %i", numPages, superPages);
+ if(maxAddr == 0)
+ {
+ int todo = numPages*2 + superPages;
+ // Ok, naieve allocation, just put it after the kernel
+ // - Allocated Bitmap
+ vaddr = MM_PAGE_BITMAP;
+ paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
+ while(todo )
+ {
+ // Allocate statics
+ for( i = 0; i < NUM_STATIC_ALLOC; i++) {
+ if(gaiStaticAllocPages[i] != 0) continue;
+ gaiStaticAllocPages[i] = paddr;
+ paddr += 0x1000;
+ }
+
+ MM_Map(vaddr, paddr);
+ vaddr += 0x1000;
+ paddr += 0x1000;
+
+ todo --;
+
+ if( todo == numPages + superPages )
+ vaddr = MM_PAGE_DBLBMP;
+ if( todo == superPages )
+ vaddr = MM_PAGE_SUPBMP;
+ }
+ }
+ // Scan for a nice range
+ else
+ {
+ int todo = numPages*2 + superPages;
+ paddr = 0;
+ vaddr = MM_PAGE_BITMAP;
+ // Scan!
+ for(
+ ent = mmapStart;
+ (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
+ ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
+ )
+ {
+ int avail;
+
+ // RAM only please
+ if( ent->Type != 1 )
+ continue;
+
+ // Let's not put it below the kernel, shall we?
+ if( ent->Base + ent->Size < (tPAddr)&gKernelBase )
+ continue;
+
+ LOG("%x <= %x && %x > %x",
+ ent->Base, (tPAddr)&gKernelBase,
+ ent->Base + ent->Size, (tPAddr)&gKernelEnd - KERNEL_BASE
+ );
+ // Check if the kernel is in this range
+ if( ent->Base <= (tPAddr)&gKernelBase
+ && ent->Base + ent->Length > (tPAddr)&gKernelEnd - KERNEL_BASE )
+ {
+ avail = ent->Length >> 12;
+ avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12;
+ paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
+ }
+ // No? then we can use all of the block
+ else
+ {
+ avail = ent->Length >> 12;
+ paddr = ent->Base;
+ }
+
+ Log("MM_InitPhys_Multiboot: paddr=0x%x, avail=0x%x pg", paddr, avail);
+
+ // Map
+ while( todo && avail --)
+ {
+ // Static Allocations
+ for( i = 0; i < NUM_STATIC_ALLOC && avail; i++) {
+ if(gaiStaticAllocPages[i] != 0) continue;
+ gaiStaticAllocPages[i] = paddr;
+ paddr += 0x1000;
+ avail --;
+ }
+ if(!avail) break;
+
+ // Map
+ MM_Map(vaddr, paddr);
+ todo --;
+ vaddr += 0x1000;
+ paddr += 0x1000;
+
+ // Alter the destination address when needed
+ if(todo == superPages+numPages)
+ vaddr = MM_PAGE_DBLBMP;
+ if(todo == superPages)
+ vaddr = MM_PAGE_SUPBMP;
+ }
+
+ // Fast quit if there's nothing left to allocate
+ if( !todo ) break;
+ }
+ }
+ // Save the current value of paddr to simplify the allocation later
+ firstFreePage = paddr;
+
+ LOG("Clearing multi bitmap");
+ // Fill the bitmaps
+ memset(gaMultiBitmap, 0, (numPages<<12)/8);
+ // - initialise to one, then clear the avaliable areas
+ memset(gaMainBitmap, -1, (numPages<<12)/8);
+ memset(gaSuperBitmap, -1, (numPages<<12)/(8*64));
+ LOG("Setting main bitmap");
+ // - Clear all Type=1 areas
+ LOG("Clearing valid regions");
+ for(
+ ent = mmapStart;
+ (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
+ ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
+ )
+ {
+ // Check if the type is RAM
+ if(ent->Type != 1) continue;
+
+ // Main bitmap
+ base = ent->Base >> 12;
+ size = ent->Size >> 12;
+
+ if(base & 63) {
+ Uint64 val = -1LL << (base & 63);
+ gaMainBitmap[base / 64] &= ~val;
+ size -= (base & 63);
+ base += 64 - (base & 63);
+ }
+ memset( &gaMainBitmap[base / 64], 0, size/8 );
+ if( size & 7 ) {
+ Uint64 val = -1LL << (size & 7);
+ val <<= (size/8)&7;
+ gaMainBitmap[base / 64] &= ~val;
+ }
+
+ // Super Bitmap
+ base = ent->Base >> 12;
+ size = ent->Size >> 12;
+ size = (size + (base & 63) + 63) >> 6;
+ base = base >> 6;
+ if(base & 63) {
+ Uint64 val = -1LL << (base & 63);
+ gaSuperBitmap[base / 64] &= ~val;
+// size -= (base & 63);
+// base += 64 - (base & 63);
+ }
+ }
+
+ // Reference the used pages
+ base = (tPAddr)&gKernelBase >> 12;
+ size = firstFreePage >> 12;
+ memset( &gaMainBitmap[base / 64], -1, size/8 );
+ if( size & 7 ) {
+ Uint64 val = -1LL << (size & 7);
+ val <<= (size/8)&7;
+ gaMainBitmap[base / 64] |= val;
+ }
+
+ // Free the unused static allocs
+ for( i = 0; i < NUM_STATIC_ALLOC; i++) {
+ if(gaiStaticAllocPages[i] != 0)
+ continue;
+ gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ]
+ &= ~(1LL << ((gaiStaticAllocPages[i]>>12)&63));
+ }
+
+ // Fill the super bitmap
+ LOG("Filling super bitmap");
+ memset(gaSuperBitmap, 0, superPages<<12);
+ for( base = 0; base < (size+63)/64; base ++)
+ {
+ if( gaMainBitmap[ base ] + 1 == 0 )
+ gaSuperBitmap[ base/64 ] |= 1LL << (base&63);
+ }
+
+ // Set free page counts
+ for( base = 1; base < giMaxPhysPage; base ++ )
+ {
+ int rangeID;
+ // Skip allocated
+ if( gaMainBitmap[ base >> 6 ] & (1LL << (base&63)) ) continue;
+
+ // Get range ID
+ rangeID = MM_int_GetRangeID( base << 12 );
+
+ // Increment free page count
+ giPhysRangeFree[ rangeID ] ++;
+
+ // Check for first free page in range
+ if(giPhysRangeFirst[ rangeID ] == 0)
+ giPhysRangeFirst[ rangeID ] = base;
+ // Set last (when the last free page is reached, this won't be
+ // updated anymore, hence will be correct)
+ giPhysRangeLast[ rangeID ] = base;
+ }
+
+ LEAVE('-');
+}
+
+/**
+ * \brief Allocate a contiguous range of physical pages with a maximum
+ * bit size of \a MaxBits
+ * \param Pages Number of pages to allocate
+ * \param MaxBits Maximum size of the physical address
+ * \note If \a MaxBits is <= 0, any sized address is used (with preference
+ * to higher addresses)
+ */
+tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
+{
+ tPAddr addr, ret;
+ int rangeID;
+ int nFree = 0, i;
+
+ ENTER("iPages iBits", Pages, MaxBits);
+
+ if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case
+ rangeID = MM_PHYS_MAX;
+ else
+ rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 );
+
+ LOG("rangeID = %i", rangeID);
+
+ Mutex_Acquire(&glPhysicalPages);
+
+ // Check if the range actually has any free pages
+ while(giPhysRangeFree[rangeID] == 0 && rangeID)
+ rangeID --;
+
+ LOG("rangeID = %i", rangeID);
+
+ // What the? Oh, man. No free pages
+ if(giPhysRangeFree[rangeID] == 0) {
+ Mutex_Release(&glPhysicalPages);
+ // TODO: Page out
+ // ATM. Just Warning
+ Warning(" MM_AllocPhysRange: Out of free pages");
+ Log_Warning("Arch",
+ "Out of memory (unable to fulfil request for %i pages), zero remaining",
+ Pages
+ );
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Check if there is enough in the range
+ if(giPhysRangeFree[rangeID] >= Pages)
+ {
+ LOG("{%i,0x%x -> 0x%x}",
+ giPhysRangeFree[rangeID],
+ giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID]
+ );
+ // Do a cheap scan, scanning upwards from the first free page in
+ // the range
+ nFree = 0;
+ addr = giPhysRangeFirst[ rangeID ];
+ while( addr <= giPhysRangeLast[ rangeID ] )
+ {
+ //Log(" MM_AllocPhysRange: addr = 0x%x", addr);
+ // Check the super bitmap
+ if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) {
+ LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr += 1LL << (6+6);
+ addr &= ~0xFFF; // (1LL << 6+6) - 1
+ continue;
+ }
+ // Check page block (64 pages)
+ if( gaMainBitmap[addr >> 6] + 1 == 0) {
+ LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr += 1LL << (6);
+ addr &= ~0x3F;
+ continue;
+ }
+ // Check individual page
+ if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) {
+ LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr ++;
+ continue;
+ }
+ nFree ++;
+ addr ++;
+ LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr);
+ if(nFree == Pages)
+ break;
+ }
+ LOG("nFree = %i", nFree);
+ // If we don't find a contiguous block, nFree will not be equal
+ // to Num, so we set it to zero and do the expensive lookup.
+ if(nFree != Pages) nFree = 0;
+ }
+
+ if( !nFree )
+ {
+ // Oops. ok, let's do an expensive check (scan down the list
+ // until a free range is found)
+// nFree = 1;
+// addr = giPhysRangeLast[ rangeID ];
+ // TODO: Expensive Check
+ Mutex_Release(&glPhysicalPages);
+ // TODO: Page out
+ // ATM. Just Warning
+ Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
+ Log_Warning("Arch",
+ "Out of memory (unable to fulfil request for %i pages)",
+ Pages
+ );
+ LEAVE('i', 0);
+ return 0;
+ }
+ LOG("nFree = %i, addr = 0x%08x", nFree, addr);
+
+ // Mark pages as allocated
+ addr -= Pages;
+ for( i = 0; i < Pages; i++, addr++ )
+ {
+ gaMainBitmap[addr >> 6] |= 1LL << (addr & 63);
+ if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) )
+ gaiPageReferences[addr] = 1;
+// Log("page %P refcount = %i", MM_GetRefCount(addr<<12));
+ rangeID = MM_int_GetRangeID(addr << 12);
+ giPhysRangeFree[ rangeID ] --;
+ LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]);
+ if(addr == giPhysRangeFirst[ rangeID ])
+ giPhysRangeFirst[ rangeID ] += 1;
+ }
+ addr -= Pages;
+ ret = addr; // Save the return address
+
+ // Update super bitmap
+ Pages += addr & (64-1);
+ addr &= ~(64-1);
+ Pages = (Pages + (64-1)) & ~(64-1);
+ for( i = 0; i < Pages/64; i++ )
+ {
+ if( gaMainBitmap[ addr >> 6 ] + 1 == 0 )
+ gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63);
+ }
+
+ Mutex_Release(&glPhysicalPages);
+ #if TRACE_REF
+ Log("MM_AllocPhysRange: ret = %P (Ref %i)", ret << 12, MM_GetRefCount(ret<<12));
+ #endif
+ LEAVE('x', ret << 12);
+ return ret << 12;
+}
+
+/**
+ * \brief Allocate a single physical page, with no preference as to address
+ * size.
+ */
+tPAddr MM_AllocPhys(void)
+{
+ int i;
+
+ // Hack to allow allocation during setup
+ for(i = 0; i < NUM_STATIC_ALLOC; i++) {
+ if( gaiStaticAllocPages[i] ) {
+ tPAddr ret = gaiStaticAllocPages[i];
+ gaiStaticAllocPages[i] = 0;
+ Log("MM_AllocPhys: Return %P, static alloc %i", ret, i);
+ return ret;
+ }
+ }
+
+ return MM_AllocPhysRange(1, -1);
+}
+
+/**
+ * \brief Reference a physical page
+ */
+void MM_RefPhys(tPAddr PAddr)
+{
+ Uint64 page = PAddr >> 12;
+
+ if( page > giMaxPhysPage ) return ;
+
+ if( PAGE_ALLOC_TEST(page) )
+ {
+ tVAddr ref_base = ((tVAddr)&gaiPageReferences[ page ]) & ~0xFFF;
+ // Allocate reference page
+ if( !MM_GetPhysAddr(ref_base) )
+ {
+ const int pages_per_refpage = PAGE_SIZE/sizeof(gaiPageReferences[0]);
+ int i;
+ int page_base = page / pages_per_refpage * pages_per_refpage;
+ if( !MM_Allocate( ref_base ) ) {
+ Log_Error("Arch", "Out of memory when allocating reference count page");
+ return ;
+ }
+ // Fill block
+ Log("Allocated references for %P-%P", page_base << 12, (page_base+pages_per_refpage)<<12);
+ for( i = 0; i < pages_per_refpage; i ++ ) {
+ int pg = page_base + i;
+ gaiPageReferences[pg] = !!PAGE_ALLOC_TEST(pg);
+ }
+ }
+ gaiPageReferences[page] ++;
+ }
+ else
+ {
+ // Allocate
+ PAGE_ALLOC_SET(page);
+ if( gaMainBitmap[page >> 6] + 1 == 0 )
+ gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63);
+ if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) )
+ gaiPageReferences[page] = 1;
+ }
+
+ #if TRACE_REF
+ Log("MM_RefPhys: %P referenced (%i)", page << 12, MM_GetRefCount(page << 12));
+ #endif
+}
+
+/**
+ * \brief Dereference a physical page
+ */
+void MM_DerefPhys(tPAddr PAddr)
+{
+ Uint64 page = PAddr >> 12;
+
+ if( PAddr >> 12 > giMaxPhysPage ) return ;
+
+ if( MM_GetPhysAddr( (tVAddr) &gaiPageReferences[page] ) )
+ {
+ gaiPageReferences[ page ] --;
+ if( gaiPageReferences[ page ] == 0 )
+ PAGE_ALLOC_CLEAR(page);
+ }
+ else
+ PAGE_ALLOC_CLEAR(page);
+
+ // Update the free counts if the page was freed
+ if( !PAGE_ALLOC_TEST(page) )
+ {
+ int rangeID;
+ rangeID = MM_int_GetRangeID( PAddr );
+ giPhysRangeFree[ rangeID ] ++;
+ if( giPhysRangeFirst[rangeID] > page )
+ giPhysRangeFirst[rangeID] = page;
+ if( giPhysRangeLast[rangeID] < page )
+ giPhysRangeLast[rangeID] = page;
+ }
+
+ // If the bitmap entry is not -1, unset the bit in the super bitmap
+ if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) {
+ gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63));
+ }
+
+ #if TRACE_REF
+ Log("Page %P dereferenced (%i)", page << 12, MM_GetRefCount(page << 12));
+ #endif
+}
+
+int MM_GetRefCount( tPAddr PAddr )
+{
+ PAddr >>= 12;
+
+ if( PAddr > giMaxPhysPage ) return 0;
+
+ if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) {
+ return gaiPageReferences[PAddr];
+ }
+
+ if( PAGE_ALLOC_TEST(PAddr) )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \brief Takes a physical address and returns the ID of its range
+ * \param Addr Physical address of page
+ * \return Range ID from eMMPhys_Ranges
+ */
+int MM_int_GetRangeID( tPAddr Addr )
+{
+ if(Addr >> 32)
+ return MM_PHYS_MAX;
+ else if(Addr >> 24)
+ return MM_PHYS_32BIT;
+ else if(Addr >> 20)
+ return MM_PHYS_24BIT;
+ else if(Addr >> 16)
+ return MM_PHYS_20BIT;
+ else
+ return MM_PHYS_16BIT;
+}
+
+int MM_SetPageNode(tPAddr PAddr, void *Node)
+{
+ tPAddr page = PAddr >> 12;
+ tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1);
+
+// if( !MM_GetRefCount(PAddr) ) return 1;
+
+ if( !MM_GetPhysAddr(node_page) ) {
+ if( !MM_Allocate(node_page) )
+ return -1;
+ memset( (void*)node_page, 0, PAGE_SIZE );
+ }
+
+ gapPageNodes[page] = Node;
+ return 0;
+}
+
+int MM_GetPageNode(tPAddr PAddr, void **Node)
+{
+// if( !MM_GetRefCount(PAddr) ) return 1;
+ PAddr >>= 12;
+
+ if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) {
+ *Node = NULL;
+ return 0;
+ }
+
+ *Node = gapPageNodes[PAddr];
+ return 0;
+}
+
--- /dev/null
+/*
+ * Acess2 x86_64 Port
+ *
+ * Virtual Memory Manager
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <mm_virt.h>
+#include <threads_int.h>
+#include <proc.h>
+#include <hal_proc.h>
+
+// === DEBUG OPTIONS ===
+#define TRACE_COW 0
+
+// === CONSTANTS ===
+#define PHYS_BITS 52 // TODO: Move out
+#define VIRT_BITS 48
+
+#define PML4_SHIFT 39
+#define PDP_SHIFT 30
+#define PDIR_SHIFT 21
+#define PTAB_SHIFT 12
+
+#define PADDR_MASK 0x7FFFFFFF##FFFFF000
+#define PAGE_MASK ((1LL << 36)-1)
+#define TABLE_MASK ((1LL << 27)-1)
+#define PDP_MASK ((1LL << 18)-1)
+#define PML4_MASK ((1LL << 9)-1)
+
+#define PF_PRESENT 0x001
+#define PF_WRITE 0x002
+#define PF_USER 0x004
+#define PF_LARGE 0x080
+#define PF_GLOBAL 0x100
+#define PF_COW 0x200
+#define PF_PAGED 0x400
+#define PF_NX 0x80000000##00000000
+
+// === MACROS ===
+#define PAGETABLE(idx) (*((Uint64*)MM_FRACTAL_BASE+((idx)&PAGE_MASK)))
+#define PAGEDIR(idx) PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK))
+#define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK))
+#define PAGEMAPLVL4(idx) PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK))
+
+#define TMPCR3() PAGEMAPLVL4(MM_TMPFRAC_BASE>>39)
+#define TMPTABLE(idx) (*((Uint64*)MM_TMPFRAC_BASE+((idx)&PAGE_MASK)))
+#define TMPDIR(idx) PAGETABLE((MM_TMPFRAC_BASE>>12)+((idx)&TABLE_MASK))
+#define TMPDIRPTR(idx) PAGEDIR((MM_TMPFRAC_BASE>>21)+((idx)&PDP_MASK))
+#define TMPMAPLVL4(idx) PAGEDIRPTR((MM_TMPFRAC_BASE>>30)+((idx)&PML4_MASK))
+
+#define INVLPG(__addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr))
+#define INVLPG_ALL() __asm__ __volatile__ ("mov %cr3,%rax;\n\tmov %rax,%cr3;")
+#define INVLPG_GLOBAL() __asm__ __volatile__ ("mov %cr4,%rax;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4")
+
+// === CONSTS ===
+//tPAddr * const gaPageTable = MM_FRACTAL_BASE;
+
+// === IMPORTS ===
+extern void Error_Backtrace(Uint IP, Uint BP);
+extern tPAddr gInitialPML4[512];
+extern void Threads_SegFault(tVAddr Addr);
+extern char _UsertextBase[];
+
+// === PROTOTYPES ===
+void MM_InitVirt(void);
+//void MM_FinishVirtualInit(void);
+void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable );
+ int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
+void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected);
+//void MM_DumpTables(tVAddr Start, tVAddr End);
+ int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer);
+ int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge);
+// int MM_Map(tVAddr VAddr, tPAddr PAddr);
+void MM_Unmap(tVAddr VAddr);
+void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts);
+//void MM_ClearUser(void);
+ int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags);
+
+// === GLOBALS ===
+tMutex glMM_TempFractalLock;
+tPAddr gMM_ZeroPage;
+
+// === CODE ===
+void MM_InitVirt(void)
+{
+// Log_Debug("MMVirt", "&PAGEMAPLVL4(0) = %p", &PAGEMAPLVL4(0));
+// MM_DumpTables(0, -1L);
+}
+
+void MM_FinishVirtualInit(void)
+{
+ PAGEMAPLVL4(0) = 0;
+}
+
+/**
+ * \brief Clone a page from an entry
+ * \param Ent Pointer to the entry in the PML4/PDP/PD/PT
+ * \param NextLevel Pointer to contents of the entry
+ * \param Addr Dest address
+ * \note Used in COW
+ */
+void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable )
+{
+ tPAddr curpage = *Ent & PADDR_MASK;
+ int bCopied = 0;
+
+ if( MM_GetRefCount( curpage ) <= 0 ) {
+ Log_KernelPanic("MMVirt", "Page %P still marked COW, but unreferenced", curpage);
+ }
+ if( MM_GetRefCount( curpage ) == 1 )
+ {
+ *Ent &= ~PF_COW;
+ *Ent |= PF_PRESENT|PF_WRITE;
+ #if TRACE_COW
+ Log_Debug("MMVirt", "COW ent at %p (%p) only %P", Ent, NextLevel, curpage);
+ #endif
+ }
+ else
+ {
+ void *tmp;
+ tPAddr paddr;
+
+ if( !(paddr = MM_AllocPhys()) ) {
+ Threads_SegFault(Addr);
+ return ;
+ }
+
+ ASSERT(paddr != curpage);
+
+ tmp = (void*)MM_MapTemp(paddr);
+ memcpy( tmp, NextLevel, 0x1000 );
+ MM_FreeTemp( (tVAddr)tmp );
+
+ #if TRACE_COW
+ Log_Debug("MMVirt", "COW ent at %p (%p) from %P to %P", Ent, NextLevel, curpage, paddr);
+ #endif
+
+ MM_DerefPhys( curpage );
+ *Ent &= PF_USER;
+ *Ent |= paddr|PF_PRESENT|PF_WRITE;
+
+ bCopied = 1;
+ }
+ INVLPG( (tVAddr)NextLevel );
+
+ // Mark COW on contents if it's a PDPT, Dir or Table
+ if(bTable)
+ {
+ Uint64 *dp = NextLevel;
+ int i;
+ for( i = 0; i < 512; i ++ )
+ {
+ if( !(dp[i] & PF_PRESENT) )
+ continue;
+
+ if( bCopied )
+ MM_RefPhys( dp[i] & PADDR_MASK );
+ if( dp[i] & PF_WRITE ) {
+ dp[i] &= ~PF_WRITE;
+ dp[i] |= PF_COW;
+ }
+ }
+ }
+}
+
+/*
+ * \brief Called on a page fault
+ */
+int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
+{
+// Log_Debug("MMVirt", "Addr = %p, ErrorCode = %x", Addr, ErrorCode);
+
+ // Catch reserved bits first
+ if( ErrorCode & 0x8 )
+ {
+ Log_Warning("MMVirt", "Reserved bits trashed!");
+ Log_Warning("MMVirt", "PML4 Ent = %P", PAGEMAPLVL4(Addr>>39));
+ if( !(PAGEMAPLVL4(Addr>>39) & PF_PRESENT) ) goto print_done;
+ Log_Warning("MMVirt", "PDP Ent = %P", PAGEDIRPTR(Addr>>30));
+ if( !(PAGEDIRPTR(Addr>>30) & PF_PRESENT) ) goto print_done;
+ Log_Warning("MMVirt", "PDir Ent = %P", PAGEDIR(Addr>>21));
+ if( !(PAGEDIR(Addr>>21) & PF_PRESENT) ) goto print_done;
+ Log_Warning("MMVirt", "PTable Ent = %P", PAGETABLE(Addr>>12));
+ if( !(PAGETABLE(Addr>>12) & PF_PRESENT) ) goto print_done;
+ print_done:
+
+ for(;;);
+ }
+
+ // TODO: Implement Copy-on-Write
+ #if 1
+ if( PAGEMAPLVL4(Addr>>39) & PF_PRESENT
+ && PAGEDIRPTR (Addr>>30) & PF_PRESENT
+ && PAGEDIR (Addr>>21) & PF_PRESENT
+ && PAGETABLE (Addr>>12) & PF_PRESENT )
+ {
+ // PML4 Entry
+ if( PAGEMAPLVL4(Addr>>39) & PF_COW )
+ {
+ tPAddr *dp = &PAGEDIRPTR((Addr>>39)*512);
+ MM_int_ClonePageEnt( &PAGEMAPLVL4(Addr>>39), dp, Addr, 1 );
+// MM_DumpTables(Addr>>39 << 39, (((Addr>>39) + 1) << 39) - 1);
+ }
+ // PDP Entry
+ if( PAGEDIRPTR(Addr>>30) & PF_COW )
+ {
+ tPAddr *dp = &PAGEDIR( (Addr>>30)*512 );
+ MM_int_ClonePageEnt( &PAGEDIRPTR(Addr>>30), dp, Addr, 1 );
+// MM_DumpTables(Addr>>30 << 30, (((Addr>>30) + 1) << 30) - 1);
+ }
+ // PD Entry
+ if( PAGEDIR(Addr>>21) & PF_COW )
+ {
+ tPAddr *dp = &PAGETABLE( (Addr>>21)*512 );
+ MM_int_ClonePageEnt( &PAGEDIR(Addr>>21), dp, Addr, 1 );
+// MM_DumpTables(Addr>>21 << 21, (((Addr>>21) + 1) << 21) - 1);
+ }
+ // PT Entry
+ if( PAGETABLE(Addr>>12) & PF_COW )
+ {
+ MM_int_ClonePageEnt( &PAGETABLE(Addr>>12), (void*)(Addr & ~0xFFF), Addr, 0 );
+ INVLPG( Addr & ~0xFFF );
+ return 0;
+ }
+ }
+ #endif
+
+ // If it was a user, tell the thread handler
+ if(ErrorCode & 4) {
+ Warning("User %s %s memory%s",
+ (ErrorCode&2?"write to":"read from"),
+ (ErrorCode&1?"bad/locked":"non-present"),
+ (ErrorCode&16?" (Instruction Fetch)":"")
+ );
+ Warning("User Pagefault: Instruction at %04x:%p accessed %p",
+ Regs->CS, Regs->RIP, Addr);
+ __asm__ __volatile__ ("sti"); // Restart IRQs
+ Error_Backtrace(Regs->RIP, Regs->RBP);
+ Threads_SegFault(Addr);
+ return 0;
+ }
+
+ // Kernel #PF
+ Debug_KernelPanic();
+ // -- Check Error Code --
+ if(ErrorCode & 8)
+ Warning("Reserved Bits Trashed!");
+ else
+ {
+ Warning("Kernel %s %s memory%s",
+ (ErrorCode&2?"write to":"read from"),
+ (ErrorCode&1?"bad/locked":"non-present"),
+ (ErrorCode&16?" (Instruction Fetch)":"")
+ );
+ }
+
+ Log("Thread %i - Code at %p accessed %p", Threads_GetTID(), Regs->RIP, Addr);
+ // Print Stack Backtrace
+ Error_Backtrace(Regs->RIP, Regs->RBP);
+
+ MM_DumpTables(0, -1);
+
+ return 1;
+}
+
+void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected)
+{
+ #define CANOICAL(addr) ((addr)&0x800000000000?(addr)|0xFFFF000000000000:(addr))
+ LogF("%016llx => ", CANOICAL(RangeStart));
+// LogF("%6llx %6llx %6llx %016llx => ",
+// MM_GetPhysAddr( (tVAddr)&PAGEDIRPTR(RangeStart>>30) ),
+// MM_GetPhysAddr( (tVAddr)&PAGEDIR(RangeStart>>21) ),
+// MM_GetPhysAddr( (tVAddr)&PAGETABLE(RangeStart>>12) ),
+// CANOICAL(RangeStart)
+// );
+ if( gMM_ZeroPage && (PAGETABLE(RangeStart>>12) & PADDR_MASK) == gMM_ZeroPage )
+ LogF("%13s", "zero" );
+ else
+ LogF("%13llx", PAGETABLE(RangeStart>>12) & PADDR_MASK );
+ LogF(" : 0x%6llx (%c%c%c%c%c%c)\r\n",
+ Length,
+ (Expected & PF_GLOBAL ? 'G' : '-'),
+ (Expected & PF_NX ? '-' : 'x'),
+ (Expected & PF_PAGED ? 'p' : '-'),
+ (Expected & PF_COW ? 'C' : '-'),
+ (Expected & PF_USER ? 'U' : '-'),
+ (Expected & PF_WRITE ? 'W' : '-')
+ );
+ #undef CANOICAL
+}
+
+/**
+ * \brief Dumps the layout of the page tables
+ */
+void MM_DumpTables(tVAddr Start, tVAddr End)
+{
+ const tPAddr FIXED_BITS = PF_PRESENT|PF_WRITE|PF_USER|PF_COW|PF_PAGED|PF_NX|PF_GLOBAL;
+ const tPAddr CHANGEABLE_BITS = ~FIXED_BITS & 0xFFF;
+ const tPAddr MASK = ~CHANGEABLE_BITS; // Physical address and access bits
+ tVAddr rangeStart = 0;
+ tPAddr expected = CHANGEABLE_BITS; // CHANGEABLE_BITS is used because it's not a vaild value
+ tVAddr curPos;
+ Uint page;
+ tPAddr expected_pml4 = PF_WRITE|PF_USER;
+ tPAddr expected_pdp = PF_WRITE|PF_USER;
+ tPAddr expected_pd = PF_WRITE|PF_USER;
+
+ Log("Table Entries: (%p to %p)", Start, End);
+
+ End &= (1L << 48) - 1;
+
+ Start >>= 12; End >>= 12;
+
+ for(page = Start, curPos = Start<<12;
+ page < End;
+ curPos += 0x1000, page++)
+ {
+ //Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27));
+ //Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18));
+ //Debug("&PAGEDIR(%i page>>9) = %p", page>>9, &PAGEDIR(page>>9));
+ //Debug("&PAGETABLE(%i page) = %p", page, &PAGETABLE(page));
+
+ // End of a range
+ if(!(PAGEMAPLVL4(page>>27) & PF_PRESENT)
+ || (PAGEMAPLVL4(page>>27) & FIXED_BITS) != expected_pml4
+ || !(PAGEDIRPTR(page>>18) & PF_PRESENT)
+ || (PAGEDIRPTR(page>>18) & FIXED_BITS) != expected_pdp
+ || !(PAGEDIR(page>>9) & PF_PRESENT)
+ || (PAGEDIR(page>>9) & FIXED_BITS) != expected_pd
+ || !(PAGETABLE(page) & PF_PRESENT)
+ || (PAGETABLE(page) & MASK) != expected)
+ {
+ if(expected != CHANGEABLE_BITS)
+ {
+ // Merge
+ expected &= expected_pml4 | ~(PF_WRITE|PF_USER);
+ expected &= expected_pdp | ~(PF_WRITE|PF_USER);
+ expected &= expected_pd | ~(PF_WRITE|PF_USER);
+ expected |= expected_pml4 & PF_NX;
+ expected |= expected_pdp & PF_NX;
+ expected |= expected_pd & PF_NX;
+ Log("expected (pml4 = %x, pdp = %x, pd = %x)",
+ expected_pml4, expected_pdp, expected_pd);
+ // Dump
+ MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected );
+ expected = CHANGEABLE_BITS;
+ }
+
+ if( curPos == 0x800000000000L )
+ curPos = 0xFFFF800000000000L;
+
+ if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) {
+ page += (1 << 27) - 1;
+ curPos += (1L << 39) - 0x1000;
+ continue;
+ }
+ if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) {
+ page += (1 << 18) - 1;
+ curPos += (1L << 30) - 0x1000;
+ continue;
+ }
+ if( !(PAGEDIR(page>>9) & PF_PRESENT) ) {
+ page += (1 << 9) - 1;
+ curPos += (1L << 21) - 0x1000;
+ continue;
+ }
+ if( !(PAGETABLE(page) & PF_PRESENT) ) continue;
+
+ expected = (PAGETABLE(page) & MASK);
+ expected_pml4 = (PAGEMAPLVL4(page>>27) & FIXED_BITS);
+ expected_pdp = (PAGEDIRPTR (page>>18) & FIXED_BITS);
+ expected_pd = (PAGEDIR (page>> 9) & FIXED_BITS);
+ rangeStart = curPos;
+ }
+ if(gMM_ZeroPage && (expected & PADDR_MASK) == gMM_ZeroPage )
+ expected = expected;
+ else if(expected != CHANGEABLE_BITS)
+ expected += 0x1000;
+ }
+
+ if(expected != CHANGEABLE_BITS) {
+ // Merge
+
+ // Dump
+ MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected );
+ expected = 0;
+ }
+}
+
+/**
+ * \brief Get a pointer to a page entry
+ * \param Addr Virtual Address
+ * \param bTemp Use the Temporary fractal mapping
+ * \param bAllocate Allocate entries
+ * \param bLargePage Request a large page
+ * \param Pointer Location to place the calculated pointer
+ * \return Page size, or -ve on error
+ */
+int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer)
+{
+ tPAddr *pmlevels[4];
+ tPAddr tmp;
+ int i, size;
+
+ #define BITMASK(bits) ( (1LL << (bits))-1 )
+
+ if( bTemp )
+ {
+ pmlevels[3] = &TMPTABLE(0); // Page Table
+ pmlevels[2] = &TMPDIR(0); // PDIR
+ pmlevels[1] = &TMPDIRPTR(0); // PDPT
+ pmlevels[0] = &TMPMAPLVL4(0); // PML4
+ }
+ else
+ {
+ pmlevels[3] = (void*)MM_FRACTAL_BASE; // Page Table
+ pmlevels[2] = &pmlevels[3][(MM_FRACTAL_BASE>>12)&BITMASK(VIRT_BITS-12)]; // PDIR
+ pmlevels[1] = &pmlevels[2][(MM_FRACTAL_BASE>>21)&BITMASK(VIRT_BITS-21)]; // PDPT
+ pmlevels[0] = &pmlevels[1][(MM_FRACTAL_BASE>>30)&BITMASK(VIRT_BITS-30)]; // PML4
+ }
+
+ // Mask address
+ Addr &= (1ULL << 48)-1;
+
+ for( size = 39, i = 0; size > 12; size -= 9, i ++ )
+ {
+ Uint64 *ent = &pmlevels[i][Addr >> size];
+// INVLPG( &pmlevels[i][ (Addr >> ADDR_SIZES[i]) &
+
+ // Check for a free large page slot
+ // TODO: Better support with selectable levels
+ if( (Addr & ((1ULL << size)-1)) == 0 && bLargePage )
+ {
+ if(Pointer) *Pointer = ent;
+ return size;
+ }
+ // Allocate an entry if required
+ if( !(*ent & PF_PRESENT) )
+ {
+ if( !bAllocate ) return -4; // If allocation is not requested, error
+ if( !(tmp = MM_AllocPhys()) ) return -2;
+ *ent = tmp | 3;
+ if( Addr < 0x800000000000 )
+ *ent |= PF_USER;
+ INVLPG( &pmlevels[i+1][ (Addr>>size)*512 ] );
+ memset( &pmlevels[i+1][ (Addr>>size)*512 ], 0, 0x1000 );
+ LOG("Init PML%i ent 0x%x %p with %P (*ent = %P)", 4 - i,
+ Addr>>size, (Addr>>size) << size, tmp, *ent);
+ }
+ // Catch large pages
+ else if( *ent & PF_LARGE )
+ {
+ // Alignment
+ if( (Addr & ((1ULL << size)-1)) != 0 ) return -3;
+ if(Pointer) *Pointer = ent;
+ return size; // Large page warning
+ }
+ }
+
+ // And, set the page table entry
+ if(Pointer) *Pointer = &pmlevels[i][Addr >> size];
+ return size;
+}
+
+/**
+ * \brief Map a physical page to a virtual one
+ * \param VAddr Target virtual address
+ * \param PAddr Physical address of page
+ * \param bTemp Use tempoary mappings
+ * \param bLarge Treat as a large page
+ */
+int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge)
+{
+ tPAddr *ent;
+ int rv;
+
+ ENTER("pVAddr PPAddr", VAddr, PAddr);
+
+ // Get page pointer (Allow allocating)
+ rv = MM_GetPageEntryPtr(VAddr, bTemp, 1, bLarge, &ent);
+ if(rv < 0) LEAVE_RET('i', 0);
+
+ if( *ent & 1 ) LEAVE_RET('i', 0);
+
+ *ent = PAddr | 3;
+
+ if( VAddr < 0x800000000000 )
+ *ent |= PF_USER;
+
+ INVLPG( VAddr );
+
+ LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \brief Map a physical page to a virtual one
+ * \param VAddr Target virtual address
+ * \param PAddr Physical address of page
+ */
+int MM_Map(tVAddr VAddr, tPAddr PAddr)
+{
+ return MM_MapEx(VAddr, PAddr, 0, 0);
+}
+
+/**
+ * \brief Removed a mapped page
+ */
+void MM_Unmap(tVAddr VAddr)
+{
+ // Check PML4
+ if( !(PAGEMAPLVL4(VAddr >> 39) & 1) ) return ;
+ // Check PDP
+ if( !(PAGEDIRPTR(VAddr >> 30) & 1) ) return ;
+ // Check Page Dir
+ if( !(PAGEDIR(VAddr >> 21) & 1) ) return ;
+
+ PAGETABLE(VAddr >> PTAB_SHIFT) = 0;
+ INVLPG( VAddr );
+}
+
+/**
+ * \brief Allocate a block of memory at the specified virtual address
+ */
+tPAddr MM_Allocate(tVAddr VAddr)
+{
+ tPAddr ret;
+
+ ENTER("xVAddr", VAddr);
+
+ // Ensure the tables are allocated before the page (keeps things neat)
+ MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL);
+
+ // Allocate the page
+ ret = MM_AllocPhys();
+ LOG("ret = %x", ret);
+ if(!ret) LEAVE_RET('i', 0);
+
+ if( !MM_Map(VAddr, ret) )
+ {
+ Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier");
+ MM_DerefPhys(ret);
+ LEAVE('i');
+ return 0;
+ }
+
+ LEAVE('X', ret);
+ return ret;
+}
+
+tPAddr MM_AllocateZero(tVAddr VAddr)
+{
+ tPAddr ret = gMM_ZeroPage;
+
+ MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL);
+
+ if(!gMM_ZeroPage) {
+ ret = gMM_ZeroPage = MM_AllocPhys();
+ MM_RefPhys(ret); // Don't free this please
+ MM_Map(VAddr, ret);
+ memset((void*)VAddr, 0, 0x1000);
+ }
+ else {
+ MM_Map(VAddr, ret);
+ }
+ MM_RefPhys(ret); // Refernce for this map
+ MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
+ return ret;
+}
+
+/**
+ * \brief Deallocate a page at a virtual address
+ */
+void MM_Deallocate(tVAddr VAddr)
+{
+ tPAddr phys;
+
+ phys = MM_GetPhysAddr(VAddr);
+ if(!phys) return ;
+
+ MM_Unmap(VAddr);
+
+ MM_DerefPhys(phys);
+}
+
+/**
+ * \brief Get the page table entry of a virtual address
+ * \param Addr Virtual Address
+ * \param Phys Location to put the physical address
+ * \param Flags Flags on the entry (set to zero if unmapped)
+ * \return Size of the entry (in address bits) - 12 = 4KiB page
+ */
+int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags)
+{
+ tPAddr *ptr;
+ int ret;
+
+ if(!Phys || !Flags) return 0;
+
+ ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr);
+ if( ret < 0 ) return 0;
+
+ *Phys = *ptr & PADDR_MASK;
+ *Flags = *ptr & 0xFFF;
+ return ret;
+}
+
+/**
+ * \brief Get the physical address of a virtual location
+ */
+tPAddr MM_GetPhysAddr(tVAddr Addr)
+{
+ tPAddr *ptr;
+ int ret;
+
+ ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr);
+ if( ret < 0 ) return 0;
+
+ if( !(*ptr & 1) ) return 0;
+
+ return (*ptr & PADDR_MASK) | (Addr & 0xFFF);
+}
+
+/**
+ * \brief Sets the flags on a page
+ */
+void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
+{
+ tPAddr *ent;
+ int rv;
+
+ // Get pointer
+ rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent);
+ if(rv < 0) return ;
+
+ // Ensure the entry is valid
+ if( !(*ent & 1) ) return ;
+
+ // Read-Only
+ if( Mask & MM_PFLAG_RO )
+ {
+ if( Flags & MM_PFLAG_RO ) {
+ *ent &= ~PF_WRITE;
+ }
+ else {
+ *ent |= PF_WRITE;
+ }
+ }
+
+ // Kernel
+ if( Mask & MM_PFLAG_KERNEL )
+ {
+ if( Flags & MM_PFLAG_KERNEL ) {
+ *ent &= ~PF_USER;
+ }
+ else {
+ *ent |= PF_USER;
+ }
+ }
+
+ // Copy-On-Write
+ if( Mask & MM_PFLAG_COW )
+ {
+ if( Flags & MM_PFLAG_COW ) {
+ *ent &= ~PF_WRITE;
+ *ent |= PF_COW;
+ INVLPG_ALL();
+ }
+ else {
+ *ent &= ~PF_COW;
+ *ent |= PF_WRITE;
+ }
+ }
+
+ // Execute
+ if( Mask & MM_PFLAG_EXEC )
+ {
+ if( Flags & MM_PFLAG_EXEC ) {
+ *ent &= ~PF_NX;
+ }
+ else {
+ *ent |= PF_NX;
+ }
+ }
+}
+
+/**
+ * \brief Get the flags applied to a page
+ */
+Uint MM_GetFlags(tVAddr VAddr)
+{
+ tPAddr *ent;
+ int rv, ret = 0;
+
+ rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent);
+ if(rv < 0) return 0;
+
+ if( !(*ent & 1) ) return 0;
+
+ // Read-Only
+ if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO;
+ // Kernel
+ if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
+ // Copy-On-Write
+ if( *ent & PF_COW ) ret |= MM_PFLAG_COW;
+ // Execute
+ if( !(*ent & PF_NX) ) ret |= MM_PFLAG_EXEC;
+
+ return ret;
+}
+
+/**
+ * \brief Check if the provided buffer is valid
+ * \return Boolean valid
+ */
+int MM_IsValidBuffer(tVAddr Addr, size_t Size)
+{
+ int bIsUser;
+ Uint64 pml4, pdp, dir, tab;
+
+ Size += Addr & (PAGE_SIZE-1);
+ Addr &= ~(PAGE_SIZE-1);
+ Addr &= ((1UL << 48)-1); // Clap to address space
+
+ pml4 = Addr >> 39;
+ pdp = Addr >> 30;
+ dir = Addr >> 21;
+ tab = Addr >> 12;
+
+ if( !(PAGEMAPLVL4(pml4) & 1) ) return 0;
+ if( !(PAGEDIRPTR(pdp) & 1) ) return 0;
+ if( !(PAGEDIR(dir) & 1) ) return 0;
+ if( !(PAGETABLE(tab) & 1) ) return 0;
+
+ bIsUser = !!(PAGETABLE(tab) & PF_USER);
+
+ while( Size >= PAGE_SIZE )
+ {
+ if( (tab & 511) == 0 )
+ {
+ dir ++;
+ if( ((dir >> 9) & 511) == 0 )
+ {
+ pdp ++;
+ if( ((pdp >> 18) & 511) == 0 )
+ {
+ pml4 ++;
+ if( !(PAGEMAPLVL4(pml4) & 1) ) return 0;
+ }
+ if( !(PAGEDIRPTR(pdp) & 1) ) return 0;
+ }
+ if( !(PAGEDIR(dir) & 1) ) return 0;
+ }
+
+ if( !(PAGETABLE(tab) & 1) ) return 0;
+ if( bIsUser && !(PAGETABLE(tab) & PF_USER) ) return 0;
+
+ tab ++;
+ Size -= PAGE_SIZE;
+ }
+ return 1;
+}
+
+// --- Hardware Mappings ---
+/**
+ * \brief Map a range of hardware pages
+ */
+tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
+{
+ tVAddr ret;
+ int num;
+
+ //TODO: Add speedups (memory of first possible free)
+ for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_TOP; ret += 0x1000 )
+ {
+ for( num = Number; num -- && ret < MM_HWMAP_TOP; ret += 0x1000 )
+ {
+ if( MM_GetPhysAddr(ret) != 0 ) break;
+ }
+ if( num >= 0 ) continue;
+
+// Log_Debug("MMVirt", "Mapping %i pages to %p (base %P)", Number, ret-Number*0x1000, PAddr);
+
+ PAddr += 0x1000 * Number;
+
+ while( Number -- )
+ {
+ ret -= 0x1000;
+ PAddr -= 0x1000;
+ MM_Map(ret, PAddr);
+ MM_RefPhys(PAddr);
+ }
+
+ return ret;
+ }
+
+ Log_Error("MM", "MM_MapHWPages - No space for %i pages", Number);
+ return 0;
+}
+
+/**
+ * \brief Free a range of hardware pages
+ */
+void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
+{
+// Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
+ while( Number -- )
+ {
+ MM_DerefPhys( MM_GetPhysAddr(VAddr) );
+ MM_Unmap(VAddr);
+ VAddr += 0x1000;
+ }
+}
+
+
+/**
+ * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
+ * \brief Allocates DMA physical memory
+ * \param Pages Number of pages required
+ * \param MaxBits Maximum number of bits the physical address can have
+ * \param PhysAddr Pointer to the location to place the physical address allocated
+ * \return Virtual address allocate
+ */
+tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
+{
+ tPAddr phys;
+ tVAddr ret;
+
+ // Sanity Check
+ if(MaxBits < 12 || !PhysAddr) return 0;
+
+ // Fast Allocate
+ if(Pages == 1 && MaxBits >= PHYS_BITS)
+ {
+ phys = MM_AllocPhys();
+ *PhysAddr = phys;
+ ret = MM_MapHWPages(phys, 1);
+ MM_DerefPhys(phys);
+ return ret;
+ }
+
+ // Slow Allocate
+ phys = MM_AllocPhysRange(Pages, MaxBits);
+ // - Was it allocated?
+ if(phys == 0) return 0;
+
+ // Allocated successfully, now map
+ ret = MM_MapHWPages(phys, Pages);
+ // MapHWPages references the pages, so deref them back down to 1
+ for(;Pages--;phys+=0x1000)
+ MM_DerefPhys(phys);
+ if( ret == 0 ) {
+ // If it didn't map, free then return 0
+ return 0;
+ }
+
+ *PhysAddr = phys;
+ return ret;
+}
+
+// --- Tempory Mappings ---
+tVAddr MM_MapTemp(tPAddr PAddr)
+{
+ const int max_slots = (MM_TMPMAP_END - MM_TMPMAP_BASE) / PAGE_SIZE;
+ tVAddr ret = MM_TMPMAP_BASE;
+ int i;
+
+ for( i = 0; i < max_slots; i ++, ret += PAGE_SIZE )
+ {
+ tPAddr *ent;
+ if( MM_GetPageEntryPtr( ret, 0, 1, 0, &ent) < 0 ) {
+ continue ;
+ }
+
+ if( *ent & 1 )
+ continue ;
+
+ *ent = PAddr | 3;
+ MM_RefPhys(PAddr);
+ INVLPG(ret);
+ return ret;
+ }
+ return 0;
+}
+
+void MM_FreeTemp(tVAddr VAddr)
+{
+ MM_Deallocate(VAddr);
+ return ;
+}
+
+
+// --- Address Space Clone --
+tPAddr MM_Clone(void)
+{
+ tPAddr ret;
+ int i;
+ tVAddr kstackbase;
+
+ // #1 Create a copy of the PML4
+ ret = MM_AllocPhys();
+ if(!ret) return 0;
+
+ // #2 Alter the fractal pointer
+ Mutex_Acquire(&glMM_TempFractalLock);
+ TMPCR3() = ret | 3;
+ INVLPG_ALL();
+
+ // #3 Set Copy-On-Write to all user pages
+ if( Threads_GetPID() != 0 )
+ {
+ for( i = 0; i < 256; i ++)
+ {
+ if( PAGEMAPLVL4(i) & PF_WRITE ) {
+ PAGEMAPLVL4(i) |= PF_COW;
+ PAGEMAPLVL4(i) &= ~PF_WRITE;
+ }
+
+ TMPMAPLVL4(i) = PAGEMAPLVL4(i);
+// Log_Debug("MM", "TMPMAPLVL4(%i) = 0x%016llx", i, TMPMAPLVL4(i));
+ if( !(TMPMAPLVL4(i) & PF_PRESENT) ) continue ;
+
+ MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK );
+ }
+ }
+ else
+ {
+ for( i = 0; i < 256; i ++ )
+ {
+ TMPMAPLVL4(i) = 0;
+ }
+ }
+
+ // #4 Map in kernel pages
+ for( i = 256; i < 512; i ++ )
+ {
+ // Skip addresses:
+ // 320 0xFFFFA.... - Kernel Stacks
+ if( i == MM_KSTACK_BASE>>39 ) continue;
+ // 509 0xFFFFFE0.. - Fractal mapping
+ if( i == MM_FRACTAL_BASE>>39 ) continue;
+ // 510 0xFFFFFE8.. - Temp fractal mapping
+ if( i == MM_TMPFRAC_BASE>>39 ) continue;
+
+ TMPMAPLVL4(i) = PAGEMAPLVL4(i);
+ if( TMPMAPLVL4(i) & 1 )
+ MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK );
+ }
+
+ // Mark Per-Process data as COW
+ TMPMAPLVL4(MM_PPD_BASE>>39) |= PF_COW;
+ TMPMAPLVL4(MM_PPD_BASE>>39) &= ~PF_WRITE;
+
+ // #5 Set fractal mapping
+ TMPMAPLVL4(MM_FRACTAL_BASE>>39) = ret | 3; // Main
+ TMPMAPLVL4(MM_TMPFRAC_BASE>>39) = 0; // Temp
+
+ // #6 Create kernel stack
+ // tThread->KernelStack is the top
+ // There is 1 guard page below the stack
+ kstackbase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE;
+
+ // Clone stack
+ TMPMAPLVL4(MM_KSTACK_BASE >> PML4_SHIFT) = 0;
+ for( i = 1; i < KERNEL_STACK_SIZE/0x1000; i ++ )
+ {
+ tPAddr phys = MM_AllocPhys();
+ tVAddr tmpmapping;
+ MM_MapEx(kstackbase+i*0x1000, phys, 1, 0);
+
+ tmpmapping = MM_MapTemp(phys);
+ if( MM_GetPhysAddr( kstackbase+i*0x1000 ) )
+ memcpy((void*)tmpmapping, (void*)(kstackbase+i*0x1000), 0x1000);
+ else
+ memset((void*)tmpmapping, 0, 0x1000);
+// if( i == 0xF )
+// Debug_HexDump("MM_Clone: *tmpmapping = ", (void*)tmpmapping, 0x1000);
+ MM_FreeTemp(tmpmapping);
+ }
+
+// MAGIC_BREAK();
+
+ // #7 Return
+ TMPCR3() = 0;
+ INVLPG_ALL();
+ Mutex_Release(&glMM_TempFractalLock);
+// Log("MM_Clone: RETURN %P", ret);
+ return ret;
+}
+
+void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts)
+{
+ Uint64 * const table_bases[] = {&PAGETABLE(0), &PAGEDIR(0), &PAGEDIRPTR(0), &PAGEMAPLVL4(0)};
+ Uint64 *table = table_bases[(LevelBits-12)/9] + (VAddr >> LevelBits);
+ int i;
+// Log("MM_int_ClearTableLevel: (VAddr=%p, LevelBits=%i, MaxEnts=%i)", VAddr, LevelBits, MaxEnts);
+ for( i = 0; i < MaxEnts; i ++ )
+ {
+ // Skip non-present tables
+ if( !(table[i] & PF_PRESENT) ) {
+ table[i] = 0;
+ continue ;
+ }
+
+ if( (table[i] & PF_COW) && MM_GetRefCount(table[i] & PADDR_MASK) > 1 ) {
+ MM_DerefPhys(table[i] & PADDR_MASK);
+ table[i] = 0;
+ continue ;
+ }
+ // Clear table contents (if it is a table)
+ if( LevelBits > 12 )
+ MM_int_ClearTableLevel(VAddr + ((tVAddr)i << LevelBits), LevelBits-9, 512);
+ MM_DerefPhys(table[i] & PADDR_MASK);
+ table[i] = 0;
+ }
+}
+
+void MM_ClearUser(void)
+{
+ MM_int_ClearTableLevel(0, 39, 256);
+}
+
+tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize)
+{
+ tVAddr ret;
+ tPAddr phys;
+ int i;
+
+ // #1 Set temp fractal to PID0
+ Mutex_Acquire(&glMM_TempFractalLock);
+ TMPCR3() = ((tPAddr)gInitialPML4 - KERNEL_BASE) | 3;
+ INVLPG_ALL();
+
+ // #2 Scan for a free stack addresss < 2^47
+ for(ret = 0x100000; ret < (1ULL << 47); ret += KERNEL_STACK_SIZE)
+ {
+ tPAddr *ptr;
+ if( MM_GetPageEntryPtr(ret, 1, 0, 0, &ptr) <= 0 ) break;
+ if( !(*ptr & 1) ) break;
+ }
+ if( ret >= (1ULL << 47) ) {
+ Mutex_Release(&glMM_TempFractalLock);
+ return 0;
+ }
+
+ // #3 Map all save the last page in the range
+ // - This acts as as guard page
+ MM_GetPageEntryPtr(ret, 1, 1, 0, NULL); // Make sure tree is allocated
+ for( i = 0; i < KERNEL_STACK_SIZE/0x1000 - 1; i ++ )
+ {
+ phys = MM_AllocPhys();
+ if(!phys) {
+ // TODO: Clean up
+ Log_Error("MM", "MM_NewWorkerStack - Unable to allocate page");
+ return 0;
+ }
+ MM_MapEx(ret + i*0x1000, phys, 1, 0);
+ MM_SetFlags(ret + i*0x1000, MM_PFLAG_KERNEL|MM_PFLAG_RO, MM_PFLAG_KERNEL);
+ }
+
+ // Copy data
+ if( StackSize > 0x1000 ) {
+ Log_Error("MM", "MM_NewWorkerStack: StackSize(0x%x) > 0x1000, cbf handling", StackSize);
+ }
+ else {
+ tVAddr tmp_addr, dest;
+ tmp_addr = MM_MapTemp(phys);
+ dest = tmp_addr + (0x1000 - StackSize);
+ memcpy( (void*)dest, StackData, StackSize );
+ Log_Debug("MM", "MM_NewWorkerStack: %p->%p %i bytes (i=%i)", StackData, dest, StackSize, i);
+ Log_Debug("MM", "MM_NewWorkerStack: ret = %p", ret);
+ MM_FreeTemp(tmp_addr);
+ }
+
+ TMPCR3() = 0;
+ Mutex_Release(&glMM_TempFractalLock);
+
+ return ret + i*0x1000;
+}
+
+/**
+ * \brief Allocate a new kernel stack
+ */
+tVAddr MM_NewKStack(void)
+{
+ tVAddr base = MM_KSTACK_BASE;
+ Uint i;
+ for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE )
+ {
+ if(MM_GetPhysAddr(base+KERNEL_STACK_SIZE-0x1000) != 0)
+ continue;
+
+ //Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE);
+ for( i = 0x1000; i < KERNEL_STACK_SIZE; i += 0x1000)
+ {
+ if( !MM_Allocate(base+i) )
+ {
+ Log_Warning("MM", "MM_NewKStack - Allocation failed");
+ for( i -= 0x1000; i; i -= 0x1000)
+ MM_Deallocate(base+i);
+ return 0;
+ }
+ }
+
+ return base + KERNEL_STACK_SIZE;
+ }
+ Log_Warning("MM", "MM_NewKStack - No address space left\n");
+ return 0;
+}
--- /dev/null
+../x86/pci.c
\ No newline at end of file
--- /dev/null
+;
+;
+;
+%include "arch/x86_64/include/common.inc.asm"
+[BITS 64]
+[section .text]
+
+[extern Threads_Exit]
+
+[global GetRIP]
+GetRIP:
+ mov rax, [rsp]
+ ret
+
+[global NewTaskHeader]
+NewTaskHeader:
+ ; [rsp+0x00]: Thread
+ ; [rsp+0x08]: Function
+ ; [rsp+0x10]: Argument
+
+ mov rdi, [rsp+0x10]
+ mov rax, [rsp+0x8]
+ add rsp, 0x10 ; Reclaim stack space (thread/fcn)
+ xchg bx, bx
+ call rax
+
+ ; Quit thread with RAX as the return code
+ xor rdi, rdi
+ mov rsi, rax
+ call Threads_Exit
+
+.hlt:
+ jmp .hlt
+
+[extern MM_Clone]
+[extern MM_DumpTables]
+[global Proc_CloneInt]
+Proc_CloneInt:
+ PUSH_GPR
+ ; Save RSP
+ mov [rdi], rsp
+ call MM_Clone
+ ; Save CR3
+ mov rsi, [rsp+0x30] ; Saved version of RSI
+ mov [rsi], rax
+ ; Undo the PUSH_GPR
+ add rsp, 0x80
+ mov rax, .newTask
+ ret
+.newTask:
+; mov rdi, 0
+; mov rsi, 0x800000000000
+; call MM_DumpTables
+ POP_GPR
+ xor eax, eax
+ ret
+
+[global SaveState]
+SaveState:
+ ; Save regs to RSI
+ add rsi, 0x80
+ SAVE_GPR rsi
+ ; Save return addr
+ mov rax, [rsp]
+ mov [rsi], rax
+ ; Return RSI as the RSP value
+ sub rsi, 0x80
+ mov [rdi], rsi
+ ; Check for
+ mov rax, .restore
+ ret
+.restore:
+ ; RSP = RSI now
+ POP_GPR
+ mov rax, [rsp]
+ mov rsp, [rsp-0x60] ; Restore RSP from the saved value
+ mov [rsp], rax ; Restore return address
+ xor eax, eax
+ ret
+
+[global SwitchTasks]
+; rdi = New RSP
+; rsi = Old RSP save loc
+; rdx = New RIP
+; rcx = Old RIP save loc
+; r8 = CR3
+SwitchTasks:
+ PUSH_GPR
+
+ ; Save state RIP and RSP
+ lea rax, [rel .restore]
+ mov [rcx], rax
+ mov [rsi], rsp
+
+ ; Change CR3 if requested
+ test r8, r8
+ jz .setState
+ mov cr3, r8
+
+ ; Make sure the stack is valid before jumping
+ invlpg [rdi-0x1000]
+ invlpg [rdi]
+ invlpg [rdi+0x1000]
+
+ ; Go to new state
+.setState:
+ mov rsp, rdi
+ jmp rdx
+
+ ; Restore point for saved state
+.restore:
+ POP_GPR
+ xor eax, eax ; Return zero
+ ret
+
+[global Proc_InitialiseSSE]
+Proc_InitialiseSSE:
+ mov rax, cr4
+ or ax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT
+ mov cr4, rax
+ mov rax, cr0
+ and ax, ~(1 << 2) ; Clear EM
+ or rax, (1 << 1) ; Set MP
+ mov rax, cr0
+ ret
+[global Proc_DisableSSE]
+Proc_DisableSSE:
+ mov rax, cr0
+ or ax, 1 << 3 ; Set TS
+ mov cr0, rax
+ ret
+[global Proc_EnableSSE]
+Proc_EnableSSE:
+ mov rax, cr0
+ and ax, ~(1 << 3) ; Clear TS
+ mov cr0, rax
+ ret
+
+[global Proc_SaveSSE]
+Proc_SaveSSE:
+ fxsave [rdi]
+ ret
+[global Proc_RestoreSSE]
+Proc_RestoreSSE:
+ fxrstor [rdi]
+ ret
+
+; vim: ft=nasm
--- /dev/null
+/*
+ * Acess2 x86_64 port
+ * proc.c
+ */
+#include <acess.h>
+#include <proc.h>
+#include <threads.h>
+#include <threads_int.h>
+#include <desctab.h>
+#include <mm_virt.h>
+#include <errno.h>
+#if USE_MP
+# include <mp.h>
+#endif
+#include <arch_config.h>
+#include <hal_proc.h>
+
+// === FLAGS ===
+#define DEBUG_TRACE_SWITCH 0
+#define BREAK_ON_SWITCH 0 // Break into bochs debugger on a task switch
+
+// === CONSTANTS ===
+
+// === TYPES ===
+typedef struct sCPU
+{
+ Uint8 APICID;
+ Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
+ Uint16 Resvd;
+ tThread *Current;
+ tThread *IdleThread;
+} tCPU;
+
+// === IMPORTS ===
+extern tGDT gGDT[];
+extern void APStartup(void); // 16-bit AP startup code
+
+extern Uint GetRIP(void); // start.asm
+extern Uint SaveState(Uint *RSP, Uint *Regs);
+extern Uint Proc_CloneInt(Uint *RSP, Uint *CR3);
+extern void NewTaskHeader(void); // Actually takes cdecl args
+extern void Proc_InitialiseSSE(void);
+extern void Proc_SaveSSE(Uint DestPtr);
+extern void Proc_DisableSSE(void);
+
+extern Uint64 gInitialPML4[512]; // start.asm
+extern int giNumCPUs;
+extern int giNextTID;
+extern int giTotalTickets;
+extern int giNumActiveThreads;
+extern tThread gThreadZero;
+extern tProcess gProcessZero;
+extern void Threads_Dump(void);
+extern void Proc_ReturnToUser(tVAddr Handler, tVAddr KStackTop, int Argument);
+extern void Time_UpdateTimestamp(void);
+extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3);
+
+// === PROTOTYPES ===
+//void ArchThreads_Init(void);
+#if USE_MP
+void MP_StartAP(int CPU);
+void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
+#endif
+void Proc_IdleTask(void *unused);
+//void Proc_Start(void);
+//tThread *Proc_GetCurThread(void);
+// int Proc_NewKThread(void (*Fcn)(void*), void *Data);
+// int Proc_Clone(Uint *Err, Uint Flags);
+// int Proc_SpawnWorker(void);
+Uint Proc_MakeUserStack(void);
+//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
+void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN;
+ int Proc_Demote(Uint *Err, int Dest, tRegs *Regs);
+//void Proc_CallFaultHandler(tThread *Thread);
+//void Proc_DumpThreadCPUState(tThread *Thread);
+//void Proc_Reschedule(void);
+void Proc_Scheduler(int CPU, Uint RSP, Uint RIP);
+
+// === GLOBALS ===
+//!\brief Used by desctab.asm in SyscallStub
+const int ci_offsetof_tThread_KernelStack = offsetof(tThread, KernelStack);
+// --- Multiprocessing ---
+#if USE_MP
+volatile int giNumInitingCPUs = 0;
+tMPInfo *gMPFloatPtr = NULL;
+tAPIC *gpMP_LocalAPIC = NULL;
+Uint8 gaAPIC_to_CPU[256] = {0};
+#endif
+tCPU gaCPUs[MAX_CPUS];
+tTSS *gTSSs = NULL;
+tTSS gTSS0 = {0};
+// --- Error Recovery ---
+Uint32 gaDoubleFaultStack[1024];
+
+// === CODE ===
+/**
+ * \fn void ArchThreads_Init(void)
+ * \brief Starts the process scheduler
+ */
+void ArchThreads_Init(void)
+{
+ Uint pos = 0;
+
+ #if USE_MP
+ tMPTable *mptable;
+
+ // Mark BSP as active
+ gaCPUs[0].State = 2;
+
+ // -- Initialise Multiprocessing
+ // Find MP Floating Table
+ // - EBDA/Last 1Kib (640KiB)
+ for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) {
+ if( *(Uint*)(pos) == MPPTR_IDENT ) {
+ Log("Possible %p", pos);
+ if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
+ gMPFloatPtr = (void*)pos;
+ break;
+ }
+ }
+ // - Last KiB (512KiB base mem)
+ if(!gMPFloatPtr) {
+ for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) {
+ if( *(Uint*)(pos) == MPPTR_IDENT ) {
+ Log("Possible %p", pos);
+ if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
+ gMPFloatPtr = (void*)pos;
+ break;
+ }
+ }
+ }
+ // - BIOS ROM
+ if(!gMPFloatPtr) {
+ for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) {
+ if( *(Uint*)(pos) == MPPTR_IDENT ) {
+ Log("Possible %p", pos);
+ if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
+ gMPFloatPtr = (void*)pos;
+ break;
+ }
+ }
+ }
+
+ // If the MP Table Exists, parse it
+ if(gMPFloatPtr)
+ {
+ int i;
+ tMPTable_Ent *ents;
+ Log("gMPFloatPtr = %p", gMPFloatPtr);
+ Log("*gMPFloatPtr = {");
+ Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig);
+ Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig);
+ Log("\t.Length = 0x%02x", gMPFloatPtr->Length);
+ Log("\t.Version = 0x%02x", gMPFloatPtr->Version);
+ Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum);
+ Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]",
+ gMPFloatPtr->Features[0], gMPFloatPtr->Features[1],
+ gMPFloatPtr->Features[2], gMPFloatPtr->Features[3],
+ gMPFloatPtr->Features[4]
+ );
+ Log("}");
+
+ mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig );
+ Log("mptable = %p", mptable);
+ Log("*mptable = {");
+ Log("\t.Sig = 0x%08x", mptable->Sig);
+ Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength);
+ Log("\t.SpecRev = 0x%02x", mptable->SpecRev);
+ Log("\t.Checksum = 0x%02x", mptable->Checksum);
+ Log("\t.OEMID = '%8c'", mptable->OemID);
+ Log("\t.ProductID = '%8c'", mptable->ProductID);
+ Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr);
+ Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize);
+ Log("\t.EntryCount = 0x%04x", mptable->EntryCount);
+ Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap);
+ Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen);
+ Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum);
+ Log("}");
+
+ gpMP_LocalAPIC = (void*)MM_MapHWPage(mptable->LocalAPICMemMap, 1);
+
+ ents = mptable->Entries;
+ giNumCPUs = 0;
+
+ for( i = 0; i < mptable->EntryCount; i ++ )
+ {
+ int entSize = 0;
+ switch( ents->Type )
+ {
+ case 0: // Processor
+ entSize = 20;
+ Log("%i: Processor", i);
+ Log("\t.APICID = %i", ents->Proc.APICID);
+ Log("\t.APICVer = 0x%02x", ents->Proc.APICVer);
+ Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags);
+ Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature);
+ Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags);
+
+
+ if( !(ents->Proc.CPUFlags & 1) ) {
+ Log("DISABLED");
+ break;
+ }
+
+ // Check if there is too many processors
+ if(giNumCPUs >= MAX_CPUS) {
+ giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped
+ break;
+ }
+
+ // Initialise CPU Info
+ gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs;
+ gaCPUs[giNumCPUs].APICID = ents->Proc.APICID;
+ gaCPUs[giNumCPUs].State = 0;
+ giNumCPUs ++;
+
+ // Send IPI
+ if( !(ents->Proc.CPUFlags & 2) )
+ {
+ MP_StartAP( giNumCPUs-1 );
+ }
+
+ break;
+ case 1: // Bus
+ entSize = 8;
+ Log("%i: Bus", i);
+ Log("\t.ID = %i", ents->Bus.ID);
+ Log("\t.TypeString = '%6c'", ents->Bus.TypeString);
+ break;
+ case 2: // I/O APIC
+ entSize = 8;
+ Log("%i: I/O APIC", i);
+ Log("\t.ID = %i", ents->IOAPIC.ID);
+ Log("\t.Version = 0x%02x", ents->IOAPIC.Version);
+ Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags);
+ Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr);
+ break;
+ case 3: // I/O Interrupt Assignment
+ entSize = 8;
+ Log("%i: I/O Interrupt Assignment", i);
+ Log("\t.IntType = %i", ents->IOInt.IntType);
+ Log("\t.Flags = 0x%04x", ents->IOInt.Flags);
+ Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID);
+ Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ);
+ Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID);
+ Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ);
+ break;
+ case 4: // Local Interrupt Assignment
+ entSize = 8;
+ Log("%i: Local Interrupt Assignment", i);
+ Log("\t.IntType = %i", ents->LocalInt.IntType);
+ Log("\t.Flags = 0x%04x", ents->LocalInt.Flags);
+ Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID);
+ Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ);
+ Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID);
+ Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ);
+ break;
+ default:
+ Log("%i: Unknown (%i)", i, ents->Type);
+ break;
+ }
+ ents = (void*)( (Uint)ents + entSize );
+ }
+
+ if( giNumCPUs > MAX_CPUS ) {
+ Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS);
+ giNumCPUs = MAX_CPUS;
+ }
+
+ while( giNumInitingCPUs )
+ MM_FinishVirtualInit();
+
+ Panic("Uh oh... MP Table Parsing is unimplemented\n");
+ }
+ else {
+ Log("No MP Table was found, assuming uniprocessor\n");
+ giNumCPUs = 1;
+ gTSSs = &gTSS0;
+ }
+ #else
+ giNumCPUs = 1;
+ gTSSs = &gTSS0;
+ MM_FinishVirtualInit();
+ #endif
+
+ #if USE_MP
+ // Initialise Normal TSS(s)
+ for(pos=0;pos<giNumCPUs;pos++)
+ {
+ #else
+ pos = 0;
+ #endif
+ gTSSs[pos].RSP0 = 0; // Set properly by scheduler
+ gGDT[7+pos*2].LimitLow = sizeof(tTSS) & 0xFFFF;
+ gGDT[7+pos*2].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
+ gGDT[7+pos*2].BaseMid = ((Uint)(&gTSSs[pos])) >> 16;
+ gGDT[7+pos*2].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
+ gGDT[7+pos*2+1].DWord[0] = ((Uint)(&gTSSs[pos])) >> 32;
+ #if USE_MP
+ }
+ for(pos=0;pos<giNumCPUs;pos++) {
+ __asm__ __volatile__ ("ltr %%ax"::"a"(0x38+pos*16));
+ }
+ #else
+ __asm__ __volatile__ ("ltr %%ax"::"a"(0x38));
+ #endif
+
+ // Set Debug registers
+ __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero));
+ __asm__ __volatile__ ("mov %%rax, %%db1" : : "a"(0));
+
+ gaCPUs[0].Current = &gThreadZero;
+
+ gProcessZero.MemState.CR3 = (Uint)gInitialPML4 - KERNEL_BASE;
+ gThreadZero.CurCPU = 0;
+ gThreadZero.KernelStack = 0xFFFFA00000000000 + KERNEL_STACK_SIZE;
+
+ // Set timer frequency
+ outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
+ outb(0x40, PIT_TIMER_DIVISOR&0xFF); // Low Byte of Divisor
+ outb(0x40, (PIT_TIMER_DIVISOR>>8)&0xFF); // High Byte
+
+ // Create Per-Process Data Block
+ if( !MM_Allocate(MM_PPD_CFG) )
+ {
+ Warning("Oh, hell, Unable to allocate PPD for Thread#0");
+ }
+
+ Proc_InitialiseSSE();
+
+ Log_Log("Proc", "Multithreading initialised");
+}
+
+#if USE_MP
+void MP_StartAP(int CPU)
+{
+ Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
+ // Set location of AP startup code and mark for a warm restart
+ *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0);
+ *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
+ outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset
+ MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);
+ giNumInitingCPUs ++;
+}
+
+void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
+{
+ Uint32 addr = (Uint)gpMP_LocalAPIC + 0x300;
+ Uint32 val;
+
+ // High
+ val = (Uint)APICID << 24;
+ Log("*%p = 0x%08x", addr+0x10, val);
+ *(Uint32*)(addr+0x10) = val;
+ // Low (and send)
+ val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
+ Log("*%p = 0x%08x", addr, val);
+ *(Uint32*)addr = val;
+}
+#endif
+
+/**
+ * \brief Idle task
+ */
+void Proc_IdleTask(void *ptr)
+{
+ tCPU *cpu = ptr;
+ cpu->IdleThread = Proc_GetCurThread();
+ cpu->IdleThread->ThreadName = (char*)"Idle Thread";
+ Threads_SetPriority( cpu->IdleThread, -1 ); // Never called randomly
+ cpu->IdleThread->Quantum = 1; // 1 slice quantum
+ for(;;) {
+ HALT(); // Just yeilds
+ Threads_Yield();
+ }
+}
+
+/**
+ * \fn void Proc_Start(void)
+ * \brief Start process scheduler
+ */
+void Proc_Start(void)
+{
+ #if USE_MP
+ int i;
+ #endif
+
+ #if USE_MP
+ // Start APs
+ for( i = 0; i < giNumCPUs; i ++ )
+ {
+ int tid;
+ if(i) gaCPUs[i].Current = NULL;
+
+ Proc_NewKThread(Proc_IdleTask, &gaCPUs[i]);
+
+ // Create Idle Task
+ gaCPUs[i].IdleThread = Threads_GetThread(tid);
+
+
+ // Start the AP
+ if( i != giProc_BootProcessorID ) {
+ MP_StartAP( i );
+ }
+ }
+
+ // BSP still should run the current task
+ gaCPUs[0].Current = &gThreadZero;
+ __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero));
+
+ // Start interrupts and wait for APs to come up
+ Log("Waiting for APs to come up\n");
+ __asm__ __volatile__ ("sti");
+ while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
+ #else
+ Proc_NewKThread(Proc_IdleTask, &gaCPUs[0]);
+
+ // Start Interrupts (and hence scheduler)
+ __asm__ __volatile__("sti");
+ #endif
+ MM_FinishVirtualInit();
+ Log_Log("Proc", "Multithreading started");
+}
+
+/**
+ * \fn tThread *Proc_GetCurThread(void)
+ * \brief Gets the current thread
+ */
+tThread *Proc_GetCurThread(void)
+{
+ #if USE_MP
+ tThread *ret;
+ __asm__ __volatile__ ("mov %%db0, %0" : "=r"(thread));
+ return ret; // gaCPUs[ GetCPUNum() ].Current;
+ #else
+ return gaCPUs[ 0 ].Current;
+ #endif
+}
+
+void Proc_ClearProcess(tProcess *Process)
+{
+ Log_Warning("Proc", "TODO: Nuke address space etc");
+}
+
+/*
+ *
+ */
+void Proc_ClearThread(tThread *Thread)
+{
+}
+
+/**
+ * \brief Create a new kernel thread
+ */
+tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
+{
+ Uint rsp;
+ tThread *newThread, *cur;
+
+ cur = Proc_GetCurThread();
+ newThread = Threads_CloneTCB(0);
+ if(!newThread) return -1;
+
+ // Create new KStack
+ newThread->KernelStack = MM_NewKStack();
+ // Check for errors
+ if(newThread->KernelStack == 0) {
+ free(newThread);
+ return -1;
+ }
+
+ rsp = newThread->KernelStack;
+ *(Uint*)(rsp-=8) = (Uint)Data; // Data (shadowed)
+ *(Uint*)(rsp-=8) = (Uint)Fcn; // Function to call
+ *(Uint*)(rsp-=8) = (Uint)newThread; // Thread ID
+
+ newThread->SavedState.RSP = rsp;
+ newThread->SavedState.RIP = (Uint)&NewTaskHeader;
+ newThread->SavedState.SSE = NULL;
+// Log("New (KThread) %p, rsp = %p\n", newThread->SavedState.RIP, newThread->SavedState.RSP);
+
+// MAGIC_BREAK();
+ Threads_AddActive(newThread);
+
+ return newThread->TID;
+}
+
+/**
+ * \fn int Proc_Clone(Uint Flags)
+ * \brief Clone the current process
+ */
+tTID Proc_Clone(Uint Flags)
+{
+ tThread *newThread, *cur = Proc_GetCurThread();
+ Uint rip;
+
+ // Sanity check
+ if( !(Flags & CLONE_VM) ) {
+ Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead");
+ return -1;
+ }
+
+ // Create new TCB
+ newThread = Threads_CloneTCB(Flags);
+ if(!newThread) return -1;
+
+ // Save core machine state
+ rip = Proc_CloneInt(&newThread->SavedState.RSP, &newThread->Process->MemState.CR3);
+ if(rip == 0) return 0; // Child
+ newThread->KernelStack = cur->KernelStack;
+ newThread->SavedState.RIP = rip;
+ newThread->SavedState.SSE = NULL;
+
+ // DEBUG
+ #if 0
+ Log("New (Clone) %p, rsp = %p, cr3 = %p", rip, newThread->SavedState.RSP, newThread->MemState.CR3);
+ {
+ Uint cr3;
+ __asm__ __volatile__ ("mov %%cr3, %0" : "=r" (cr3));
+ Log("Current CR3 = 0x%x, PADDR(RSP) = 0x%x", cr3, MM_GetPhysAddr(newThread->SavedState.RSP));
+ }
+ #endif
+ // /DEBUG
+
+ // Lock list and add to active
+ Threads_AddActive(newThread);
+
+ return newThread->TID;
+}
+
+/**
+ * \fn int Proc_SpawnWorker(void)
+ * \brief Spawns a new worker thread
+ */
+int Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
+{
+ tThread *new, *cur;
+ Uint stack_contents[3];
+
+ cur = Proc_GetCurThread();
+
+ // Create new thread
+ new = Threads_CloneThreadZero();
+ if(!new) {
+ Warning("Proc_SpawnWorker - Out of heap space!\n");
+ return -1;
+ }
+
+ // Create the stack contents
+ stack_contents[2] = (Uint)Data;
+ stack_contents[1] = (Uint)Fcn;
+ stack_contents[0] = (Uint)new;
+
+ // Create a new worker stack (in PID0's address space)
+ // - The stack is built by this code using stack_contents
+ new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents));
+
+ new->SavedState.RSP = new->KernelStack - sizeof(stack_contents);
+ new->SavedState.RIP = (Uint)&NewTaskHeader;
+ new->SavedState.SSE = NULL;
+
+// Log("New (Worker) %p, rsp = %p\n", new->SavedState.RIP, new->SavedState.RSP);
+
+ // Mark as active
+ new->Status = THREAD_STAT_PREINIT;
+ Threads_AddActive( new );
+
+ return new->TID;
+}
+
+/**
+ * \brief Creates a new user stack
+ */
+Uint Proc_MakeUserStack(void)
+{
+ int i;
+ Uint base = USER_STACK_TOP - USER_STACK_SZ;
+
+ // Check Prospective Space
+ for( i = USER_STACK_SZ >> 12; i--; )
+ {
+ if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
+ break;
+ }
+
+ if(i != -1) return 0;
+
+ // Allocate Stack - Allocate incrementally to clean up MM_Dump output
+ // - Most of the user stack is the zero page
+ for( i = 0; i < (USER_STACK_SZ-USER_STACK_PREALLOC)/0x1000; i++ )
+ {
+ MM_AllocateZero( base + (i<<12) );
+ }
+ // - but the top USER_STACK_PREALLOC pages are actually allocated
+ for( ; i < USER_STACK_SZ/0x1000; i++ )
+ {
+ tPAddr alloc = MM_Allocate( base + (i<<12) );
+ if( !alloc )
+ {
+ // Error
+ Log_Error("Proc", "Unable to allocate user stack (%i pages requested)", USER_STACK_SZ/0x1000);
+ while( i -- )
+ MM_Deallocate( base + (i<<12) );
+ return 0;
+ }
+ }
+
+ return base + USER_STACK_SZ;
+}
+
+void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
+{
+ Uint *stack;
+ int i;
+ const char **envp = NULL;
+ Uint16 ss, cs;
+
+
+ // Copy Arguments
+ stack = (void*)Proc_MakeUserStack();
+ if(!stack) {
+ Log_Error("Proc", "Unable to create user stack!");
+ Threads_Exit(0, -1);
+ }
+ stack -= (DataSize+7)/8;
+ memcpy( stack, ArgV, DataSize );
+ free(ArgV);
+
+ // Adjust Arguments and environment
+ if(DataSize)
+ {
+ Uint delta = (Uint)stack - (Uint)ArgV;
+ ArgV = (const char**)stack;
+ for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta;
+ envp = &ArgV[i+1];
+ for( i = 0; envp[i]; i++ ) envp[i] += delta;
+ }
+
+ // User Mode Segments
+ // 0x2B = 64-bit
+ ss = 0x23; cs = 0x2B;
+
+ // Arguments
+ *--stack = (Uint)envp;
+ *--stack = (Uint)ArgV;
+ *--stack = (Uint)ArgC;
+ *--stack = Base;
+
+ Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
+}
+
+void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
+{
+ if( !(CS == 0x1B || CS == 0x2B) || SS != 0x23 ) {
+ Log_Error("Proc", "Proc_StartProcess: CS / SS are not valid (%x, %x)",
+ CS, SS);
+ Threads_Exit(0, -1);
+ }
+// Log("Proc_StartProcess: (SS=%x, Stack=%p, Flags=%x, CS=%x, IP=%p)", SS, Stack, Flags, CS, IP);
+// MM_DumpTables(0, USER_MAX);
+ if(CS == 0x1B)
+ {
+ // 32-bit return
+ __asm__ __volatile__ (
+ "mov %0, %%rsp;\n\t" // Set stack pointer
+ "mov %2, %%r11;\n\t" // Set RFLAGS
+ "sysret;\n\t"
+ : : "r" (Stack), "c" (IP), "r" (Flags)
+ );
+ }
+ else
+ {
+ // 64-bit return
+ __asm__ __volatile__ (
+ "mov %0, %%rsp;\n\t" // Set stack pointer
+ "mov %2, %%r11;\n\t" // Set RFLAGS
+ "sysretq;\n\t"
+ : : "r" (Stack), "c" (IP), "r" (Flags)
+ );
+ }
+ for(;;);
+}
+
+/**
+ * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
+ * \brief Demotes a process to a lower permission level
+ * \param Err Pointer to user's errno
+ * \param Dest New Permission Level
+ * \param Regs Pointer to user's register structure
+ */
+int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
+{
+ int cpl = Regs->CS & 3;
+ // Sanity Check
+ if(Dest > 3 || Dest < 0) {
+ *Err = -EINVAL;
+ return -1;
+ }
+
+ // Permission Check
+ if(cpl > Dest) {
+ *Err = -EACCES;
+ return -1;
+ }
+
+ // Change the Segment Registers
+ Regs->CS = (((Dest+1)<<4) | Dest) - 8;
+ Regs->SS = ((Dest+1)<<4) | Dest;
+
+ return 0;
+}
+
+/**
+ * \brief Calls a signal handler in user mode
+ * \note Used for signals
+ */
+void Proc_CallFaultHandler(tThread *Thread)
+{
+ // Never returns
+ Proc_ReturnToUser(Thread->FaultHandler, Thread->KernelStack, Thread->CurFaultNum);
+ for(;;);
+}
+
+void Proc_DumpThreadCPUState(tThread *Thread)
+{
+ Log(" At %04x:%016llx", Thread->SavedState.UserCS, Thread->SavedState.UserRIP);
+}
+
+void Proc_Reschedule(void)
+{
+ tThread *nextthread, *curthread;
+ int cpu = GetCPUNum();
+
+ // TODO: Wait for it?
+ if(IS_LOCKED(&glThreadListLock)) return;
+
+ curthread = gaCPUs[cpu].Current;
+
+ nextthread = Threads_GetNextToRun(cpu, curthread);
+
+ if(nextthread == curthread) return ;
+ if(!nextthread)
+ nextthread = gaCPUs[cpu].IdleThread;
+ if(!nextthread)
+ return ;
+
+ #if DEBUG_TRACE_SWITCH
+ LogF("\nSwitching to task CR3 = 0x%x, RIP = %p, RSP = %p - %i (%s)\n",
+ nextthread->Process->MemState.CR3,
+ nextthread->SavedState.RIP,
+ nextthread->SavedState.RSP,
+ nextthread->TID,
+ nextthread->ThreadName
+ );
+ #endif
+
+ // Update CPU state
+ gaCPUs[cpu].Current = nextthread;
+ gTSSs[cpu].RSP0 = nextthread->KernelStack-4;
+ __asm__ __volatile__ ("mov %0, %%db0" : : "r" (nextthread));
+
+ if( curthread )
+ {
+ // Save FPU/MMX/XMM/SSE state
+ if( curthread->SavedState.SSE )
+ {
+ Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
+ curthread->SavedState.bSSEModified = 0;
+ Proc_DisableSSE();
+ }
+ SwitchTasks(
+ nextthread->SavedState.RSP, &curthread->SavedState.RSP,
+ nextthread->SavedState.RIP, &curthread->SavedState.RIP,
+ nextthread->Process->MemState.CR3
+ );
+ }
+ else
+ {
+ Uint tmp;
+ SwitchTasks(
+ nextthread->SavedState.RSP, &tmp,
+ nextthread->SavedState.RIP, &tmp,
+ nextthread->Process->MemState.CR3
+ );
+ }
+ return ;
+}
+
+/**
+ * \fn void Proc_Scheduler(int CPU)
+ * \brief Swap current thread and clears dead threads
+ */
+void Proc_Scheduler(int CPU, Uint RSP, Uint RIP)
+{
+#if 0
+ tThread *thread;
+
+ // If the spinlock is set, let it complete
+ if(IS_LOCKED(&glThreadListLock)) return;
+
+ // Get current thread
+ thread = gaCPUs[CPU].Current;
+
+ if( thread )
+ {
+ tRegs *regs;
+ // Reduce remaining quantum and continue timeslice if non-zero
+ if(thread->Remaining--) return;
+ // Reset quantum for next call
+ thread->Remaining = thread->Quantum;
+
+ // TODO: Make this more stable somehow
+ {
+ regs = (tRegs*)(RSP+(1)*8); // CurThread
+ thread->SavedState.UserCS = regs->CS;
+ thread->SavedState.UserRIP = regs->RIP;
+ }
+ }
+
+ // ACK Timer here?
+
+ Proc_Reschedule();
+#endif
+}
+
+// === EXPORTS ===
+EXPORT(Proc_SpawnWorker);
--- /dev/null
+/home/tpg/Projects/RealmodeEmulator/src/rme.c
\ No newline at end of file
--- /dev/null
+/home/tpg/Projects/RealmodeEmulator/src/rme.h
\ No newline at end of file
--- /dev/null
+;
+; Acess2 x86_64 port
+;
+
+%include "arch/x86_64/include/common.inc.asm"
+
+[BITS 32]
+
+;KERNEL_BASE equ 0xFFFF800000000000
+KERNEL_BASE equ 0xFFFFFFFF80000000
+
+[section .multiboot]
+mboot:
+ ; Multiboot macros to make a few lines later more readable
+ MULTIBOOT_PAGE_ALIGN equ 1<<0
+ MULTIBOOT_MEMORY_INFO equ 1<<1
+ MULTIBOOT_AOUT_KLUDGE equ 1<<16
+ MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
+ MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO; | MULTIBOOT_AOUT_KLUDGE
+ MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
+
+ ; This is the GRUB Multiboot header. A boot signature
+ dd MULTIBOOT_HEADER_MAGIC
+ dd MULTIBOOT_HEADER_FLAGS
+ dd MULTIBOOT_CHECKSUM
+ [extern __load_addr]
+ [extern __bss_start]
+ [extern gKernelEnd]
+ ; a.out kludge
+ dd mboot ; Location of Multiboot Header
+ dd __load_addr ; Load address
+ dd __bss_start - KERNEL_BASE ; End of .data
+ dd gKernelEnd - KERNEL_BASE ; End of .bss (and kernel)
+ dd start - KERNEL_BASE ; Entrypoint
+
+[extern start64]
+
+[section .text]
+[global start]
+start:
+ mov [gMultibootMagic - KERNEL_BASE], eax
+ mov [gMultibootPtr - KERNEL_BASE], ebx
+
+ ; Check for Long Mode support
+ mov eax, 0x80000000
+ cpuid
+ cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
+ mov eax, 0x80000001
+ cpuid
+ jb .not64bitCapable
+ test edx, 1<<29
+ jz .not64bitCapable
+
+ ; Enable PGE (Page Global Enable)
+ ; + PAE (Physical Address Extension)
+ ; + PSE (Page Size Extensions)
+ mov eax, cr4
+ or eax, 0x80|0x20|0x10
+ mov cr4, eax
+
+ ; Load PDP4
+ mov eax, gInitialPML4 - KERNEL_BASE
+ mov cr3, eax
+
+ ; Enable IA-32e mode
+ ; (Also enables SYSCALL and NX)
+ mov ecx, 0xC0000080
+ rdmsr
+ or eax, (1 << 11)|(1 << 8)|(1 << 0) ; NXE, LME, SCE
+ wrmsr
+
+ ; Enable paging
+ mov eax, cr0
+ or eax, 0x80010000 ; PG & WP
+ mov cr0, eax
+
+ ; Load GDT
+ lgdt [gGDTPtr - KERNEL_BASE]
+ jmp 0x08:start64 - KERNEL_BASE
+
+.not64bitCapable:
+ mov ah, 0x0F
+ mov edi, 0xB8000
+ mov esi, csNot64BitCapable - KERNEL_BASE
+
+.loop:
+ lodsb
+ test al, al
+ jz .hlt
+ stosw
+ jmp .loop
+
+.hlt:
+ cli
+ hlt
+ jmp .hlt
+
+[section .data]
+[global gGDT]
+[global gGDTPtr]
+gGDT:
+ dd 0,0
+ dd 0x00000000, 0x00209A00 ; 0x08: 64-bit Code
+ dd 0x00000000, 0x00009200 ; 0x10: 64-bit Data
+ dd 0x00000000, 0x0040FA00 ; 0x18: 32-bit User Code
+ dd 0x00000000, 0x0040F200 ; 0x20: User Data
+ dd 0x00000000, 0x0020FA00 ; 0x28: 64-bit User Code
+ dd 0x00000000, 0x0000F200 ; 0x30: User Data (64 version)
+ times MAX_CPUS dd 0, 0x00008900, 0, 0 ; 0x38+16*n: TSS 0
+gGDTPtr:
+ dw $-gGDT-1
+ dd gGDT-KERNEL_BASE
+ dd 0
+[global gMultibootPtr]
+[global gMultibootMagic]
+gMultibootMagic:
+ dd 0
+gMultibootPtr:
+ dd 0
+
+[section .padata]
+[global gInitialPML4]
+gInitialPML4: ; Covers 256 TiB (Full 48-bit Virtual Address Space)
+ dd gInitialPDP - KERNEL_BASE + 3, 0 ; Identity Map Low 4Mb
+ times 0xA0*2-1 dq 0
+ dd gStackPDP - KERNEL_BASE + 3, 0
+ times 512-4-($-gInitialPML4)/8 dq 0
+ dd gInitialPML4 - KERNEL_BASE + 3, 0 ; Fractal Mapping
+ dq 0
+ dq 0
+ dd gHighPDP - KERNEL_BASE + 3, 0 ; Map Low 4Mb to kernel base
+
+gInitialPDP: ; Covers 512 GiB
+ dd gInitialPD - KERNEL_BASE + 3, 0
+ times 511 dq 0
+
+gStackPDP:
+ dd gStackPD - KERNEL_BASE + 3, 0
+ times 511 dq 0
+
+gHighPDP: ; Covers 512 GiB
+ times 510 dq 0
+ ;dq 0 + 0x143 ; 1 GiB Page from zero
+ dd gInitialPD - KERNEL_BASE + 3, 0
+ dq 0
+
+gInitialPD: ; Covers 1 GiB
+; dq 0 + 0x143 ; 1 GiB Page from zero
+ dd gInitialPT1 - KERNEL_BASE + 3, 0
+ dd gInitialPT2 - KERNEL_BASE + 3, 0
+ times 510 dq 0
+
+gStackPD:
+ dd gKStackPT - KERNEL_BASE + 3, 0
+ times 511 dq 0
+
+gKStackPT: ; Covers 2 MiB
+ ; Initial stack - 64KiB
+ dq 0
+ %assign i 0
+ %rep INITIAL_KSTACK_SIZE-1
+ dd gInitialKernelStack - KERNEL_BASE + i*0x1000 + 0x103, 0
+ %assign i i+1
+ %endrep
+ times 512-INITIAL_KSTACK_SIZE dq 0
+gInitialPT1: ; 2 MiB
+ %assign i 0
+ %rep 512
+ dq i*4096+0x103
+ %assign i i+1
+ %endrep
+gInitialPT2: ; 2 MiB
+ %assign i 512
+ %rep 512
+ dq i*4096+0x103
+ %assign i i+1
+ %endrep
+
+[section .padata]
+[global gInitialKernelStack]
+gInitialKernelStack:
+ times 0x1000*(INITIAL_KSTACK_SIZE-1) db 0 ; 8 Pages
+
+[section .rodata]
+csNot64BitCapable:
+ db "Not 64-bit Capable",0
+
+; vim: ft=nasm
--- /dev/null
+;
+; Acess2 x86_64 Port
+;
+%include "arch/x86_64/include/common.inc.asm"
+[bits 64]
+;KERNEL_BASE equ 0xFFFF800000000000
+KERNEL_BASE equ 0xFFFFFFFF80000000
+
+[extern kmain]
+
+[extern gMultibootPtr]
+[extern gMultibootMagic]
+
+[section .text]
+[global start64]
+start64:
+ ; Load Registers
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; Go to high memory
+ mov rax, start64.himem
+ jmp rax
+.himem:
+
+ xor rax, rax
+ mov dr0, rax ; Set CPU0
+
+ ; Clear the screen
+ mov rax, 0x1F201F201F201F20 ; Set the screen to White on blue, space (4 characters)
+ mov edi, 0xB8000
+ mov ecx, 80*25*2/8
+ rep stosq
+
+ ; Set kernel stack
+ mov rsp, 0xFFFFA00000000000 + INITIAL_KSTACK_SIZE*0x1000
+
+ ; Call main
+ mov edi, [gMultibootMagic - KERNEL_BASE]
+ mov esi, [gMultibootPtr - KERNEL_BASE]
+ call kmain
+
+ cli
+.hlt:
+ hlt
+ jmp .hlt
+
+[global GetCPUNum]
+GetCPUNum:
+ mov rax, dr1
+ ret
+
+KSTACK_USERSTATE_SIZE equ (5+2+16+2)*8 ; IRET, ErrorNum, ErrorCode, GPRs, FS&GS
+[global Proc_ReturnToUser]
+Proc_ReturnToUser:
+ ; RDI - Handler
+ ; RSI - Kernel Stack
+ ; RDX - Signal num
+
+ ;
+ ; NOTE: This can cause corruption if the signal happens while the user
+ ; has called a kernel operation.
+ ; Good thing this can only be called on a user fault.
+ ;
+
+ xchg bx, bx
+ ; Get and alter User SP
+ mov rcx, [rsi-0x20] ; Get user SP
+ xor eax, eax
+ mov [rcx-16], rax
+ sub rcx, 16
+
+ ; Drop down to user mode
+ cli
+ mov rsp, rcx ; Set SP
+ mov rcx, rdi ; SYSRET IP
+
+ mov rdi, rdx ; Argument for handler
+ mov r11, 0x202 ; RFlags
+ db 0x48
+ sysret
+
+; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args)
+; Call a function passing the array as arguments
+[global CallWithArgArray]
+CallWithArgArray:
+ push rbp
+ mov rbp, rsp
+ push r10
+ push r11
+
+ mov [rbp+2*8], rdi ; Save Ptr to stack
+
+ mov r11, rsi ; NArgs
+ mov r10, rdx ; Args
+
+ ; Arg 1: RDI
+ mov rdi, [r10]
+ add r10, 8
+ dec r11
+ jz .call
+ ; Arg 2: RSI
+ mov rsi, [r10]
+ add r10, 8
+ dec r11
+ jz .call
+ ; Arg 3: RDX
+ mov rdx, [r10]
+ add r10, 8
+ dec r11
+ jz .call
+ ; Arg 4: RCX
+ mov rcx, [r10]
+ add r10, 8
+ dec r11
+ jz .call
+ ; Arg 5: R8
+ mov r8, [r10]
+ add r10, 8
+ dec r11
+ jz .call
+ ; Arg 6: R9
+ mov r9, [r10]
+ add r10, 8
+ dec r11
+ jz .call
+ ; No support for more
+
+.call:
+ mov rax, [rbp+2*8] ; Ptr
+ call rax
+
+ pop r11
+ pop r10
+
+ lea rsp, [rbp]
+ pop rbp
+ ret
+
+; vim: ft=nasm
--- /dev/null
+/*
+ * Acess2 Kernel
+ * Timekeeping
+ * arch/x86_64/time.c
+ */
+#include <acess.h>
+#include <arch_config.h>
+
+// === MACROS ===
+#define TIMER_QUANTUM 100
+#define TIMER_FREQ PIT_TIMER_BASE_N/(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR)
+#define MS_PER_TICK_WHOLE (1000*(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR)/PIT_TIMER_BASE_N)
+#define MS_PER_TICK_FRACT ((0x80000000ULL*1000ULL*PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR/PIT_TIMER_BASE_N)&0x7FFFFFFF)
+
+// === IMPORTS ===
+extern volatile Sint64 giTimestamp;
+extern volatile Uint64 giTicks;
+extern volatile Uint64 giPartMiliseconds;
+extern void Timer_CallTimers(void);
+
+// === GLOBALS ===
+volatile Uint64 giTime_TSCAtLastTick = 0;
+volatile Uint64 giTime_TSCPerTick = 0;
+
+// === PROTOTYPES ===
+//Sint64 now(void);
+ int Time_Setup(void);
+void Time_UpdateTimestamp(void);
+Uint64 Time_ReadTSC(void);
+
+// === CODE ===
+/**
+ * \fn Sint64 now()
+ * \brief Return the current timestamp
+ */
+Sint64 now(void)
+{
+ Uint64 tsc = Time_ReadTSC();
+ tsc -= giTime_TSCAtLastTick;
+ tsc *= MS_PER_TICK_WHOLE;
+ if( giTime_TSCPerTick ) {
+ tsc /= giTime_TSCPerTick;
+ }
+ else
+ tsc = 0;
+ return giTimestamp + tsc;
+}
+
+/**
+ * \fn int Time_Setup(void)
+ * \brief Sets the system time from the Realtime-Clock
+ */
+int Time_Setup(void)
+{
+ Log_Log("Timer", "PIT Timer firing at %iHz, %i.0x%08x miliseconds per tick",
+ TIMER_FREQ, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT);
+
+ // TODO: Read time from RTC
+
+ return 0;
+}
+
+/**
+ * \brief Called on the timekeeping IRQ
+ */
+void Time_UpdateTimestamp(void)
+{
+ Uint64 curTSC = Time_ReadTSC();
+
+ if( giTime_TSCAtLastTick )
+ {
+ giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick;
+ }
+ giTime_TSCAtLastTick = curTSC;
+
+ giTicks ++;
+ giTimestamp += MS_PER_TICK_WHOLE;
+ giPartMiliseconds += MS_PER_TICK_FRACT;
+ if(giPartMiliseconds > 0x80000000) {
+ giTimestamp ++;
+ giPartMiliseconds -= 0x80000000;
+ }
+
+ Timer_CallTimers();
+}
+
+#if 0
+/**
+ * \fn void Time_TimerThread(void)
+ */
+void Time_TimerThread(void)
+{
+ Sint64 next;
+ Threads_SetName("TIMER");
+
+ next = giTimestamp + TIMER_QUANTUM;
+ for(;;)
+ {
+ while(giTimestamp < next) Threads_Yield();
+ next = giTimestamp + TIMER_QUANTUM;
+ Timer_CallTimers();
+ }
+}
+#endif
+
+Uint64 Time_ReadTSC(void)
+{
+ Uint32 a, d;
+ __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d));
+ return ((Uint64)d << 32) | a;
+}
--- /dev/null
+/*
+ */
+#include <acess.h>
+#include <vm8086.h>
+#include <modules.h>
+//#include "rme.h"
+
+// === CONSTANTS ===
+#define VM8086_STACK_SEG 0x9F00
+#define VM8086_STACK_OFS 0x0AFE
+#define VM8086_PAGES_PER_INST 4
+
+// === PROTOTYPES ===
+ int VM8086_Install(char **Arguments);
+//tVM8086 *VM8086_Init(void);
+//void VM8086_Free(tVM8086 *State);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL);
+tMutex glVM8086_Process;
+//tRME_State *gpVM8086_State;
+tPID gVM8086_WorkerPID;
+tTID gVM8086_CallingThread;
+tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions
+
+// === CODE ===
+int VM8086_Install(char **Arguments)
+{
+ //gpVM8086_State = RME_CreateState();
+ return MODULE_ERR_OK;
+}
+
+tVM8086 *VM8086_Init(void)
+{
+ return NULL;
+}
+
+void VM8086_Free(tVM8086 *State)
+{
+
+}
+
+void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset)
+{
+ return NULL;
+}
+
+void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset)
+{
+ return NULL;
+}
+
+void VM8086_Int(tVM8086 *State, Uint8 Interrupt)
+{
+
+}
--- /dev/null
+Acess2 Binary File Loader Specifcation
+--------------------------------------
+
+Binary file loaders are defined by creating a \a tBinaryType variable that
+is registered with the kernel.
+
+\a tBinaryType contains seven fields.
+-# \a Next
+ This field is used internally by the kernel and should be set to NULL
+ when the definition is initialise and not changed by the driver.
+-# \a Ident
+ This field tells the kernel how to recognise this file format. If the
+ first 32-bits of the file (ANDed with the \a Mask field) match this
+ value, the file will be passed to this loader.
+-# \a Mask
+ Determines what bits in the first 32-bits of the file matter for the
+ identifcation.
+-# \a Name
+ This is a C string that uniquely identifies this binary format.
+-# \a Load
+ This field is a pointer to a function that takes a VFS Handle of the
+ source exectuable file as an argument and returns a \a tBinary
+ pointer that defines the location and attributes of the exectable's
+ segments. (See \a tBinary for more information)
--- /dev/null
+/*\r
+ * Acess v0.1\r
+ * ELF Executable Loader Code\r
+ */\r
+#define DEBUG 0\r
+#include <acess.h>\r
+#include <binary.h>\r
+#include "elf.h"\r
+\r
+#define DEBUG_WARN 1\r
+\r
+// === PROTOTYPES ===\r
+tBinary *Elf_Load(int fp);\r
+tBinary *Elf_Load64(int fp, Elf64_Ehdr *hdr);\r
+tBinary *Elf_Load32(int fp, Elf32_Ehdr *hdr);\r
+ int Elf_Relocate(void *Base);\r
+ int Elf_Relocate32(void *Base);\r
+ int Elf_GetSymbol(void *Base, const char *Name, Uint *ret);\r
+ int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base);\r
+Uint Elf_Int_HashString(const char *str);\r
+\r
+// === GLOBALS ===\r
+tBinaryType gELF_Info = {\r
+ NULL,\r
+ 0x464C457F, 0xFFFFFFFF, // '\x7FELF'\r
+ "ELF",\r
+ Elf_Load, Elf_Relocate, Elf_GetSymbol\r
+ };\r
+\r
+// === CODE ===\r
+tBinary *Elf_Load(int fp)\r
+{\r
+ Elf64_Ehdr hdr;\r
+ \r
+ // Read ELF Header\r
+ VFS_Read(fp, sizeof(hdr), &hdr);\r
+ \r
+ // Check the file type\r
+ if(hdr.e_ident[0] != 0x7F || hdr.e_ident[1] != 'E' || hdr.e_ident[2] != 'L' || hdr.e_ident[3] != 'F') {\r
+ Log_Warning("ELF", "Non-ELF File was passed to the ELF loader");\r
+ return NULL;\r
+ }\r
+\r
+ switch(hdr.e_ident[4]) // EI_CLASS\r
+ {\r
+ case ELFCLASS32:\r
+ return Elf_Load32(fp, (Elf32_Ehdr*)&hdr);\r
+ case ELFCLASS64:\r
+ return Elf_Load64(fp, &hdr);\r
+ default:\r
+ Log_Warning("ELF", "Unknown EI_CLASS value %i", hdr.e_ident[4]);\r
+ return NULL;\r
+ }\r
+}\r
+\r
+tBinary *Elf_Load64(int FD, Elf64_Ehdr *Header)\r
+{\r
+ tBinary *ret;\r
+ Elf64_Phdr phtab[Header->e_phnum];\r
+ int nLoadSegments;\r
+ int i, j;\r
+ \r
+ // Sanity check\r
+ if( Header->e_phoff == 0 )\r
+ {\r
+ Log_Warning("ELF", "No program header, panic!");\r
+ return NULL;\r
+ }\r
+ if( Header->e_shentsize != sizeof(Elf64_Shdr) ) {\r
+ Log_Warning("ELF", "Header gives shentsize as %i, my type is %i",\r
+ Header->e_shentsize, sizeof(Elf64_Shdr) );\r
+ }\r
+ if( Header->e_phentsize != sizeof(Elf64_Phdr) ) {\r
+ Log_Warning("ELF", "Header gives phentsize as %i, my type is %i",\r
+ Header->e_phentsize, sizeof(Elf64_Phdr) );\r
+ }\r
+\r
+ LOG("Header = {");\r
+ LOG(" e_ident = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",\r
+ Header->e_ident[0], Header->e_ident[1], Header->e_ident[2], Header->e_ident[3],\r
+ Header->e_ident[4], Header->e_ident[5], Header->e_ident[6], Header->e_ident[7],\r
+ Header->e_ident[8], Header->e_ident[9], Header->e_ident[10], Header->e_ident[11],\r
+ Header->e_ident[12], Header->e_ident[13], Header->e_ident[14], Header->e_ident[15]\r
+ );\r
+ LOG(" e_type = %i", Header->e_type);\r
+ LOG(" e_machine = %i", Header->e_machine);\r
+ LOG(" e_version = %i", Header->e_version);\r
+ LOG(" e_entry = 0x%llx", Header->e_entry);\r
+ LOG(" e_phoff = 0x%llx", Header->e_phoff);\r
+ LOG(" e_shoff = 0x%llx", Header->e_shoff);\r
+ LOG(" e_flags = 0x%x", Header->e_flags);\r
+ LOG(" e_ehsize = %i", Header->e_ehsize);\r
+ LOG(" e_phentsize = %i", Header->e_phentsize);\r
+ LOG(" e_phnum = %i", Header->e_phnum);\r
+ LOG(" e_shentsize = %i", Header->e_shentsize);\r
+ LOG(" e_shnum = %i", Header->e_shnum);\r
+ LOG(" e_shstrndx = %i", Header->e_shstrndx);\r
+ LOG("}");\r
+\r
+ // Load Program Header table\r
+ VFS_Seek(FD, Header->e_phoff, SEEK_SET);\r
+ VFS_Read(FD, sizeof(Elf64_Phdr)*Header->e_phnum, phtab);\r
+\r
+ // Count load segments\r
+ nLoadSegments = 0;\r
+ for( i = 0; i < Header->e_phnum; i ++ )\r
+ {\r
+ if( phtab[i].p_type != PT_LOAD ) continue ;\r
+ nLoadSegments ++;\r
+ }\r
+ \r
+ // Allocate Information Structure\r
+ ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*nLoadSegments );\r
+ // Fill Info Struct\r
+ ret->Entry = Header->e_entry;\r
+ ret->Base = -1; // Set Base to maximum value\r
+ ret->NumSections = nLoadSegments;\r
+ ret->Interpreter = NULL;\r
+\r
+ j = 0; // LoadSections[] index\r
+ for( i = 0; i < Header->e_phnum; i ++ )\r
+ {\r
+ LOG("phtab[%i] = {", i);\r
+ LOG(" .p_type = %i", phtab[i].p_type);\r
+ LOG(" .p_flags = 0x%x", phtab[i].p_flags);\r
+ LOG(" .p_offset = 0x%llx", phtab[i].p_offset);\r
+ LOG(" .p_vaddr = 0x%llx", phtab[i].p_vaddr);\r
+ LOG(" .p_paddr = 0x%llx", phtab[i].p_paddr);\r
+ LOG(" .p_filesz = 0x%llx", phtab[i].p_filesz);\r
+ LOG(" .p_memsz = 0x%llx", phtab[i].p_memsz);\r
+ LOG(" .p_align = 0x%llx", phtab[i].p_align);\r
+ LOG("}");\r
+\r
+ // Get Interpreter Name\r
+ if( phtab[i].p_type == PT_INTERP )\r
+ {\r
+ char *tmp;\r
+ if(ret->Interpreter) continue;\r
+ tmp = malloc(phtab[i].p_filesz);\r
+ VFS_Seek(FD, phtab[i].p_offset, 1);\r
+ VFS_Read(FD, phtab[i].p_filesz, tmp);\r
+ ret->Interpreter = Binary_RegInterp(tmp);\r
+ LOG("Interpreter '%s'", tmp);\r
+ free(tmp);\r
+ continue;\r
+ }\r
+ \r
+ if( phtab[i].p_type != PT_LOAD ) continue ;\r
+ \r
+ // Find the executable base\r
+ if( phtab[i].p_vaddr < ret->Base ) ret->Base = phtab[i].p_vaddr;\r
+\r
+ ret->LoadSections[j].Offset = phtab[i].p_offset;\r
+ ret->LoadSections[j].Virtual = phtab[i].p_vaddr;\r
+ ret->LoadSections[j].FileSize = phtab[i].p_filesz;\r
+ ret->LoadSections[j].MemSize = phtab[i].p_memsz;\r
+ \r
+ ret->LoadSections[j].Flags = 0;\r
+ if( !(phtab[i].p_flags & PF_W) )\r
+ ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO;\r
+ if( phtab[i].p_flags & PF_X )\r
+ ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC;\r
+ j ++;\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header)\r
+{\r
+ tBinary *ret;\r
+ Elf32_Phdr *phtab;\r
+ int i, j;\r
+ int iLoadCount;\r
+\r
+ ENTER("xFD", FD);\r
+\r
+ // Check architecture with current CPU\r
+ // - TODO: Support kernel level emulation\r
+ #if ARCH_IS_x86\r
+ if( Header->machine != EM_386 )\r
+ {\r
+ Log_Warning("ELF", "Unknown architecure on ELF-32");\r
+ LEAVE_RET('n');\r
+ return NULL;\r
+ }\r
+ #endif\r
+\r
+ // Check for a program header\r
+ if(Header->phoff == 0) {\r
+ #if DEBUG_WARN\r
+ Log_Warning("ELF", "File does not contain a program header (phoff == 0)");\r
+ #endif\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Read Program Header Table\r
+ phtab = malloc( sizeof(Elf32_Phdr) * Header->phentcount );\r
+ if( !phtab ) {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ LOG("hdr.phoff = 0x%08x", Header->phoff);\r
+ VFS_Seek(FD, Header->phoff, SEEK_SET);\r
+ VFS_Read(FD, sizeof(Elf32_Phdr)*Header->phentcount, phtab);\r
+ \r
+ // Count Pages\r
+ iLoadCount = 0;\r
+ LOG("Header->phentcount = %i", Header->phentcount);\r
+ for( i = 0; i < Header->phentcount; i++ )\r
+ {\r
+ // Ignore Non-LOAD types\r
+ if(phtab[i].Type != PT_LOAD)\r
+ continue;\r
+ iLoadCount ++;\r
+ LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize);\r
+ }\r
+ \r
+ LOG("iLoadCount = %i", iLoadCount);\r
+ \r
+ // Allocate Information Structure\r
+ ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*iLoadCount );\r
+ // Fill Info Struct\r
+ ret->Entry = Header->entrypoint;\r
+ ret->Base = -1; // Set Base to maximum value\r
+ ret->NumSections = iLoadCount;\r
+ ret->Interpreter = NULL;\r
+ \r
+ // Load Pages\r
+ j = 0;\r
+ for( i = 0; i < Header->phentcount; i++ )\r
+ {\r
+ //LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type);\r
+ LOG("phtab[%i] = {", i);\r
+ LOG(" .Type = 0x%08x", phtab[i].Type);\r
+ LOG(" .Offset = 0x%08x", phtab[i].Offset);\r
+ LOG(" .VAddr = 0x%08x", phtab[i].VAddr);\r
+ LOG(" .PAddr = 0x%08x", phtab[i].PAddr);\r
+ LOG(" .FileSize = 0x%08x", phtab[i].FileSize);\r
+ LOG(" .MemSize = 0x%08x", phtab[i].MemSize);\r
+ LOG(" .Flags = 0x%08x", phtab[i].Flags);\r
+ LOG(" .Align = 0x%08x", phtab[i].Align);\r
+ LOG(" }");\r
+ // Get Interpreter Name\r
+ if( phtab[i].Type == PT_INTERP )\r
+ {\r
+ char *tmp;\r
+ if(ret->Interpreter) continue;\r
+ tmp = malloc(phtab[i].FileSize);\r
+ VFS_Seek(FD, phtab[i].Offset, 1);\r
+ VFS_Read(FD, phtab[i].FileSize, tmp);\r
+ ret->Interpreter = Binary_RegInterp(tmp);\r
+ LOG("Interpreter '%s'", tmp);\r
+ free(tmp);\r
+ continue;\r
+ }\r
+ // Ignore non-LOAD types\r
+ if(phtab[i].Type != PT_LOAD) continue;\r
+ \r
+ // Find Base\r
+ if(phtab[i].VAddr < ret->Base) ret->Base = phtab[i].VAddr;\r
+ \r
+ LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}",\r
+ i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize);\r
+ \r
+ ret->LoadSections[j].Offset = phtab[i].Offset;\r
+ ret->LoadSections[j].FileSize = phtab[i].FileSize;\r
+ ret->LoadSections[j].Virtual = phtab[i].VAddr;\r
+ ret->LoadSections[j].MemSize = phtab[i].MemSize;\r
+ ret->LoadSections[j].Flags = 0;\r
+ if( !(phtab[i].Flags & PF_W) )\r
+ ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO;\r
+ if( phtab[i].Flags & PF_X )\r
+ ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC;\r
+ j ++;\r
+ }\r
+ \r
+ // Clean Up\r
+ free(phtab);\r
+ // Return\r
+ LEAVE('p', ret);\r
+ return ret;\r
+}\r
+\r
+// --- ELF RELOCATION ---\r
+int Elf_Relocate(void *Base)\r
+{\r
+ Elf64_Ehdr *hdr = Base;\r
+ \r
+ switch( hdr->e_ident[EI_CLASS] )\r
+ {\r
+ case ELFCLASS32:\r
+ return Elf_Relocate32(Base);\r
+ case ELFCLASS64:\r
+ return 0;\r
+ default:\r
+ return 1;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ * \brief Relocates a loaded ELF Executable\r
+ */\r
+int Elf_Relocate32(void *Base)\r
+{\r
+ Elf32_Ehdr *hdr = Base;\r
+ Elf32_Phdr *phtab;\r
+ int i, j; // Counters\r
+ char *libPath;\r
+ Uint iRealBase = -1;\r
+ Uint iBaseDiff;\r
+ int iSegmentCount;\r
+ int iSymCount = 0;\r
+ Elf32_Rel *rel = NULL;\r
+ Elf32_Rela *rela = NULL;\r
+ Uint32 *pltgot = NULL;\r
+ void *plt = NULL;\r
+ Uint32 *ptr;\r
+ int relSz=0, relEntSz=8;\r
+ int relaSz=0, relaEntSz=8;\r
+ int pltSz=0, pltType=0;\r
+ Elf32_Dyn *dynamicTab = NULL; // Dynamic Table Pointer\r
+ char *dynstrtab = NULL; // .dynamic String Table\r
+ Elf32_Sym *dynsymtab = NULL;\r
+ int bFailed = 0;\r
+ \r
+ ENTER("pBase", Base);\r
+ \r
+ // Parse Program Header to get Dynamic Table\r
+ phtab = (void *)( (tVAddr)Base + hdr->phoff );\r
+ iSegmentCount = hdr->phentcount;\r
+ for(i = 0; i < iSegmentCount; i ++ )\r
+ {\r
+ // Determine linked base address\r
+ if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr)\r
+ iRealBase = phtab[i].VAddr;\r
+ \r
+ // Find Dynamic Section\r
+ if(phtab[i].Type == PT_DYNAMIC) {\r
+ if(dynamicTab) {\r
+ Log_Warning("ELF", "Elf_Relocate - Multiple PT_DYNAMIC segments\n");\r
+ continue;\r
+ }\r
+ dynamicTab = (void *) (tVAddr) phtab[i].VAddr;\r
+ j = i; // Save Dynamic Table ID\r
+ break;\r
+ }\r
+ }\r
+ \r
+ // Check if a PT_DYNAMIC segement was found\r
+ if(!dynamicTab) {\r
+ Log_Warning("ELF", "Elf_Relocate: No PT_DYNAMIC segment in image, returning\n");\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ \r
+ // Page Align real base\r
+ iRealBase &= ~0xFFF;\r
+ \r
+ // Adjust "Real" Base\r
+ iBaseDiff = (Uint)Base - iRealBase;\r
+ // Adjust Dynamic Table\r
+ dynamicTab = (void *) ((Uint)dynamicTab + iBaseDiff);\r
+ \r
+ // === Get Symbol table and String Table ===\r
+ for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
+ {\r
+ switch(dynamicTab[j].d_tag)\r
+ {\r
+ // --- Symbol Table ---\r
+ case DT_SYMTAB:\r
+ dynamicTab[j].d_val += iBaseDiff;\r
+ dynsymtab = (void*) (tVAddr) dynamicTab[j].d_val;\r
+ hdr->misc.SymTable = dynamicTab[j].d_val; // Saved in unused bytes of ident\r
+ break;\r
+ \r
+ // --- String Table ---\r
+ case DT_STRTAB:\r
+ dynamicTab[j].d_val += iBaseDiff;\r
+ dynstrtab = (void*) (tVAddr) dynamicTab[j].d_val;\r
+ break;\r
+ \r
+ // --- Hash Table --\r
+ case DT_HASH:\r
+ dynamicTab[j].d_val += iBaseDiff;\r
+ iSymCount = ((Uint*)((tVAddr)dynamicTab[j].d_val))[1];\r
+ hdr->misc.HashTable = dynamicTab[j].d_val; // Saved in unused bytes of ident\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( !dynsymtab && iSymCount > 0 ) {\r
+ Log_Warning("ELF", "Elf_Relocate: No Dynamic symbol table, but count >0");\r
+ return 0;\r
+ }\r
+\r
+ // Alter Symbols to true base\r
+ for(i = 0; i < iSymCount; i ++)\r
+ {\r
+ dynsymtab[i].value += iBaseDiff;\r
+ dynsymtab[i].nameOfs += (Uint)dynstrtab;\r
+ //LOG("Sym '%s' = 0x%x (relocated)\n", dynsymtab[i].name, dynsymtab[i].value);\r
+ }\r
+ \r
+ // === Add to loaded list (can be imported now) ===\r
+ //Binary_AddLoaded( (Uint)Base );\r
+\r
+ // === Parse Relocation Data ===\r
+ for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
+ {\r
+ switch(dynamicTab[j].d_tag)\r
+ {\r
+ // --- Shared Library Name ---\r
+ case DT_SONAME:\r
+ LOG(".so Name '%s'\n", dynstrtab+dynamicTab[j].d_val);\r
+ break;\r
+ // --- Needed Library ---\r
+ case DT_NEEDED:\r
+ libPath = dynstrtab + dynamicTab[j].d_val;\r
+ Log_Notice("ELF", "%p - Required Library '%s' (Ignored in kernel mode)\n", Base, libPath);\r
+ break;\r
+ // --- PLT/GOT ---\r
+ case DT_PLTGOT: pltgot = (void*)(iBaseDiff+dynamicTab[j].d_val); break;\r
+ case DT_JMPREL: plt = (void*)(iBaseDiff+dynamicTab[j].d_val); break;\r
+ case DT_PLTREL: pltType = dynamicTab[j].d_val; break;\r
+ case DT_PLTRELSZ: pltSz = dynamicTab[j].d_val; break;\r
+ \r
+ // --- Relocation ---\r
+ case DT_REL: rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
+ case DT_RELSZ: relSz = dynamicTab[j].d_val; break;\r
+ case DT_RELENT: relEntSz = dynamicTab[j].d_val; break;\r
+ \r
+ case DT_RELA: rela = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
+ case DT_RELASZ: relaSz = dynamicTab[j].d_val; break;\r
+ case DT_RELAENT: relaEntSz = dynamicTab[j].d_val; break;\r
+ }\r
+ }\r
+ \r
+ // Parse Relocation Entries\r
+ if(rel && relSz)\r
+ {\r
+ j = relSz / relEntSz;\r
+ for( i = 0; i < j; i++ )\r
+ {\r
+ ptr = (void*)(iBaseDiff + rel[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) {\r
+ bFailed = 1;\r
+ }\r
+ }\r
+ }\r
+ // Parse Relocation Entries\r
+ if(rela && relaSz)\r
+ {\r
+ j = relaSz / relaEntSz;\r
+ for( i = 0; i < j; i++ )\r
+ {\r
+ ptr = (void*)(iBaseDiff + rela[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(rela[i].r_info, ptr, rela[i].r_addend, dynsymtab, (Uint)Base) ) {\r
+ bFailed = 1;\r
+ }\r
+ }\r
+ }\r
+ \r
+ // === Process PLT (Procedure Linkage Table) ===\r
+ if(plt && pltSz)\r
+ {\r
+ if(pltType == DT_REL)\r
+ {\r
+ Elf32_Rel *pltRel = plt;\r
+ j = pltSz / sizeof(Elf32_Rel);\r
+ LOG("PLT Rel - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j);\r
+ for(i = 0; i < j; i++)\r
+ {\r
+ ptr = (void*)(iBaseDiff + pltRel[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(pltRel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) {\r
+ bFailed = 1;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ Elf32_Rela *pltRela = plt;\r
+ j = pltSz / sizeof(Elf32_Rela);\r
+ LOG("PLT RelA - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j);\r
+ for(i=0;i<j;i++)\r
+ {\r
+ ptr = (void*)(iBaseDiff + pltRela[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(pltRela[i].r_info, ptr, pltRela[i].r_addend, dynsymtab, (Uint)Base) ) {\r
+ bFailed = 1;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ if(bFailed) {\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ \r
+ LEAVE('x', 1);\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn void Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base)\r
+ * \brief Performs a relocation\r
+ * \param r_info Field from relocation entry\r
+ * \param ptr Pointer to location of relocation\r
+ * \param addend Value to add to symbol\r
+ * \param symtab Symbol Table\r
+ * \param base Base of loaded binary\r
+ */\r
+int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base)\r
+{\r
+ Uint val;\r
+ int type = ELF32_R_TYPE(r_info);\r
+ int sym = ELF32_R_SYM(r_info);\r
+ char *sSymName = symtab[sym].name;\r
+ \r
+ //LogF("Elf_Int_DoRelocate: (r_info=0x%x, ptr=0x%x, addend=0x%x, .., base=0x%x)\n",\r
+ // r_info, ptr, addend, base);\r
+ \r
+ switch( type )\r
+ {\r
+ // Standard 32 Bit Relocation (S+A)\r
+ case R_386_32:\r
+ if( !Elf_GetSymbol((void*)base, sSymName, &val) ) // Search this binary first\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ LOG("%08x R_386_32 *0x%x += 0x%x('%s')", r_info, ptr, val, sSymName);\r
+ *ptr = val + addend;\r
+ break;\r
+ \r
+ // 32 Bit Relocation wrt. Offset (S+A-P)\r
+ case R_386_PC32:\r
+ if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ LOG("%08x R_386_PC32 *0x%x = 0x%x + 0x%x('%s') - 0x%x", r_info, ptr, *ptr, val, sSymName, (Uint)ptr );\r
+ // TODO: Check if it needs the true value of ptr or the compiled value\r
+ // NOTE: Testing using true value\r
+ *ptr = val + addend - (Uint)ptr;\r
+ break;\r
+\r
+ // Absolute Value of a symbol (S)\r
+ case R_386_GLOB_DAT:\r
+ if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ LOG("%08x R_386_GLOB_DAT *0x%x = 0x%x (%s)", r_info, ptr, val, sSymName);\r
+ *ptr = val;\r
+ break;\r
+ \r
+ // Absolute Value of a symbol (S)\r
+ case R_386_JMP_SLOT:\r
+ if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ LOG("%08x R_386_JMP_SLOT *0x%x = 0x%x (%s)", r_info, ptr, val, sSymName);\r
+ *ptr = val;\r
+ break;\r
+\r
+ // Base Address (B+A)\r
+ case R_386_RELATIVE:\r
+ LOG("%08x R_386_RELATIVE *0x%x = 0x%x + 0x%x", r_info, ptr, base, addend);\r
+ *ptr = base + addend;\r
+ break;\r
+ \r
+ default:\r
+ LOG("Rel 0x%x: 0x%x,%i", ptr, sym, type);\r
+ break;\r
+ }\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn int Elf_GetSymbol(void *Base, const char *name, Uint *ret)\r
+ * \brief Get a symbol from the loaded binary\r
+ */\r
+int Elf_GetSymbol(void *Base, const char *Name, Uint *ret)\r
+{\r
+ Elf32_Ehdr *hdr = (void*)Base;\r
+ Elf32_Sym *symtab;\r
+ int nbuckets = 0;\r
+ int iSymCount = 0;\r
+ int i;\r
+ Uint *pBuckets;\r
+ Uint *pChains;\r
+ Uint iNameHash;\r
+\r
+ if(!Base) return 0;\r
+\r
+ pBuckets = (void *) hdr->misc.HashTable;\r
+ symtab = (void *) hdr->misc.SymTable;\r
+ \r
+ nbuckets = pBuckets[0];\r
+ iSymCount = pBuckets[1];\r
+ pBuckets = &pBuckets[2];\r
+ pChains = &pBuckets[ nbuckets ];\r
+ \r
+ // Get hash\r
+ iNameHash = Elf_Int_HashString(Name);\r
+ iNameHash %= nbuckets;\r
+\r
+ // Check Bucket\r
+ i = pBuckets[ iNameHash ];\r
+ if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[i].name, Name) == 0) {\r
+ if(ret) *ret = symtab[ i ].value;\r
+ return 1;\r
+ }\r
+ \r
+ // Walk Chain\r
+ while(pChains[i] != STN_UNDEF)\r
+ {\r
+ i = pChains[i];\r
+ if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[ i ].name, Name) == 0) {\r
+ if(ret) *ret = symtab[ i ].value;\r
+ return 1;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn Uint Elf_Int_HashString(char *str)\r
+ * \brief Hash a string in the ELF format\r
+ * \param str String to hash\r
+ * \return Hash value\r
+ */\r
+Uint Elf_Int_HashString(const char *str)\r
+{\r
+ Uint h = 0, g;\r
+ while(*str)\r
+ {\r
+ h = (h << 4) + *str++;\r
+ if( (g = h & 0xf0000000) )\r
+ h ^= g >> 24;\r
+ h &= ~g;\r
+ }\r
+ return h;\r
+}\r
--- /dev/null
+/**\r
+ * \file elf.h\r
+ * \brief ELF Exeutable Loader\r
+ */\r
+#ifndef _BIN_ELF_H\r
+#define _BIN_ELF_H\r
+\r
+typedef Uint16 Elf64_Half;\r
+typedef Uint32 Elf64_Word;\r
+typedef Uint64 Elf64_Addr;\r
+typedef Uint64 Elf64_Off;\r
+typedef Uint64 Elf64_Xword;\r
+\r
+enum e_ident_values\r
+{\r
+ EI_MAG0,\r
+ EI_MAG1,\r
+ EI_MAG2,\r
+ EI_MAG3,\r
+ EI_CLASS,\r
+ EI_DATA,\r
+ EI_VERSION,\r
+ EI_OSABI,\r
+ EI_ABIVERSION,\r
+ EI_PAD,\r
+ EI_NIDENT = 16,\r
+};\r
+\r
+#define ELFCLASS32 1\r
+#define ELFCLASS64 2\r
+\r
+/**\r
+ * \brief ELF File Header\r
+ */\r
+struct sElf32_Ehdr\r
+{\r
+ union {\r
+ char ident[16]; //!< Identifier Bytes\r
+ struct {\r
+ Uint Ident1;\r
+ Uint Ident2;\r
+ Uint HashTable;\r
+ Uint SymTable;\r
+ } misc;\r
+ };\r
+ Uint16 filetype; //!< File Type\r
+ Uint16 machine; //!< Machine / Arch\r
+ Uint32 version; //!< Version (File?)\r
+ Uint32 entrypoint; //!< Entry Point\r
+ Uint32 phoff; //!< Program Header Offset\r
+ Uint32 shoff; //!< Section Header Offset\r
+ Uint32 flags; //!< Flags\r
+ Uint16 headersize; //!< Header Size\r
+ Uint16 phentsize; //!< Program Header Entry Size\r
+ Uint16 phentcount; //!< Program Header Entry Count\r
+ Uint16 shentsize; //!< Section Header Entry Size\r
+ Uint16 shentcount; //!< Section Header Entry Count\r
+ Uint16 shstrindex; //!< Section Header String Table Index\r
+} __attribute__ ((packed));\r
+\r
+typedef struct\r
+{\r
+ unsigned char e_ident[16];\r
+ Elf64_Half e_type;\r
+ Elf64_Half e_machine;\r
+ Elf64_Word e_version;\r
+ Elf64_Addr e_entry;\r
+ Elf64_Off e_phoff;\r
+ Elf64_Off e_shoff;\r
+ Elf64_Word e_flags;\r
+ Elf64_Half e_ehsize;\r
+ Elf64_Half e_phentsize;\r
+ Elf64_Half e_phnum;\r
+ Elf64_Half e_shentsize;\r
+ Elf64_Half e_shnum;\r
+ Elf64_Half e_shstrndx;\r
+} Elf64_Ehdr;\r
+\r
+/**\r
+ * \brief Executable Types\r
+ */\r
+enum eElf32_ExecTypes\r
+{\r
+ ET_NONE = 0, //!< NULL Type\r
+ ET_REL = 1, //!< Relocatable (Object)\r
+ ET_EXEC = 2, //!< Executable\r
+ ET_DYN = 3, //!< Dynamic Library\r
+ ET_CORE = 4, //!< Core?\r
+ ET_LOPROC = 0xFF00, //!< Low Impl Defined\r
+ ET_HIPROC = 0xFFFF //!< High Impl Defined\r
+};\r
+\r
+/**\r
+ \name Section IDs\r
+ \{\r
+*/\r
+#define SHN_UNDEF 0 //!< Undefined Section\r
+#define SHN_LORESERVE 0xFF00 //!< Low Reserved\r
+#define SHN_LOPROC 0xFF00 //!< Low Impl Defined\r
+#define SHN_HIPROC 0xFF1F //!< High Impl Defined\r
+#define SHN_ABS 0xFFF1 //!< Absolute Address (Base: 0, Size: -1)\r
+#define SHN_COMMON 0xFFF2 //!< Common\r
+#define SHN_HIRESERVE 0xFFFF //!< High Reserved\r
+//! \}\r
+\r
+/**\r
+ \enum eElfSectionTypes\r
+ \brief ELF Section Types\r
+*/\r
+enum eElfSectionTypes {\r
+ SHT_NULL, //0\r
+ SHT_PROGBITS, //1\r
+ SHT_SYMTAB, //2\r
+ SHT_STRTAB, //3\r
+ SHT_RELA, //4\r
+ SHT_HASH, //5\r
+ SHT_DYNAMIC, //6\r
+ SHT_NOTE, //7\r
+ SHT_NOBITS, //8\r
+ SHT_REL, //9\r
+ SHT_SHLIB, //A\r
+ SHT_DYNSYM, //B\r
+ SHT_LAST, //C\r
+ SHT_LOPROC = 0x70000000,\r
+ SHT_HIPROC = 0x7fffffff,\r
+ SHT_LOUSER = 0x80000000,\r
+ SHT_HIUSER = 0xffffffff\r
+};\r
+\r
+#define SHF_WRITE 0x1\r
+#define SHF_ALLOC 0x2\r
+#define SHF_EXECINSTR 0x4\r
+#define SHF_MASKPROC 0xf0000000\r
+\r
+struct sElf32_Shent {\r
+ Uint32 name;\r
+ Uint32 type;\r
+ Uint32 flags;\r
+ Uint32 address;\r
+ Uint32 offset;\r
+ Uint32 size;\r
+ Uint32 link;\r
+ Uint32 info;\r
+ Uint32 addralign;\r
+ Uint32 entsize;\r
+} __attribute__ ((packed)); //sizeof = 40\r
+\r
+typedef struct\r
+{\r
+ Elf64_Word sh_name;\r
+ Elf64_Word sh_type;\r
+ Elf64_Xword sh_flags;\r
+ Elf64_Addr sh_addr;\r
+ Elf64_Off sh_offset;\r
+ Elf64_Xword sh_size;\r
+ Elf64_Word sh_link;\r
+ Elf64_Word sh_info;\r
+ Elf64_Xword sh_addralign;\r
+ Elf64_Xword sh_entsize;\r
+} Elf64_Shdr;\r
+\r
+struct elf_sym_s {\r
+ union {\r
+ Uint32 nameOfs;\r
+ char *name;\r
+ };\r
+ Uint32 value; //Address\r
+ Uint32 size;\r
+ Uint8 info;\r
+ Uint8 other;\r
+ Uint16 shndx;\r
+} __attribute__ ((packed));\r
+#define STN_UNDEF 0 // Undefined Symbol\r
+\r
+enum {\r
+ PT_NULL, //0\r
+ PT_LOAD, //1\r
+ PT_DYNAMIC, //2\r
+ PT_INTERP, //3\r
+ PT_NOTE, //4\r
+ PT_SHLIB, //5\r
+ PT_PHDR, //6\r
+ PT_LOPROC = 0x70000000,\r
+ PT_HIPROC = 0x7fffffff\r
+};\r
+\r
+struct sElf32_Phdr {\r
+ Uint32 Type;\r
+ Uint32 Offset;\r
+ Uint32 VAddr;\r
+ Uint32 PAddr;\r
+ Uint32 FileSize;\r
+ Uint32 MemSize;\r
+ Uint32 Flags;\r
+ Uint32 Align;\r
+} __attribute__ ((packed));\r
+\r
+typedef struct\r
+{\r
+ Elf64_Word p_type;\r
+ Elf64_Word p_flags;\r
+ Elf64_Off p_offset;\r
+ Elf64_Addr p_vaddr;\r
+ Elf64_Addr p_paddr;\r
+ Elf64_Xword p_filesz;\r
+ Elf64_Xword p_memsz;\r
+ Elf64_Xword p_align;\r
+} Elf64_Phdr;\r
+\r
+#define PF_X 1\r
+#define PF_W 2\r
+#define PF_R 4\r
+\r
+struct elf32_rel_s {\r
+ Uint32 r_offset;\r
+ Uint32 r_info;\r
+} __attribute__ ((packed));\r
+\r
+struct elf32_rela_s {\r
+ Uint32 r_offset;\r
+ Uint32 r_info;\r
+ Sint32 r_addend;\r
+} __attribute__ ((packed));\r
+\r
+enum {\r
+ R_386_NONE = 0, // none\r
+ R_386_32, // S+A\r
+ R_386_PC32, // S+A-P\r
+ R_386_GOT32, // G+A-P\r
+ R_386_PLT32, // L+A-P\r
+ R_386_COPY, // none\r
+ R_386_GLOB_DAT, // S\r
+ R_386_JMP_SLOT, // S\r
+ R_386_RELATIVE, // B+A\r
+ R_386_GOTOFF, // S+A-GOT\r
+ R_386_GOTPC, // GOT+A-P\r
+ R_386_LAST // none\r
+};\r
+\r
+#define ELF32_R_SYM(i) ((i)>>8) // Takes an info value and returns a symbol index\r
+#define ELF32_R_TYPE(i) ((i)&0xFF) // Takes an info value and returns a type\r
+#define ELF32_R_INFO(s,t) (((s)<<8)+((t)&0xFF)) // Takes a type and symbol index and returns an info value\r
+\r
+struct elf32_dyn_s {\r
+ Uint32 d_tag;\r
+ Uint32 d_val; //Also d_ptr\r
+} __attribute__ ((packed));\r
+\r
+enum {\r
+ DT_NULL, //!< Marks End of list\r
+ DT_NEEDED, //!< Offset in strtab to needed library\r
+ DT_PLTRELSZ, //!< Size in bytes of PLT\r
+ DT_PLTGOT, //!< Address of PLT/GOT\r
+ DT_HASH, //!< Address of symbol hash table\r
+ DT_STRTAB, //!< String Table address\r
+ DT_SYMTAB, //!< Symbol Table address\r
+ DT_RELA, //!< Relocation table address\r
+ DT_RELASZ, //!< Size of relocation table\r
+ DT_RELAENT, //!< Size of entry in relocation table\r
+ DT_STRSZ, //!< Size of string table\r
+ DT_SYMENT, //!< Size of symbol table entry\r
+ DT_INIT, //!< Address of initialisation function\r
+ DT_FINI, //!< Address of termination function\r
+ DT_SONAME, //!< String table offset of so name\r
+ DT_RPATH, //!< String table offset of library path\r
+ DT_SYMBOLIC,//!< Reverse order of symbol searching for library, search libs first then executable\r
+ DT_REL, //!< Relocation Entries (Elf32_Rel instead of Elf32_Rela)\r
+ DT_RELSZ, //!< Size of above table (bytes)\r
+ DT_RELENT, //!< Size of entry in above table\r
+ DT_PLTREL, //!< Relocation entry of PLT\r
+ DT_DEBUG, //!< Debugging Entry - Unknown contents\r
+ DT_TEXTREL, //!< Indicates that modifcations to a non-writeable segment may occur\r
+ DT_JMPREL, //!< Address of PLT only relocation entries\r
+ DT_LOPROC = 0x70000000, //!< Low Definable\r
+ DT_HIPROC = 0x7FFFFFFF //!< High Definable\r
+};\r
+\r
+typedef struct sElf32_Ehdr Elf32_Ehdr;\r
+typedef struct sElf32_Phdr Elf32_Phdr;\r
+typedef struct sElf32_Shent Elf32_Shent;\r
+typedef struct elf_sym_s elf_symtab;\r
+typedef struct elf_sym_s Elf32_Sym;\r
+typedef struct elf32_rel_s Elf32_Rel;\r
+typedef struct elf32_rela_s Elf32_Rela;\r
+typedef struct elf32_dyn_s Elf32_Dyn;\r
+\r
+#endif // defined(_EXE_ELF_H)\r
--- /dev/null
+/*\r
+ * Acess v1\r
+ * Portable Executable Loader\r
+ */\r
+#define DEBUG 1\r
+#include <acess.h>\r
+#include <binary.h>\r
+#include <modules.h>\r
+#include "pe.h"\r
+\r
+// === PROTOTYPES ===\r
+ int PE_Install(char **Arguments);\r
+tBinary *PE_Load(int fp);\r
+tBinary *MZ_Open(int fp);\r
+ int PE_Relocate(void *Base);\r
+ int PE_GetSymbol(void *Base, const char *Name, Uint *Ret);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, 0x0032, BinPE, PE_Install, NULL, NULL);\r
+const char *gsPE_DefaultInterpreter = "/Acess/Libs/ld-acess.so";\r
+tBinaryType gPE_Loader = {\r
+ NULL,\r
+ ('M'|('Z'<<8)), 0xFFFF, // 'MZ'\r
+ "PE/DOS",\r
+ PE_Load, PE_Relocate, PE_GetSymbol\r
+ };\r
+\r
+// === CODE ===\r
+int PE_Install(char **Arguments)\r
+{\r
+ Binary_RegisterType(&gPE_Loader);\r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ * \brief Loads a PE Binary\r
+ */\r
+tBinary *PE_Load(int FP)\r
+{\r
+ int count, i, j;\r
+ int iSectCount;\r
+ tBinary *ret;\r
+ tPE_DOS_HEADER dosHdr;\r
+ tPE_IMAGE_HEADERS peHeaders;\r
+ tPE_SECTION_HEADER *peSections;\r
+ char namebuf[9] = {0};\r
+ Uint iFlags, iVA;\r
+ \r
+ ENTER("xFP", FP);\r
+ \r
+ // Read DOS header and check\r
+ VFS_Read(FP, sizeof(tPE_DOS_HEADER), &dosHdr);\r
+ if( dosHdr.Ident != ('M'|('Z'<<8)) ) {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // - Read PE Header\r
+ VFS_Seek(FP, dosHdr.PeHdrOffs, SEEK_SET);\r
+ if( VFS_Tell(FP) != dosHdr.PeHdrOffs ) {\r
+ ret = MZ_Open(FP);\r
+ LEAVE('p', ret);\r
+ return ret;\r
+ }\r
+ VFS_Read(FP, sizeof(tPE_IMAGE_HEADERS), &peHeaders);\r
+ \r
+ // - Check PE Signature and pass on to the MZ Loader if invalid\r
+ if( peHeaders.Signature != (('P')|('E'<<8)) ) {\r
+ ret = MZ_Open(FP);\r
+ LEAVE('p', ret);\r
+ return ret;\r
+ }\r
+ \r
+ // Read Sections (Uses `count` as a temp variable)\r
+ count = sizeof(tPE_SECTION_HEADER) * peHeaders.FileHeader.SectionCount;\r
+ peSections = malloc( count );\r
+ if(!peSections)\r
+ {\r
+ Warning("PE_Load - Unable to allocate `peSections`, 0x%x bytes", count);\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ VFS_Read(FP, count, peSections);\r
+ \r
+ // Count Pages\r
+ iSectCount = 1; // 1st page is headers\r
+ for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ )\r
+ {\r
+ // Check if the section is loadable\r
+ // (VA is zero in non-loadable sections)\r
+ if(peSections[i].RVA + peHeaders.OptHeader.ImageBase == 0) continue;\r
+ \r
+ // Moar pages\r
+ iSectCount ++;\r
+ }\r
+ \r
+ LOG("%i Sections", iSectCount);\r
+ \r
+ // Initialise Executable Information\r
+ ret = malloc(sizeof(tBinary) + sizeof(tBinarySection)*iSectCount);\r
+ \r
+ ret->Entry = peHeaders.OptHeader.EntryPoint + peHeaders.OptHeader.ImageBase;\r
+ ret->Base = peHeaders.OptHeader.ImageBase;\r
+ ret->Interpreter = gsPE_DefaultInterpreter;\r
+ ret->NumSections = iSectCount;\r
+ \r
+ LOG("Entry=%p, BaseAddress=0x%x\n", ret->Entry, ret->Base);\r
+ \r
+ ret->LoadSections[0].Virtual = peHeaders.OptHeader.ImageBase;\r
+ ret->LoadSections[0].Offset = 0;\r
+ ret->LoadSections[0].FileSize = 4096;\r
+ ret->LoadSections[0].MemSize = 4096;\r
+ ret->LoadSections[0].Flags = 0;\r
+ \r
+ // Parse Sections\r
+ j = 1; // Page Index\r
+ for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ )\r
+ {\r
+ tBinarySection *sect = &ret->LoadSections[j];\r
+ iVA = peSections[i].RVA + peHeaders.OptHeader.ImageBase;\r
+ \r
+ // Skip non-loadable sections\r
+ if(iVA == 0) continue;\r
+ \r
+ // Create Name Buffer\r
+ memcpy(namebuf, peSections[i].Name, 8);\r
+ LOG("Section %i '%s', iVA = %p", i, namebuf, iVA);\r
+ \r
+ // Create Flags\r
+ iFlags = 0;\r
+ if(peSections[i].Flags & PE_SECTION_FLAG_MEM_EXECUTE)\r
+ iFlags |= BIN_SECTFLAG_EXEC;\r
+ if( !(peSections[i].Flags & PE_SECTION_FLAG_MEM_WRITE) )\r
+ iFlags |= BIN_SECTFLAG_RO;\r
+ \r
+ sect->Virtual = iVA;\r
+ sect->Offset = peSections[i].RawOffs;\r
+ sect->FileSize = peSections[i].RawSize;\r
+ sect->MemSize = peSections[i].VirtualSize;\r
+ sect->Flags = iFlags;\r
+ j ++;\r
+ \r
+ LOG("%i Name:'%s', RVA: 0x%x, Size: 0x%x (0x%x), Ofs: 0x%x, Flags: 0x%x",\r
+ i, namebuf, \r
+ iVA,\r
+ peSections[i].VirtualSize, peSections[i].RawSize, peSections[i].RawOffs,\r
+ peSections[i].Flags\r
+ );\r
+ \r
+ }\r
+ // Free Executable Memory\r
+ free(peSections);\r
+ \r
+ LEAVE('p', ret);\r
+ return ret;\r
+}\r
+\r
+/**\r
+ */\r
+tBinary *MZ_Open(int FP)\r
+{\r
+ ENTER("xFP", FP);\r
+ UNIMPLEMENTED();\r
+ LEAVE('n');\r
+ return NULL;\r
+}\r
+\r
+int PE_Relocate(void *Base)\r
+{\r
+ tPE_DOS_HEADER *dosHdr;\r
+ tPE_IMAGE_HEADERS *peHeaders;\r
+ tPE_SECTION_HEADER *peSections;\r
+ tPE_DATA_DIR *directory;\r
+ tPE_IMPORT_DIR *impDir;\r
+ int i;\r
+ Uint iBase = (Uint)Base;\r
+ #if 0\r
+ void *hLibrary;\r
+ char *libPath;\r
+ #endif\r
+ \r
+ ENTER("pBase", Base);\r
+ dosHdr = Base;\r
+ peHeaders = (void*)( iBase + dosHdr->PeHdrOffs );\r
+ LOG("Prefered Base %p", peHeaders->OptHeader.ImageBase);\r
+ peSections = (void*)( iBase + sizeof(tPE_IMAGE_HEADERS) );\r
+ \r
+ directory = (void*)(peSections[0].RVA + iBase);\r
+ \r
+ // === Load Import Tables\r
+ impDir = (void*)( directory[PE_DIR_IMPORT].RVA + iBase );\r
+ for( i = 0; impDir[i].DLLName != NULL; i++ )\r
+ {\r
+ impDir[i].DLLName += iBase;\r
+ impDir[i].ImportLookupTable += iBase/4;\r
+ impDir[i].ImportAddressTable += iBase/4;\r
+ LOG("DLL Required '%s'(0x%x)", impDir[i].DLLName, impDir[i].DLLName);\r
+ #if 0\r
+ libPath = FindLibrary(impDir[i].DLLName);\r
+ if(libPath == NULL)\r
+ {\r
+ Warning("PE_Relocate - Unable to find library '%s'");\r
+ LEAVE('i', -1);\r
+ return -1;\r
+ }\r
+ LOG("DLL Path = '%s'", libPath);\r
+ hLibrary = DynLib_Load(libPath, 0);\r
+ #endif\r
+ }\r
+ \r
+ for(i=0;i<PE_DIR_LAST;i++)\r
+ LOG("directory[%i] = {RVA=0x%x,Size=0x%x}", i, directory[i].RVA, directory[i].Size);\r
+ \r
+ LEAVE('i', 0);\r
+ return 0;\r
+}\r
+\r
+int PE_GetSymbol(void *Base, const char *Name, Uint *Ret)\r
+{\r
+ return 0;\r
+}\r
--- /dev/null
+/*\r
+AcessOS/AcessBasic v1\r
+PE Loader\r
+HEADER\r
+*/\r
+#ifndef _EXE_PE_H\r
+#define _EXE_PE_H\r
+\r
+enum ePE_MACHINES {\r
+ PE_MACHINE_I386 = 0x14c, // Intel 386+\r
+ PE_MACHINE_IA64 = 0x200 // Intel-64\r
+};\r
+\r
+enum ePE_DIR_INDX {\r
+ PE_DIR_EXPORT, // 0\r
+ PE_DIR_IMPORT, // 1\r
+ PE_DIR_RESOURCE, // 2\r
+ PE_DIR_EXCEPTION, // 3\r
+ PE_DIR_SECRURITY, // 4\r
+ PE_DIR_RELOC, // 5\r
+ PE_DIR_DEBUG, // 6\r
+ PE_DIR_COPYRIGHT, // 7\r
+ PE_DIR_ARCHITECTURE,// 8\r
+ PE_DIR_GLOBALPTR, // 9\r
+ PE_DIR_TLS, // 10\r
+ PE_DIR_LOAD_CFG, // 11\r
+ PE_DIR_BOUND_IMPORT,// 12\r
+ PE_DIR_IAT, // 13\r
+ PE_DIR_DELAY_IMPORT,// 14\r
+ PE_DIR_COM_DESCRIPTOR, //15\r
+ PE_DIR_LAST\r
+};\r
+\r
+typedef struct {\r
+ Uint32 RVA;\r
+ Uint32 Size;\r
+} tPE_DATA_DIR;\r
+\r
+typedef struct {\r
+ Uint32 *ImportLookupTable; //0x80000000 is Ordninal Flag\r
+ Uint32 TimeStamp;\r
+ Uint32 FowarderChain;\r
+ char *DLLName;\r
+ Uint32 *ImportAddressTable; // Array of Addresses - To be edited by loader\r
+} tPE_IMPORT_DIR;\r
+\r
+typedef struct {\r
+ Uint16 Hint;\r
+ char Name[]; // Zero Term String\r
+} tPE_HINT_NAME;\r
+\r
+typedef struct {\r
+ char Name[8];\r
+ Uint32 VirtualSize;\r
+ Uint32 RVA;\r
+ Uint32 RawSize;\r
+ Uint32 RawOffs;\r
+ Uint32 RelocationsPtr; //Set to 0 in executables\r
+ Uint32 LineNumberPtr; //Pointer to Line Numbers\r
+ Uint16 RelocationCount; // Set to 0 in executables\r
+ Uint16 LineNumberCount;\r
+ Uint32 Flags;\r
+} tPE_SECTION_HEADER;\r
+\r
+#define PE_SECTION_FLAG_CODE 0x00000020 // Section contains executable code.\r
+#define PE_SECTION_FLAG_IDATA 0x00000040 // Section contains initialized data.\r
+#define PE_SECTION_FLAG_UDATA 0x00000080 // Section contains uninitialized data.\r
+#define PE_SECTION_FLAG_DISCARDABLE 0x02000000 // Section can be discarded as needed.\r
+#define PE_SECTION_FLAG_MEM_NOT_CACHED 0x04000000 // Section cannot be cached.\r
+#define PE_SECTION_FLAG_MEM_NOT_PAGED 0x08000000 // Section is not pageable.\r
+#define PE_SECTION_FLAG_MEM_SHARED 0x10000000 // Section can be shared in memory.\r
+#define PE_SECTION_FLAG_MEM_EXECUTE 0x20000000 // Section can be executed as code.\r
+#define PE_SECTION_FLAG_MEM_READ 0x40000000 // Section can be read.\r
+#define PE_SECTION_FLAG_MEM_WRITE 0x80000000 // Section can be written to.\r
+\r
+typedef struct {\r
+ Uint32 page;\r
+ Uint32 size;\r
+ Uint16 ents[];\r
+} tPE_FIXUP_BLOCK;\r
+\r
+//File Header\r
+typedef struct {\r
+ Uint16 Machine;\r
+ Uint16 SectionCount;\r
+ Uint32 CreationTimestamp;\r
+ Uint32 SymbolTableOffs;\r
+ Uint32 SymbolCount;\r
+ Uint16 OptHeaderSize;\r
+ Uint16 Flags;\r
+} tPE_FILE_HEADER;\r
+\r
+typedef struct {\r
+ Uint16 Magic; //0x10b: 32Bit, 0x20b: 64Bit\r
+ Uint16 LinkerVersion;\r
+ Uint32 CodeSize; //Sum of all Code Segment Sizes\r
+ Uint32 DataSize; //Sum of all Intialised Data Segments\r
+ Uint32 BssSize; //Sum of all Unintialised Data Segments\r
+ Uint32 EntryPoint;\r
+ Uint32 CodeRVA;\r
+ Uint32 DataRVA;\r
+ Uint32 ImageBase; //Prefered Base Address\r
+ Uint32 SectionAlignment;\r
+ Uint32 FileAlignment;\r
+ Uint32 WindowsVersion; //Unused/Irrelevent\r
+ Uint32 ImageVersion; //Unused/Irrelevent\r
+ Uint32 SubsystemVersion; //Unused, Set to 4\r
+ Uint32 Win32Version; //Unused\r
+ Uint32 ImageSize;\r
+ Uint32 HeaderSize;\r
+ Uint32 Checksum; //Unknown Method, Can be set to 0\r
+ Uint16 Subsystem; //Required Windows Subsystem (None, GUI, Console)\r
+ Uint16 DllFlags;\r
+ Uint32 MaxStackSize; //Reserved Stack Size\r
+ Uint32 InitialStackSize; //Commited Stack Size\r
+ Uint32 InitialReservedHeap; // Reserved Heap Size\r
+ Uint32 InitialCommitedHeap; // Commited Heap Size\r
+ Uint32 LoaderFlags; // Obselete\r
+ Uint32 NumberOfDirEntries;\r
+ tPE_DATA_DIR Directory[16];\r
+} tPE_OPT_HEADER;\r
+\r
+// Root Header\r
+typedef struct {\r
+ Uint32 Signature;\r
+ tPE_FILE_HEADER FileHeader;\r
+ tPE_OPT_HEADER OptHeader;\r
+} tPE_IMAGE_HEADERS;\r
+\r
+typedef struct {\r
+ Uint16 Ident;\r
+ Uint16 Resvd[29];\r
+ Uint32 PeHdrOffs; // File address of new exe header\r
+} tPE_DOS_HEADER;\r
+\r
+#endif\r
--- /dev/null
+/*
+ * Acess2
+ * Common Binary Loader
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <binary.h>
+#include <mm_virt.h>
+#include <hal_proc.h>
+#include <vfs_threads.h>
+
+// === CONSTANTS ===
+#define BIN_LOWEST MM_USER_MIN // 1MiB
+#define BIN_GRANUALITY 0x10000 // 64KiB
+#define BIN_HIGHEST (USER_LIB_MAX-BIN_GRANUALITY) // Just below the kernel
+#define KLIB_LOWEST MM_MODULE_MIN
+#define KLIB_GRANUALITY 0x10000 // 32KiB
+#define KLIB_HIGHEST (MM_MODULE_MAX-KLIB_GRANUALITY)
+
+// === TYPES ===
+typedef struct sKernelBin {
+ struct sKernelBin *Next;
+ void *Base;
+ tBinary *Info;
+} tKernelBin;
+
+// === IMPORTS ===
+extern char *Threads_GetName(int ID);
+extern tKernelSymbol gKernelSymbols[];
+extern tKernelSymbol gKernelSymbolsEnd[];
+extern tBinaryType gELF_Info;
+
+// === PROTOTYPES ===
+ int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer);
+tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint);
+tBinary *Binary_GetInfo(tMount MountID, tInode InodeID);
+tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax);
+tVAddr Binary_IsMapped(tBinary *Binary);
+tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path);
+void Binary_Dereference(tBinary *Info);
+#if 0
+Uint Binary_Relocate(void *Base);
+#endif
+Uint Binary_GetSymbolEx(const char *Name, Uint *Value);
+#if 0
+Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val);
+#endif
+ int Binary_int_CheckMemFree( tVAddr _start, size_t _len );
+
+// === GLOBALS ===
+tShortSpinlock glBinListLock;
+tBinary *glLoadedBinaries = NULL;
+char **gsaRegInterps = NULL;
+ int giRegInterps = 0;
+tShortSpinlock glKBinListLock;
+tKernelBin *glLoadedKernelLibs;
+tBinaryType *gRegBinTypes = &gELF_Info;
+
+// === FUNCTIONS ===
+/**
+ * \brief Registers a binary type
+ */
+int Binary_RegisterType(tBinaryType *Type)
+{
+ Type->Next = gRegBinTypes;
+ gRegBinTypes = Type;
+ return 1;
+}
+
+/**
+ * \fn int Proc_Spawn(const char *Path)
+ */
+int Proc_Spawn(const char *Path)
+{
+ char stackPath[strlen(Path)+1];
+ ENTER("sPath", Path);
+
+ strcpy(stackPath, Path);
+
+ LOG("stackPath = '%s'", stackPath);
+
+ if(Proc_Clone(CLONE_VM|CLONE_NOUSER) == 0)
+ {
+ // CHILD
+ const char *args[2] = {stackPath, NULL};
+ LOG("stackPath = '%s'", stackPath);
+ Proc_Execve(stackPath, args, &args[1], 0);
+ for(;;);
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \todo Document
+ */
+int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer)
+{
+ int size, argc=0, envc=0;
+ int i;
+ char *strbuf;
+ const char **arrays;
+
+ // Calculate size
+ size = 0;
+ if( ArgV && *ArgV )
+ {
+ const char **argv = *ArgV;
+ for( argc = 0; argv[argc]; argc ++ )
+ size += strlen( argv[argc] ) + 1;
+ }
+ if( EnvP && *EnvP )
+ {
+ const char **envp = *EnvP;
+ for( envc = 0; envp[envc]; envc ++ )
+ size += strlen( envp[envc] ) + 1;
+ }
+ size = (size + sizeof(void*)-1) & ~(sizeof(void*)-1); // Word align
+ size += (argc+1+envc+1)*sizeof(void*); // Arrays
+ if( Path )
+ {
+ size += strlen( *Path ) + 1;
+ }
+
+ if( DestBuffer )
+ {
+ arrays = DestBuffer;
+ strbuf = (void*)&arrays[argc+1+envc+1];
+
+ // Fill ArgV
+ if( ArgV && *ArgV )
+ {
+ const char **argv = *ArgV;
+ for( i = 0; argv[i]; i ++ )
+ {
+ arrays[i] = strbuf;
+ strcpy(strbuf, argv[i]);
+ strbuf += strlen( argv[i] ) + 1;
+ }
+ *ArgV = arrays;
+ arrays += i;
+ }
+ *arrays++ = NULL;
+ // Fill EnvP
+ if( EnvP && *EnvP )
+ {
+ const char **envp = *EnvP;
+ for( i = 0; envp[i]; i ++ )
+ {
+ arrays[i] = strbuf;
+ strcpy(strbuf, envp[i]);
+ strbuf += strlen( envp[i] ) + 1;
+ }
+ *EnvP = arrays;
+ arrays += i;
+ }
+ *arrays++ = NULL;
+ // Fill path
+ if( Path )
+ {
+ strcpy(strbuf, *Path);
+ *Path = strbuf;
+ }
+ }
+
+ return size;
+}
+
+/**
+ * \brief Create a new process with the specified set of file descriptors
+ */
+int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs)
+{
+ void *handles;
+ void *cachebuf;
+ int size;
+ tPID ret;
+
+ // --- Save File, ArgV and EnvP
+ size = Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, NULL );
+ cachebuf = malloc( size );
+ Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, cachebuf );
+
+ // Cache the VFS handles
+ handles = VFS_SaveHandles(nFD, FDs);
+
+ // Create new process
+ ret = Proc_Clone(CLONE_VM|CLONE_NOUSER);
+ if( ret == 0 )
+ {
+ VFS_RestoreHandles(nFD, handles);
+ VFS_FreeSavedHandles(nFD, handles);
+ // Frees cachebuf
+ Proc_Execve(Binary, ArgV, EnvP, size);
+ for(;;);
+ }
+ if( ret < 0 )
+ {
+ VFS_FreeSavedHandles(nFD, handles);
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Replace the current user image with another
+ * \param File File to load as the next image
+ * \param ArgV Arguments to pass to user
+ * \param EnvP User's environment
+ * \note Called Proc_ for historical reasons
+ */
+int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize)
+{
+ void *cachebuf;
+ tVAddr entry;
+ Uint base; // Uint because Proc_StartUser wants it
+ int argc;
+
+ ENTER("sFile pArgV pEnvP", File, ArgV, EnvP);
+
+ // --- Save File, ArgV and EnvP
+ if( DataSize == 0 )
+ {
+ DataSize = Binary_int_CacheArgs( &File, &ArgV, &EnvP, NULL );
+ cachebuf = malloc( DataSize );
+ Binary_int_CacheArgs( &File, &ArgV, &EnvP, cachebuf );
+ }
+
+ // --- Get argc
+ for( argc = 0; ArgV && ArgV[argc]; argc ++ );
+
+ // --- Set Process Name
+ Threads_SetName(File);
+
+ // --- Clear User Address space
+ // NOTE: This is a little roundabout, maybe telling ClearUser to not touch the
+ // PPD area would be a better idea.
+ {
+ int nfd = *Threads_GetMaxFD();
+ void *handles;
+ handles = VFS_SaveHandles(nfd, NULL);
+ VFS_CloseAllUserHandles();
+ MM_ClearUser();
+ VFS_RestoreHandles(nfd, handles);
+ VFS_FreeSavedHandles(nfd, handles);
+ }
+
+ // --- Load new binary
+ base = Binary_Load(File, &entry);
+ if(base == 0)
+ {
+ Log_Warning("Binary", "Proc_Execve - Unable to load '%s'", File);
+ LEAVE('-');
+ Threads_Exit(0, -10);
+ for(;;);
+ }
+
+ LOG("entry = 0x%x, base = 0x%x", entry, base);
+ LEAVE('-');
+ // --- And... Jump to it
+ Proc_StartUser(entry, base, argc, ArgV, DataSize);
+ for(;;); // Tell GCC that we never return
+}
+
+/**
+ * \brief Load a binary into the current address space
+ * \param Path Path to binary to load
+ * \param EntryPoint Pointer for exectuable entry point
+ * \return Virtual address where the binary has been loaded
+ */
+tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint)
+{
+ tMount mount_id;
+ tInode inode;
+ tBinary *pBinary;
+ tVAddr base = -1;
+
+ ENTER("sPath pEntryPoint", Path, EntryPoint);
+
+ // Sanity Check Argument
+ if(Path == NULL) {
+ LEAVE('x', 0);
+ return 0;
+ }
+
+ // Check if this path has been loaded before.
+ #if 0
+ // TODO: Implement a list of string/tBinary pairs for loaded bins
+ #endif
+
+ // Get Inode
+ {
+ int fd;
+ tFInfo info;
+ fd = VFS_Open(Path, VFS_OPENFLAG_READ|VFS_OPENFLAG_EXEC);
+ if( fd == -1 ) {
+ LOG("%s does not exist", Path);
+ LEAVE_RET('x', 0);
+ }
+ VFS_FInfo(fd, &info, 0);
+ VFS_Close(fd);
+ mount_id = info.mount;
+ inode = info.inode;
+ LOG("mount_id = %i, inode = %i", mount_id, inode);
+ }
+
+ // TODO: Also get modifcation time?
+
+ // Check if the binary has already been loaded
+ if( !(pBinary = Binary_GetInfo(mount_id, inode)) )
+ pBinary = Binary_DoLoad(mount_id, inode, Path); // Else load it
+
+ // Error Check
+ if(pBinary == NULL) {
+ LEAVE('x', 0);
+ return 0;
+ }
+
+ // Map into process space
+ base = Binary_MapIn(pBinary, Path, BIN_LOWEST, BIN_HIGHEST);
+
+ // Check for errors
+ if(base == 0) {
+ LEAVE('x', 0);
+ return 0;
+ }
+
+ // Interpret
+ if(pBinary->Interpreter) {
+ tVAddr start;
+ if( Binary_Load(pBinary->Interpreter, &start) == 0 ) {
+ LEAVE('x', 0);
+ return 0;
+ }
+ *EntryPoint = start;
+ }
+ else
+ *EntryPoint = pBinary->Entry - pBinary->Base + base;
+
+ // Return
+ LOG("*EntryPoint = 0x%x", *EntryPoint);
+ LEAVE('x', base);
+ return base; // Pass the base as an argument to the user if there is an interpreter
+}
+
+/**
+ * \brief Finds a matching binary entry
+ * \param MountID Mountpoint ID of binary file
+ * \param InodeID Inode ID of the file
+ * \return Pointer to the binary definition (if already loaded)
+ */
+tBinary *Binary_GetInfo(tMount MountID, tInode InodeID)
+{
+ tBinary *pBinary;
+ for(pBinary = glLoadedBinaries; pBinary; pBinary = pBinary->Next)
+ {
+ if(pBinary->MountID == MountID && pBinary->Inode == InodeID)
+ return pBinary;
+ }
+ return NULL;
+}
+
+/**
+ * \brief Maps an already-loaded binary into an address space.
+ * \param Binary Pointer to globally stored binary definition
+ * \param Path Path to the binary's file (for debug)
+ * \param LoadMin Lowest location to map to
+ * \param LoadMax Highest location to map to
+ * \return Base load address
+ */
+tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax)
+{
+ tVAddr base;
+ int i, fd;
+
+ ENTER("pBinary sPath pLoadMin pLoadMax", Binary, Path, LoadMin, LoadMax);
+
+ // Reference Executable (Makes sure that it isn't unloaded)
+ Binary->ReferenceCount ++;
+
+ // Get Binary Base
+ base = Binary->Base;
+
+ // Check if base is free
+ if(base != 0)
+ {
+ LOG("Checking base %p", base);
+ for( i = 0; i < Binary->NumSections; i ++ )
+ {
+ if( Binary_int_CheckMemFree( Binary->LoadSections[i].Virtual, Binary->LoadSections[i].MemSize ) )
+ {
+ base = 0;
+ LOG("Address 0x%x is taken\n", Binary->LoadSections[i].Virtual);
+ break;
+ }
+ }
+ }
+
+ // Check if the executable has no base or it is not free
+ if(base == 0)
+ {
+ // If so, give it a base
+ base = LoadMax;
+ while(base >= LoadMin)
+ {
+ for( i = 0; i < Binary->NumSections; i ++ )
+ {
+ tVAddr addr = Binary->LoadSections[i].Virtual - Binary->Base + base;
+ if( Binary_int_CheckMemFree( addr, Binary->LoadSections[i].MemSize ) )
+ break;
+ }
+ // If space was found, break
+ if(i == Binary->NumSections) break;
+ // Else decrement pointer and try again
+ base -= BIN_GRANUALITY;
+ }
+ LOG("Allocated base %p", base);
+ }
+
+ // Error Check
+ if(base < LoadMin) {
+ Log_Warning("Binary", "Executable '%s' cannot be loaded, no space", Path);
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Map Executable In
+ fd = VFS_OpenInode(Binary->MountID, Binary->Inode, VFS_OPENFLAG_READ);
+ for( i = 0; i < Binary->NumSections; i ++ )
+ {
+ tBinarySection *sect = &Binary->LoadSections[i];
+ Uint protflags, mapflags;
+ tVAddr addr = sect->Virtual - Binary->Base + base;
+ LOG("%i - %p to offset 0x%llx (%x)", i, addr, sect->Offset, sect->Flags);
+
+ protflags = MMAP_PROT_READ;
+ mapflags = MMAP_MAP_FIXED;
+
+ if( sect->Flags & BIN_SECTFLAG_EXEC )
+ protflags |= MMAP_PROT_EXEC;
+ // Read only pages are COW
+ if( sect->Flags & BIN_SECTFLAG_RO ) {
+ VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_SHARED|mapflags, fd, sect->Offset );
+ }
+ else {
+ protflags |= MMAP_PROT_WRITE;
+ VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_PRIVATE|mapflags, fd, sect->Offset );
+ }
+
+ // Apply anonymous memory for BSS
+ if( sect->FileSize < sect->MemSize ) {
+ mapflags |= MMAP_MAP_ANONYMOUS;
+ VFS_MMap(
+ (void*)(addr + sect->FileSize), sect->MemSize - sect->FileSize,
+ protflags, MMAP_MAP_PRIVATE|mapflags,
+ 0, 0
+ );
+ }
+ }
+
+ Log_Debug("Binary", "PID %i - Mapped '%s' to %p", Threads_GetPID(), Path, base);
+ VFS_Close(fd);
+
+ LEAVE('p', base);
+ return base;
+}
+
+#if 0
+/**
+ * \fn Uint Binary_IsMapped(tBinary *binary)
+ * \brief Check if a binary is already mapped into the address space
+ * \param binary Binary information to check
+ * \return Current Base or 0
+ */
+Uint Binary_IsMapped(tBinary *binary)
+{
+ Uint iBase;
+
+ // Check prefered base
+ iBase = binary->Base;
+ if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF))
+ return iBase;
+
+ for(iBase = BIN_HIGHEST;
+ iBase >= BIN_LOWEST;
+ iBase -= BIN_GRANUALITY)
+ {
+ if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF))
+ return iBase;
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * \fn tBinary *Binary_DoLoad(char *truePath)
+ * \brief Loads a binary file into memory
+ * \param truePath Absolute filename of binary
+ */
+tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path)
+{
+ tBinary *pBinary;
+ int fp;
+ Uint32 ident;
+ tBinaryType *bt = gRegBinTypes;
+
+ ENTER("iMountID XInode sPath", MountID, Inode, Path);
+
+ // Open File
+ fp = VFS_OpenInode(MountID, Inode, VFS_OPENFLAG_READ);
+ if(fp == -1) {
+ LOG("Unable to load file, access denied");
+ LEAVE('n');
+ return NULL;
+ }
+
+ LOG("fp = 0x%x", fp);
+
+ // Read File Type
+ VFS_Read(fp, 4, &ident);
+ VFS_Seek(fp, 0, SEEK_SET);
+
+ LOG("ident = 0x%x", ident);
+
+ // Determine the type
+ for(; bt; bt = bt->Next)
+ {
+ if( (ident & bt->Mask) != (Uint32)bt->Ident )
+ continue;
+ LOG("bt = %p (%s)", bt, bt->Name);
+ pBinary = bt->Load(fp);
+ break;
+ }
+
+ // Close File
+ VFS_Close(fp);
+
+ // Catch errors
+ if(!bt) {
+ Log_Warning("Binary", "'%s' is an unknown file type. (%02x %02x %02x %02x)",
+ Path, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF);
+ LEAVE('n');
+ return NULL;
+ }
+
+ LOG("pBinary = %p", pBinary);
+
+ // Error Check
+ if(pBinary == NULL) {
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Initialise Structure
+ pBinary->ReferenceCount = 0;
+ pBinary->MountID = MountID;
+ pBinary->Inode = Inode;
+
+ // Debug Information
+ LOG("Interpreter: '%s'", pBinary->Interpreter);
+ LOG("Base: 0x%x, Entry: 0x%x", pBinary->Base, pBinary->Entry);
+ LOG("NumSections: %i", pBinary->NumSections);
+
+ // Add to the list
+ SHORTLOCK(&glBinListLock);
+ pBinary->Next = glLoadedBinaries;
+ glLoadedBinaries = pBinary;
+ SHORTREL(&glBinListLock);
+
+ // TODO: Register the path with the binary
+
+ // Return
+ LEAVE('p', pBinary);
+ return pBinary;
+}
+
+/**
+ * \fn void Binary_Unload(void *Base)
+ * \brief Unload / Unmap a binary
+ * \param Base Loaded Base
+ * \note Currently used only for kernel libaries
+ */
+void Binary_Unload(void *Base)
+{
+ tKernelBin *pKBin;
+ tKernelBin *prev = NULL;
+ int i;
+
+ if((Uint)Base < 0xC0000000)
+ {
+ // TODO: User Binaries
+ Log_Warning("BIN", "Unloading user binaries is currently unimplemented");
+ return;
+ }
+
+ // Kernel Libraries
+ for(pKBin = glLoadedKernelLibs;
+ pKBin;
+ prev = pKBin, pKBin = pKBin->Next)
+ {
+ // Check the base
+ if(pKBin->Base != Base) continue;
+ // Deallocate Memory
+ for(i = 0; i < pKBin->Info->NumSections; i++)
+ {
+ // TODO: VFS_MUnmap();
+ }
+ // Dereference Binary
+ Binary_Dereference( pKBin->Info );
+ // Remove from list
+ if(prev) prev->Next = pKBin->Next;
+ else glLoadedKernelLibs = pKBin->Next;
+ // Free Kernel Lib
+ free(pKBin);
+ return;
+ }
+}
+
+/**
+ * \fn void Binary_Dereference(tBinary *Info)
+ * \brief Dereferences and if nessasary, deletes a binary
+ * \param Info Binary information structure
+ */
+void Binary_Dereference(tBinary *Info)
+{
+ // Decrement reference count
+ Info->ReferenceCount --;
+
+ // Check if it is still in use
+ if(Info->ReferenceCount) return;
+
+ /// \todo Implement binary freeing
+}
+
+/**
+ * \fn char *Binary_RegInterp(char *Path)
+ * \brief Registers an Interpreter
+ * \param Path Path to interpreter provided by executable
+ */
+char *Binary_RegInterp(char *Path)
+{
+ int i;
+ // NULL Check Argument
+ if(Path == NULL) return NULL;
+ // NULL Check the array
+ if(gsaRegInterps == NULL)
+ {
+ giRegInterps = 1;
+ gsaRegInterps = malloc( sizeof(char*) );
+ gsaRegInterps[0] = malloc( strlen(Path) );
+ strcpy(gsaRegInterps[0], Path);
+ return gsaRegInterps[0];
+ }
+
+ // Scan Array
+ for( i = 0; i < giRegInterps; i++ )
+ {
+ if(strcmp(gsaRegInterps[i], Path) == 0)
+ return gsaRegInterps[i];
+ }
+
+ // Interpreter is not in list
+ giRegInterps ++;
+ gsaRegInterps = malloc( sizeof(char*)*giRegInterps );
+ gsaRegInterps[i] = malloc( strlen(Path) );
+ strcpy(gsaRegInterps[i], Path);
+ return gsaRegInterps[i];
+}
+
+// ============
+// Kernel Binary Handling
+// ============
+/**
+ * \fn void *Binary_LoadKernel(const char *File)
+ * \brief Load a binary into kernel space
+ * \note This function shares much with #Binary_Load, but does it's own mapping
+ * \param File File to load into the kernel
+ */
+void *Binary_LoadKernel(const char *File)
+{
+ tBinary *pBinary;
+ tKernelBin *pKBinary;
+ tVAddr base = -1;
+ tMount mount_id;
+ tInode inode;
+
+ ENTER("sFile", File);
+
+ // Sanity Check Argument
+ if(File == NULL) {
+ LEAVE('n');
+ return 0;
+ }
+
+ {
+ int fd = VFS_Open(File, VFS_OPENFLAG_READ);
+ tFInfo info;
+ if(fd == -1) {
+ LEAVE('n');
+ return NULL;
+ }
+ VFS_FInfo(fd, &info, 0);
+ mount_id = info.mount;
+ inode = info.inode;
+ VFS_Close(fd);
+ }
+
+ // Check if the binary has already been loaded
+ if( (pBinary = Binary_GetInfo(mount_id, inode)) )
+ {
+ for(pKBinary = glLoadedKernelLibs;
+ pKBinary;
+ pKBinary = pKBinary->Next )
+ {
+ if(pKBinary->Info == pBinary) {
+ LEAVE('p', pKBinary->Base);
+ return pKBinary->Base;
+ }
+ }
+ }
+ else
+ pBinary = Binary_DoLoad(mount_id, inode, File); // Else load it
+
+ // Error Check
+ if(pBinary == NULL) {
+ LEAVE('n');
+ return NULL;
+ }
+
+ // --------------
+ // Now pBinary is valid (either freshly loaded or only user mapped)
+ // So, map it into kernel space
+ // --------------
+
+ // Reference Executable (Makes sure that it isn't unloaded)
+ pBinary->ReferenceCount ++;
+
+ Binary_MapIn(pBinary, File, KLIB_LOWEST, KLIB_HIGHEST);
+
+ // Relocate Library
+ if( !Binary_Relocate( (void*)base ) )
+ {
+ Log_Warning("Binary", "Relocation of '%s' failed, unloading", File);
+ Binary_Unload( (void*)base );
+ Binary_Dereference( pBinary );
+ LEAVE('n');
+ return 0;
+ }
+
+ // Add to list (relocator must look at itself manually, not via Binary_GetSymbol)
+ pKBinary = malloc(sizeof(*pKBinary));
+ pKBinary->Base = (void*)base;
+ pKBinary->Info = pBinary;
+ SHORTLOCK( &glKBinListLock );
+ pKBinary->Next = glLoadedKernelLibs;
+ glLoadedKernelLibs = pKBinary;
+ SHORTREL( &glKBinListLock );
+
+ LEAVE('p', base);
+ return (void*)base;
+}
+
+/**
+ * \fn Uint Binary_Relocate(void *Base)
+ * \brief Relocates a loaded binary (used by kernel libraries)
+ * \param Base Loaded base address of binary
+ * \return Boolean Success
+ */
+Uint Binary_Relocate(void *Base)
+{
+ Uint32 ident = *(Uint32*) Base;
+ tBinaryType *bt = gRegBinTypes;
+
+ for(; bt; bt = bt->Next)
+ {
+ if( (ident & bt->Mask) == (Uint)bt->Ident )
+ return bt->Relocate( (void*)Base);
+ }
+
+ Log_Warning("BIN", "%p is an unknown file type. (%02x %02x %02x %02x)",
+ Base, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF);
+ return 0;
+}
+
+/**
+ * \fn int Binary_GetSymbol(char *Name, Uint *Val)
+ * \brief Get a symbol value
+ * \return Value of symbol or -1 on error
+ *
+ * Gets the value of a symbol from either the currently loaded
+ * libraries or the kernel's exports.
+ */
+int Binary_GetSymbol(const char *Name, Uint *Val)
+{
+ if( Binary_GetSymbolEx(Name, Val) ) return 1;
+ return 0;
+}
+
+/**
+ * \fn Uint Binary_GetSymbolEx(char *Name, Uint *Value)
+ * \brief Get a symbol value
+ *
+ * Gets the value of a symbol from either the currently loaded
+ * libraries or the kernel's exports.
+ */
+Uint Binary_GetSymbolEx(const char *Name, Uint *Value)
+{
+ int i;
+ tKernelBin *pKBin;
+ int numKSyms = ((Uint)&gKernelSymbolsEnd-(Uint)&gKernelSymbols)/sizeof(tKernelSymbol);
+
+ // Scan Kernel
+ for( i = 0; i < numKSyms; i++ )
+ {
+ if(strcmp(Name, gKernelSymbols[i].Name) == 0) {
+ *Value = gKernelSymbols[i].Value;
+ return 1;
+ }
+ }
+
+ // Scan Loaded Libraries
+ for(pKBin = glLoadedKernelLibs;
+ pKBin;
+ pKBin = pKBin->Next )
+ {
+ if( Binary_FindSymbol(pKBin->Base, Name, Value) ) {
+ return 1;
+ }
+ }
+
+ Log_Warning("BIN", "Unable to find symbol '%s'", Name);
+ return 0;
+}
+
+/**
+ * \fn Uint Binary_FindSymbol(void *Base, char *Name, Uint *Val)
+ * \brief Get a symbol from the specified library
+ * \param Base Base address
+ * \param Name Name of symbol to find
+ * \param Val Pointer to place final value
+ */
+Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val)
+{
+ Uint32 ident = *(Uint32*) Base;
+ tBinaryType *bt = gRegBinTypes;
+
+ for(; bt; bt = bt->Next)
+ {
+ if( (ident & bt->Mask) == (Uint)bt->Ident )
+ return bt->GetSymbol(Base, Name, Val);
+ }
+
+ Log_Warning("BIN", "Binary_FindSymbol - %p is an unknown file type. (%02x %02x %02x %02x)",
+ Base, ident&0xFF, ident>>8, ident>>16, ident>>24);
+ return 0;
+}
+
+/**
+ * \brief Check if a range of memory is fully free
+ * \return Inverse boolean free (0 if all pages are unmapped)
+ */
+int Binary_int_CheckMemFree( tVAddr _start, size_t _len )
+{
+ _len += _start & (PAGE_SIZE-1);
+ _len = (_len + PAGE_SIZE - 1) & ~(PAGE_SIZE-1);
+ _start &= ~(PAGE_SIZE-1);
+ for( ; _len > PAGE_SIZE; _len -= PAGE_SIZE, _start += PAGE_SIZE ) {
+ if( MM_GetPhysAddr(_start) != 0 )
+ return 1;
+ }
+ if( _len == PAGE_SIZE && MM_GetPhysAddr(_start) != 0 )
+ return 1;
+ return 0;
+}
+
+
+// === EXPORTS ===
+EXPORT(Binary_FindSymbol);
+EXPORT(Binary_Unload);
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * debug.c
+ *
+ * TODO: Move the Debug_putchar methods out to the arch/ tree
+ */
+#include <acess.h>
+#include <stdarg.h>
+
+#define DEBUG_MAX_LINE_LEN 256
+
+#define LOCK_DEBUG_OUTPUT 1
+
+#define TRACE_TO_KTERM 0
+
+// === IMPORTS ===
+extern void Threads_Dump(void);
+extern void KernelPanic_SetMode(void);
+extern void KernelPanic_PutChar(char Ch);
+
+// === PROTOTYPES ===
+static void Debug_Putchar(char ch);
+static void Debug_Puts(int bUseKTerm, const char *Str);
+void Debug_DbgOnlyFmt(const char *format, va_list args);
+void Debug_FmtS(int bUseKTerm, const char *format, ...);
+void Debug_Fmt(int bUseKTerm, const char *format, va_list args);
+void Debug_SetKTerminal(const char *File);
+
+// === GLOBALS ===
+ int gDebug_Level = 0;
+ int giDebug_KTerm = -1;
+ int gbDebug_IsKPanic = 0;
+volatile int gbInPutChar = 0;
+#if LOCK_DEBUG_OUTPUT
+tShortSpinlock glDebug_Lock;
+#endif
+
+// === CODE ===
+static void Debug_Putchar(char ch)
+{
+ Debug_PutCharDebug(ch);
+ if( !gbDebug_IsKPanic )
+ {
+ if(gbInPutChar) return ;
+ gbInPutChar = 1;
+ if(giDebug_KTerm != -1)
+ VFS_Write(giDebug_KTerm, 1, &ch);
+ gbInPutChar = 0;
+ }
+ else
+ KernelPanic_PutChar(ch);
+}
+
+static void Debug_Puts(int UseKTerm, const char *Str)
+{
+ int len = 0;
+
+ Debug_PutStringDebug(Str);
+
+ if( gbDebug_IsKPanic )
+ {
+ for( len = 0; Str[len]; len ++ )
+ KernelPanic_PutChar( Str[len] );
+ }
+ else
+ for( len = 0; Str[len]; len ++ );
+
+ // Output to the kernel terminal
+ if( UseKTerm && !gbDebug_IsKPanic && giDebug_KTerm != -1)
+ {
+ if(gbInPutChar) return ;
+ gbInPutChar = 1;
+ VFS_Write(giDebug_KTerm, len, Str);
+ gbInPutChar = 0;
+ }
+}
+
+void Debug_DbgOnlyFmt(const char *format, va_list args)
+{
+ Debug_Fmt(0, format, args);
+}
+
+void Debug_Fmt(int bUseKTerm, const char *format, va_list args)
+{
+ char buf[DEBUG_MAX_LINE_LEN];
+ int len;
+ buf[DEBUG_MAX_LINE_LEN-1] = 0;
+ len = vsnprintf(buf, DEBUG_MAX_LINE_LEN-1, format, args);
+ //if( len < DEBUG_MAX_LINE )
+ // do something
+ Debug_Puts(bUseKTerm, buf);
+ return ;
+}
+
+void Debug_FmtS(int bUseKTerm, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ Debug_Fmt(bUseKTerm, format, args);
+ va_end(args);
+}
+
+void Debug_KernelPanic(void)
+{
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+ gbDebug_IsKPanic = 1;
+ KernelPanic_SetMode();
+}
+
+/**
+ * \fn void LogF(const char *Msg, ...)
+ * \brief Raw debug log (no new line, no prefix)
+ */
+void LogF(const char *Fmt, ...)
+{
+ va_list args;
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ va_start(args, Fmt);
+
+ Debug_Fmt(1, Fmt, args);
+
+ va_end(args);
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+/**
+ * \fn void Debug(const char *Msg, ...)
+ * \brief Print only to the debug channel (not KTerm)
+ */
+void Debug(const char *Fmt, ...)
+{
+ va_list args;
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ Debug_Puts(0, "Debug: ");
+ va_start(args, Fmt);
+ Debug_DbgOnlyFmt(Fmt, args);
+ va_end(args);
+ Debug_PutCharDebug('\r');
+ Debug_PutCharDebug('\n');
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+/**
+ * \fn void Log(const char *Msg, ...)
+ */
+void Log(const char *Fmt, ...)
+{
+ va_list args;
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ Debug_Puts(1, "Log: ");
+ va_start(args, Fmt);
+ Debug_Fmt(1, Fmt, args);
+ va_end(args);
+ Debug_Puts(1, "\r\n");
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+void Warning(const char *Fmt, ...)
+{
+ va_list args;
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ Debug_Puts(1, "Warning: ");
+ va_start(args, Fmt);
+ Debug_Fmt(1, Fmt, args);
+ va_end(args);
+ Debug_Putchar('\r');
+ Debug_Putchar('\n');
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+void Panic(const char *Fmt, ...)
+{
+ va_list args;
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+ // And never SHORTREL
+
+ Debug_KernelPanic();
+
+ Debug_Puts(1, "Panic: ");
+ va_start(args, Fmt);
+ Debug_Fmt(1, Fmt, args);
+ va_end(args);
+ Debug_Putchar('\r');
+ Debug_Putchar('\n');
+
+ Threads_Dump();
+
+ for(;;) ;
+}
+
+void Debug_SetKTerminal(const char *File)
+{
+ int tmp;
+ if(giDebug_KTerm != -1) {
+ tmp = giDebug_KTerm;
+ giDebug_KTerm = -1;
+ VFS_Close(tmp);
+ }
+ tmp = VFS_Open(File, VFS_OPENFLAG_WRITE);
+// Log_Log("Debug", "Opened '%s' as 0x%x", File, tmp);
+ giDebug_KTerm = tmp;
+// Log_Log("Debug", "Returning to %p", __builtin_return_address(0));
+}
+
+void Debug_Enter(const char *FuncName, const char *ArgTypes, ...)
+{
+ va_list args;
+ int i;
+ int pos;
+ tTID tid = Threads_GetTID();
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ i = gDebug_Level ++;
+
+ va_start(args, ArgTypes);
+
+ Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now());
+ while(i--) Debug_Puts(TRACE_TO_KTERM, " ");
+
+ Debug_Puts(TRACE_TO_KTERM, FuncName);
+ Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid);
+ Debug_Puts(TRACE_TO_KTERM, ": (");
+
+ while(*ArgTypes)
+ {
+ pos = strpos(ArgTypes, ' ');
+ if(pos == -1 || pos > 1) {
+ if(pos == -1)
+ Debug_Puts(TRACE_TO_KTERM, ArgTypes+1);
+ else {
+ Debug_FmtS(TRACE_TO_KTERM, "%.*s", pos-1, ArgTypes+1);
+ }
+ Debug_Puts(TRACE_TO_KTERM, "=");
+ }
+ switch(*ArgTypes)
+ {
+ case 'p': Debug_FmtS(TRACE_TO_KTERM, "%p", va_arg(args, void*)); break;
+ case 'P': Debug_FmtS(TRACE_TO_KTERM, "%P", va_arg(args, tPAddr)); break;
+ case 's': Debug_FmtS(TRACE_TO_KTERM, "'%s'", va_arg(args, char*)); break;
+ case 'i': Debug_FmtS(TRACE_TO_KTERM, "%i", va_arg(args, int)); break;
+ case 'u': Debug_FmtS(TRACE_TO_KTERM, "%u", va_arg(args, Uint)); break;
+ case 'x': Debug_FmtS(TRACE_TO_KTERM, "0x%x", va_arg(args, Uint)); break;
+ case 'b': Debug_FmtS(TRACE_TO_KTERM, "0b%b", va_arg(args, Uint)); break;
+ case 'X': Debug_FmtS(TRACE_TO_KTERM, "0x%llx", va_arg(args, Uint64)); break; // Extended (64-Bit)
+ case 'B': Debug_FmtS(TRACE_TO_KTERM, "0b%llb", va_arg(args, Uint64)); break; // Extended (64-Bit)
+ }
+ if(pos != -1) {
+ Debug_Puts(TRACE_TO_KTERM, ", ");
+ }
+
+ if(pos == -1) break;
+ ArgTypes = &ArgTypes[pos+1];
+ }
+
+ va_end(args);
+ Debug_Puts(TRACE_TO_KTERM, ")\r\n");
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+
+void Debug_Log(const char *FuncName, const char *Fmt, ...)
+{
+ va_list args;
+ int i = gDebug_Level;
+ tTID tid = Threads_GetTID();
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now());
+ while(i--) Debug_Puts(TRACE_TO_KTERM, " ");
+
+ Debug_Puts(TRACE_TO_KTERM, FuncName);
+ Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid);
+ Debug_Puts(TRACE_TO_KTERM, ": ");
+
+ va_start(args, Fmt);
+ Debug_Fmt(TRACE_TO_KTERM, Fmt, args);
+ va_end(args);
+
+ Debug_Puts(TRACE_TO_KTERM, "\r\n");
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+
+void Debug_Leave(const char *FuncName, char RetType, ...)
+{
+ va_list args;
+ int i;
+ tTID tid = Threads_GetTID();
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTLOCK(&glDebug_Lock);
+ #endif
+
+ i = --gDebug_Level;
+
+ va_start(args, RetType);
+
+ if( i == -1 ) {
+ gDebug_Level = 0;
+ i = 0;
+ }
+ Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now());
+ // Indenting
+ while(i--) Debug_Puts(TRACE_TO_KTERM, " ");
+
+ Debug_Puts(TRACE_TO_KTERM, FuncName);
+ Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid);
+ Debug_Puts(TRACE_TO_KTERM, ": RETURN");
+
+ // No Return
+ if(RetType == '-') {
+ Debug_Puts(TRACE_TO_KTERM, "\r\n");
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+ return;
+ }
+
+ switch(RetType)
+ {
+ case 'n': Debug_Puts(TRACE_TO_KTERM, " NULL"); break;
+ case 'p': Debug_Fmt(TRACE_TO_KTERM, " %p", args); break;
+ case 'P': Debug_Fmt(TRACE_TO_KTERM, " %P", args); break; // PAddr
+ case 's': Debug_Fmt(TRACE_TO_KTERM, " '%s'", args); break;
+ case 'i': Debug_Fmt(TRACE_TO_KTERM, " %i", args); break;
+ case 'u': Debug_Fmt(TRACE_TO_KTERM, " %u", args); break;
+ case 'x': Debug_Fmt(TRACE_TO_KTERM, " 0x%x", args); break;
+ // Extended (64-Bit)
+ case 'X': Debug_Fmt(TRACE_TO_KTERM, " 0x%llx", args); break;
+ }
+ Debug_Puts(TRACE_TO_KTERM, "\r\n");
+
+ va_end(args);
+
+ #if LOCK_DEBUG_OUTPUT
+ SHORTREL(&glDebug_Lock);
+ #endif
+}
+
+void Debug_HexDump(const char *Header, const void *Data, Uint Length)
+{
+ const Uint8 *cdat = Data;
+ Uint pos = 0;
+ LogF("%014lli ", now());
+ Debug_Puts(1, Header);
+ LogF(" (Hexdump of %p)\r\n", Data);
+
+ #define CH(n) ((' '<=cdat[(n)]&&cdat[(n)]<0x7F) ? cdat[(n)] : '.')
+
+ while(Length >= 16)
+ {
+ LogF("%014lli Log: %04x:"
+ " %02x %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x"
+ " %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n",
+ now(),
+ pos,
+ cdat[ 0], cdat[ 1], cdat[ 2], cdat[ 3], cdat[ 4], cdat[ 5], cdat[ 6], cdat[ 7],
+ cdat[ 8], cdat[ 9], cdat[10], cdat[11], cdat[12], cdat[13], cdat[14], cdat[15],
+ CH(0), CH(1), CH(2), CH(3), CH(4), CH(5), CH(6), CH(7),
+ CH(8), CH(9), CH(10), CH(11), CH(12), CH(13), CH(14), CH(15)
+ );
+ Length -= 16;
+ cdat += 16;
+ pos += 16;
+ }
+
+ {
+ int i ;
+ LogF("%014lli Log: %04x: ", now(), pos);
+ for(i = 0; i < Length; i ++)
+ {
+ LogF("%02x ", cdat[i]);
+ }
+ for( ; i < 16; i ++) LogF(" ");
+ LogF(" ");
+ for(i = 0; i < Length; i ++)
+ {
+ if( i == 8 ) LogF(" ");
+ LogF("%c", CH(i));
+ }
+
+ Debug_Putchar('\r');
+ Debug_Putchar('\n');
+ }
+}
+
+// --- EXPORTS ---
+EXPORT(Debug);
+EXPORT(Log);
+EXPORT(Warning);
+EXPORT(Debug_Enter);
+EXPORT(Debug_Log);
+EXPORT(Debug_Leave);
--- /dev/null
+# Acess2 Module/Driver Templater Makefile
+# Makefile.tpl
+
+-include ../../Makefile.cfg
+
+CPPFLAGS = -I../include -I../arch/$(ARCHDIR)/include -DARCH=$(ARCH) -DBUILD_MODULE
+CFLAGS = -Wall -Werror $(CPPFLAGS)
+
+.PHONY: all clean
+
+all: bochsvbe.kmd
+
+%.kmd: %.o
+ $(CC) -shared -nostdlib -o $@ $<
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+#ata_x86.kmd: ata_x86.o
+#bochsvbe.kmd: bochsvbe.o
--- /dev/null
+/* AcessOS
+ * FIFO Pipe Driver
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define DEFAULT_RING_SIZE 2048
+#define PF_BLOCKING 1
+
+// === TYPES ===
+typedef struct sPipe {
+ struct sPipe *Next;
+ char *Name;
+ tVFS_Node Node;
+ Uint Flags;
+ int ReadPos;
+ int WritePos;
+ int BufSize;
+ char *Buffer;
+} tPipe;
+
+// === PROTOTYPES ===
+ int FIFO_Install(char **Arguments);
+ int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data);
+char *FIFO_ReadDir(tVFS_Node *Node, int Id);
+tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename);
+ int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+void FIFO_Reference(tVFS_Node *Node);
+void FIFO_Close(tVFS_Node *Node);
+ int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
+Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+tPipe *FIFO_Int_NewPipe(int Size, const char *Name);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x0032, FIFO, FIFO_Install, NULL, NULL);
+tVFS_NodeType gFIFO_DirNodeType = {
+ .TypeName = "FIFO Dir Node",
+ .ReadDir = FIFO_ReadDir,
+ .FindDir = FIFO_FindDir,
+ .MkNod = FIFO_MkNod,
+ .Relink = FIFO_Relink,
+ .IOCtl = FIFO_IOCtl
+};
+tVFS_NodeType gFIFO_PipeNodeType = {
+ .TypeName = "FIFO Pipe Node",
+ .Read = FIFO_Read,
+ .Write = FIFO_Write,
+ .Close = FIFO_Close,
+ .Reference = FIFO_Reference
+};
+tDevFS_Driver gFIFO_DriverInfo = {
+ NULL, "fifo",
+ {
+ .Size = 1,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRW,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gFIFO_DirNodeType
+ }
+};
+tVFS_Node gFIFO_AnonNode = {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRW,
+ };
+tPipe *gFIFO_NamedPipes = NULL;
+
+// === CODE ===
+/**
+ * \fn int FIFO_Install(char **Options)
+ * \brief Installs the FIFO Driver
+ */
+int FIFO_Install(char **Options)
+{
+ DevFS_AddDevice( &gFIFO_DriverInfo );
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \fn int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ */
+int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
+{
+ return 0;
+}
+
+/**
+ * \fn char *FIFO_ReadDir(tVFS_Node *Node, int Id)
+ * \brief Reads from the FIFO root
+ */
+char *FIFO_ReadDir(tVFS_Node *Node, int Id)
+{
+ tPipe *tmp = gFIFO_NamedPipes;
+
+ // Entry 0 is Anon Pipes
+ if(Id == 0) return strdup("anon");
+
+ // Find the id'th node
+ while(--Id && tmp) tmp = tmp->Next;
+ // If node found, return it
+ if(tmp) return strdup(tmp->Name);
+ // else error return
+ return NULL;
+}
+
+/**
+ * \fn tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename)
+ * \brief Find a file in the FIFO root
+ * \note Creates an anon pipe if anon is requested
+ */
+tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename)
+{
+ tPipe *tmp;
+ if(!Filename) return NULL;
+
+ // NULL String Check
+ if(Filename[0] == '\0') return NULL;
+
+ // Anon Pipe
+ if(Filename[0] == 'a' && Filename[1] == 'n'
+ && Filename[2] == 'o' && Filename[3] == 'n'
+ && Filename[4] == '\0') {
+ tmp = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon");
+ return &tmp->Node;
+ }
+
+ // Check Named List
+ tmp = gFIFO_NamedPipes;
+ while(tmp)
+ {
+ if(strcmp(tmp->Name, Filename) == 0)
+ return &tmp->Node;
+ tmp = tmp->Next;
+ }
+ return NULL;
+}
+
+/**
+ * \fn int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
+ */
+int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
+{
+ return 0;
+}
+
+void FIFO_Reference(tVFS_Node *Node)
+{
+ if(!Node->ImplPtr) return ;
+
+ Node->ReferenceCount ++;
+}
+
+/**
+ * \fn void FIFO_Close(tVFS_Node *Node)
+ * \brief Close a FIFO end
+ */
+void FIFO_Close(tVFS_Node *Node)
+{
+ tPipe *pipe;
+ if(!Node->ImplPtr) return ;
+
+ Node->ReferenceCount --;
+ if(Node->ReferenceCount) return ;
+
+ pipe = Node->ImplPtr;
+
+ if(strcmp(pipe->Name, "anon") == 0) {
+ Log_Debug("FIFO", "Pipe %p closed", Node->ImplPtr);
+ free(Node->ImplPtr);
+ return ;
+ }
+
+ return ;
+}
+
+/**
+ * \fn int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
+ * \brief Relink a file (Deletes named pipes)
+ */
+int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
+{
+ tPipe *pipe, *tmp;
+
+ if(Node != &gFIFO_DriverInfo.RootNode) return 0;
+
+ // Can't relink anon
+ if(strcmp(OldName, "anon")) return 0;
+
+ // Find node
+ for(pipe = gFIFO_NamedPipes;
+ pipe;
+ pipe = pipe->Next)
+ {
+ if(strcmp(pipe->Name, OldName) == 0)
+ break;
+ }
+ if(!pipe) return 0;
+
+ // Relink a named pipe
+ if(NewName) {
+ // Check new name
+ for(tmp = gFIFO_NamedPipes;
+ tmp;
+ tmp = tmp->Next)
+ {
+ if(strcmp(tmp->Name, NewName) == 0) return 0;
+ }
+ // Create new name
+ free(pipe->Name);
+ pipe->Name = malloc(strlen(NewName)+1);
+ strcpy(pipe->Name, NewName);
+ return 1;
+ }
+
+ // Unlink the pipe
+ if(Node->ImplPtr) {
+ free(Node->ImplPtr);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \fn Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from a fifo pipe
+ */
+Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tPipe *pipe = Node->ImplPtr;
+ Uint len;
+ Uint remaining = Length;
+
+ if(!pipe) return 0;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ while(remaining)
+ {
+ // Wait for buffer to fill
+ if(pipe->Flags & PF_BLOCKING)
+ {
+ if( pipe->ReadPos == pipe->WritePos )
+ VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "FIFO_Read");
+
+ }
+ else
+ {
+ if(pipe->ReadPos == pipe->WritePos)
+ {
+ VFS_MarkAvaliable(Node, 0);
+ LEAVE('i', 0);
+ return 0;
+ }
+ }
+
+ len = remaining;
+ if( pipe->ReadPos < pipe->WritePos )
+ {
+ int avail_bytes = pipe->WritePos - pipe->ReadPos;
+ if( avail_bytes < remaining ) len = avail_bytes;
+ }
+ else
+ {
+ int avail_bytes = pipe->WritePos + pipe->BufSize - pipe->ReadPos;
+ if( avail_bytes < remaining ) len = avail_bytes;
+ }
+
+ LOG("len = %i, remaining = %i", len, remaining);
+
+ // Check if read overflows buffer
+ if(len > pipe->BufSize - pipe->ReadPos)
+ {
+ int ofs = pipe->BufSize - pipe->ReadPos;
+ memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], ofs);
+ memcpy((Uint8*)Buffer + ofs, &pipe->Buffer, len-ofs);
+ }
+ else
+ {
+ memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], len);
+ }
+
+ // Increment read position
+ pipe->ReadPos += len;
+ pipe->ReadPos %= pipe->BufSize;
+
+ // Mark some flags
+ if( pipe->ReadPos == pipe->WritePos ) {
+ VFS_MarkAvaliable(Node, 0);
+ }
+ VFS_MarkFull(Node, 0); // Buffer can't still be full
+
+ // Decrement Remaining Bytes
+ remaining -= len;
+ // Increment Buffer address
+ Buffer = (Uint8*)Buffer + len;
+
+ // TODO: Option to read differently
+ LEAVE('i', len);
+ return len;
+ }
+
+ LEAVE('i', Length);
+ return Length;
+
+}
+
+/**
+ * \fn Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Write to a fifo pipe
+ */
+Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tPipe *pipe = Node->ImplPtr;
+ Uint len;
+ Uint remaining = Length;
+
+ if(!pipe) return 0;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ while(remaining)
+ {
+ // Wait for buffer to empty
+ if(pipe->Flags & PF_BLOCKING) {
+ if( pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize )
+ VFS_SelectNode(Node, VFS_SELECT_WRITE, NULL, "FIFO_Write");
+
+ len = remaining;
+ if( pipe->ReadPos > pipe->WritePos )
+ {
+ int rem_space = pipe->ReadPos - pipe->WritePos;
+ if(rem_space < remaining) len = rem_space;
+ }
+ else
+ {
+ int rem_space = pipe->ReadPos + pipe->BufSize - pipe->WritePos;
+ if(rem_space < remaining) len = rem_space;
+ }
+ }
+ else
+ {
+ if(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize)
+ {
+ LEAVE('i', 0);
+ return 0;
+ }
+ // Write buffer
+ if(pipe->ReadPos - pipe->WritePos < remaining)
+ len = pipe->ReadPos - pipe->WritePos;
+ else
+ len = remaining;
+ }
+
+ // Check if write overflows buffer
+ if(len > pipe->BufSize - pipe->WritePos)
+ {
+ int ofs = pipe->BufSize - pipe->WritePos;
+ memcpy(&pipe->Buffer[pipe->WritePos], Buffer, ofs);
+ memcpy(&pipe->Buffer, (Uint8*)Buffer + ofs, len-ofs);
+ }
+ else
+ {
+ memcpy(&pipe->Buffer[pipe->WritePos], Buffer, len);
+ }
+
+ // Increment read position
+ pipe->WritePos += len;
+ pipe->WritePos %= pipe->BufSize;
+
+ // Mark some flags
+ if( pipe->ReadPos == pipe->WritePos ) {
+ VFS_MarkFull(Node, 1); // Buffer full
+ }
+ VFS_MarkAvaliable(Node, 1);
+
+ // Decrement Remaining Bytes
+ remaining -= len;
+ // Increment Buffer address
+ Buffer = (Uint8*)Buffer + len;
+ }
+
+ LEAVE('i', Length);
+ return Length;
+}
+
+// --- HELPERS ---
+/**
+ * \fn tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
+ * \brief Create a new pipe
+ */
+tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
+{
+ tPipe *ret;
+ int namelen = strlen(Name) + 1;
+ int allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size + namelen;
+
+ ret = calloc(1, allocsize);
+ if(!ret) return NULL;
+
+ // Clear Return
+ ret->Flags = PF_BLOCKING;
+
+ // Allocate Buffer
+ ret->BufSize = Size;
+ ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) );
+
+ // Set name (and FIFO name)
+ ret->Name = ret->Buffer + Size;
+ strcpy(ret->Name, Name);
+ // - Start empty, max of `Size`
+ //Semaphore_Init( &ret->Semaphore, 0, Size, "FIFO", ret->Name );
+
+ // Set Node
+ ret->Node.ReferenceCount = 1;
+ ret->Node.Size = 0;
+ ret->Node.ImplPtr = ret;
+ ret->Node.UID = Threads_GetUID();
+ ret->Node.GID = Threads_GetGID();
+ ret->Node.NumACLs = 1;
+ ret->Node.ACLs = (void*)( (Uint)ret + sizeof(tPipe) );
+ ret->Node.ACLs->Group = 0;
+ ret->Node.ACLs->ID = ret->Node.UID;
+ ret->Node.ACLs->Inv = 0;
+ ret->Node.ACLs->Perms = -1;
+ ret->Node.CTime
+ = ret->Node.MTime
+ = ret->Node.ATime = now();
+ ret->Node.Type = &gFIFO_PipeNodeType;
+
+ return ret;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - IO Cache
+ *
+ * By thePowersGang (John Hodge)
+ *
+ * TODO: Convert to use spare physical pages instead
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <iocache.h>
+
+// === TYPES ===
+typedef struct sIOCache_Ent tIOCache_Ent;
+typedef struct sIOCache_PageInfo tIOCache_PageInfo;
+
+// === STRUCTURES ===
+struct sIOCache_Ent
+{
+ tIOCache_Ent *Next;
+ Uint64 Num;
+ Sint64 LastAccess;
+ Sint64 LastWrite;
+ Uint8 Data[];
+};
+
+struct sIOCache_PageInfo
+{
+ tIOCache_PageInfo *GlobalNext;
+ tIOCache_PageInfo *CacheNext;
+ tIOCache *Owner;
+ tPAddr BasePhys;
+ Uint64 BaseOffset;
+};
+
+struct sIOCache
+{
+ tIOCache *Next;
+ int SectorSize;
+ tMutex Lock;
+ int Mode;
+ Uint32 ID;
+ tIOCache_WriteCallback Write;
+ int CacheSize;
+ int CacheUsed;
+ tIOCache_Ent *Entries;
+};
+
+// === GLOBALS ===
+tShortSpinlock glIOCache_Caches;
+tIOCache *gIOCache_Caches = NULL;
+ int giIOCache_NumCaches = 0;
+tIOCache_PageInfo *gIOCache_GlobalPages;
+
+// === CODE ===
+/**
+ * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
+ * \brief Creates a new IO Cache
+ */
+tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize )
+{
+ tIOCache *ret = calloc( 1, sizeof(tIOCache) );
+
+ // Sanity Check
+ if(!ret) return NULL;
+
+ // Fill Structure
+ ret->SectorSize = SectorSize;
+ ret->Mode = IOCACHE_WRITEBACK;
+ ret->ID = ID;
+ ret->Write = Write;
+ ret->CacheSize = CacheSize;
+
+ // Append to list
+ SHORTLOCK( &glIOCache_Caches );
+ ret->Next = gIOCache_Caches;
+ gIOCache_Caches = ret;
+ SHORTREL( &glIOCache_Caches );
+
+ // Return
+ return ret;
+}
+
+/**
+ * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
+ * \brief Read from a cached sector
+ */
+int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer )
+{
+
+ ENTER("pCache XSector pBuffer", Cache, Sector, Buffer);
+
+ // Sanity Check!
+ if(!Cache || !Buffer) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Lock
+ Mutex_Acquire( &Cache->Lock );
+ if(Cache->CacheSize == 0) {
+ Mutex_Release( &Cache->Lock );
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ #if IOCACHE_USE_PAGES
+ tIOCache_PageInfo *page;
+ size_t offset = (Sector*Cache->SectorSize) % PAGE_SIZE;
+ Uint64 wanted_base = (Sector*Cache->SectorSize) & ~(PAGE_SIZE-1);
+ for( page = Cache->Pages; page; page = page->CacheNext )
+ {
+ void *tmp;
+ if(page->BaseOffset < WantedBase) continue;
+ if(page->BaseOffset > WantedBase) break;
+ tmp = MM_MapTemp( page->BasePhys );
+ memcpy( Buffer, tmp + offset, Cache->SectorSize );
+ MM_FreeTemp( tmp );
+ }
+ #else
+ tIOCache_Ent *ent;
+ // Search the list
+ for( ent = Cache->Entries; ent; ent = ent->Next )
+ {
+ // Have we found what we are looking for?
+ if( ent->Num == Sector ) {
+ memcpy(Buffer, ent->Data, Cache->SectorSize);
+ ent->LastAccess = now();
+ Mutex_Release( &Cache->Lock );
+ LEAVE('i', 1);
+ return 1;
+ }
+ // It's a sorted list, so as soon as we go past `Sector` we know
+ // it's not there
+ if(ent->Num > Sector) break;
+ }
+ #endif
+
+ Mutex_Release( &Cache->Lock );
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
+ * \brief Cache a sector
+ */
+int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer )
+{
+ tIOCache_Ent *ent, *prev;
+ tIOCache_Ent *new;
+ tIOCache_Ent *oldest = NULL, *oldestPrev;
+
+ // Sanity Check!
+ if(!Cache || !Buffer)
+ return -1;
+
+ // Lock
+ Mutex_Acquire( &Cache->Lock );
+ if(Cache->CacheSize == 0) {
+ Mutex_Release( &Cache->Lock );
+ return -1;
+ }
+
+ // Search the list
+ prev = (tIOCache_Ent*)&Cache->Entries;
+ for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next )
+ {
+ // Is it already here?
+ if( ent->Num == Sector ) {
+ Mutex_Release( &Cache->Lock );
+ return 0;
+ }
+
+ // Check if we have found the oldest entry
+ if( !oldest || oldest->LastAccess > ent->LastAccess ) {
+ oldest = ent;
+ oldestPrev = prev;
+ }
+
+ // Here we go!
+ if(ent->Num > Sector)
+ break;
+ }
+
+ // Create the new entry
+ new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize );
+ new->Next = ent;
+ new->Num = Sector;
+ new->LastAccess = now();
+ new->LastWrite = 0; // Zero is special, it means unmodified
+ memcpy(new->Data, Buffer, Cache->SectorSize);
+
+ // Have we reached the maximum cached entries?
+ if( Cache->CacheUsed == Cache->CacheSize )
+ {
+ tIOCache_Ent *savedPrev = prev;
+ oldestPrev = (tIOCache_Ent*)&Cache->Entries;
+ // If so, search for the least recently accessed entry
+ for( ; ent; prev = ent, ent = ent->Next )
+ {
+ // Check if we have found the oldest entry
+ if( !oldest || oldest->LastAccess > ent->LastAccess ) {
+ oldest = ent;
+ oldestPrev = prev;
+ }
+ }
+ if( !oldest ) {
+ Log_Error("IOCache", "Cache full, but also empty");
+ return -1;
+ }
+ // Remove from list, write back and free
+ oldestPrev->Next = oldest->Next;
+ if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL)
+ Cache->Write(Cache->ID, oldest->Num, oldest->Data);
+ free(oldest);
+
+ // Decrement the used count
+ Cache->CacheUsed --;
+
+ // Restore `prev`
+ prev = savedPrev;
+ }
+
+ // Append to list
+ prev->Next = new;
+ Cache->CacheUsed ++;
+
+ // Release Spinlock
+ Mutex_Release( &Cache->Lock );
+
+ // Return success
+ return 1;
+}
+
+/**
+ * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
+ * \brief Read from a cached sector
+ */
+int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer )
+{
+ tIOCache_Ent *ent;
+
+ // Sanity Check!
+ if(!Cache || !Buffer)
+ return -1;
+ // Lock
+ Mutex_Acquire( &Cache->Lock );
+ if(Cache->CacheSize == 0) {
+ Mutex_Release( &Cache->Lock );
+ return -1;
+ }
+
+ // Search the list
+ for( ent = Cache->Entries; ent; ent = ent->Next )
+ {
+ // Have we found what we are looking for?
+ if( ent->Num == Sector ) {
+ memcpy(ent->Data, Buffer, Cache->SectorSize);
+ ent->LastAccess = ent->LastWrite = now();
+
+ if(Cache->Mode == IOCACHE_WRITEBACK) {
+ Cache->Write(Cache->ID, Sector, Buffer);
+ ent->LastWrite = 0;
+ }
+
+ Mutex_Release( &Cache->Lock );
+ return 1;
+ }
+ // It's a sorted list, so as soon as we go past `Sector` we know
+ // it's not there
+ if(ent->Num > Sector) break;
+ }
+
+ Mutex_Release( &Cache->Lock );
+ return 0;
+}
+
+/**
+ * \fn void IOCache_Flush( tIOCache *Cache )
+ * \brief Flush a cache
+ */
+void IOCache_Flush( tIOCache *Cache )
+{
+ tIOCache_Ent *ent;
+
+ if( Cache->Mode == IOCACHE_VIRTUAL ) return;
+
+ // Lock
+ Mutex_Acquire( &Cache->Lock );
+ if(Cache->CacheSize == 0) {
+ Mutex_Release( &Cache->Lock );
+ return;
+ }
+
+ // Write All
+ for( ent = Cache->Entries; ent; ent = ent->Next )
+ {
+ Cache->Write(Cache->ID, ent->Num, ent->Data);
+ ent->LastWrite = 0;
+ }
+
+ Mutex_Release( &Cache->Lock );
+}
+
+/**
+ * \fn void IOCache_Destroy( tIOCache *Cache )
+ * \brief Destroy a cache
+ */
+void IOCache_Destroy( tIOCache *Cache )
+{
+ tIOCache_Ent *ent, *prev = NULL;
+
+ // Lock
+ Mutex_Acquire( &Cache->Lock );
+ if(Cache->CacheSize == 0) {
+ Mutex_Release( &Cache->Lock );
+ return;
+ }
+
+ // Free All
+ for(ent = Cache->Entries;
+ ent;
+ prev = ent, ent = ent->Next, free(prev) )
+ {
+ if( Cache->Mode != IOCACHE_VIRTUAL )
+ {
+ Cache->Write(Cache->ID, ent->Num, ent->Data);
+ ent->LastWrite = 0;
+ }
+ }
+
+ Cache->CacheSize = 0;
+
+ Mutex_Release( &Cache->Lock );
+
+ // Remove from list
+ SHORTLOCK( &glIOCache_Caches );
+ {
+ tIOCache *cache;
+ tIOCache *prev_cache = (tIOCache*)&gIOCache_Caches;
+ for(cache = gIOCache_Caches;
+ cache;
+ prev_cache = cache, cache = cache->Next )
+ {
+ if(cache == Cache) {
+ prev_cache->Next = cache->Next;
+ break;
+ }
+ }
+ }
+ SHORTREL( &glIOCache_Caches );
+
+ free(Cache);
+}
--- /dev/null
+/*\r
+ * AcessOS/AcessBasic v0.1\r
+ * PCI Bus Driver\r
+ */\r
+#define DEBUG 0\r
+#include <acess.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+#include <drv_pci_int.h>\r
+\r
+#define LIST_DEVICES 1\r
+\r
+// === STRUCTURES ===\r
+typedef struct sPCIDevice\r
+{\r
+ Uint16 bus, slot, fcn;\r
+ Uint16 vendor, device;\r
+ Uint32 class; // Class:Subclass:ProgIf\r
+ Uint8 revision;\r
+ Uint32 ConfigCache[256/4];\r
+ char Name[8];\r
+ tVFS_Node Node;\r
+} tPCIDevice;\r
+\r
+// === CONSTANTS ===\r
+#define SPACE_STEP 5\r
+#define MAX_RESERVED_PORT 0xD00\r
+\r
+// === PROTOTYPES ===\r
+ int PCI_Install(char **Arguments);\r
+ int PCI_ScanBus(int ID, int bFill);\r
+ \r
+char *PCI_int_ReadDirRoot(tVFS_Node *node, int pos);\r
+tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename);\r
+Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset);\r
+Uint64 PCI_int_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer);\r
+ int PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL);\r
+ int giPCI_BusCount = 1;\r
+ int giPCI_InodeHandle = -1;\r
+ int giPCI_DeviceCount = 0;\r
+tPCIDevice *gPCI_Devices = NULL;\r
+tVFS_NodeType gPCI_RootNodeType = {\r
+ .TypeName = "PCI Root Node",\r
+ .ReadDir = PCI_int_ReadDirRoot,\r
+ .FindDir = PCI_int_FindDirRoot\r
+};\r
+tVFS_NodeType gPCI_DevNodeType = {\r
+ .TypeName = "PCI Dev Node",\r
+ .Read = PCI_int_ReadDevice\r
+};\r
+tDevFS_Driver gPCI_DriverStruct = {\r
+ NULL, "pci",\r
+ {\r
+ .Flags = VFS_FFLAG_DIRECTORY,\r
+ .Size = -1,\r
+ .NumACLs = 1,\r
+ .ACLs = &gVFS_ACL_EveryoneRX,\r
+ .Type = &gPCI_RootNodeType\r
+ }\r
+};\r
+Uint32 *gaPCI_PortBitmap = NULL;\r
+Uint32 gaPCI_BusBitmap[256/32];\r
+ \r
+// === CODE ===\r
+/**\r
+ * \brief Scan the PCI Bus for devices\r
+ * \param Arguments Boot-time parameters\r
+ */\r
+int PCI_Install(char **Arguments)\r
+{\r
+ int i;\r
+ void *tmpPtr;\r
+ \r
+ // Build Portmap\r
+ gaPCI_PortBitmap = malloc( 1 << 13 );\r
+ if( !gaPCI_PortBitmap ) {\r
+ Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13);\r
+ return MODULE_ERR_MALLOC;\r
+ }\r
+ memset( gaPCI_PortBitmap, 0, 1 << 13 );\r
+ for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )\r
+ gaPCI_PortBitmap[i] = -1;\r
+ for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )\r
+ gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;\r
+ \r
+ // Scan Bus (Bus 0, Don't fill gPCI_Devices)\r
+ i = PCI_ScanBus(0, 0);\r
+ if(i != MODULE_ERR_OK) return i;\r
+ \r
+ if(giPCI_DeviceCount == 0) {\r
+ Log_Notice("PCI", "No devices were found");\r
+ return MODULE_ERR_NOTNEEDED;\r
+ }\r
+ \r
+ // Allocate device buffer\r
+ tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice));\r
+ if(tmpPtr == NULL) {\r
+ Log_Warning("PCI", "Malloc ERROR");\r
+ return MODULE_ERR_MALLOC;\r
+ }\r
+ gPCI_Devices = tmpPtr;\r
+ \r
+ Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount);\r
+ \r
+ // Reset counts\r
+ giPCI_DeviceCount = 0;\r
+ giPCI_BusCount = 0;\r
+ memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap));\r
+ // Rescan, filling the PCI device array\r
+ PCI_ScanBus(0, 1);\r
+ \r
+ // Complete Driver Structure\r
+ gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
+ \r
+ // And add to DevFS\r
+ DevFS_AddDevice(&gPCI_DriverStruct);\r
+ \r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ * \brief Scans a specific PCI Bus\r
+ * \param BusID PCI Bus ID to scan\r
+ * \param bFill Fill the \a gPCI_Devices array?\r
+ */\r
+int PCI_ScanBus(int BusID, int bFill)\r
+{\r
+ int dev, fcn;\r
+ tPCIDevice devInfo;\r
+ \r
+ if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) )\r
+ return MODULE_ERR_OK;\r
+ \r
+ gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32));\r
+ \r
+ for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus\r
+ {\r
+ for( fcn = 0; fcn < 8; fcn++ ) // Max 8 functions per device\r
+ {\r
+ // Check if the device/function exists\r
+ if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))\r
+ continue;\r
+ \r
+ if(devInfo.class == PCI_OC_PCIBRIDGE)\r
+ {\r
+ #if LIST_DEVICES\r
+ if( !bFill )\r
+ Log_Log("PCI", "Bridge @ %i,%i:%i (0x%x:0x%x)",\r
+ BusID, dev, fcn, devInfo.vendor, devInfo.device);\r
+ #endif\r
+ //TODO: Handle PCI-PCI Bridges\r
+ //PCI_ScanBus(devInfo.???, bFill);\r
+ giPCI_BusCount ++;\r
+ }\r
+ else\r
+ {\r
+ #if LIST_DEVICES\r
+ if( !bFill )\r
+ Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x",\r
+ BusID, dev, fcn, devInfo.class, devInfo.vendor, devInfo.device);\r
+ #endif\r
+ }\r
+ \r
+ if( bFill ) {\r
+ devInfo.Node.Inode = giPCI_DeviceCount;\r
+ memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice));\r
+ }\r
+ giPCI_DeviceCount ++;\r
+ \r
+ // If bit 23 of (soemthing) is set, there are sub-functions\r
+ if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )\r
+ break;\r
+ }\r
+ }\r
+ \r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ * \brief Read from Root of PCI Driver\r
+*/\r
+char *PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos)\r
+{\r
+ ENTER("pNode iPos", Node, Pos);\r
+ if(Pos < 0 || Pos >= giPCI_DeviceCount) {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ LEAVE('s', gPCI_Devices[Pos].Name);\r
+ return strdup( gPCI_Devices[Pos].Name );\r
+}\r
+/**\r
+ */\r
+tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename)\r
+{\r
+ int bus,slot,fcn;\r
+ int i;\r
+ // Validate Filename (Pointer and length)\r
+ if(!filename || strlen(filename) != 7)\r
+ return NULL;\r
+ // Check for spacers\r
+ if(filename[2] != '.' || filename[5] != ':')\r
+ return NULL;\r
+ \r
+ // Get Information\r
+ if(filename[0] < '0' || filename[0] > '9') return NULL;\r
+ bus = (filename[0] - '0')*10;\r
+ if(filename[1] < '0' || filename[1] > '9') return NULL;\r
+ bus += filename[1] - '0';\r
+ if(filename[3] < '0' || filename[3] > '9') return NULL;\r
+ slot = (filename[3] - '0')*10;\r
+ if(filename[4] < '0' || filename[4] > '9') return NULL;\r
+ slot += filename[4] - '0';\r
+ if(filename[6] < '0' || filename[6] > '9') return NULL;\r
+ fcn = filename[6] - '0';\r
+ \r
+ // Find Match\r
+ for(i=0;i<giPCI_DeviceCount;i++)\r
+ {\r
+ if(gPCI_Devices[i].bus != bus) continue;\r
+ if(gPCI_Devices[i].slot != slot) continue;\r
+ if(gPCI_Devices[i].fcn != fcn) continue;\r
+ \r
+ return &gPCI_Devices[i].Node;\r
+ }\r
+ \r
+ // Error Return\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ */\r
+Uint64 PCI_int_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
+{ \r
+ if( pos + length > 256 ) return 0;\r
+ \r
+ memcpy(\r
+ buffer,\r
+ (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
+ length);\r
+ \r
+ return length;\r
+}\r
+\r
+// --- Kernel Code Interface ---\r
+/**\r
+ * \brief Counts the devices with the specified codes\r
+ * \param vendor Vendor ID\r
+ * \param device Device ID\r
+ */\r
+int PCI_CountDevices(Uint16 vendor, Uint16 device)\r
+{\r
+ int i, ret=0;\r
+ for(i=0;i<giPCI_DeviceCount;i++)\r
+ {\r
+ if(gPCI_Devices[i].vendor != vendor) continue;\r
+ if(gPCI_Devices[i].device != device) continue;\r
+ ret ++;\r
+ }\r
+ return ret;\r
+}\r
+\r
+/**\r
+ * \brief Gets the ID of the specified PCI device\r
+ * \param vendor Vendor ID\r
+ * \param device Device ID\r
+ * \param idx Number of matching entry wanted\r
+ */\r
+tPCIDev PCI_GetDevice(Uint16 vendor, Uint16 device, int idx)\r
+{\r
+ int i, j=0;\r
+ for( i = 0; i < giPCI_DeviceCount; i ++ )\r
+ {\r
+ if(gPCI_Devices[i].vendor != vendor) continue;\r
+ if(gPCI_Devices[i].device != device) continue;\r
+ if(j == idx) return i;\r
+ j ++;\r
+ }\r
+ return -1;\r
+}\r
+\r
+/**\r
+ * \brief Gets the ID of a device by it's class code\r
+ * \param class Class Code\r
+ * \param mask Mask for class comparison\r
+ * \param prev ID of previous device (-1 for no previous)\r
+ */\r
+tPCIDev PCI_GetDeviceByClass(Uint32 class, Uint32 mask, tPCIDev prev)\r
+{\r
+ int i;\r
+ // Check if prev is negative (meaning get first)\r
+ if(prev < 0) i = 0;\r
+ else i = prev+1;\r
+ \r
+ for( ; i < giPCI_DeviceCount; i++ )\r
+ {\r
+ if((gPCI_Devices[i].class & mask) == class)\r
+ return i;\r
+ }\r
+ return -1;\r
+}\r
+\r
+int PCI_GetDeviceInfo(tPCIDev ID, Uint16 *Vendor, Uint16 *Device, Uint32 *Class)\r
+{\r
+ tPCIDevice *dev = &gPCI_Devices[ID];\r
+ if(ID < 0 || ID >= giPCI_DeviceCount) return 1;\r
+ \r
+ if(Vendor) *Vendor = dev->vendor;\r
+ if(Device) *Device = dev->device;\r
+ if(Class) *Class = dev->class;\r
+ return 0;\r
+}\r
+\r
+int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision)\r
+{\r
+ tPCIDevice *dev = &gPCI_Devices[ID];\r
+ if(ID < 0 || ID >= giPCI_DeviceCount) return 1;\r
+ \r
+ if(Revision) *Revision = dev->revision;\r
+ return 0;\r
+}\r
+\r
+int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID)\r
+{\r
+ tPCIDevice *dev = &gPCI_Devices[ID];\r
+ if(ID < 0 || ID >= giPCI_DeviceCount) return 1;\r
+ \r
+ if(SubsystemVendor) *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF;\r
+ if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16;\r
+\r
+ return 0;\r
+}\r
+\r
+Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset)\r
+{\r
+ Bus &= 0xFF;\r
+ Slot &= 0x1F;\r
+ Fcn &= 7;\r
+ Offset &= 0xFC;\r
+ return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC);\r
+}\r
+\r
+Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size)\r
+{\r
+ tPCIDevice *dev;\r
+ Uint32 dword, addr;\r
+ \r
+ if( ID < 0 || ID >= giPCI_DeviceCount ) return 0;\r
+ if( Offset < 0 || Offset > 256 ) return 0;\r
+\r
+ // TODO: Should I support non-aligned reads?\r
+ if( Offset & (Size - 1) ) return 0;\r
+\r
+ dev = &gPCI_Devices[ID];\r
+ addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
+\r
+ dword = PCI_CfgReadDWord(addr);\r
+ gPCI_Devices[ID].ConfigCache[Offset/4] = dword;\r
+ switch( Size )\r
+ {\r
+ case 1: return (dword >> (8 * (Offset&3))) & 0xFF;\r
+ case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF;\r
+ case 4: return dword;\r
+ default:\r
+ return 0;\r
+ }\r
+}\r
+\r
+void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value)\r
+{\r
+ tPCIDevice *dev;\r
+ Uint32 dword, addr;\r
+ int shift;\r
+ if( ID < 0 || ID >= giPCI_DeviceCount ) return ;\r
+ if( Offset < 0 || Offset > 256 ) return ;\r
+ \r
+ dev = &gPCI_Devices[ID];\r
+ addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
+\r
+ if(Size != 4)\r
+ dword = PCI_CfgReadDWord(addr);\r
+ switch(Size)\r
+ {\r
+ case 1:\r
+ shift = (Offset&3)*8;\r
+ dword &= ~(0xFF << shift);\r
+ dword |= Value << shift;\r
+ break;\r
+ case 2:\r
+ shift = (Offset&2)*8;\r
+ dword &= ~(0xFFFF << shift);\r
+ dword |= Value << shift;\r
+ break;\r
+ case 4:\r
+ dword = Value;\r
+ break;\r
+ default:\r
+ return;\r
+ }\r
+ PCI_CfgWriteDWord(addr, dword);\r
+}\r
+\r
+/**\r
+ * \brief Get the IRQ assigned to a device\r
+ */\r
+Uint8 PCI_GetIRQ(tPCIDev id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[15] & 0xFF;\r
+ //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
+}\r
+\r
+/**\r
+ * \brief Read the a BAR (base address register) from the PCI config space\r
+ */\r
+Uint32 PCI_GetBAR(tPCIDev id, int BARNum)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ if(BARNum < 0 || BARNum >= 6)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[4+BARNum];\r
+}\r
+\r
+/**\r
+ * \brief Get device information for a slot/function\r
+ */\r
+int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info)\r
+{\r
+ Uint32 vendor_dev, tmp;\r
+ int i;\r
+ Uint32 addr;\r
+ addr = PCI_int_GetBusAddr(bus, slot, fcn, 0); \r
+\r
+ vendor_dev = PCI_CfgReadDWord( addr );\r
+ if((vendor_dev & 0xFFFF) == 0xFFFF) // Invalid Device\r
+ return 0;\r
+\r
+ info->ConfigCache[0] = vendor_dev;\r
+ for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )\r
+ {\r
+ info->ConfigCache[i] = PCI_CfgReadDWord(addr);\r
+ } \r
+\r
+ info->bus = bus;\r
+ info->slot = slot;\r
+ info->fcn = fcn;\r
+ info->vendor = vendor_dev & 0xFFFF;\r
+ info->device = vendor_dev >> 16;\r
+ tmp = info->ConfigCache[2];\r
+ info->revision = tmp & 0xFF;\r
+ info->class = tmp >> 8;\r
+ \r
+// #if LIST_DEVICES\r
+// Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
+// Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
+// Log("Class: 0x%06x", info->class);\r
+// #endif\r
+ \r
+ // Make node name\r
+ info->Name[0] = '0' + bus/10;\r
+ info->Name[1] = '0' + bus%10;\r
+ info->Name[2] = '.';\r
+ info->Name[3] = '0' + slot/10;\r
+ info->Name[4] = '0' + slot%10;\r
+ info->Name[5] = ':';\r
+ info->Name[6] = '0' + fcn;\r
+ info->Name[7] = '\0';\r
+ \r
+ // Create VFS Node\r
+ memset( &info->Node, 0, sizeof(tVFS_Node) );\r
+ info->Node.Size = 256;\r
+ \r
+ info->Node.NumACLs = 1;\r
+ info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
+ \r
+ info->Node.Type = &gPCI_RootNodeType;\r
+ \r
+ return 1;\r
+}\r
+\r
+// === EXPORTS ===\r
+//*\r
+EXPORT(PCI_CountDevices);\r
+EXPORT(PCI_GetDevice);\r
+EXPORT(PCI_GetDeviceByClass);\r
+EXPORT(PCI_GetDeviceInfo);\r
+EXPORT(PCI_GetDeviceVersion);\r
+EXPORT(PCI_GetDeviceSubsys);\r
+//EXPORT(PCI_AssignPort);\r
+EXPORT(PCI_GetBAR);\r
+EXPORT(PCI_GetIRQ);\r
+//*/\r
--- /dev/null
+/*
+ * Acess2
+ * - Kernel Status Driver
+ */
+#define DEBUG 1
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <fs_sysfs.h>
+
+// === CONSTANTS ===
+#define VERSION ((0 << 8) | (1)) // 0.01
+
+// === TYPES ===
+typedef struct sSysFS_Ent
+{
+ struct sSysFS_Ent *Next;
+ struct sSysFS_Ent *ListNext;
+ struct sSysFS_Ent *Parent;
+ tVFS_Node Node;
+ char Name[];
+} tSysFS_Ent;
+
+// === PROTOTYPES ===
+ int SysFS_Install(char **Arguments);
+ int SysFS_IOCtl(tVFS_Node *Node, int Id, void *Data);
+
+#if 0
+ int SysFS_RegisterFile(const char *Path, const char *Data, int Length);
+ int SysFS_UpdateFile(int ID, const char *Data, int Length);
+ int SysFS_RemoveFile(int ID);
+#endif
+
+char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Id);
+tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename);
+Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+void SysFS_Comm_CloseFile(tVFS_Node *Node);
+
+// === GLOBALS ===
+extern tSysFS_Ent gSysFS_Version; // Defined Later
+extern tSysFS_Ent gSysFS_Root; // Defined Later
+MODULE_DEFINE(0, VERSION, SysFS, SysFS_Install, NULL, NULL);
+tVFS_NodeType gSysFS_FileNodeType = {
+ .TypeName = "SysFS File",
+ .Read = SysFS_Comm_ReadFile
+ };
+tVFS_NodeType gSysFS_DirNodeType = {
+ .TypeName = "SysFS Dir",
+ .ReadDir = SysFS_Comm_ReadDir,
+ .FindDir = SysFS_Comm_FindDir
+ };
+tSysFS_Ent gSysFS_Version_Kernel = {
+ NULL, NULL, // Nexts
+ &gSysFS_Version, // Parent
+ {
+ .Inode = 1, // File #1
+ .ImplPtr = NULL,
+ .ImplInt = (Uint)&gSysFS_Version_Kernel, // Self-Link
+ .Size = 0,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRO,
+ .Type = &gSysFS_FileNodeType
+ },
+ "Kernel"
+};
+tSysFS_Ent gSysFS_Version = {
+ NULL, NULL,
+ &gSysFS_Root,
+ {
+ .Size = 1,
+ .ImplPtr = &gSysFS_Version_Kernel,
+ .ImplInt = (Uint)&gSysFS_Version, // Self-Link
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gSysFS_DirNodeType
+ },
+ "Version"
+};
+// Root of the SysFS tree (just used to keep the code clean)
+tSysFS_Ent gSysFS_Root = {
+ NULL, NULL,
+ NULL,
+ {
+ .Size = 1,
+ .ImplPtr = &gSysFS_Version,
+ .ImplInt = (Uint)&gSysFS_Root // Self-Link
+ },
+ "/"
+};
+tDevFS_Driver gSysFS_DriverInfo = {
+ NULL, "system",
+ {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .ImplPtr = &gSysFS_Version,
+ .Size = sizeof(gSysFS_Root)/sizeof(tSysFS_Ent),
+ .Type = &gSysFS_DirNodeType
+ }
+};
+ int giSysFS_NextFileID = 2;
+tSysFS_Ent *gSysFS_FileList;
+
+// === CODE ===
+/**
+ * \fn int SysFS_Install(char **Options)
+ * \brief Installs the SysFS Driver
+ */
+int SysFS_Install(char **Options)
+{
+ {
+ const char *fmt = "Acess2 "EXPAND_STR(KERNEL_VERSION)" "EXPAND_STR(ARCHDIR)" build %i, hash %s";
+ gSysFS_Version_Kernel.Node.Size = sprintf(NULL, fmt, BUILD_NUM, gsGitHash);
+ gSysFS_Version_Kernel.Node.ImplPtr = malloc( gSysFS_Version_Kernel.Node.Size + 1 );
+ sprintf(gSysFS_Version_Kernel.Node.ImplPtr, fmt, BUILD_NUM, gsGitHash);
+ }
+
+ DevFS_AddDevice( &gSysFS_DriverInfo );
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \fn int SysFS_RegisterFile(char *Path, char *Data, int Length)
+ * \brief Registers a file (buffer) for the user to be able to read from
+ * \param Path Path for the file to be accessable from (relative to SysFS root)
+ * \param Data Pointer to the data buffer (must be non-volatile)
+ * \param Length Length of the data buffer
+ * \return The file's identifier
+ */
+int SysFS_RegisterFile(const char *Path, const char *Data, int Length)
+{
+ int start = 0;
+ int tmp;
+ tSysFS_Ent *ent = NULL;
+ tSysFS_Ent *child, *prev;
+
+ // Find parent directory
+ while( (tmp = strpos(&Path[start], '/')) != -1 )
+ {
+ prev = NULL;
+
+ if(ent)
+ child = ent->Node.ImplPtr;
+ else
+ child = gSysFS_DriverInfo.RootNode.ImplPtr;
+ for( ; child; prev = child, child = child->Next )
+ {
+ if( strncmp( &Path[start], child->Name, tmp+1 ) == '/' )
+ break;
+ }
+
+ // Need a new directory?
+ if( !child )
+ {
+ child = calloc( 1, sizeof(tSysFS_Ent)+tmp+1 );
+ child->Next = NULL;
+ memcpy(child->Name, &Path[start], tmp);
+ child->Name[tmp] = '\0';
+ child->Parent = ent;
+ child->Node.Inode = 0;
+ child->Node.ImplPtr = NULL;
+ child->Node.ImplInt = (Uint)child; // Uplink
+ child->Node.NumACLs = 1;
+ child->Node.ACLs = &gVFS_ACL_EveryoneRX;
+ child->Node.Flags = VFS_FFLAG_DIRECTORY;
+ child->Node.Type = &gSysFS_DirNodeType;
+ if( !prev ) {
+ if(ent)
+ ent->Node.ImplPtr = child;
+ else
+ gSysFS_DriverInfo.RootNode.ImplPtr = child;
+ // ^^^ Impossible (There is already /Version)
+ }
+ else
+ prev->Next = child;
+ if(ent)
+ ent->Node.Size ++;
+ else
+ gSysFS_DriverInfo.RootNode.Size ++;
+ Log_Log("SysFS", "Added directory '%s'", child->Name);
+ }
+
+ ent = child;
+
+ start = tmp+1;
+ }
+
+ // ent: Parent tSysFS_Ent or NULL
+ // start: beginning of last path element
+
+ // Check if the name is taken
+ prev = NULL;
+ if(ent)
+ child = ent->Node.ImplPtr;
+ else
+ child = gSysFS_DriverInfo.RootNode.ImplPtr;
+ for( ; child; child = child->Next )
+ {
+ if( strcmp( &Path[start], child->Name ) == 0 )
+ break;
+ }
+ if( child ) {
+ Log_Warning("SysFS", "'%s' is taken (in '%s')\n", &Path[start], Path);
+ return 0;
+ }
+
+ // Create new node
+ child = calloc( 1, sizeof(tSysFS_Ent)+strlen(&Path[start])+1 );
+ child->Next = NULL;
+ strcpy(child->Name, &Path[start]);
+ child->Parent = ent;
+
+ child->Node.Inode = giSysFS_NextFileID++;
+ child->Node.ImplPtr = (void*)Data;
+ child->Node.ImplInt = (Uint)child; // Uplink
+ child->Node.Size = Length;
+ child->Node.NumACLs = 1;
+ child->Node.ACLs = &gVFS_ACL_EveryoneRO;
+ child->Node.Type = &gSysFS_FileNodeType;
+
+ // Add to parent's child list
+ if(ent) {
+ ent->Node.Size ++;
+ child->Next = ent->Node.ImplPtr;
+ ent->Node.ImplPtr = child;
+ }
+ else {
+ gSysFS_DriverInfo.RootNode.Size ++;
+ child->Next = gSysFS_DriverInfo.RootNode.ImplPtr;
+ gSysFS_DriverInfo.RootNode.ImplPtr = child;
+ }
+ // Add to global file list
+ child->ListNext = gSysFS_FileList;
+ gSysFS_FileList = child;
+
+ Log_Log("SysFS", "Added '%s' (%p)", Path, Data);
+
+ return child->Node.Inode;
+}
+
+/**
+ * \fn int SysFS_UpdateFile(int ID, char *Data, int Length)
+ * \brief Updates a file
+ * \param ID Identifier returned by ::SysFS_RegisterFile
+ * \param Data Pointer to the data buffer
+ * \param Length Length of the data buffer
+ * \return Boolean Success
+ */
+int SysFS_UpdateFile(int ID, const char *Data, int Length)
+{
+ tSysFS_Ent *ent;
+
+ for( ent = gSysFS_FileList; ent; ent = ent->Next )
+ {
+ // It's a reverse sorted list
+ if(ent->Node.Inode < ID) return 0;
+ if(ent->Node.Inode == ID)
+ {
+ ent->Node.ImplPtr = (void*)Data;
+ ent->Node.Size = Length;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \fn int SysFS_RemoveFile(int ID)
+ * \brief Removes a file from user access
+ * \param ID Identifier returned by ::SysFS_RegisterFile
+ * \return Boolean Success
+ * \note If a handle is still open to the file, it will be invalidated
+ */
+int SysFS_RemoveFile(int ID)
+{
+ tSysFS_Ent *file;
+ tSysFS_Ent *ent, *parent, *prev;
+
+ prev = NULL;
+ for( ent = gSysFS_FileList; ent; prev = ent, ent = ent->Next )
+ {
+ // It's a reverse sorted list
+ if(ent->Node.Inode < ID) return 0;
+ if(ent->Node.Inode == ID) break;
+ }
+
+ if(!ent) return 0;
+
+ // Set up for next part
+ file = ent;
+ parent = file->Parent;
+
+ // Remove from file list
+ if(prev)
+ prev->ListNext = file->ListNext;
+ else
+ gSysFS_FileList = file->ListNext;
+ file->Node.Size = 0;
+ file->Node.ImplPtr = NULL;
+
+ // Search parent directory
+ for( ent = parent->Node.ImplPtr; ent; prev = ent, ent = ent->Next )
+ {
+ if( ent == file ) break;
+ }
+ if(!ent) {
+ Log_Warning("SysFS", "Bookkeeping Error: File in list, but not in directory");
+ return 0;
+ }
+
+ // Remove from parent directory
+ if(prev)
+ prev->Next = ent->Next;
+ else
+ parent->Node.ImplPtr = ent->Next;
+
+ // Free if not in use
+ if(file->Node.ReferenceCount == 0)
+ free(file);
+
+ return 1;
+}
+
+/**
+ * \fn char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos)
+ * \brief Reads from a SysFS directory
+ */
+char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr;
+ if(Pos < 0 || Pos >= Node->Size) return NULL;
+
+ for( ; child; child = child->Next, Pos-- )
+ {
+ if( Pos == 0 ) return strdup(child->Name);
+ }
+ return NULL;
+}
+
+/**
+ * \fn tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename)
+ * \brief Find a file in a SysFS directory
+ */
+tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename)
+{
+ tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr;
+
+ for( ; child; child = child->Next )
+ {
+ if( strcmp(child->Name, Filename) == 0 )
+ return &child->Node;
+ }
+
+ return NULL;
+}
+
+/**
+ * \fn Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from an exposed buffer
+ */
+Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ if( Offset > Node->Size ) return -1;
+ if( Length > Node->Size ) Length = Node->Size;
+ if( Offset + Length > Node->Size) Length = Node->Size - Offset;
+
+ if( Node->ImplPtr )
+ memcpy(Buffer, (void*)((Uint)Node->ImplPtr+(Uint)Offset), Length);
+ else
+ return -1;
+
+ return Length;
+}
+
+/**
+ * \fn void SysFS_Comm_CloseFile(tVFS_Node *Node)
+ * \brief Closes an open file
+ * \param Node Node to close
+ */
+void SysFS_Comm_CloseFile(tVFS_Node *Node)
+{
+ // Dereference
+ Node->ReferenceCount --;
+ if( Node->ReferenceCount > 0 ) return;
+
+ // Check if it is still valid
+ if( Node->ImplPtr ) return;
+
+ // Delete
+ free( (void*)Node->ImplInt );
+}
--- /dev/null
+/*
+ * Acess2 Virtual Terminal Driver
+ */
+#define DEBUG 0
+#include "vterm.h"
+#include <fs_devfs.h>
+#include <modules.h>
+#include <api_drv_keyboard.h>
+#include <api_drv_video.h>
+#include <errno.h>
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define VERSION ((0<<8)|(50))
+
+#define NUM_VTS 8
+//#define DEFAULT_OUTPUT "BochsGA"
+#define DEFAULT_OUTPUT "Vesa"
+#define FALLBACK_OUTPUT "x86_VGAText"
+#define DEFAULT_INPUT "PS2Keyboard"
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen
+//#define DEFAULT_SCROLLBACK 0
+
+// === TYPES ===
+
+// === IMPORTS ===
+extern void Debug_SetKTerminal(const char *File);
+
+// === PROTOTYPES ===
+ int VT_Install(char **Arguments);
+char *VT_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name);
+ int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
+Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data);
+
+// === CONSTANTS ===
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL);
+tVFS_NodeType gVT_RootNodeType = {
+ .TypeName = "VTerm Root",
+ .ReadDir = VT_ReadDir,
+ .FindDir = VT_FindDir,
+ .IOCtl = VT_Root_IOCtl
+ };
+tVFS_NodeType gVT_TermNodeType = {
+ .TypeName = "VTerm",
+ .Read = VT_Read,
+ .Write = VT_Write,
+ .IOCtl = VT_Terminal_IOCtl
+ };
+tDevFS_Driver gVT_DrvInfo = {
+ NULL, "VTerm",
+ {
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Size = NUM_VTS,
+ .Inode = -1,
+ .NumACLs = 0,
+ .Type = &gVT_RootNodeType
+ }
+};
+// --- Terminals ---
+tVTerm gVT_Terminals[NUM_VTS];
+ int giVT_CurrentTerminal = 0;
+tVTerm *gpVT_CurTerm = &gVT_Terminals[0];
+// --- Video State ---
+short giVT_RealWidth = DEFAULT_WIDTH; //!< Screen Width
+short giVT_RealHeight = DEFAULT_HEIGHT; //!< Screen Height
+ int giVT_Scrollback = DEFAULT_SCROLLBACK;
+// --- Driver Handles ---
+char *gsVT_OutputDevice = NULL;
+char *gsVT_InputDevice = NULL;
+ int giVT_OutputDevHandle = -2;
+ int giVT_InputDevHandle = -2;
+
+// === CODE ===
+/**
+ * \fn int VT_Install(char **Arguments)
+ * \brief Installs the Virtual Terminal Driver
+ */
+int VT_Install(char **Arguments)
+{
+ int i;
+
+ // Scan Arguments
+ if(Arguments)
+ {
+ char **args;
+ const char *arg;
+ for(args = Arguments; (arg = *args); args++ )
+ {
+ char data[strlen(arg)+1];
+ char *opt = data;
+ char *val;
+
+ val = strchr(arg, '=');
+ strcpy(data, arg);
+ if( val ) {
+ data[ val - arg ] = '\0';
+ val ++;
+ }
+ Log_Debug("VTerm", "Argument '%s'", arg);
+
+ if( strcmp(opt, "Video") == 0 ) {
+ if( !gsVT_OutputDevice )
+ gsVT_OutputDevice = strdup(val);
+ }
+ else if( strcmp(opt, "Input") == 0 ) {
+ if( !gsVT_InputDevice )
+ gsVT_InputDevice = strdup(val);
+ }
+ else if( strcmp(opt, "Width") == 0 ) {
+ giVT_RealWidth = atoi( val );
+ }
+ else if( strcmp(opt, "Height") == 0 ) {
+ giVT_RealHeight = atoi( val );
+ }
+ else if( strcmp(opt, "Scrollback") == 0 ) {
+ giVT_Scrollback = atoi( val );
+ }
+ }
+ }
+
+ // Apply Defaults
+ if(!gsVT_OutputDevice) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
+ else if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
+ if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)FALLBACK_OUTPUT;
+ if( Module_EnsureLoaded( gsVT_OutputDevice ) ) {
+ Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT);
+ return MODULE_ERR_MISC;
+ }
+
+ if(!gsVT_InputDevice) gsVT_InputDevice = (char*)DEFAULT_INPUT;
+ else if( Module_EnsureLoaded( gsVT_InputDevice ) ) gsVT_InputDevice = (char*)DEFAULT_INPUT;
+
+ // Create device paths
+ {
+ char *tmp;
+ tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 );
+ strcpy(tmp, "/Devices/");
+ strcpy(&tmp[9], gsVT_OutputDevice);
+ gsVT_OutputDevice = tmp;
+
+ tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 );
+ strcpy(tmp, "/Devices/");
+ strcpy(&tmp[9], gsVT_InputDevice);
+ gsVT_InputDevice = tmp;
+ }
+
+ Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice);
+ Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice);
+
+ VT_InitOutput();
+ VT_InitInput();
+
+ // Create Nodes
+ for( i = 0; i < NUM_VTS; i++ )
+ {
+ gVT_Terminals[i].Mode = TERM_MODE_TEXT;
+ gVT_Terminals[i].Flags = 0;
+// gVT_Terminals[i].Flags = VT_FLAG_HIDECSR; //HACK - Stop all those memcpy calls
+ gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
+ gVT_Terminals[i].WritePos = 0;
+ gVT_Terminals[i].AltWritePos = 0;
+ gVT_Terminals[i].ViewPos = 0;
+ gVT_Terminals[i].ReadingThread = -1;
+ gVT_Terminals[i].ScrollHeight = 0;
+
+ // Initialise
+ VT_int_ChangeMode( &gVT_Terminals[i],
+ TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight );
+
+ gVT_Terminals[i].Name[0] = '0'+i;
+ gVT_Terminals[i].Name[1] = '\0';
+ gVT_Terminals[i].Node.Inode = i;
+ gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i];
+ gVT_Terminals[i].Node.NumACLs = 0; // Only root can open virtual terminals
+
+ gVT_Terminals[i].Node.Type = &gVT_TermNodeType;
+// Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name);
+ }
+
+ // Add to DevFS
+ DevFS_AddDevice( &gVT_DrvInfo );
+
+ // Set kernel output to VT0
+ Debug_SetKTerminal("/Devices/VTerm/0");
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Set the video resolution
+ * \param Width New screen width
+ * \param Height New screen height
+ */
+void VT_SetResolution(int Width, int Height)
+{
+ tVideo_IOCtl_Mode mode = {0};
+ int tmp;
+ int i;
+
+ // Create the video mode
+ mode.width = Width;
+ mode.height = Height;
+ mode.bpp = 32;
+ mode.flags = 0;
+
+ // Set video mode
+ VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode );
+ tmp = mode.id;
+ if( Width != mode.width || Height != mode.height )
+ {
+ Log_Warning("VTerm",
+ "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)",
+ giVT_RealWidth, giVT_RealHeight,
+ mode.width, mode.height
+ );
+ giVT_RealWidth = mode.width;
+ giVT_RealHeight = mode.height;
+ }
+ VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp );
+
+ // Resize text terminals if needed
+ if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) )
+ {
+ int newBufSize = (giVT_RealWidth/giVT_CharWidth)
+ *(giVT_RealHeight/giVT_CharHeight)
+ *(giVT_Scrollback+1);
+ //tVT_Char *tmp;
+ // Resize the text terminals
+ Log_Debug("VTerm", "Resizing terminals to %ix%i",
+ giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight);
+ for( i = 0; i < NUM_VTS; i ++ )
+ {
+ if( gVT_Terminals[i].Mode != TERM_MODE_TEXT ) continue;
+
+ gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth;
+ gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight;
+ gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight;
+
+ gVT_Terminals[i].Text = realloc(
+ gVT_Terminals[i].Text,
+ newBufSize*sizeof(tVT_Char)
+ );
+ }
+ }
+}
+
+/**
+ * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
+ * \brief Read from the VTerm Directory
+ */
+char *VT_ReadDir(tVFS_Node *Node, int Pos)
+{
+ if(Pos < 0) return NULL;
+ if(Pos >= NUM_VTS) return NULL;
+ return strdup( gVT_Terminals[Pos].Name );
+}
+
+/**
+ * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
+ * \brief Find an item in the VTerm directory
+ * \param Node Root node
+ * \param Name Name (number) of the terminal
+ */
+tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
+{
+ int num;
+
+ ENTER("pNode sName", Node, Name);
+
+ // Open the input and output files if needed
+ if(giVT_OutputDevHandle == -2) VT_InitOutput();
+ if(giVT_InputDevHandle == -2) VT_InitInput();
+
+ // Sanity check name
+ if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
+ LEAVE('n');
+ return NULL;
+ }
+ // Get index
+ num = Name[0] - '0';
+ if(num >= NUM_VTS) {
+ LEAVE('n');
+ return NULL;
+ }
+ // Return node
+ LEAVE('p', &gVT_Terminals[num].Node);
+ return &gVT_Terminals[num].Node;
+}
+
+/**
+ * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief Control the VTerm Driver
+ */
+int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
+{
+ int len;
+ switch(Id)
+ {
+ case DRV_IOCTL_TYPE: return DRV_TYPE_MISC;
+ case DRV_IOCTL_IDENT: memcpy(Data, "VT\0\0", 4); return 0;
+ case DRV_IOCTL_VERSION: return VERSION;
+ case DRV_IOCTL_LOOKUP: return 0;
+
+ case 4: // Get Video Driver
+ if(Data) strcpy(Data, gsVT_OutputDevice);
+ return strlen(gsVT_OutputDevice);
+
+ case 5: // Set Video Driver
+ if(!Data) return -EINVAL;
+ if(Threads_GetUID() != 0) return -EACCES;
+
+ len = strlen(Data);
+
+ // TODO: Check if the string used is a heap string
+
+ free(gsVT_OutputDevice);
+
+ gsVT_OutputDevice = malloc(len+1);
+ strcpy(gsVT_OutputDevice, Data);
+
+ VFS_Close(giVT_OutputDevHandle);
+ giVT_OutputDevHandle = -1;
+
+ VT_InitOutput();
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \brief Read from a virtual terminal
+ */
+Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ int pos = 0;
+ int avail;
+ tVTerm *term = &gVT_Terminals[ Node->Inode ];
+ Uint32 *codepoint_buf = Buffer;
+ Uint32 *codepoint_in;
+
+ Mutex_Acquire( &term->ReadingLock );
+
+ // Check current mode
+ switch(term->Mode)
+ {
+ // Text Mode (UTF-8)
+ case TERM_MODE_TEXT:
+ VT_int_UpdateCursor(term, 1);
+
+ VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)");
+
+ avail = term->InputWrite - term->InputRead;
+ if(avail < 0)
+ avail += MAX_INPUT_CHARS8;
+ if(avail > Length - pos)
+ avail = Length - pos;
+
+ while( avail -- )
+ {
+ ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead];
+ pos ++;
+ term->InputRead ++;
+ while(term->InputRead >= MAX_INPUT_CHARS8)
+ term->InputRead -= MAX_INPUT_CHARS8;
+ }
+ break;
+
+ //case TERM_MODE_FB:
+ // Other - UCS-4
+ default:
+ VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)");
+
+ avail = term->InputWrite - term->InputRead;
+ if(avail < 0)
+ avail += MAX_INPUT_CHARS32;
+ Length /= 4;
+ if(avail > Length - pos)
+ avail = Length - pos;
+
+ codepoint_in = (void*)term->InputBuffer;
+ codepoint_buf = Buffer;
+
+ while( avail -- )
+ {
+ codepoint_buf[pos] = codepoint_in[term->InputRead];
+ pos ++;
+ term->InputRead ++;
+ while(term->InputRead >= MAX_INPUT_CHARS32)
+ term->InputRead -= MAX_INPUT_CHARS32;
+ }
+ pos *= 4;
+ break;
+ }
+
+ // Mark none avaliable if buffer empty
+ if( term->InputRead == term->InputWrite )
+ VFS_MarkAvaliable(&term->Node, 0);
+
+ term->ReadingThread = -1;
+
+// VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT);
+
+ Mutex_Release( &term->ReadingLock );
+
+ return pos;
+}
+
+/**
+ * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+ * \brief Write to a virtual terminal
+ */
+Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tVTerm *term = &gVT_Terminals[ Node->Inode ];
+ int size;
+
+ // Write
+ switch( term->Mode )
+ {
+ // Print Text
+ case TERM_MODE_TEXT:
+ VT_int_PutString(term, Buffer, Length);
+ break;
+
+ // Framebuffer :)
+ case TERM_MODE_FB:
+ // - Sanity Checking
+ size = term->Width*term->Height*4;
+ if( Offset > size ) {
+ Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)",
+ Offset, size);
+ return 0;
+ }
+ if( Offset + Length > size ) {
+ Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)",
+ Offset+Length, size);
+ Length = size - Offset;
+ }
+
+ // Update screen if needed
+ if( Node->Inode == giVT_CurrentTerminal )
+ {
+ if( giVT_RealHeight > term->Height )
+ Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4;
+ // Handle undersized virtual terminals
+ if( giVT_RealWidth > term->Width )
+ {
+ // No? :( Well, just center it
+ int x, y, w, h;
+ Uint dst_ofs;
+ // TODO: Fix to handle the final line correctly?
+ x = Offset/4; y = x / term->Width; x %= term->Width;
+ w = Length/4+x; h = w / term->Width; w %= term->Width;
+
+ // Center
+ x += (giVT_RealWidth - term->Width) / 2;
+ dst_ofs = (x + y * giVT_RealWidth) * 4;
+ while(h--)
+ {
+ VFS_WriteAt( giVT_OutputDevHandle,
+ dst_ofs,
+ term->Width * 4,
+ Buffer
+ );
+ Buffer = (void*)( (Uint)Buffer + term->Width*4 );
+ dst_ofs += giVT_RealWidth * 4;
+ }
+ return 0;
+ }
+ else
+ {
+ return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer );
+ }
+ }
+ else
+ {
+ if( !term->Buffer )
+ term->Buffer = malloc( term->Width * term->Height * 4 );
+ // Copy to the local cache
+ memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length );
+ }
+ break;
+ // Just pass on (for now)
+ // TODO: Handle locally too to ensure no information is lost on
+ // VT Switch (and to isolate terminals from each other)
+ case TERM_MODE_2DACCEL:
+ //case TERM_MODE_3DACCEL:
+ if( Node->Inode == giVT_CurrentTerminal )
+ {
+ VFS_Write( giVT_OutputDevHandle, Length, Buffer );
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief Call an IO Control on a virtual terminal
+ */
+int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
+{
+ int *iData = Data;
+ int ret;
+ tVTerm *term = Node->ImplPtr;
+ ENTER("pNode iId pData", Node, Id, Data);
+
+ if(Id >= DRV_IOCTL_LOOKUP) {
+ // Only root can fiddle with graphics modes
+ // TODO: Remove this and replace with user ownership
+ if( Threads_GetUID() != 0 ) return -1;
+ }
+
+ switch(Id)
+ {
+ // --- Core Defined
+ case DRV_IOCTL_TYPE:
+ LEAVE('i', DRV_TYPE_TERMINAL);
+ return DRV_TYPE_TERMINAL;
+ case DRV_IOCTL_IDENT:
+ memcpy(Data, "VT\0\0", 4);
+ LEAVE('i', 0);
+ return 0;
+ case DRV_IOCTL_VERSION:
+ LEAVE('x', VERSION);
+ return VERSION;
+ case DRV_IOCTL_LOOKUP:
+ LEAVE('i', 0);
+ return 0;
+
+ // Get/Set the mode (and apply any changes)
+ case TERM_IOCTL_MODETYPE:
+ if(Data != NULL)
+ {
+ if( CheckMem(Data, sizeof(int)) == 0 ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData);
+
+ // Update mode if needed
+ if( term->Mode != *iData || term->NewWidth || term->NewHeight)
+ {
+ // Adjust for text mode
+ if( *iData == TERM_MODE_TEXT ) {
+ term->NewHeight *= giVT_CharHeight;
+ term->NewWidth *= giVT_CharWidth;
+ }
+ // Fill unchanged dimensions
+ if(term->NewHeight == 0) term->NewHeight = term->Height;
+ if(term->NewWidth == 0) term->NewWidth = term->Width;
+ // Set new mode
+ VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight);
+ // Clear unapplied dimensions
+ term->NewWidth = 0;
+ term->NewHeight = 0;
+ }
+
+ // Update the screen dimensions
+ if(Node->Inode == giVT_CurrentTerminal)
+ VT_SetTerminal( giVT_CurrentTerminal );
+ }
+ LEAVE('i', term->Mode);
+ return term->Mode;
+
+ // Get/set the terminal width
+ case TERM_IOCTL_WIDTH:
+ if(Data != NULL) {
+ if( CheckMem(Data, sizeof(int)) == 0 ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ term->NewWidth = *iData;
+ }
+ if( term->NewWidth )
+ ret = term->NewWidth;
+ else if( term->Mode == TERM_MODE_TEXT )
+ ret = term->TextWidth;
+ else
+ ret = term->Width;
+ LEAVE('i', ret);
+ return ret;
+
+ // Get/set the terminal height
+ case TERM_IOCTL_HEIGHT:
+ if(Data != NULL) {
+ if( CheckMem(Data, sizeof(int)) == 0 ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ term->NewHeight = *iData;
+ }
+ if( term->NewHeight )
+ ret = term->NewHeight;
+ else if( term->Mode == TERM_MODE_TEXT )
+ ret = term->TextHeight;
+ else
+ ret = term->Height;
+ LEAVE('i', ret);
+ return ret;
+
+ case TERM_IOCTL_FORCESHOW:
+ Log_Log("VTerm", "Thread %i forced VTerm %i to be shown",
+ Threads_GetTID(), (int)Node->Inode);
+ VT_SetTerminal( Node->Inode );
+ LEAVE('i', 1);
+ return 1;
+
+ case TERM_IOCTL_GETSETCURSOR:
+ if(Data != NULL)
+ {
+ tVideo_IOCtl_Pos *pos = Data;
+ if( !CheckMem(Data, sizeof(*pos)) ) {
+ errno = -EINVAL;
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ if( term->Mode == TERM_MODE_TEXT )
+ {
+ if(term->Flags & VT_FLAG_ALTBUF)
+ term->AltWritePos = pos->x + pos->y * term->TextWidth;
+ else
+ term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos;
+ VT_int_UpdateCursor(term, 0);
+ }
+ else
+ {
+ term->VideoCursorX = pos->x;
+ term->VideoCursorY = pos->y;
+ VT_int_UpdateCursor(term, 1);
+ }
+ }
+ ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos;
+ LEAVE('i', ret);
+ return ret;
+
+ case TERM_IOCTL_SETCURSORBITMAP: {
+ tVideo_IOCtl_Bitmap *bmp = Data;
+ if( Data == NULL )
+ {
+ free( term->VideoCursor );
+ term->VideoCursor = NULL;
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Sanity check bitmap
+ if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) {
+ Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
+ errno = -EINVAL;
+ LEAVE_RET('i', -1);
+ }
+ if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) {
+ Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
+ errno = -EINVAL;
+ LEAVE_RET('i', -1);
+ }
+
+ // Reallocate if needed
+ if(term->VideoCursor)
+ {
+ if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) {
+ free(term->VideoCursor);
+ term->VideoCursor = NULL;
+ }
+ }
+ if(!term->VideoCursor) {
+ term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
+ if(!term->VideoCursor) {
+ Log_Error("VTerm", "Unable to allocate memory for cursor");
+ errno = -ENOMEM;
+ LEAVE_RET('i', -1);
+ }
+ }
+
+ memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
+
+ Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i",
+ (int)term->Node.Inode, bmp, bmp->W, bmp->H);
+
+ if(gpVT_CurTerm == term)
+ VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor);
+
+ LEAVE('i', 0);
+ return 0; }
+ }
+ LEAVE('i', -1);
+ return -1;
+}
+
+/**
+ * \fn void VT_SetTerminal(int ID)
+ * \brief Set the current terminal
+ */
+void VT_SetTerminal(int ID)
+{
+ // Copy the screen state
+ if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT )
+ {
+ if( !gpVT_CurTerm->Buffer )
+ gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 );
+ if( gpVT_CurTerm->Width < giVT_RealWidth )
+ {
+ int line;
+ Uint ofs = 0;
+ Uint32 *dest = gpVT_CurTerm->Buffer;
+ // Slower scanline copy
+ for( line = 0; line < gpVT_CurTerm->Height; line ++ )
+ {
+ VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest);
+ ofs += giVT_RealWidth * 4;
+ dest += gpVT_CurTerm->Width;
+ }
+ }
+ else
+ {
+ VFS_ReadAt(giVT_OutputDevHandle,
+ 0, gpVT_CurTerm->Height*giVT_RealWidth*4,
+ gpVT_CurTerm->Buffer
+ );
+ }
+ }
+
+ // Update current terminal ID
+ Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID);
+ giVT_CurrentTerminal = ID;
+ gpVT_CurTerm = &gVT_Terminals[ID];
+
+ if( gpVT_CurTerm->Mode == TERM_MODE_TEXT )
+ {
+ VT_SetMode( VIDEO_BUFFMT_TEXT );
+ }
+ else
+ {
+ // Update the cursor image
+ if(gpVT_CurTerm->VideoCursor)
+ VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor);
+ VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER );
+ }
+
+ if(gpVT_CurTerm->Buffer)
+ {
+ // TODO: Handle non equal sized
+ VFS_WriteAt(
+ giVT_OutputDevHandle,
+ 0,
+ gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32),
+ gpVT_CurTerm->Buffer
+ );
+ }
+
+ VT_int_UpdateCursor(gpVT_CurTerm, 1);
+ // Update the screen
+ VT_int_UpdateScreen(gpVT_CurTerm, 1);
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm.h
+ * - Virtual Terminal - Common
+ */
+#ifndef _VTERM_H_
+#define _VTERM_H_
+
+#include <acess.h>
+#include <api_drv_video.h> // tVT_Char
+#include <api_drv_terminal.h>
+#include <vfs.h>
+
+// === CONSTANTS ===
+#define MAX_INPUT_CHARS32 64
+#define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4)
+#define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16))
+
+/**
+ * \{
+ */
+#define VT_FLAG_HIDECSR 0x01 //!< Hide the cursor
+#define VT_FLAG_ALTBUF 0x02 //!< Alternate screen buffer
+#define VT_FLAG_RAWIN 0x04 //!< Don't handle ^Z/^C/^V
+#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer
+#define VT_FLAG_SHOWCSR 0x20 //!< Always show the text cursor
+/**
+ * \}
+ */
+
+enum eVT_InModes {
+ VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100/xterm Emulation)
+ VT_INMODE_TEXT32, // UTF-32 Text Mode (Acess Native)
+ NUM_VT_INMODES
+};
+
+
+// === TYPES ==
+typedef struct sVTerm tVTerm;
+
+// === STRUCTURES ===
+struct sVTerm
+{
+ int Mode; //!< Current Mode (see ::eTplTerminal_Modes)
+ int Flags; //!< Flags (see VT_FLAG_*)
+
+ short NewWidth; //!< Un-applied dimensions (Width)
+ short NewHeight; //!< Un-applied dimensions (Height)
+ short Width; //!< Virtual Width
+ short Height; //!< Virtual Height
+ short TextWidth; //!< Text Virtual Width
+ short TextHeight; //!< Text Virtual Height
+
+ Uint32 CurColour; //!< Current Text Colour
+
+ int ViewPos; //!< View Buffer Offset (Text Only)
+ int WritePos; //!< Write Buffer Offset (Text Only)
+ tVT_Char *Text;
+
+ tVT_Char *AltBuf; //!< Alternate Screen Buffer
+ int AltWritePos; //!< Alternate write position
+ short ScrollTop; //!< Top of scrolling region (smallest)
+ short ScrollHeight; //!< Length of scrolling region
+
+ int VideoCursorX;
+ int VideoCursorY;
+
+ tMutex ReadingLock; //!< Lock the VTerm when a process is reading from it
+ tTID ReadingThread; //!< Owner of the lock
+ int InputRead; //!< Input buffer read position
+ int InputWrite; //!< Input buffer write position
+ char InputBuffer[MAX_INPUT_CHARS8];
+// tSemaphore InputSemaphore;
+
+ Uint32 *Buffer;
+
+ // TODO: Do I need to keep this about?
+ // When should it be deallocated? on move to text mode, or some other time
+ // Call set again, it's freed, and if NULL it doesn't get reallocated.
+ tVideo_IOCtl_Bitmap *VideoCursor;
+
+ char Name[2]; //!< Name of the terminal
+ tVFS_Node Node;
+};
+
+// === GOBALS ===
+extern tVTerm *gpVT_CurTerm;
+extern int giVT_Scrollback;
+extern short giVT_RealWidth; //!< Screen Width
+extern short giVT_RealHeight; //!< Screen Width
+extern char *gsVT_OutputDevice;
+extern char *gsVT_InputDevice;
+extern int giVT_OutputDevHandle;
+extern int giVT_InputDevHandle;
+
+// === FUNCTIONS ===
+extern void VT_SetResolution(int Width, int Height);
+extern void VT_SetTerminal(int ID);
+// --- Output ---
+extern void VT_InitOutput(void);
+extern void VT_SetMode(int Mode);
+extern void VT_int_ScrollFramebuffer( tVTerm *Term, int Count );
+extern void VT_int_UpdateCursor( tVTerm *Term, int bShow );
+extern void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll );
+// --- Input ---
+extern void VT_InitInput(void);
+extern void VT_KBCallBack(Uint32 Codepoint);
+// --- VT100 Emulation ---
+extern void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args);
+extern int VT_int_ParseEscape(tVTerm *Term, const char *Buffer);
+// --- Terminal Buffer ---
+extern void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count);
+extern void VT_int_PutChar(tVTerm *Term, Uint32 Ch);
+extern void VT_int_ScrollText(tVTerm *Term, int Count);
+extern void VT_int_ClearLine(tVTerm *Term, int Num);
+extern void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight);
+extern void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm_font.c
+ * - Virtual Terminal - Font rendering code
+ */
+#include "vterm.h"
+#include <api_drv_terminal.h>
+
+// ---
+// Font Render
+// ---
+#define MONOSPACE_FONT 10816
+
+#if MONOSPACE_FONT == 10808 // 8x8
+# include "vterm_font_8x8.h"
+#elif MONOSPACE_FONT == 10816 // 8x16
+# include "vterm_font_8x16.h"
+#endif
+
+// === PROTOTYPES ===
+Uint8 *VT_Font_GetChar(Uint32 Codepoint);
+
+// === GLOBALS ===
+int giVT_CharWidth = FONT_WIDTH;
+int giVT_CharHeight = FONT_HEIGHT;
+
+// === CODE ===
+/**
+ * \brief Render a font character
+ */
+void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC)
+{
+ Uint8 *font;
+ int x, y;
+
+ // 8-bpp and below
+ if( Depth <= 8 )
+ {
+ Uint8 *buf = Buffer;
+
+ font = VT_Font_GetChar(Codepoint);
+
+ for(y = 0; y < FONT_HEIGHT; y ++)
+ {
+ for(x = 0; x < FONT_WIDTH; x ++)
+ {
+ if(*font & (1 << (FONT_WIDTH-x-1)))
+ buf[x] = FGC;
+ else
+ buf[x] = BGC;
+ }
+ buf = (void*)( (tVAddr)buf + Pitch );
+ font ++;
+ }
+ }
+ // 16-bpp and below
+ else if( Depth <= 16 )
+ {
+ Uint16 *buf = Buffer;
+
+ font = VT_Font_GetChar(Codepoint);
+
+ for(y = 0; y < FONT_HEIGHT; y ++)
+ {
+ for(x = 0; x < FONT_WIDTH; x ++)
+ {
+ if(*font & (1 << (FONT_WIDTH-x-1)))
+ buf[x] = FGC;
+ else
+ buf[x] = BGC;
+ }
+ buf = (void*)( (tVAddr)buf + Pitch );
+ font ++;
+ }
+ }
+ // 24-bpp colour
+ // - Special handling to not overwrite the next pixel
+ //TODO: Endian issues here
+ else if( Depth == 24 )
+ {
+ Uint8 *buf = Buffer;
+ Uint8 bg_r = (BGC >> 16) & 0xFF;
+ Uint8 bg_g = (BGC >> 8) & 0xFF;
+ Uint8 bg_b = (BGC >> 0) & 0xFF;
+ Uint8 fg_r = (FGC >> 16) & 0xFF;
+ Uint8 fg_g = (FGC >> 8) & 0xFF;
+ Uint8 fg_b = (FGC >> 0) & 0xFF;
+
+ font = VT_Font_GetChar(Codepoint);
+
+ for(y = 0; y < FONT_HEIGHT; y ++)
+ {
+ for(x = 0; x < FONT_WIDTH; x ++)
+ {
+ Uint8 r, g, b;
+
+ if(*font & (1 << (FONT_WIDTH-x-1))) {
+ r = fg_r; g = fg_g; b = fg_b;
+ }
+ else {
+ r = bg_r; g = bg_g; b = bg_b;
+ }
+ buf[x*3+0] = b;
+ buf[x*3+1] = g;
+ buf[x*3+2] = r;
+ }
+ buf = (void*)( (tVAddr)buf + Pitch );
+ font ++;
+ }
+ }
+ // 32-bpp colour (nice and easy)
+ else if( Depth == 32 )
+ {
+ Uint32 *buf = Buffer;
+
+ font = VT_Font_GetChar(Codepoint);
+
+ for(y = 0; y < FONT_HEIGHT; y ++)
+ {
+ for(x = 0; x < FONT_WIDTH; x ++)
+ {
+ if(*font & (1 << (FONT_WIDTH-x-1)))
+ buf[x] = FGC;
+ else
+ buf[x] = BGC;
+ }
+ buf = (Uint32*)( (tVAddr)buf + Pitch );
+ font ++;
+ }
+ }
+}
+
+/**
+ * \fn Uint32 VT_Colour12to24(Uint16 Col12)
+ * \brief Converts a 12-bit colour into 24 bits
+ */
+Uint32 VT_Colour12to24(Uint16 Col12)
+{
+ Uint32 ret;
+ int tmp;
+ tmp = Col12 & 0xF;
+ ret = (tmp << 0) | (tmp << 4);
+ tmp = (Col12 & 0xF0) >> 4;
+ ret |= (tmp << 8) | (tmp << 12);
+ tmp = (Col12 & 0xF00) >> 8;
+ ret |= (tmp << 16) | (tmp << 20);
+ return ret;
+}
+/**
+ * \brief Converts a 12-bit colour into 15 bits
+ */
+Uint16 VT_Colour12to15(Uint16 Col12)
+{
+ Uint32 ret;
+ int tmp;
+ tmp = Col12 & 0xF;
+ ret = (tmp << 1) | (tmp & 1);
+ tmp = (Col12 & 0xF0) >> 4;
+ ret |= ( (tmp << 1) | (tmp & 1) ) << 5;
+ tmp = (Col12 & 0xF00) >> 8;
+ ret |= ( (tmp << 1) | (tmp & 1) ) << 10;
+ return ret;
+}
+
+/**
+ * \brief Converts a 12-bit colour into any other depth
+ * \param Col12 12-bit source colour
+ * \param Depth Desired bit deptj
+ * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5)
+ */
+Uint32 VT_Colour12toN(Uint16 Col12, int Depth)
+{
+ Uint32 ret;
+ Uint32 r, g, b;
+ int rSize, gSize, bSize;
+
+ // Fast returns
+ if( Depth == 24 ) return VT_Colour12to24(Col12);
+ if( Depth == 15 ) return VT_Colour12to15(Col12);
+ // - 32 is a special case, it's usually 24-bit colour with an unused byte
+ if( Depth == 32 ) return VT_Colour12to24(Col12);
+
+ // Bounds checks
+ if( Depth < 8 ) return 0;
+ if( Depth > 32 ) return 0;
+
+ r = Col12 & 0xF;
+ g = (Col12 & 0xF0) >> 4;
+ b = (Col12 & 0xF00) >> 8;
+
+ rSize = gSize = bSize = Depth / 3;
+ if( rSize + gSize + bSize < Depth ) // Depth % 3 == 1
+ gSize ++;
+ if( rSize + gSize + bSize < Depth ) // Depth % 3 == 2
+ bSize ++;
+
+ // Expand
+ r <<= rSize - 4; g <<= gSize - 4; b <<= bSize - 4;
+ // Fill with the lowest bit
+ if( Col12 & 0x001 ) r |= (1 << (rSize - 4)) - 1;
+ if( Col12 & 0x010 ) r |= (1 << (gSize - 4)) - 1;
+ if( Col12 & 0x100 ) r |= (1 << (bSize - 4)) - 1;
+
+ // Create output
+ ret = r;
+ ret |= g << rSize;
+ ret |= b << (rSize + gSize);
+
+ return ret;
+}
+
+/**
+ * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint)
+ * \brief Gets an index into the font array given a Unicode Codepoint
+ * \note See http://en.wikipedia.org/wiki/CP437
+ */
+Uint8 *VT_Font_GetChar(Uint32 Codepoint)
+{
+ int index = 0;
+ if(Codepoint < 128)
+ return &VTermFont[Codepoint*FONT_HEIGHT];
+ switch(Codepoint)
+ {
+ case 0xC7: index = 128; break; // Ç
+ case 0xFC: index = 129; break; // ü
+ case 0xE9: index = 130; break; // é
+ case 0xE2: index = 131; break; // â
+ case 0xE4: index = 132; break; // ä
+ case 0xE0: index = 133; break; // Ã
+ case 0xE5: index = 134; break; // å
+ case 0xE7: index = 135; break; // ç
+ case 0xEA: index = 136; break; // ê
+ case 0xEB: index = 137; break; // ë
+ case 0xE8: index = 138; break; // è
+ case 0xEF: index = 139; break; // ï
+ case 0xEE: index = 140; break; // î
+ case 0xEC: index = 141; break; // ì
+ case 0xC4: index = 142; break; // Ä
+ case 0xC5: index = 143; break; // Ã…
+ }
+
+ return &VTermFont[index*FONT_HEIGHT];
+}
+
+EXPORTAS(&giVT_CharWidth, giVT_CharWidth);
+EXPORTAS(&giVT_CharHeight, giVT_CharHeight);
+EXPORT(VT_Font_Render);
+EXPORT(VT_Colour12to24);
--- /dev/null
+/*
+ * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup
+ * Altered for Acess2
+ */
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 16
+static Uint8 VTermFont[256*16]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
--- /dev/null
+/*
+ * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup
+ * Altered for Acess2
+ */
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 8
+static Uint8 VTermFont[256*8]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+ 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00,
+ 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38,
+ 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
+ 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00,
+ 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00,
+ 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00,
+ 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00,
+ 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18,
+ 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00,
+ 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30,
+ 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7,
+ 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70,
+ 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00,
+ 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00,
+ 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00,
+ 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f,
+ 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00,
+ 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00,
+ 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0,
+ 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00,
+ 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
+ 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00,
+ 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0,
+ 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
+ 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00,
+ 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00,
+ 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0,
+ 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00,
+ 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00,
+ 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00,
+ 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00,
+ 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70,
+ 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c,
+ 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm_input.c
+ * - Virtual Terminal - Input code
+ */
+#include "vterm.h"
+#include <api_drv_keyboard.h>
+
+// === GLOBALS ===
+// --- Key States --- (Used for VT Switching/Magic Combos)
+ int gbVT_CtrlDown = 0;
+ int gbVT_AltDown = 0;
+ int gbVT_SysrqDown = 0;
+
+// === CODE ===
+/**
+ * \fn void VT_InitInput()
+ * \brief Initialises the input
+ */
+void VT_InitInput()
+{
+ giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ);
+ if(giVT_InputDevHandle == -1) {
+ Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice);
+ return ;
+ }
+ VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack);
+}
+
+/**
+ * \fn void VT_KBCallBack(Uint32 Codepoint)
+ * \brief Called on keyboard interrupt
+ * \param Codepoint Pseudo-UTF32 character
+ *
+ * Handles a key press and sends the key code to the user's buffer.
+ * If the code creates a kernel-magic sequence, it is not passed to the
+ * user and is handled in-kernel.
+ */
+void VT_KBCallBack(Uint32 Codepoint)
+{
+ tVTerm *term = gpVT_CurTerm;
+
+ // Catch VT binds
+ switch( Codepoint & KEY_ACTION_MASK )
+ {
+ case KEY_ACTION_RELEASE:
+ switch(Codepoint & KEY_CODEPOINT_MASK)
+ {
+ case KEY_LALT: gbVT_AltDown &= ~1; break;
+ case KEY_RALT: gbVT_AltDown &= ~2; break;
+ case KEY_LCTRL: gbVT_CtrlDown &= ~1; break;
+ case KEY_RCTRL: gbVT_CtrlDown &= ~2; break;
+ }
+ break;
+
+ case KEY_ACTION_PRESS:
+ switch(Codepoint & KEY_CODEPOINT_MASK)
+ {
+ case KEY_LALT: gbVT_AltDown |= 1; break;
+ case KEY_RALT: gbVT_AltDown |= 2; break;
+ case KEY_LCTRL: gbVT_CtrlDown |= 1; break;
+ case KEY_RCTRL: gbVT_CtrlDown |= 2; break;
+ }
+
+ if(!gbVT_AltDown || !gbVT_CtrlDown)
+ break;
+ switch(Codepoint & KEY_CODEPOINT_MASK)
+ {
+ case KEY_F1: VT_SetTerminal(0); return;
+ case KEY_F2: VT_SetTerminal(1); return;
+ case KEY_F3: VT_SetTerminal(2); return;
+ case KEY_F4: VT_SetTerminal(3); return;
+ case KEY_F5: VT_SetTerminal(4); return;
+ case KEY_F6: VT_SetTerminal(5); return;
+ case KEY_F7: VT_SetTerminal(6); return;
+ case KEY_F8: VT_SetTerminal(7); return;
+ case KEY_F9: VT_SetTerminal(8); return;
+ case KEY_F10: VT_SetTerminal(9); return;
+ case KEY_F11: VT_SetTerminal(10); return;
+ case KEY_F12: VT_SetTerminal(11); return;
+ }
+
+ // Scrolling is only valid in text mode
+ if(gpVT_CurTerm->Mode != TERM_MODE_TEXT)
+ break;
+
+ switch(Codepoint & KEY_CODEPOINT_MASK)
+ {
+ // Scrolling
+ case KEY_PGUP:
+ if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF )
+ return ;
+ gpVT_CurTerm->ViewPos = MAX(
+ 0,
+ gpVT_CurTerm->ViewPos - gpVT_CurTerm->Width
+ );
+ return;
+ case KEY_PGDOWN:
+ if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF )
+ return ;
+ gpVT_CurTerm->ViewPos = MIN(
+ gpVT_CurTerm->ViewPos + gpVT_CurTerm->Width,
+ gpVT_CurTerm->Width * gpVT_CurTerm->Height*giVT_Scrollback
+ );
+ return;
+ }
+ break;
+ }
+
+ // Encode key
+ if(term->Mode == TERM_MODE_TEXT)
+ {
+ Uint8 buf[6] = {0};
+ int len = 0;
+
+ // Ignore anything that isn't a press or refire
+ if( (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_PRESS
+ && (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_REFIRE
+ )
+ {
+ return ;
+ }
+
+ Codepoint &= KEY_CODEPOINT_MASK;
+
+ // Ignore Modifer Keys
+ if(Codepoint > KEY_MODIFIERS) return;
+
+ // Get UTF-8/ANSI Encoding
+ switch(Codepoint)
+ {
+ // 0: No translation, don't send to user
+ case 0: break;
+ case KEY_LEFT:
+ buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D';
+ len = 3;
+ break;
+ case KEY_RIGHT:
+ buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'C';
+ len = 3;
+ break;
+ case KEY_UP:
+ buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'A';
+ len = 3;
+ break;
+ case KEY_DOWN:
+ buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'B';
+ len = 3;
+ break;
+
+ case KEY_PGUP:
+ buf[0] = '\x1B'; buf[1] = '['; buf[2] = '5'; buf[3] = '~';
+ len = 4;
+ break;
+ case KEY_PGDOWN:
+ buf[0] = '\x1B'; buf[1] = '['; buf[2] = '6'; buf[3] = '~';
+ len = 4;
+ break;
+
+ // Attempt to encode in UTF-8
+ default:
+ len = WriteUTF8( buf, Codepoint );
+ if(len == 0) {
+ Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint);
+ }
+ break;
+ }
+
+ if(len == 0) {
+ // Unprintable / Don't Pass
+ return;
+ }
+
+#if 0
+ // Handle meta characters
+ if( !(term->Flags & VT_FLAG_RAWIN) )
+ {
+ switch(buf[0])
+ {
+ case '\3': // ^C
+
+ break;
+ }
+ }
+#endif
+
+ // Write
+ if( MAX_INPUT_CHARS8 - term->InputWrite >= len )
+ memcpy( &term->InputBuffer[term->InputWrite], buf, len );
+ else {
+ memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite );
+ memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) );
+ }
+ // Roll the buffer over
+ term->InputWrite += len;
+ term->InputWrite %= MAX_INPUT_CHARS8;
+ if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) {
+ term->InputRead = term->InputWrite + 1;
+ term->InputRead %= MAX_INPUT_CHARS8;
+ }
+ }
+ else
+ {
+ // Encode the raw key event
+ Uint32 *raw_in = (void*)term->InputBuffer;
+
+ #if 0
+ // Drop new keys
+ if( term->InputWrite == term->InputRead )
+ return ;
+ #endif
+
+ raw_in[ term->InputWrite ] = Codepoint;
+ term->InputWrite ++;
+ if(term->InputWrite >= MAX_INPUT_CHARS32)
+ term->InputWrite -= MAX_INPUT_CHARS32;
+
+ #if 1
+ // TODO: Should old or new be dropped?
+ if(term->InputRead == term->InputWrite) {
+ term->InputRead ++;
+ if( term->InputRead >= MAX_INPUT_CHARS32 )
+ term->InputRead -= MAX_INPUT_CHARS32;
+ }
+ #endif
+ }
+
+ VFS_MarkAvaliable(&term->Node, 1);
+}
+
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm_input.c
+ * - Virtual Terminal - Input code
+ */
+#include "vterm.h"
+#include <api_drv_video.h>
+
+// === CODE ===
+/**
+ * \fn void VT_InitOutput()
+ * \brief Initialise Video Output
+ */
+void VT_InitOutput()
+{
+ giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE);
+ if(giVT_OutputDevHandle == -1) {
+ Log_Warning("VTerm", "Oh F**k, I can't open the video device '%s'", gsVT_OutputDevice);
+ return ;
+ }
+ VT_SetResolution( giVT_RealWidth, giVT_RealHeight );
+ VT_SetTerminal( 0 );
+ VT_SetMode( VIDEO_BUFFMT_TEXT );
+}
+
+/**
+ * \brief Set video output buffer mode
+ */
+void VT_SetMode(int Mode)
+{
+ VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode );
+}
+
+/**
+ * \fn void VT_int_ScrollFramebuffer( tVTerm *Term, int Count )
+ * \note Scrolls the framebuffer down by \a Count text lines
+ */
+void VT_int_ScrollFramebuffer( tVTerm *Term, int Count )
+{
+ int tmp;
+ struct {
+ Uint8 Op;
+ Uint16 DstX, DstY;
+ Uint16 SrcX, SrcY;
+ Uint16 W, H;
+ } PACKED buf;
+
+ // Only update if this is the current terminal
+ if( Term != gpVT_CurTerm ) return;
+
+ if( Count > Term->ScrollHeight ) Count = Term->ScrollHeight;
+ if( Count < -Term->ScrollHeight ) Count = -Term->ScrollHeight;
+
+ // Switch to 2D Command Stream
+ tmp = VIDEO_BUFFMT_2DSTREAM;
+ VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
+
+ // BLIT to 0,0 from 0,giVT_CharHeight
+ buf.Op = VIDEO_2DOP_BLIT;
+ buf.SrcX = 0; buf.DstX = 0;
+ // TODO: Don't assume character dimensions
+ buf.W = Term->TextWidth * giVT_CharWidth;
+ if( Count > 0 )
+ {
+ buf.SrcY = (Term->ScrollTop+Count) * giVT_CharHeight;
+ buf.DstY = Term->ScrollTop * giVT_CharHeight;
+ }
+ else // Scroll up, move text down
+ {
+ Count = -Count;
+ buf.SrcY = Term->ScrollTop * giVT_CharHeight;
+ buf.DstY = (Term->ScrollTop+Count) * giVT_CharHeight;
+ }
+ buf.H = (Term->ScrollHeight-Count) * giVT_CharHeight;
+ VFS_WriteAt(giVT_OutputDevHandle, 0, sizeof(buf), &buf);
+
+ // Restore old mode (this function is only called during text mode)
+ tmp = VIDEO_BUFFMT_TEXT;
+ VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
+}
+
+void VT_int_UpdateCursor( tVTerm *Term, int bShow )
+{
+ tVideo_IOCtl_Pos csr_pos;
+
+ if( Term != gpVT_CurTerm ) return ;
+
+ if( !bShow )
+ {
+ csr_pos.x = -1;
+ csr_pos.y = -1;
+ }
+ else if( Term->Mode == TERM_MODE_TEXT )
+ {
+ int offset;
+
+// if( !(Term->Flags & VT_FLAG_SHOWCSR)
+// && ( (Term->Flags & VT_FLAG_HIDECSR) || !Term->Node.ReadThreads)
+// )
+ if( !Term->Text || Term->Flags & VT_FLAG_HIDECSR )
+ {
+ csr_pos.x = -1;
+ csr_pos.y = -1;
+ }
+ else
+ {
+ if(Term->Flags & VT_FLAG_ALTBUF)
+ offset = Term->AltWritePos;
+ else
+ offset = Term->WritePos - Term->ViewPos;
+
+ csr_pos.x = offset % Term->TextWidth;
+ csr_pos.y = offset / Term->TextWidth;
+ if( 0 > csr_pos.y || csr_pos.y >= Term->TextHeight )
+ csr_pos.y = -1, csr_pos.x = -1;
+ }
+ }
+ else
+ {
+ csr_pos.x = Term->VideoCursorX;
+ csr_pos.y = Term->VideoCursorY;
+ }
+ VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos);
+}
+
+/**
+ * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
+ * \brief Updates the video framebuffer
+ */
+void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
+{
+ tVT_Char *buffer;
+ int view_pos, write_pos;
+ // Only update if this is the current terminal
+ if( Term != gpVT_CurTerm ) return;
+
+ switch( Term->Mode )
+ {
+ case TERM_MODE_TEXT:
+ view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewPos;
+ write_pos = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltWritePos : Term->WritePos;
+ buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text;
+ // Re copy the entire screen?
+ if(UpdateAll) {
+ VFS_WriteAt(
+ giVT_OutputDevHandle,
+ 0,
+ Term->TextWidth*Term->TextHeight*sizeof(tVT_Char),
+ &buffer[view_pos]
+ );
+ }
+ // Only copy the current line
+ else {
+ int ofs = write_pos - write_pos % Term->TextWidth;
+ VFS_WriteAt(
+ giVT_OutputDevHandle,
+ (ofs - view_pos)*sizeof(tVT_Char),
+ Term->TextWidth*sizeof(tVT_Char),
+ &buffer[ofs]
+ );
+ }
+ break;
+ case TERM_MODE_FB:
+ break;
+ }
+
+ VT_int_UpdateCursor(Term, 1);
+}
+
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm_termbuf.c
+ * - Virtual Terminal - Terminal buffer manipulation
+ */
+#include "vterm.h"
+
+// === CODE ===
+
+/**
+ * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
+ * \brief Print a string to the Virtual Terminal
+ */
+void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
+{
+ Uint32 val;
+ int i;
+
+ // Iterate
+ for( i = 0; i < Count; i++ )
+ {
+ // Handle escape sequences
+ if( Buffer[i] == 0x1B )
+ {
+ i ++;
+ i += VT_int_ParseEscape(Term, (const char*)&Buffer[i]) - 1;
+ continue;
+ }
+
+ // Fast check for non UTF-8
+ if( Buffer[i] < 128 ) // Plain ASCII
+ VT_int_PutChar(Term, Buffer[i]);
+ else { // UTF-8
+ i += ReadUTF8(&Buffer[i], &val) - 1;
+ VT_int_PutChar(Term, val);
+ }
+ }
+ // Update Screen
+ VT_int_UpdateScreen( Term, 0 );
+}
+
+/**
+ * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
+ * \brief Write a single character to a VTerm
+ */
+void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
+{
+ int i;
+ tVT_Char *buffer;
+ int write_pos;
+
+ if(Term->Flags & VT_FLAG_ALTBUF) {
+ buffer = Term->AltBuf;
+ write_pos = Term->AltWritePos;
+ }
+ else {
+ buffer = Term->Text;
+ write_pos = Term->WritePos;
+ }
+
+ switch(Ch)
+ {
+ case '\0': return; // Ignore NULL byte
+ case '\n':
+ VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
+ write_pos += Term->TextWidth;
+ case '\r':
+ write_pos -= write_pos % Term->TextWidth;
+ break;
+
+ case '\t': { int tmp = write_pos / Term->TextWidth;
+ write_pos %= Term->TextWidth;
+ do {
+ buffer[ write_pos ].Ch = '\0';
+ buffer[ write_pos ].Colour = Term->CurColour;
+ write_pos ++;
+ } while(write_pos & 7);
+ write_pos += tmp * Term->TextWidth;
+ break; }
+
+ case '\b':
+ // Backspace is invalid at Offset 0
+ if(write_pos == 0) break;
+
+ write_pos --;
+ // Singe Character
+ if(buffer[ write_pos ].Ch != '\0') {
+ buffer[ write_pos ].Ch = 0;
+ buffer[ write_pos ].Colour = Term->CurColour;
+ break;
+ }
+ // Tab
+ i = 7; // Limit it to 8
+ do {
+ buffer[ write_pos ].Ch = 0;
+ buffer[ write_pos ].Colour = Term->CurColour;
+ write_pos --;
+ } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
+ if(buffer[ write_pos ].Ch != '\0')
+ write_pos ++;
+ break;
+
+ default:
+ buffer[ write_pos ].Ch = Ch;
+ buffer[ write_pos ].Colour = Term->CurColour;
+ // Update the line before wrapping
+ if( (write_pos + 1) % Term->TextWidth == 0 )
+ VT_int_UpdateScreen( Term, 0 );
+ write_pos ++;
+ break;
+ }
+
+ if(Term->Flags & VT_FLAG_ALTBUF)
+ {
+ Term->AltBuf = buffer;
+ Term->AltWritePos = write_pos;
+
+ if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight)
+ {
+ Term->AltWritePos -= Term->TextWidth;
+ VT_int_ScrollText(Term, 1);
+ }
+
+ }
+ else
+ {
+ Term->Text = buffer;
+ Term->WritePos = write_pos;
+ // Move Screen
+ // - Check if we need to scroll the entire scrollback buffer
+ if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1))
+ {
+ int base;
+
+ // Update previous line
+ Term->WritePos -= Term->TextWidth;
+ VT_int_UpdateScreen( Term, 0 );
+
+ // Update view position
+ base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback);
+ if(Term->ViewPos < base)
+ Term->ViewPos += Term->Width;
+ if(Term->ViewPos > base)
+ Term->ViewPos = base;
+
+ VT_int_ScrollText(Term, 1);
+ }
+ // Ok, so we only need to scroll the screen
+ else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
+ {
+ // Update the last line
+ Term->WritePos -= Term->TextWidth;
+ VT_int_UpdateScreen( Term, 0 );
+ Term->WritePos += Term->TextWidth;
+
+ VT_int_ScrollText(Term, 1);
+
+ Term->ViewPos += Term->TextWidth;
+ }
+ }
+
+ //LEAVE('-');
+}
+
+void VT_int_ScrollText(tVTerm *Term, int Count)
+{
+ tVT_Char *buf;
+ int height, init_write_pos;
+ int len, i;
+ int scroll_top, scroll_height;
+
+ // Get buffer pointer and attributes
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ {
+ buf = Term->AltBuf;
+ height = Term->TextHeight;
+ init_write_pos = Term->AltWritePos;
+ scroll_top = Term->ScrollTop;
+ scroll_height = Term->ScrollHeight;
+ }
+ else
+ {
+ buf = Term->Text;
+ height = Term->TextHeight*(giVT_Scrollback+1);
+ init_write_pos = Term->WritePos;
+ scroll_top = 0;
+ scroll_height = height;
+ }
+
+ // Scroll text downwards
+ if( Count > 0 )
+ {
+ int base;
+
+ // Set up
+ if(Count > scroll_height) Count = scroll_height;
+ base = Term->TextWidth*(scroll_top + scroll_height - Count);
+ len = Term->TextWidth*(scroll_height - Count);
+
+ // Scroll terminal cache
+ memmove(
+ &buf[Term->TextWidth*scroll_top],
+ &buf[Term->TextWidth*(scroll_top+Count)],
+ len*sizeof(tVT_Char)
+ );
+ // Clear last rows
+ for( i = 0; i < Term->TextWidth*Count; i ++ )
+ {
+ buf[ base + i ].Ch = 0;
+ buf[ base + i ].Colour = Term->CurColour;
+ }
+
+ // Update Screen
+ VT_int_ScrollFramebuffer( Term, Count );
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ Term->AltWritePos = base;
+ else
+ Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
+ for( i = 0; i < Count; i ++ )
+ {
+ VT_int_UpdateScreen( Term, 0 );
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ Term->AltWritePos += Term->TextWidth;
+ else
+ Term->WritePos += Term->TextWidth;
+ }
+ }
+ else
+ {
+ Count = -Count;
+ if(Count > scroll_height) Count = scroll_height;
+
+ len = Term->TextWidth*(scroll_height - Count);
+
+ // Scroll terminal cache
+ memmove(
+ &buf[Term->TextWidth*(scroll_top+Count)],
+ &buf[Term->TextWidth*scroll_top],
+ len*sizeof(tVT_Char)
+ );
+ // Clear preceding rows
+ for( i = 0; i < Term->TextWidth*Count; i ++ )
+ {
+ buf[ i ].Ch = 0;
+ buf[ i ].Colour = Term->CurColour;
+ }
+
+ VT_int_ScrollFramebuffer( Term, -Count );
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ Term->AltWritePos = Term->TextWidth*scroll_top;
+ else
+ Term->WritePos = Term->ViewPos;
+ for( i = 0; i < Count; i ++ )
+ {
+ VT_int_UpdateScreen( Term, 0 );
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ Term->AltWritePos += Term->TextWidth;
+ else
+ Term->WritePos += Term->TextWidth;
+ }
+ }
+
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ Term->AltWritePos = init_write_pos;
+ else
+ Term->WritePos = init_write_pos;
+}
+
+/**
+ * \brief Clears a line in a virtual terminal
+ * \param Term Terminal to modify
+ * \param Num Line number to clear
+ */
+void VT_int_ClearLine(tVTerm *Term, int Num)
+{
+ int i;
+ tVT_Char *cell;
+
+ if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ;
+
+ cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text;
+ cell = &cell[ Num*Term->TextWidth ];
+
+ for( i = Term->TextWidth; i--; )
+ {
+ cell[ i ].Ch = 0;
+ cell[ i ].Colour = Term->CurColour;
+ }
+}
+
+/**
+ * \brief Update the screen mode
+ * \param Term Terminal to update
+ * \param NewMode New mode to set
+ * \param NewWidth New framebuffer width
+ * \param NewHeight New framebuffer height
+ */
+void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight)
+{
+ int oldW = Term->Width;
+ int oldTW = Term->TextWidth;
+ int oldH = Term->Height;
+ int oldTH = Term->TextHeight;
+ tVT_Char *oldTBuf = Term->Text;
+ Uint32 *oldFB = Term->Buffer;
+ int w, h, i;
+
+ // TODO: Increase RealWidth/RealHeight when this happens
+ if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth;
+ if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
+
+ Term->Mode = NewMode;
+
+ // Fast exit if no resolution change
+ if(NewWidth == Term->Width && NewHeight == Term->Height)
+ return ;
+
+ // Calculate new dimensions
+ Term->Width = NewWidth;
+ Term->Height = NewHeight;
+ Term->TextWidth = NewWidth / giVT_CharWidth;
+ Term->TextHeight = NewHeight / giVT_CharHeight;
+ Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
+
+ // Allocate new buffers
+ // - Text
+ Term->Text = calloc(
+ Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
+ sizeof(tVT_Char)
+ );
+ if(oldTBuf) {
+ // Copy old buffer
+ w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
+ h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
+ h *= giVT_Scrollback + 1;
+ for( i = 0; i < h; i ++ )
+ {
+ memcpy(
+ &Term->Text[i*Term->TextWidth],
+ &oldTBuf[i*oldTW],
+ w*sizeof(tVT_Char)
+ );
+ }
+ free(oldTBuf);
+ }
+
+ // - Alternate Text
+ Term->AltBuf = realloc(
+ Term->AltBuf,
+ Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
+ );
+
+ // - Framebuffer
+ if(oldFB) {
+ Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
+ // Copy old buffer
+ w = (oldW > Term->Width) ? Term->Width : oldW;
+ h = (oldH > Term->Height) ? Term->Height : oldH;
+ for( i = 0; i < h; i ++ )
+ {
+ memcpy(
+ &Term->Buffer[i*Term->Width],
+ &oldFB[i*oldW],
+ w*sizeof(Uint32)
+ );
+ }
+ free(oldFB);
+ }
+
+ // Debug
+ switch(NewMode)
+ {
+ case TERM_MODE_TEXT:
+ Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
+ Term, Term->TextWidth, Term->TextHeight);
+ break;
+ case TERM_MODE_FB:
+ Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
+ Term, Term->Width, Term->Height);
+ break;
+ //case TERM_MODE_2DACCEL:
+ //case TERM_MODE_3DACCEL:
+ // return;
+ }
+}
+
+
+void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
+{
+ if(Enabled)
+ Term->Flags |= VT_FLAG_ALTBUF;
+ else
+ Term->Flags &= ~VT_FLAG_ALTBUF;
+ VT_int_UpdateScreen(Term, 1);
+}
+
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vterm_vt100.c
+ * - Virtual Terminal - VT100 (Kinda) Emulation
+ */
+#include "vterm.h"
+
+// === CONSTANTS ===
+const Uint16 caVT100Colours[] = {
+ // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray
+ // Same again, but bright
+ VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA,
+ VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE
+ };
+
+// === CODE ===
+/**
+ * \brief Handle a standard large escape code
+ *
+ * Handles any escape code of the form \x1B[n,...A where n is an integer
+ * and A is any letter.
+ */
+void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args)
+{
+ int tmp = 1;
+ switch(CmdChar)
+ {
+ // Left
+ case 'D':
+ tmp = -1;
+ // Right
+ case 'C':
+ if(argc == 1) tmp *= args[0];
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ {
+ if( (Term->AltWritePos + tmp) % Term->TextWidth == 0 ) {
+ Term->AltWritePos -= Term->AltWritePos % Term->TextWidth;
+ Term->AltWritePos += Term->TextWidth - 1;
+ }
+ else
+ Term->AltWritePos += tmp;
+ }
+ else
+ {
+ if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) {
+ Term->WritePos -= Term->WritePos % Term->TextWidth;
+ Term->WritePos += Term->TextWidth - 1;
+ }
+ else
+ Term->WritePos += tmp;
+ }
+ break;
+
+ // Erase
+ case 'J':
+ switch(args[0])
+ {
+ case 0: // Erase below
+ break;
+ case 1: // Erase above
+ break;
+ case 2: // Erase all
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ {
+ int i = Term->TextHeight;
+ while( i-- ) VT_int_ClearLine(Term, i);
+ Term->AltWritePos = 0;
+ VT_int_UpdateScreen(Term, 1);
+ }
+ else
+ {
+ int i = Term->TextHeight * (giVT_Scrollback + 1);
+ while( i-- ) VT_int_ClearLine(Term, i);
+ Term->WritePos = 0;
+ Term->ViewPos = 0;
+ VT_int_UpdateScreen(Term, 1);
+ }
+ break;
+ }
+ break;
+
+ // Erase in line
+ case 'K':
+ switch(args[0])
+ {
+ case 0: // Erase to right
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ {
+ int i, max;
+ max = Term->Width - Term->AltWritePos % Term->Width;
+ for( i = 0; i < max; i ++ )
+ Term->AltBuf[Term->AltWritePos+i].Ch = 0;
+ }
+ else
+ {
+ int i, max;
+ max = Term->Width - Term->WritePos % Term->Width;
+ for( i = 0; i < max; i ++ )
+ Term->Text[Term->WritePos+i].Ch = 0;
+ }
+ VT_int_UpdateScreen(Term, 0);
+ break;
+ case 1: // Erase to left
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ {
+ int i = Term->AltWritePos % Term->Width;
+ while( i -- )
+ Term->AltBuf[Term->AltWritePos++].Ch = 0;
+ }
+ else
+ {
+ int i = Term->WritePos % Term->Width;
+ while( i -- )
+ Term->Text[Term->WritePos++].Ch = 0;
+ }
+ VT_int_UpdateScreen(Term, 0);
+ break;
+ case 2: // Erase all
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ {
+ VT_int_ClearLine(Term, Term->AltWritePos / Term->Width);
+ }
+ else
+ {
+ VT_int_ClearLine(Term, Term->WritePos / Term->Width);
+ }
+ VT_int_UpdateScreen(Term, 0);
+ break;
+ }
+ break;
+
+ // Set cursor position
+ case 'H':
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ Term->AltWritePos = args[0] + args[1]*Term->TextWidth;
+ else
+ Term->WritePos = args[0] + args[1]*Term->TextWidth;
+ //Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]);
+ break;
+
+ // Scroll up `n` lines
+ case 'S':
+ tmp = -1;
+ // Scroll down `n` lines
+ case 'T':
+ if(argc == 1) tmp *= args[0];
+ if( Term->Flags & VT_FLAG_ALTBUF )
+ VT_int_ScrollText(Term, tmp);
+ else
+ {
+ if(Term->ViewPos/Term->TextWidth + tmp < 0)
+ break;
+ if(Term->ViewPos/Term->TextWidth + tmp > Term->TextHeight * (giVT_Scrollback + 1))
+ break;
+
+ Term->ViewPos += Term->TextWidth*tmp;
+ }
+ break;
+
+ // Set Font flags
+ case 'm':
+ for( ; argc--; )
+ {
+ int colour_idx;
+ // Flags
+ if( 0 <= args[argc] && args[argc] <= 8)
+ {
+ switch(args[argc])
+ {
+ case 0: Term->CurColour = DEFAULT_COLOUR; break; // Reset
+ case 1: Term->CurColour |= 0x80000000; break; // Bright
+ case 2: Term->CurColour &= ~0x80000000; break; // Dim
+ }
+ }
+ // Foreground Colour
+ else if(30 <= args[argc] && args[argc] <= 37) {
+ // Get colour index, accounting for bright bit
+ colour_idx = args[argc]-30 + ((Term->CurColour>>28) & 8);
+ Term->CurColour &= 0x8000FFFF;
+ Term->CurColour |= (Uint32)caVT100Colours[ colour_idx ] << 16;
+ }
+ // Background Colour
+ else if(40 <= args[argc] && args[argc] <= 47) {
+ // Get colour index, accounting for bright bit
+ colour_idx = args[argc]-40 + ((Term->CurColour>>12) & 8);
+ Term->CurColour &= 0xFFFF8000;
+ Term->CurColour |= caVT100Colours[ colour_idx ];
+ }
+ else {
+ Log_Warning("VTerm", "Unknown font flag %i", args[argc]);
+ }
+ }
+ break;
+
+ // Set scrolling region
+ case 'r':
+ if( argc != 2 ) break;
+ Term->ScrollTop = args[0];
+ Term->ScrollHeight = args[1] - args[0];
+ break;
+
+ default:
+ Log_Warning("VTerm", "Unknown control sequence '\\x1B[%c'", CmdChar);
+ break;
+ }
+}
+
+/**
+ * \fn int VT_int_ParseEscape(tVTerm *Term, const char *Buffer)
+ * \brief Parses a VT100 Escape code
+ */
+int VT_int_ParseEscape(tVTerm *Term, const char *Buffer)
+{
+ char c;
+ int argc = 0, j = 1;
+ int args[6] = {0,0,0,0};
+ int bQuestionMark = 0;
+
+ switch(Buffer[0])
+ {
+ //Large Code
+ case '[':
+ // Get Arguments
+ c = Buffer[j++];
+ if(c == '?') {
+ bQuestionMark = 1;
+ c = Buffer[j++];
+ }
+ if( '0' <= c && c <= '9' )
+ {
+ do {
+ if(c == ';') c = Buffer[j++];
+ while('0' <= c && c <= '9') {
+ args[argc] *= 10;
+ args[argc] += c-'0';
+ c = Buffer[j++];
+ }
+ argc ++;
+ } while(c == ';');
+ }
+
+ // Get Command
+ if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
+ {
+ if( bQuestionMark )
+ {
+ switch(c)
+ {
+ // DEC Private Mode Set
+ case 'h':
+ if(argc != 1) break;
+ switch(args[0])
+ {
+ case 25:
+ Term->Flags &= ~VT_FLAG_HIDECSR;
+ break;
+ case 1047:
+ VT_int_ToggleAltBuffer(Term, 1);
+ break;
+ }
+ break;
+ case 'l':
+ if(argc != 1) break;
+ switch(args[0])
+ {
+ case 25:
+ Term->Flags |= VT_FLAG_HIDECSR;
+ break;
+ case 1047:
+ VT_int_ToggleAltBuffer(Term, 0);
+ break;
+ }
+ break;
+ default:
+ Log_Warning("VTerm", "Unknown control sequence '\\x1B[?%c'", c);
+ break;
+ }
+ }
+ else
+ {
+ VT_int_ParseEscape_StandardLarge(Term, c, argc, args);
+ }
+ }
+ break;
+
+ default:
+ Log_Notice("VTerm", "TODO: Handle short escape codes");
+ break;
+ }
+
+ //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer);
+ return j;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge
+ *
+ * drvutil.c
+ * - Common Driver/Filesystem Helper Functions
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <api_drv_disk.h>
+#include <api_drv_video.h>
+
+// === TYPES ===
+
+// === PROTOTYPES ===
+//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);
+//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src);
+//void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);
+//void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);
+void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf);
+//void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);
+void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
+void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
+
+// === GLOBALS ===
+tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = {
+ NULL,
+ DrvUtil_Video_2D_Fill,
+ DrvUtil_Video_2D_Blit
+};
+tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor = {
+ 8, 16,
+ 0, 0,
+ {
+ 0, 0, 0 , 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0, 0, 0 , 0, 0, 0, 0, 0,
+ 0, 0, 0 , 0, 0, 0, 0, 0
+ }
+};
+
+// === CODE ===
+// --- Video Driver Helpers ---
+int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length,
+ tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers)
+{
+ const Uint8 *stream = Buffer;
+ int rem = Length;
+ int op;
+ while( rem )
+ {
+ rem --;
+ op = *stream;
+ stream ++;
+
+ if(op > NUM_VIDEO_2DOPS) {
+ Log_Warning("DrvUtil",
+ "DrvUtil_Video_2DStream: Unknown operation %i",
+ op);
+ return Length-rem;
+ }
+
+ if(op*sizeof(void*) > SizeofHandlers) {
+ Log_Warning("DrvUtil",
+ "DrvUtil_Video_2DStream: Driver does not support op %i",
+ op);
+ return Length-rem;
+ }
+
+ switch(op)
+ {
+ case VIDEO_2DOP_NOP: break;
+
+ case VIDEO_2DOP_FILL:
+ if(rem < 10) return Length-rem;
+
+ if(!Handlers->Fill) {
+ Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
+ " does not support VIDEO_2DOP_FILL");
+ return Length-rem;
+ }
+
+ Handlers->Fill(
+ Ent,
+ ((const Uint16*)stream)[0], ((const Uint16*)stream)[1],
+ ((const Uint16*)stream)[2], ((const Uint16*)stream)[3],
+ ((const Uint32*)stream)[4]
+ );
+
+ rem -= 10;
+ stream += 10;
+ break;
+
+ case VIDEO_2DOP_BLIT:
+ if(rem < 12) return Length-rem;
+
+ if(!Handlers->Blit) {
+ Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
+ " does not support VIDEO_2DOP_BLIT");
+ return Length-rem;
+ }
+
+ Handlers->Blit(
+ Ent,
+ ((const Uint16*)stream)[0], ((const Uint16*)stream)[1],
+ ((const Uint16*)stream)[2], ((const Uint16*)stream)[3],
+ ((const Uint16*)stream)[4], ((const Uint16*)stream)[5]
+ );
+
+ rem -= 12;
+ stream += 12;
+ break;
+
+ }
+ }
+ return 0;
+}
+
+int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Buffer)
+{
+ Uint8 *dest;
+ const Uint32 *src = Buffer;
+ int csr_x, csr_y;
+ int x, y;
+ int bytes_per_px = (FBInfo->Depth + 7) / 8;
+ ENTER("pFBInfo xOffset xLength pBuffer",
+ Mode, FBInfo, Offset, Length, Buffer);
+
+ csr_x = FBInfo->CursorX;
+ csr_y = FBInfo->CursorY;
+
+ DrvUtil_Video_RemoveCursor(FBInfo);
+
+ switch( FBInfo->BufferFormat )
+ {
+ case VIDEO_BUFFMT_TEXT:
+ {
+ const tVT_Char *chars = Buffer;
+ int widthInChars = FBInfo->Width/giVT_CharWidth;
+ int heightInChars = FBInfo->Height/giVT_CharHeight;
+ int i;
+
+ LOG("bytes_per_px = %i", bytes_per_px);
+ LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars);
+
+ Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char);
+
+ x = Offset % widthInChars; y = Offset / widthInChars;
+ LOG("x = %i, y = %i", x, y);
+
+ // Sanity Check
+ if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0);
+ if(y >= heightInChars) LEAVE_RET('i', 0);
+
+ if( Offset + Length > heightInChars*widthInChars )
+ {
+ Length = heightInChars*widthInChars - Offset;
+ }
+
+ dest = FBInfo->Framebuffer;
+ LOG("dest = %p", dest);
+ dest += y * giVT_CharHeight * FBInfo->Pitch;
+ LOG("dest = %p", dest);
+
+ for( i = 0; i < Length; i++ )
+ {
+ if( y >= heightInChars )
+ {
+ Log_Notice("DrvUtil", "Stopped at %i", i);
+ break;
+ }
+
+ VT_Font_Render(
+ chars->Ch,
+ dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch,
+ VT_Colour12toN(chars->BGCol, FBInfo->Depth),
+ VT_Colour12toN(chars->FGCol, FBInfo->Depth)
+ );
+
+ chars ++;
+ x ++;
+ if( x >= widthInChars )
+ {
+ x = 0;
+ y ++;
+ dest += FBInfo->Pitch*giVT_CharHeight;
+ LOG("dest = %p", dest);
+ }
+ }
+ Length = i * sizeof(tVT_Char);
+ }
+ break;
+
+ case VIDEO_BUFFMT_FRAMEBUFFER:
+ if(FBInfo->Width*FBInfo->Height*4 < Offset+Length)
+ {
+ Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow");
+ return 0;
+ }
+
+ switch(FBInfo->Depth)
+ {
+ case 15:
+ case 16:
+ Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in LFB write");
+ break;
+ case 24:
+ x = Offset % FBInfo->Width;
+ y = Offset / FBInfo->Width;
+ dest = (Uint8*)FBInfo->Framebuffer + y*FBInfo->Pitch;
+ for( ; Length >= 4; Length -= 4 )
+ {
+ dest[x*3+0] = *src & 0xFF;
+ dest[x*3+1] = (*src >> 8) & 0xFF;
+ dest[x*3+2] = (*src >> 16) & 0xFF;
+ x ++;
+ if(x == FBInfo->Width) {
+ dest += FBInfo->Pitch;
+ x = 0;
+ }
+ }
+ break;
+ case 32:
+ // Copy to Frambuffer
+ if( FBInfo->Pitch != FBInfo->Width*4 )
+ {
+ Uint32 *px;
+ // Pitch isn't 4*Width
+ x = Offset % FBInfo->Width;
+ y = Offset / FBInfo->Height;
+
+ px = (Uint32*)FBInfo->Framebuffer + y*FBInfo->Pitch/4;
+
+ for( ; Length >= 4; Length -= 4, x )
+ {
+ px[x++] = *src ++;
+ if( x == FBInfo->Width ) {
+ x = 0;
+ px += FBInfo->Pitch;
+ }
+ }
+ }
+ else
+ {
+ dest = (Uint8 *)FBInfo->Framebuffer + Offset;
+ memcpy(dest, Buffer, Length);
+ }
+ break;
+ default:
+ Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Unknown bit depthn %i", FBInfo->Depth);
+ break;
+ }
+ break;
+
+ case VIDEO_BUFFMT_2DSTREAM:
+ Length = DrvUtil_Video_2DStream(
+ FBInfo, Buffer, Length,
+ &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions)
+ );
+ break;
+
+ default:
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ DrvUtil_Video_DrawCursor(FBInfo, csr_x, csr_y);
+
+ LEAVE('x', Length);
+ return Length;
+}
+
+int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap)
+{
+ int csrX = Buf->CursorX, csrY = Buf->CursorY;
+ size_t size;
+
+ // Clear old bitmap
+ if( Buf->CursorBitmap )
+ {
+ DrvUtil_Video_RemoveCursor(Buf);
+ if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H )
+ {
+ free( Buf->CursorSaveBuf );
+ Buf->CursorSaveBuf = NULL;
+ }
+ if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor)
+ free(Buf->CursorBitmap);
+ Buf->CursorBitmap = NULL;
+ }
+
+ // If the new bitmap is null, disable drawing
+ if( !Bitmap )
+ {
+ Buf->CursorX = -1;
+ Buf->CursorY = -1;
+ return 0;
+ }
+
+ // Sanity check the bitmap
+ if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) )
+ {
+ Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap);
+ errno = -EINVAL;
+ return -1;
+ }
+
+ // Don't take a copy of the DrvUtil provided cursor
+ if( Bitmap == &gDrvUtil_TextModeCursor )
+ {
+ Buf->CursorBitmap = Bitmap;
+ }
+ else
+ {
+ size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4;
+
+ // Take a copy
+ Buf->CursorBitmap = malloc( size );
+ memcpy(Buf->CursorBitmap, Bitmap, size);
+ }
+
+ // Restore cursor position
+ DrvUtil_Video_DrawCursor(Buf, csrX, csrY);
+ return 0;
+}
+
+void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y)
+{
+ int render_ox=0, render_oy=0, render_w, render_h;
+
+ DrvUtil_Video_RemoveCursor(Buf);
+
+ // X < 0 disables the cursor
+ if( X < 0 ) {
+ Buf->CursorX = -1;
+ return ;
+ }
+
+ // Sanity checking
+ if( X < 0 || Y < 0 ) return;
+ if( X >= Buf->Width || Y >= Buf->Height ) return;
+
+ // Ensure the cursor is enabled
+ if( !Buf->CursorBitmap ) return ;
+
+ // Save cursor position (for changing the bitmap)
+ Buf->CursorX = X; Buf->CursorY = Y;
+
+ // Apply cursor's center offset
+ X -= Buf->CursorBitmap->XOfs;
+ Y -= Buf->CursorBitmap->YOfs;
+
+ // Get the width of the cursor on screen (clipping to right/bottom edges)
+ render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W;
+ render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H;
+
+ // Clipp to left/top edges
+ if(X < 0) { render_ox = -X; X = 0; }
+ if(Y < 0) { render_oy = -Y; Y = 0; }
+
+ // Save values
+ Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h;
+ Buf->CursorDestX = X; Buf->CursorDestY = Y;
+ Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy;
+
+ // Call render routine
+ DrvUtil_Video_RenderCursor(Buf);
+}
+
+void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
+{
+ int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY;
+ int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH;
+ int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY;
+ int bytes_per_px = (Buf->Depth + 7) / 8;
+ int save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ void *dest;
+ Uint32 *src;
+ int x, y;
+
+ dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px;
+ src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x;
+
+ // Allocate save buffer if not already
+ if( !Buf->CursorSaveBuf )
+ Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px );
+
+ // Save behind the cursor
+ for( y = 0; y < render_h; y ++ )
+ memcpy(
+ (Uint8*)Buf->CursorSaveBuf + y*save_pitch,
+ (Uint8*)dest + y*Buf->Pitch,
+ render_w*bytes_per_px
+ );
+
+ // Draw the cursor
+ switch(Buf->Depth)
+ {
+ case 15:
+ case 16:
+ Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw");
+ break;
+ case 24:
+ for( y = 0; y < render_h; y ++ )
+ {
+ Uint8 *px;
+ px = dest;
+ for(x = 0; x < render_w; x ++, px += 3)
+ {
+ Uint32 value = src[x];
+ // TODO: Should I implement alpha blending?
+ if(value & 0xFF000000)
+ {
+ px[0] = value & 0xFF;
+ px[1] = (value >> 8) & 0xFF;
+ px[2] = (value >> 16) & 0xFF;
+ }
+ else
+ ;
+ }
+ src += Buf->CursorBitmap->W;
+ dest = (Uint8*)dest + Buf->Pitch;
+ }
+ break;
+ case 32:
+ for( y = 0; y < render_h; y ++ )
+ {
+ Uint32 *px;
+ px = dest;
+ for(x = 0; x < render_w; x ++, px ++)
+ {
+ Uint32 value = src[x];
+ // TODO: Should I implement alpha blending?
+ if(value & 0xFF000000)
+ *px = value;
+ else
+ ; // NOP, completely transparent
+ }
+ src += Buf->CursorBitmap->W;
+ dest = (Uint8*)dest + Buf->Pitch;
+ }
+ break;
+ default:
+ Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth);
+ Buf->CursorX = -1;
+ break;
+ }
+}
+
+void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
+{
+ int bytes_per_px = (Buf->Depth + 7) / 8;
+ int y, save_pitch;
+ Uint8 *dest, *src;
+
+ // Just a little sanity
+ if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ;
+ if( !Buf->CursorSaveBuf ) return ;
+
+// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
+
+ // Set up
+ save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
+ src = Buf->CursorSaveBuf;
+
+ // Copy each line back
+ for( y = 0; y < Buf->CursorRenderH; y ++ )
+ {
+ memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
+ src += save_pitch;
+ dest += Buf->Pitch;
+ }
+
+ // Set the cursor as removed
+ Buf->CursorX = -1;
+}
+
+void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
+{
+ tDrvUtil_Video_BufInfo *FBInfo = Ent;
+
+ // TODO: Handle non-32bit modes
+ if( FBInfo->Depth != 32 ) return;
+
+ // TODO: Be less hacky
+ int pitch = FBInfo->Pitch/4;
+ Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X;
+ while( H -- ) {
+ Uint32 *tmp;
+ int i;
+ tmp = buf;
+ for(i=W;i--;tmp++) *tmp = Colour;
+ buf += pitch;
+ }
+}
+
+void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
+{
+ tDrvUtil_Video_BufInfo *FBInfo = Ent;
+ int scrnpitch = FBInfo->Pitch;
+ int bytes_per_px = (FBInfo->Depth + 7) / 8;
+ int dst = DstY*scrnpitch + DstX;
+ int src = SrcY*scrnpitch + SrcX;
+ int tmp;
+
+ //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)",
+ // Ent, DstX, DstY, SrcX, SrcY, W, H);
+
+ if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX;
+ if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX;
+ if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY;
+ if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY;
+
+ //Debug("W = %i, H = %i", W, H);
+
+ if( dst > src ) {
+ // Reverse copy
+ dst += H*scrnpitch;
+ src += H*scrnpitch;
+ while( H -- ) {
+ dst -= scrnpitch;
+ src -= scrnpitch;
+ tmp = W*bytes_per_px;
+ for( tmp = W; tmp --; ) {
+ *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp);
+ }
+ }
+ }
+ else if(W == FBInfo->Width && FBInfo->Pitch == FBInfo->Width*bytes_per_px) {
+ memmove((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, H*FBInfo->Pitch);
+ }
+ else {
+ // Normal copy is OK
+ while( H -- ) {
+ memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px);
+ dst += scrnpitch;
+ src += scrnpitch;
+ }
+ }
+ //Log("Vesa_2D_Blit: RETURN");
+}
+
+
+// --- Disk Driver Helpers ---
+Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,
+ tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument)
+{
+ Uint8 tmp[BlockSize]; // C99
+ Uint64 block = Start / BlockSize;
+ int offset = Start - block * BlockSize;
+ int leading = BlockSize - offset;
+ Uint64 num;
+ int tailings;
+ Uint64 ret;
+
+ ENTER("XStart XLength pBuffer pReadBlocks XBlockSize xArgument",
+ Start, Length, Buffer, ReadBlocks, BlockSize, Argument);
+
+ // Non aligned start, let's fix that!
+ if(offset != 0)
+ {
+ if(leading > Length) leading = Length;
+ LOG("Reading %i bytes from Block1+%i", leading, offset);
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ memcpy( Buffer, &tmp[offset], leading );
+
+ if(leading == Length) {
+ LEAVE('i', leading);
+ return leading;
+ }
+
+ Buffer = (Uint8*)Buffer + leading;
+ block ++;
+ num = ( Length - leading ) / BlockSize;
+ tailings = Length - num * BlockSize - leading;
+ }
+ else {
+ num = Length / BlockSize;
+ tailings = Length % BlockSize;
+ }
+
+ // Read central blocks
+ if(num)
+ {
+ LOG("Reading %i blocks", num);
+ ret = ReadBlocks(block, num, Buffer, Argument);
+ if(ret != num ) {
+ LEAVE('X', leading + ret * BlockSize);
+ return leading + ret * BlockSize;
+ }
+ }
+
+ // Read last tailing block
+ if(tailings != 0)
+ {
+ LOG("Reading %i bytes from last block", tailings);
+ block += num;
+ Buffer = (Uint8*)Buffer + num * BlockSize;
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('X', leading + num * BlockSize);
+ return leading + num * BlockSize;
+ }
+ memcpy( Buffer, tmp, tailings );
+ }
+
+ LEAVE('X', Length);
+ return Length;
+}
+
+Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer,
+ tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks,
+ Uint64 BlockSize, Uint Argument)
+{
+ Uint8 tmp[BlockSize]; // C99
+ Uint64 block = Start / BlockSize;
+ int offset = Start - block * BlockSize;
+ int leading = BlockSize - offset;
+ Uint64 num;
+ int tailings;
+ Uint64 ret;
+
+ ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize xArgument",
+ Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument);
+
+ // Non aligned start, let's fix that!
+ if(offset != 0)
+ {
+ if(leading > Length) leading = Length;
+ LOG("Writing %i bytes to Block1+%i", leading, offset);
+ // Read a copy of the block
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ // Modify
+ memcpy( &tmp[offset], Buffer, leading );
+ // Write Back
+ ret = WriteBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ if(leading == Length) {
+ LEAVE('i', leading);
+ return leading;
+ }
+
+ Buffer = (Uint8*)Buffer + leading;
+ block ++;
+ num = ( Length - leading ) / BlockSize;
+ tailings = Length - num * BlockSize - leading;
+ }
+ else {
+ num = Length / BlockSize;
+ tailings = Length % BlockSize;
+ }
+
+ // Read central blocks
+ if(num)
+ {
+ LOG("Writing %i blocks", num);
+ ret = WriteBlocks(block, num, Buffer, Argument);
+ if(ret != num ) {
+ LEAVE('X', leading + ret * BlockSize);
+ return leading + ret * BlockSize;
+ }
+ }
+
+ // Read last tailing block
+ if(tailings != 0)
+ {
+ LOG("Writing %i bytes to last block", tailings);
+ block += num;
+ Buffer = (Uint8*)Buffer + num * BlockSize;
+ // Read
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('X', leading + num * BlockSize);
+ return leading + num * BlockSize;
+ }
+ // Modify
+ memcpy( tmp, Buffer, tailings );
+ // Write
+ ret = WriteBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('X', leading + num * BlockSize);
+ return leading + num * BlockSize;
+ }
+
+ }
+
+ LEAVE('X', Length);
+ return Length;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * events.c
+ * - Thread level event handling
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <threads_int.h>
+#include <events.h>
+
+// === CODE ===
+void Threads_PostEvent(tThread *Thread, Uint32 EventMask)
+{
+ // Sanity checking
+ if( !Thread ) return ;
+ if( EventMask == 0 ) return ;
+ // TODO: Check that only one bit is set?
+
+ ENTER("pThread xEventMask", Thread, EventMask);
+
+ SHORTLOCK( &Thread->IsLocked );
+
+ Thread->EventState |= EventMask;
+ LOG("Thread->EventState = 0x%x", Thread->EventState);
+
+ // Currently sleeping on an event?
+ if( Thread->Status == THREAD_STAT_EVENTSLEEP )
+ {
+ // Waiting on this event?
+ if( (Uint32)Thread->RetStatus & EventMask )
+ {
+ // Wake up
+ LOG("Waking thread %p(%i %s)", Thread, Thread->TID, Thread->ThreadName);
+ Threads_AddActive(Thread);
+ }
+ }
+
+ SHORTREL( &Thread->IsLocked );
+ LEAVE('-');
+}
+
+/**
+ * \brief Wait for an event to occur
+ */
+Uint32 Threads_WaitEvents(Uint32 EventMask)
+{
+ Uint32 rv;
+ tThread *us = Proc_GetCurThread();
+
+ ENTER("xEventMask", EventMask);
+
+ // Early return check
+ if( EventMask == 0 )
+ {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ LOG("us = %p(%i %s)", us, us->TID, us->ThreadName);
+
+ // Check if a wait is needed
+ SHORTLOCK( &us->IsLocked );
+ while( !(us->EventState & EventMask) )
+ {
+ LOG("Locked and preparing for wait");
+ // Wait
+ us->RetStatus = EventMask; // HACK: Store EventMask in RetStatus
+ SHORTLOCK( &glThreadListLock );
+ us = Threads_RemActive();
+ us->Status = THREAD_STAT_EVENTSLEEP;
+ // Note stored anywhere because we're woken using other means
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &us->IsLocked );
+ while(us->Status == THREAD_STAT_EVENTSLEEP) Threads_Yield();
+ // Woken when lock is acquired
+ SHORTLOCK( &us->IsLocked );
+ }
+
+ // Get return value and clear changed event bits
+ rv = us->EventState & EventMask;
+ us->EventState &= ~EventMask;
+
+ SHORTREL( &us->IsLocked );
+
+ LEAVE('x', rv);
+ return rv;
+}
+
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * heap.c
+ */
+#include <acess.h>
+#include <mm_virt.h>
+#include <heap_int.h>
+
+#define WARNINGS 1
+#define DEBUG_TRACE 0
+#define VERBOSE_DUMP 0
+
+// === CONSTANTS ===
+#define HEAP_INIT_SIZE 0x8000 // 32 KiB
+#define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words
+#define POW2_SIZES 0
+#define COMPACT_HEAP 0 // Use 4 byte header?
+#define FIRST_FIT 0
+
+//#define MAGIC_FOOT 0x2ACE5505
+//#define MAGIC_FREE 0xACE55000
+//#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE
+#define MAGIC_FOOT 0x544F4F46 // 'FOOT'
+#define MAGIC_FREE 0x45455246 // 'FREE'
+#define MAGIC_USED 0x44455355 // 'USED'
+
+// === PROTOTYPES ===
+void Heap_Install(void);
+void *Heap_Extend(int Bytes);
+void *Heap_Merge(tHeapHead *Head);
+//void *Heap_Allocate(const char *File, int Line, size_t Bytes);
+//void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
+//void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
+//void Heap_Deallocate(void *Ptr);
+void Heap_Dump(void);
+void Heap_Stats(void);
+
+// === GLOBALS ===
+tMutex glHeap;
+void *gHeapStart;
+void *gHeapEnd;
+
+// === CODE ===
+void Heap_Install(void)
+{
+ gHeapStart = (void*)MM_KHEAP_BASE;
+ gHeapEnd = (void*)MM_KHEAP_BASE;
+ Heap_Extend(HEAP_INIT_SIZE);
+}
+
+/**
+ * \brief Extend the size of the heap
+ */
+void *Heap_Extend(int Bytes)
+{
+ Uint i;
+ tHeapHead *head = gHeapEnd;
+ tHeapFoot *foot;
+
+ // Bounds Check
+ if( (tVAddr)gHeapEnd == MM_KHEAP_MAX )
+ return NULL;
+
+ if( Bytes == 0 ) {
+ Log_Warning("Heap", "Heap_Extend called with Bytes=%i", Bytes);
+ return NULL;
+ }
+
+ // Bounds Check
+ if( (tVAddr)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) {
+// Bytes = MM_KHEAP_MAX - (tVAddr)gHeapEnd;
+ return NULL;
+ }
+
+ // Heap expands in pages
+ for( i = 0; i < (Bytes+0xFFF) >> 12; i ++ )
+ {
+ if( !MM_Allocate( (tVAddr)gHeapEnd+(i<<12) ) )
+ {
+ Warning("OOM - Heap_Extend");
+ return NULL;
+ }
+ }
+
+ // Increas heap end
+ gHeapEnd = (Uint8*)gHeapEnd + (i << 12);
+
+ // Create Block
+ head->Size = (Bytes+0xFFF)&~0xFFF;
+ head->Magic = MAGIC_FREE;
+ foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) );
+ foot->Head = head;
+ foot->Magic = MAGIC_FOOT;
+
+ return Heap_Merge(head); // Merge with previous block
+}
+
+/**
+ * \brief Merges two ajacent heap blocks
+ */
+void *Heap_Merge(tHeapHead *Head)
+{
+ tHeapFoot *foot;
+ tHeapFoot *thisFoot;
+ tHeapHead *head;
+
+ //Log("Heap_Merge: (Head=%p)", Head);
+
+ thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
+ if((Uint)thisFoot > (Uint)gHeapEnd) return NULL;
+
+ // Merge Left (Down)
+ foot = (void*)( (Uint)Head - sizeof(tHeapFoot) );
+ if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart)
+ && foot->Head->Magic == MAGIC_FREE) {
+ foot->Head->Size += Head->Size; // Increase size
+ thisFoot->Head = foot->Head; // Change backlink
+ Head->Magic = 0; // Clear old head
+ Head->Size = 0;
+ Head = foot->Head; // Save new head address
+ foot->Head = NULL; // Clear central footer
+ foot->Magic = 0;
+ }
+
+ // Merge Right (Upwards)
+ head = (void*)( (Uint)Head + Head->Size );
+ if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE)
+ {
+ Head->Size += head->Size;
+ foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
+ foot->Head = Head; // Update Backlink
+ thisFoot->Head = NULL; // Clear old footer
+ thisFoot->Magic = 0;
+ head->Size = 0; // Clear old header
+ head->Magic = 0;
+ }
+
+ // Return new address
+ return Head;
+}
+
+/**
+ * \param File Allocating source file
+ * \param Line Source line
+ * \param __Bytes Size of region to allocate
+ */
+void *Heap_Allocate(const char *File, int Line, size_t __Bytes)
+{
+ tHeapHead *head, *newhead;
+ tHeapFoot *foot, *newfoot;
+ tHeapHead *best = NULL;
+ Uint bestSize = 0; // Speed hack
+ size_t Bytes;
+
+ if( __Bytes == 0 ) {
+ //return NULL; // TODO: Return a known un-mapped range.
+ return INVLPTR;
+ }
+
+ // Get required size
+ #if POW2_SIZES
+ Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot);
+ Bytes = 1UUL << LOG2(__Bytes);
+ #else
+ Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
+ #endif
+
+ // Lock Heap
+ Mutex_Acquire(&glHeap);
+
+ // Traverse Heap
+ for( head = gHeapStart;
+ (Uint)head < (Uint)gHeapEnd;
+ head = (void*)((Uint)head + head->Size)
+ )
+ {
+ // Alignment Check
+ #if POW2_SIZES
+ if( head->Size != 1UUL << LOG2(head->Size) ) {
+ #else
+ if( head->Size & (MIN_SIZE-1) ) {
+ #endif
+ Mutex_Release(&glHeap); // Release spinlock
+ #if WARNINGS
+ Log_Warning("Heap", "Size of heap address %p is invalid not aligned (0x%x)", head, head->Size);
+ Heap_Dump();
+ #endif
+ return NULL;
+ }
+
+ // Check if allocated
+ if(head->Magic == MAGIC_USED) continue;
+ // Error check
+ if(head->Magic != MAGIC_FREE) {
+ Mutex_Release(&glHeap); // Release spinlock
+ #if WARNINGS
+ Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)",
+ head, &head->Magic, head->Magic);
+ Heap_Dump();
+ #endif
+ return NULL;
+ }
+
+ // Size check
+ if(head->Size < Bytes) continue;
+
+ // Perfect fit
+ if(head->Size == Bytes) {
+ head->Magic = MAGIC_USED;
+ head->File = File;
+ head->Line = Line;
+ head->ValidSize = __Bytes;
+ head->AllocateTime = now();
+ Mutex_Release(&glHeap); // Release spinlock
+ #if DEBUG_TRACE
+ Debug("[Heap ] Malloc'd %p (%i bytes), returning to %p",
+ head->Data, head->Size, __builtin_return_address(0));
+ #endif
+ return head->Data;
+ }
+
+ // Break out of loop
+ #if FIRST_FIT
+ best = head;
+ bestSize = head->Size;
+ break;
+ #else
+ // or check if the block is the best size
+ if(bestSize > head->Size) {
+ best = head;
+ bestSize = head->Size;
+ }
+ #endif
+ }
+
+ // If no block large enough is found, create one
+ if(!best)
+ {
+ best = Heap_Extend( Bytes );
+ // Check for errors
+ if(!best) {
+ Mutex_Release(&glHeap); // Release spinlock
+ return NULL;
+ }
+ // Check size
+ if(best->Size == Bytes) {
+ best->Magic = MAGIC_USED; // Mark block as used
+ best->File = File;
+ best->Line = Line;
+ best->ValidSize = __Bytes;
+ best->AllocateTime = now();
+ Mutex_Release(&glHeap); // Release spinlock
+ #if DEBUG_TRACE
+ Debug("[Heap ] Malloc'd %p (%i bytes), returning to %s:%i", best->Data, best->Size, File, Line);
+ #endif
+ return best->Data;
+ }
+ }
+
+ // Split Block
+ newhead = (void*)( (Uint)best + Bytes );
+ newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) );
+ foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) );
+
+ newfoot->Head = best; // Create new footer
+ newfoot->Magic = MAGIC_FOOT;
+ newhead->Size = best->Size - Bytes; // Create new header
+ newhead->Magic = MAGIC_FREE;
+ foot->Head = newhead; // Update backlink in old footer
+ best->Size = Bytes; // Update size in old header
+ best->ValidSize = __Bytes;
+ best->Magic = MAGIC_USED; // Mark block as used
+ best->File = File;
+ best->Line = Line;
+ best->AllocateTime = now();
+
+ Mutex_Release(&glHeap); // Release spinlock
+ #if DEBUG_TRACE
+ Debug("[Heap ] Malloc'd %p (0x%x bytes), returning to %s:%i",
+ best->Data, best->Size, File, Line);
+ #endif
+ return best->Data;
+}
+
+/**
+ * \brief Free an allocated memory block
+ */
+void Heap_Deallocate(void *Ptr)
+{
+ tHeapHead *head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
+ tHeapFoot *foot;
+
+ // INVLPTR is returned from Heap_Allocate when the allocation
+ // size is zero.
+ if( Ptr == INVLPTR ) return;
+
+ #if DEBUG_TRACE
+ Debug("[Heap ] free: %p freed by %p (%i old)", Ptr, __builtin_return_address(0), now()-head->AllocateTime);
+ #endif
+
+ // Alignment Check
+ if( (Uint)Ptr & (sizeof(Uint)-1) ) {
+ Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr);
+ return;
+ }
+
+ // Sanity check
+ if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd)
+ {
+ Log_Warning("Heap", "free - Passed a non-heap address by %p (%p < %p < %p)\n",
+ __builtin_return_address(0), gHeapStart, Ptr, gHeapEnd);
+ return;
+ }
+
+ // Check memory block - Header
+ head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
+ if(head->Magic == MAGIC_FREE) {
+ Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0));
+ return;
+ }
+ if(head->Magic != MAGIC_USED) {
+ Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic);
+ Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
+ return;
+ }
+
+ // Check memory block - Footer
+ foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
+ if(foot->Head != head) {
+ Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head);
+ Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
+ return;
+ }
+ if(foot->Magic != MAGIC_FOOT) {
+ Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)", head, &foot->Magic, foot->Magic);
+ Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
+ return;
+ }
+
+ // Lock
+ Mutex_Acquire( &glHeap );
+
+ // Mark as free
+ head->Magic = MAGIC_FREE;
+ //head->File = NULL;
+ //head->Line = 0;
+ head->ValidSize = 0;
+ // Merge blocks
+ Heap_Merge( head );
+
+ // Release
+ Mutex_Release( &glHeap );
+}
+
+/**
+ * \brief Increase/Decrease the size of an allocation
+ * \param File Calling File
+ * \param Line Calling Line
+ * \param __ptr Old memory
+ * \param __size New Size
+ */
+void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
+{
+ tHeapHead *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) );
+ tHeapHead *nextHead;
+ tHeapFoot *foot;
+ Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1);
+
+ // Check for reallocating NULL
+ if(__ptr == NULL) return Heap_Allocate(File, Line, __size);
+
+ // Check if resize is needed
+ if(newSize <= head->Size) return __ptr;
+
+ // Check if next block is free
+ nextHead = (void*)( (Uint)head + head->Size );
+
+ // Extend into next block
+ if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
+ {
+ Uint size = nextHead->Size + head->Size;
+ foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) );
+ // Exact Fit
+ if(size == newSize) {
+ head->Size = newSize;
+ head->ValidSize = __size;
+ head->File = File;
+ head->Line = Line;
+ foot->Head = head;
+ nextHead->Magic = 0;
+ nextHead->Size = 0;
+ return __ptr;
+ }
+ // Create a new heap block
+ nextHead = (void*)( (Uint)head + newSize );
+ nextHead->Size = size - newSize;
+ nextHead->Magic = MAGIC_FREE;
+ foot->Head = nextHead; // Edit 2nd footer
+ head->Size = newSize; // Edit first header
+ head->File = File;
+ head->Line = Line;
+ head->ValidSize = __size;
+ // Create new footer
+ foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) );
+ foot->Head = head;
+ foot->Magic = MAGIC_FOOT;
+ return __ptr;
+ }
+
+ // Extend downwards?
+ foot = (void*)( (Uint)head - sizeof(tHeapFoot) );
+ nextHead = foot->Head;
+ if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
+ {
+ Uint size = nextHead->Size + head->Size;
+ // Inexact fit, split things up
+ if(size > newSize)
+ {
+ // TODO
+ Warning("[Heap ] TODO: Space efficient realloc when new size is smaller");
+ }
+
+ // Exact fit
+ if(size >= newSize)
+ {
+ Uint oldDataSize;
+ // Set 1st (new/lower) header
+ nextHead->Magic = MAGIC_USED;
+ nextHead->Size = newSize;
+ nextHead->File = File;
+ nextHead->Line = Line;
+ nextHead->ValidSize = __size;
+ // Get 2nd (old) footer
+ foot = (void*)( (Uint)nextHead + newSize );
+ foot->Head = nextHead;
+ // Save old data size
+ oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead);
+ // Clear old header
+ head->Size = 0;
+ head->Magic = 0;
+ // Copy data
+ memcpy(nextHead->Data, __ptr, oldDataSize);
+ // Return
+ return nextHead->Data;
+ }
+ // On to the expensive then
+ }
+
+ // Well, darn
+ nextHead = Heap_Allocate( File, Line, __size );
+ nextHead -= 1;
+ nextHead->File = File;
+ nextHead->Line = Line;
+ nextHead->ValidSize = __size;
+
+ memcpy(
+ nextHead->Data,
+ __ptr,
+ head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead)
+ );
+
+ free(__ptr);
+
+ return nextHead->Data;
+}
+
+/**
+ * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
+ * \brief Allocate and Zero a buffer in memory
+ * \param File Allocating file
+ * \param Line Line of allocation
+ * \param Bytes Size of the allocation
+ */
+void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
+{
+ void *ret = Heap_Allocate(File, Line, Bytes);
+ if(ret == NULL) return NULL;
+
+ memset( ret, 0, Bytes );
+
+ return ret;
+}
+
+/**
+ * \fn int Heap_IsHeapAddr(void *Ptr)
+ * \brief Checks if an address is a heap pointer
+ */
+int Heap_IsHeapAddr(void *Ptr)
+{
+ tHeapHead *head;
+ if((Uint)Ptr < (Uint)gHeapStart) return 0;
+ if((Uint)Ptr > (Uint)gHeapEnd) return 0;
+ if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
+
+ head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
+ if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE)
+ return 0;
+
+ return 1;
+}
+
+/**
+ */
+void Heap_Validate(void)
+{
+ Heap_Dump();
+}
+
+#if WARNINGS
+void Heap_Dump(void)
+{
+ tHeapHead *head, *badHead;
+ tHeapFoot *foot = NULL;
+
+ head = gHeapStart;
+ while( (Uint)head < (Uint)gHeapEnd )
+ {
+ foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
+ #if VERBOSE_DUMP
+ Log_Log("Heap", "%p (0x%P): 0x%08x (%i) %4C",
+ head, MM_GetPhysAddr((tVAddr)head), head->Size, head->ValidSize, &head->Magic);
+ Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
+ if(head->File) {
+ Log_Log("Heap", "%sowned by %s:%i",
+ (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
+ }
+ #endif
+
+ // Sanity Check Header
+ if(head->Size == 0) {
+ Log_Warning("Heap", "HALTED - Size is zero");
+ break;
+ }
+ if(head->Size & (MIN_SIZE-1)) {
+ Log_Warning("Heap", "HALTED - Size is malaligned");
+ break;
+ }
+ if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
+ Log_Warning("Heap", "HALTED - Head Magic is Bad");
+ break;
+ }
+
+ // Check footer
+ if(foot->Magic != MAGIC_FOOT) {
+ Log_Warning("Heap", "HALTED - Foot Magic is Bad");
+ break;
+ }
+ if(head != foot->Head) {
+ Log_Warning("Heap", "HALTED - Footer backlink is invalid");
+ break;
+ }
+
+ #if VERBOSE_DUMP
+ Log_Log("Heap", "");
+ #endif
+
+ // All OK? Go to next
+ head = foot->NextHead;
+ }
+
+ // If the heap is valid, ok!
+ if( (tVAddr)head == (tVAddr)gHeapEnd )
+ return ;
+
+ // Check for a bad return
+ if( (tVAddr)head >= (tVAddr)gHeapEnd )
+ return ;
+
+ #if !VERBOSE_DUMP
+ Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
+ head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
+ if(foot)
+ Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic);
+ if(head->File) {
+ Log_Log("Heap", "%sowned by %s:%i",
+ (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
+ }
+ Log_Log("Heap", "");
+ #endif
+
+
+ badHead = head;
+
+ // Work backwards
+ foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) );
+ Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot);
+ head = foot->Head;
+ while( (tVAddr)head >= (tVAddr)badHead )
+ {
+ Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
+ head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
+ Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
+ if(head->File)
+ Log_Log("Heap", "%sowned by %s:%i",
+ (head->Magic!=MAGIC_USED?"was ":""),
+ head->File, head->Line);
+ Log_Log("Heap", "");
+
+ // Sanity Check Header
+ if(head->Size == 0) {
+ Log_Warning("Heap", "HALTED - Size is zero");
+ break;
+ }
+ if(head->Size & (MIN_SIZE-1)) {
+ Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
+ break ;
+ }
+ if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
+ Log_Warning("Heap", "HALTED - Head Magic is Bad");
+ break;
+ }
+
+ // Check footer
+ if(foot->Magic != MAGIC_FOOT) {
+ Log_Warning("Heap", "HALTED - Foot Magic is Bad");
+ break;
+ }
+ if(head != foot->Head) {
+ Log_Warning("Heap", "HALTED - Footer backlink is invalid");
+ break;
+ }
+
+ if(head == badHead) break;
+
+ foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) );
+ head = foot->Head;
+ Log_Debug("Heap", "head=%p", head);
+ }
+
+ Panic("Heap_Dump - Heap is corrupted, kernel panic!");
+}
+#endif
+
+#if 1
+void Heap_Stats(void)
+{
+ tHeapHead *head;
+ int nBlocks = 0;
+ int nFree = 0;
+ int totalBytes = 0;
+ int freeBytes = 0;
+ int maxAlloc=0, minAlloc=-1;
+ int avgAlloc, frag, overhead;
+
+ for(head = gHeapStart;
+ (Uint)head < (Uint)gHeapEnd;
+ head = (void*)( (Uint)head + head->Size )
+ )
+ {
+ nBlocks ++;
+ totalBytes += head->Size;
+ if( head->Magic == MAGIC_FREE )
+ {
+ nFree ++;
+ freeBytes += head->Size;
+ }
+ else if( head->Magic == MAGIC_USED) {
+ if(maxAlloc < head->Size) maxAlloc = head->Size;
+ if(minAlloc == -1 || minAlloc > head->Size)
+ minAlloc = head->Size;
+ }
+ else {
+ Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
+ break;
+ }
+
+ // Print the block info?
+ #if 1
+ if( head->Magic == MAGIC_FREE )
+ Log_Debug("Heap", "%p (%P) - 0x%x free",
+ head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size);
+ else
+ Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)",
+ head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size, head->ValidSize, head->File, head->Line,
+ now() - head->AllocateTime
+ );
+ #endif
+ }
+
+ Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
+ Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
+ if(nBlocks != 0)
+ frag = (nFree-1)*10000/nBlocks;
+ else
+ frag = 0;
+ Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
+ if(nBlocks <= nFree)
+ avgAlloc = 0;
+ else
+ avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
+ if(avgAlloc != 0)
+ overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
+ else
+ overhead = 0;
+ Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
+ avgAlloc, overhead/100, overhead%100
+ );
+ Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes",
+ minAlloc, maxAlloc);
+
+ // Scan and get distribution
+ #if 1
+ if(nBlocks > 0)
+ {
+ struct {
+ Uint Size;
+ Uint Count;
+ } sizeCounts[nBlocks];
+ int i;
+
+ memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
+
+ for(head = gHeapStart;
+ (Uint)head < (Uint)gHeapEnd;
+ head = (void*)( (Uint)head + head->Size )
+ )
+ {
+ for( i = 0; i < nBlocks; i ++ ) {
+ if( sizeCounts[i].Size == 0 )
+ break;
+ if( sizeCounts[i].Size == head->Size )
+ break;
+ }
+ // Should never reach this part (in a non-concurrent case)
+ if( i == nBlocks ) continue;
+ sizeCounts[i].Size = head->Size;
+ sizeCounts[i].Count ++;
+ #if 1
+ //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
+ // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
+ // );
+ //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
+ // sizeCounts[i].Size, sizeCounts[i].Count);
+ #endif
+ }
+
+ for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
+ {
+ Log("Heap_Stats: 0x%x - %i blocks",
+ sizeCounts[i].Size, sizeCounts[i].Count
+ );
+ }
+ }
+ #endif
+}
+#endif
+
+// === EXPORTS ===
+EXPORT(Heap_Allocate);
+EXPORT(Heap_AllocateZero);
+EXPORT(Heap_Reallocate);
+EXPORT(Heap_Deallocate);
+EXPORT(Heap_IsHeapAddr);
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * acess.h
+ */
+#ifndef _ACESS_H
+#define _ACESS_H
+/**
+ * \file acess.h
+ * \brief Acess2 Kernel API Core
+ */
+
+//! NULL Pointer
+#define NULL ((void*)0)
+//! Pack a structure
+#define PACKED __attribute__((packed))
+//! Mark a function as not returning
+#define NORETURN __attribute__((noreturn))
+//! Mark a parameter as unused
+#define UNUSED(x) UNUSED_##x __attribute__((unused))
+//! Get the offset of a member in a structure
+#define offsetof(st, m) ((Uint)((char *)&((st *)(0))->m - (char *)0 ))
+
+/**
+ * \name Boolean constants
+ * \{
+ */
+#define TRUE 1
+#define FALSE 0
+/**
+ * \}
+ */
+
+#include <arch.h>
+#include <stdarg.h>
+#include "errno.h"
+
+// --- Types ---
+typedef Uint32 tPID; //!< Process ID type
+typedef Uint32 tTID; //!< Thread ID Type
+typedef Uint32 tUID; //!< User ID Type
+typedef Uint32 tGID; //!< Group ID Type
+typedef Sint64 tTimestamp; //!< Timestamp (miliseconds since 00:00 1 Jan 1970)
+typedef Sint64 tTime; //!< Same again
+typedef struct sShortSpinlock tShortSpinlock; //!< Opaque (kinda) spinlock
+typedef int bool; //!< Boolean type
+
+// --- Helper Macros ---
+/**
+ * \name Helper Macros
+ * \{
+ */
+#define CONCAT(x,y) x ## y
+#define EXPAND_CONCAT(x,y) CONCAT(x,y)
+#define STR(x) #x
+#define EXPAND_STR(x) STR(x)
+
+extern char __buildnum[];
+#define BUILD_NUM ((int)(Uint)&__buildnum)
+extern const char gsGitHash[];
+
+#define VER2(major,minor) ((((major)&0xFF)<<8)|((minor)&0xFF))
+/**
+ * \}
+ */
+
+//! \brief Error number
+#define errno (*Threads_GetErrno())
+
+// === CONSTANTS ===
+// --- Memory Flags --
+/**
+ * \name Memory Flags
+ * \{
+ * \todo Move to mm_virt.h
+ */
+#define MM_PFLAG_RO 0x01 // Writes disallowed
+#define MM_PFLAG_EXEC 0x02 // Allow execution
+#define MM_PFLAG_NOPAGE 0x04 // Prevent from being paged out
+#define MM_PFLAG_COW 0x08 // Copy-On-Write
+#define MM_PFLAG_KERNEL 0x10 // Kernel-Only (Ring0)
+/**
+ * \}
+ */
+// --- Interface Flags & Macros
+/**
+ * \name Flags for Proc_Clone
+ * \{
+ */
+//! Clone the entire process
+#define CLONE_VM 0x10
+//! Don't copy user pages
+#define CLONE_NOUSER 0x20
+/**
+ * \}
+ */
+
+// === Types ===
+/**
+ * \brief Thread root function
+ *
+ * Function pointer prototype of a thread entrypoint. When it
+ * returns, Threads_Exit is called
+ */
+typedef void (*tThreadFunction)(void*);
+
+// === Kernel Export Macros ===
+/**
+ * \name Kernel exports
+ * \{
+ */
+//! Kernel symbol definition
+typedef struct sKernelSymbol {
+ const char *Name; //!< Symbolic name
+ tVAddr Value; //!< Value of the symbol
+} tKernelSymbol;
+//! Export a pointer symbol (function/array)
+#define EXPORT(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_name}
+//! Export a variable
+#define EXPORTV(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)&_name}
+//! Export a symbol under another name
+#define EXPORTAS(_sym,_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_sym}
+/**
+ * \}
+ */
+
+// === FUNCTIONS ===
+// --- IRQs ---
+/**
+ * \name IRQ hander registration
+ * \{
+ */
+extern int IRQ_AddHandler(int Num, void (*Callback)(int, void*), void *Ptr);
+extern void IRQ_RemHandler(int Handle);
+/**
+ * \}
+ */
+
+// --- Logging ---
+/**
+ * \name Logging to kernel ring buffer
+ * \{
+ */
+extern void Log_KernelPanic(const char *Ident, const char *Message, ...);
+extern void Log_Panic(const char *Ident, const char *Message, ...);
+extern void Log_Error(const char *Ident, const char *Message, ...);
+extern void Log_Warning(const char *Ident, const char *Message, ...);
+extern void Log_Notice(const char *Ident, const char *Message, ...);
+extern void Log_Log(const char *Ident, const char *Message, ...);
+extern void Log_Debug(const char *Ident, const char *Message, ...);
+/**
+ * \}
+ */
+
+// --- Debug ---
+/**
+ * \name Debugging and Errors
+ * \{
+ */
+extern void Debug_KernelPanic(void); //!< Initiate a kernel panic
+extern void Panic(const char *Msg, ...); //!< Print a panic message (initiates a kernel panic)
+extern void Warning(const char *Msg, ...); //!< Print a warning message
+extern void LogF(const char *Fmt, ...); //!< Print a log message without a trailing newline
+extern void Log(const char *Fmt, ...); //!< Print a log message
+extern void Debug(const char *Fmt, ...); //!< Print a debug message (doesn't go to KTerm)
+extern void LogV(const char *Fmt, va_list Args); //!< va_list Log message
+extern void Debug_Enter(const char *FuncName, const char *ArgTypes, ...);
+extern void Debug_Log(const char *FuncName, const char *Fmt, ...);
+extern void Debug_Leave(const char *FuncName, char RetType, ...);
+extern void Debug_HexDump(const char *Header, const void *Data, Uint Length);
+#define UNIMPLEMENTED() Warning("'%s' unimplemented", __func__)
+#if DEBUG
+# define ENTER(_types...) Debug_Enter((char*)__func__, _types)
+# define LOG(_fmt...) Debug_Log((char*)__func__, _fmt)
+# define LEAVE(_t...) Debug_Leave((char*)__func__, _t)
+# define LEAVE_RET(_t,_v...) do{LEAVE(_t,_v);return _v;}while(0)
+# define LEAVE_RET0() do{LEAVE('-');return;}while(0)
+#else
+# define ENTER(...)
+# define LOG(...)
+# define LEAVE(...)
+# define LEAVE_RET(_t,_v...) return (_v)
+# define LEAVE_RET0() return
+#endif
+#if SANITY
+# define ASSERT(expr) do{if(!(expr))Panic("%s: Assertion '"#expr"' failed",(char*)__func__);}while(0)
+#else
+# define ASSERT(expr)
+#endif
+/**
+ * \}
+ */
+
+// --- IO ---
+#if NO_IO_BUS
+#define inb(a) (Log_Panic("Arch", STR(ARCHDIR)" does not support in*/out* (%s:%i)", __FILE__, __LINE__),0)
+#define inw(a) inb(a)
+#define ind(a) inb(a)
+#define inq(a) inb(a)
+#define outb(a,b) inb(a)
+#define outw(a,b) inb(a)
+#define outd(a,b) inb(a)
+#define outq(a,b) inb(a)
+#else
+/**
+ * \name I/O Memory Access
+ * \{
+ */
+extern void outb(Uint16 Port, Uint8 Data);
+extern void outw(Uint16 Port, Uint16 Data);
+extern void outd(Uint16 Port, Uint32 Data);
+extern void outq(Uint16 Port, Uint64 Data);
+extern Uint8 inb(Uint16 Port);
+extern Uint16 inw(Uint16 Port);
+extern Uint32 ind(Uint16 Port);
+extern Uint64 inq(Uint16 Port);
+/**
+ * \}
+ */
+#endif
+// --- Memory Management ---
+/**
+ * \name Memory Management
+ * \{
+ * \todo Move to mm_virt.h
+ */
+/**
+ * \brief Allocate a physical page at \a VAddr
+ * \param VAddr Virtual Address to allocate at
+ * \return Physical address allocated
+ */
+extern tPAddr MM_Allocate(tVAddr VAddr) __attribute__ ((warn_unused_result));
+/**
+ * \brief Deallocate a page
+ * \param VAddr Virtual address to unmap
+ */
+extern void MM_Deallocate(tVAddr VAddr);
+/**
+ * \brief Map a physical page at \a PAddr to \a VAddr
+ * \param VAddr Target virtual address
+ * \param PAddr Physical address to map
+ * \return Boolean Success
+ */
+extern int MM_Map(tVAddr VAddr, tPAddr PAddr);
+/**
+ * \brief Get the physical address of \a Addr
+ * \param Addr Address of the page to get the physical address of
+ * \return Physical page mapped at \a Addr
+ */
+extern tPAddr MM_GetPhysAddr(tVAddr Addr);
+/**
+ * \brief Set the access flags on a page
+ * \param VAddr Virtual address of the page
+ * \param Flags New flags value
+ * \param Mask Flags to set
+ */
+extern void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask);
+/**
+ * \brief Get the flags on a flag
+ * \param VAddr Virtual address of page
+ * \return Flags value of the page
+ */
+extern Uint MM_GetFlags(tVAddr VAddr);
+/**
+ * \brief Checks is a memory range is user accessable
+ * \param VAddr Base address to check
+ * \return 1 if the memory is all user-accessable, 0 otherwise
+ */
+#define MM_IsUser(VAddr) (!(MM_GetFlags((VAddr))&MM_PFLAG_KERNEL))
+/**
+ * \brief Temporarily map a page into the address space
+ * \param PAddr Physical addres to map
+ * \return Virtual address of page in memory
+ * \note There is only a limited ammount of slots avaliable
+ */
+extern tVAddr MM_MapTemp(tPAddr PAddr);
+/**
+ * \brief Free a temporarily mapped page
+ * \param VAddr Allocate virtual addres of page
+ */
+extern void MM_FreeTemp(tVAddr VAddr);
+/**
+ * \brief Map a physcal address range into the virtual address space
+ * \param PAddr Physical address to map in
+ * \param Number Number of pages to map
+ */
+extern tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number);
+/**
+ * \brief Allocates DMA physical memory
+ * \param Pages Number of pages required
+ * \param MaxBits Maximum number of bits the physical address can have
+ * \param PhysAddr Pointer to the location to place the physical address allocated
+ * \return Virtual address allocate
+ */
+extern tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr);
+/**
+ * \brief Unmaps an allocated hardware range
+ * \param VAddr Virtual address allocate by ::MM_MapHWPages or ::MM_AllocDMA
+ * \param Number Number of pages to free
+ */
+extern void MM_UnmapHWPages(tVAddr VAddr, Uint Number);
+/**
+ * \brief Allocate a single physical page
+ * \return Physical address allocated
+ */
+extern tPAddr MM_AllocPhys(void);
+/**
+ * \brief Allocate a contiguous range of physical pages
+ * \param Pages Number of pages to allocate
+ * \param MaxBits Maximum number of address bits allowed
+ * \return First physical address allocated
+ */
+extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
+/**
+ * \brief Reference a physical page
+ * \param PAddr Page to mark as referenced
+ */
+extern void MM_RefPhys(tPAddr PAddr);
+/**
+ * \brief Dereference a physical page
+ * \param PAddr Page to dereference
+ */
+extern void MM_DerefPhys(tPAddr PAddr);
+/**
+ * \brief Get the number of times a page has been referenced
+ * \param PAddr Address to check
+ * \return Reference count for the page
+ */
+extern int MM_GetRefCount(tPAddr PAddr);
+/**
+ * \brief Set the node associated with a page
+ * \param PAddr Physical address of page
+ * \param Node Node pointer (tVFS_Node)
+ * \return Boolean failure
+ * \retval 0 Success
+ * \retval 1 Page not allocated
+ */
+extern int MM_SetPageNode(tPAddr PAddr, void *Node);
+/**
+ * \brief Get the node associated with a page
+ * \param PAddr Physical address of page
+ * \param Node Node pointer (tVFS_Node) destination
+ * \return Boolean failure
+ * \retval 0 Success
+ * \retval 1 Page not allocated
+ */
+extern int MM_GetPageNode(tPAddr PAddr, void **Node);
+/**
+ * \}
+ */
+
+// --- Memory Manipulation ---
+/**
+ * \name Memory Manipulation
+ * \{
+ */
+extern int memcmp(const void *m1, const void *m2, size_t count);
+extern void *memcpy(void *dest, const void *src, size_t count);
+extern void *memcpyd(void *dest, const void *src, size_t count);
+extern void *memmove(void *dest, const void *src, size_t len);
+extern void *memset(void *dest, int val, size_t count);
+extern void *memsetd(void *dest, Uint32 val, size_t count);
+/**
+ * \}
+ */
+/**
+ * \name Memory Validation
+ * \{
+ */
+extern int CheckString(const char *String);
+extern int CheckMem(const void *Mem, int Num);
+/**
+ * \}
+ */
+
+// --- Endianness ---
+/**
+ * \name Endianness Swapping
+ * \{
+ */
+#ifdef __BIG_ENDIAN__
+#define LittleEndian16(_val) SwapEndian16(_val)
+#define LittleEndian32(_val) SwapEndian32(_val)
+#define BigEndian16(_val) (_val)
+#define BigEndian32(_val) (_val)
+#else
+#define LittleEndian16(_val) (_val)
+#define LittleEndian32(_val) (_val)
+#define BigEndian16(_val) SwapEndian16(_val)
+#define BigEndian32(_val) SwapEndian32(_val)
+#endif
+extern Uint16 SwapEndian16(Uint16 Val);
+extern Uint32 SwapEndian32(Uint32 Val);
+/**
+ * \}
+ */
+
+// --- Strings ---
+/**
+ * \name Strings
+ * \{
+ */
+extern int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
+extern int sprintf(char *__s, const char *__format, ...);
+extern size_t strlen(const char *Str);
+extern char *strcpy(char *__dest, const char *__src);
+extern char *strncpy(char *__dest, const char *__src, size_t max);
+extern char *strcat(char *__dest, const char *__src);
+extern char *strncat(char *__dest, const char *__src, size_t n);
+extern int strcmp(const char *__str1, const char *__str2);
+extern int strncmp(const char *Str1, const char *Str2, size_t num);
+extern int strucmp(const char *Str1, const char *Str2);
+// strdup macro is defined in heap.h
+extern char *_strdup(const char *File, int Line, const char *Str);
+extern char **str_split(const char *__str, char __ch);
+extern char *strchr(const char *__s, int __c);
+extern int strpos(const char *Str, char Ch);
+extern int strpos8(const char *str, Uint32 search);
+extern void itoa(char *buf, Uint64 num, int base, int minLength, char pad);
+extern int atoi(const char *string);
+extern int ParseInt(const char *string, int *Val);
+extern int ReadUTF8(const Uint8 *str, Uint32 *Val);
+extern int WriteUTF8(Uint8 *str, Uint32 Val);
+extern int ModUtil_SetIdent(char *Dest, const char *Value);
+extern int ModUtil_LookupString(const char **Array, const char *Needle);
+
+extern Uint8 ByteSum(const void *Ptr, int Size);
+extern int Hex(char *Dest, size_t Size, const Uint8 *SourceData);
+extern int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString);
+/**
+ * \}
+ */
+
+/**
+ * \brief Get a random number
+ * \return Random number
+ * \note Current implementation is a linear congruency
+ */
+extern int rand(void);
+/**
+ * \brief Call a function with a variable number of arguments
+ * \param Function Function address
+ * \param NArgs Number of entries in \a Args
+ * \param Args Array of arguments
+ * \return Integer from called Function
+ */
+extern int CallWithArgArray(void *Function, int NArgs, Uint *Args);
+
+// --- Heap ---
+#include <heap.h>
+/**
+ * \brief Magic heap allocation function
+ */
+extern void *alloca(size_t Size);
+
+// --- Modules ---
+/**
+ * \name Modules
+ * \{
+ */
+extern int Module_LoadMem(void *Buffer, Uint Length, const char *ArgStr);
+extern int Module_LoadFile(const char *Path, const char *ArgStr);
+/**
+ * \}
+ */
+
+// --- Timing ---
+/**
+ * \name Time and Timing
+ * \{
+ */
+/**
+ * \brief Create a timestamp from a time
+ */
+extern tTime timestamp(int sec, int mins, int hrs, int day, int month, int year);
+/**
+ * \brief Extract the date/time from a timestamp
+ */
+extern void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
+/**
+ * \brief Gets the current timestamp (miliseconds since Midnight 1st January 1970)
+ */
+extern Sint64 now(void);
+/**
+ * \}
+ */
+
+// --- Threads ---
+/**
+ * \name Threads and Processes
+ * \{
+ */
+extern int Proc_SpawnWorker(void (*Fcn)(void*), void *Data);
+extern int Proc_Spawn(const char *Path);
+extern int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs);
+extern int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize);
+extern void Threads_Exit(int TID, int Status);
+extern void Threads_Yield(void);
+extern void Threads_Sleep(void);
+extern int Threads_WakeTID(tTID Thread);
+extern tPID Threads_GetPID(void);
+extern tTID Threads_GetTID(void);
+extern tUID Threads_GetUID(void);
+extern tGID Threads_GetGID(void);
+extern int SpawnTask(tThreadFunction Function, void *Arg);
+extern int *Threads_GetErrno(void);
+extern int Threads_SetName(const char *NewName);
+/**
+ * \}
+ */
+
+// --- Simple Math ---
+//! Divide and round up
+extern int DivUp(int num, int dem);
+//! Divide and Modulo 64-bit unsigned integer
+extern Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem);
+
+static inline int MIN(int a, int b) { return a < b ? a : b; }
+static inline int MAX(int a, int b) { return a > b ? a : b; }
+
+#include <binary_ext.h>
+#include <vfs_ext.h>
+#include <mutex.h>
+
+#endif
--- /dev/null
+/*
+ * Acess2
+ * - Abstract Data Types
+ */
+#ifndef _ADT_H_
+#define _ADT_H_
+
+/**
+ * \name Ring Buffers
+ * \{
+ */
+typedef struct sRingBuffer
+{
+ size_t Start; //!< Start of data in ring buffer
+ size_t Length; //!< Number of data bytes in buffer
+ size_t Space; //!< Allocated space in buffer
+ tShortSpinlock Lock; //!< Lock to prevent collisions
+ char Data[]; //!< Buffer
+} tRingBuffer;
+
+/**
+ * \brief Create a ring buffer \a Space bytes large
+ * \param Space Ammount of space to allocate within the buffer
+ * \return Pointer to the buffer structure
+ */
+extern tRingBuffer *RingBuffer_Create(size_t Space);
+/**
+ * \brief Read at most \a Length bytes from the buffer
+ * \param Dest Destinaton buffer
+ * \param Buffer Source ring buffer
+ * \param Length Requested number of bytes
+ * \return Number of bytes read
+ */
+extern size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length);
+/**
+ * \brief Write at most \a Length bytes to the buffer
+ * \param Buffer Destination ring buffer
+ * \param Source Source buffer
+ * \param Length Provided number of bytes
+ * \return Number of bytes written
+ */
+extern size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length);
+/**
+ * \}
+ */
+
+
+#endif
--- /dev/null
+/**
+ * \file api_drv_common.h
+ * \brief Common Driver Interface Definitions
+ * \author John Hodge (thePowersGang)
+ *
+ * \section Introduction
+ * There are two ways Acess drivers can communicate with userspace
+ * applications, both are through the VFS. The first is by exposing a
+ * device as a file buffer, the second is by sending commands via the
+ * ioctl() system call.
+ * All drivers in Acess must at least conform to the specifcation in this
+ * file (even if it is just implementing eTplDrv_IOCtl.DRV_IOCTL_TYPE and
+ * returning eTplDrv_Type.DRV_TYPE_NULL, however, doing so is discouraged)
+ *
+ * \section ioctls Core IOCtl calls
+ * As said, the core Acess driver specifcation defines specific IOCtl calls
+ * that all drivers should implement. The four core IOCtls (defined in
+ * ::eTplDrv_IOCtl) allow another binary (wether it be a user-mode application
+ * or another driver) to tell what type of device a driver provides, the
+ * basic identifcation of the driver (4 character ID and BCD version number)
+ * and be able to use externally standardised calls that may not have
+ * standardised call numbers.
+ * NOTE: All ioctl calls WILL return -1 if the driver ran into an error
+ * of its own fault while executing the call. If the user was at fault
+ * (e.g. by passing a bad buffer) the call will return -2.
+ *
+ * \section types Driver Types
+ * When the eTplDrv_IOCtl.DRV_IOCTL_TYPE call is made, the driver should
+ * return the relevant entry in the ::eTplDrv_Type enumeration that describes
+ * what sub-specifcation (and hence, what device type) it implements.
+ * These device types are described in their own files, which are liked
+ * from their entries in ::eTplDrv_Type.
+ */
+#ifndef _API_DRV_COMMON_H
+#define _API_DRV_COMMON_H
+
+/**
+ * \enum eTplDrv_IOCtl
+ * \brief Common IOCtl Calls
+ */
+enum eTplDrv_IOCtl {
+ /**
+ * ioctl(...)
+ * \brief Get driver type
+ * \return The relevant entry from ::eTplDrv_Type
+ */
+ DRV_IOCTL_TYPE,
+
+ /**
+ * ioctl(..., char *dest[32])
+ * \brief Get driver identifier string
+ * \return 0 on no error
+ *
+ * This call sets the 32-byte array \a dest to the drivers 31 charater
+ * identifier. This identifier must be unique to the driver series.
+ */
+ DRV_IOCTL_IDENT,
+
+ /**
+ * ioctl(...)
+ * \brief Get driver version number
+ * \return 24-bit BCD version number (2.2.2)
+ *
+ * This call returns the 6-digit (2 major, 2 minor, 2 patch) version
+ * number of the driver.
+ */
+ DRV_IOCTL_VERSION,
+
+ /**
+ * ioctl(..., char *name)
+ * \brief Get a IOCtl call ID from a symbolic name
+ * \return ID number of the call, or 0 if not found
+ *
+ * This call allows user applications to not need to know the ID numbers
+ * of this driver's IOCtl calls by taking a string and returning the
+ * IOCtl call number associated with that method name.
+ */
+ DRV_IOCTL_LOOKUP,
+
+ /**
+ * \brief First non-reserved IOCtl number for driver extension
+ */
+ DRV_IOCTL_USERMIN = 0x1000,
+};
+
+/**
+ * \brief eTplDrv_IOCtl.DRV_IOCTL_LOOKUP names for the core IOCtls
+ * These are the official lookup names of the core calls
+ */
+#define DRV_IOCTLNAMES "type", "ident", "version", "lookup"
+
+/**
+ * \brief Helper macro for the base IOCtl calls
+ * \param _type Type number from eTplDrv_Type to return
+ * \param _ident String of max 32-characters that identifies this driver
+ * \param _version Driver's 8.8.8 BCD version number
+ * \param _ioctls Pointer to the IOCtls string array
+ * \warning If you have DEBUG enabled in the calling file, this function
+ * will do LEAVE()s before returning, so make sure that the
+ * IOCtl function is ENTER()ed when using debug with this macro
+ *
+ * Usage: (Id is the IOCtl call ID)
+ * \code
+ * switch(Id)
+ * {
+ * BASE_IOCTLS(DRV_TYPE_MISC, "Ident", 0x100, csaIOCtls)
+ * // Your IOCtls go here, starting at index 4
+ * }
+ * \endcode
+ */
+#define BASE_IOCTLS(_type, _ident, _version, _ioctls) \
+ case DRV_IOCTL_TYPE: LEAVE('i', (_type)); return (_type);\
+ case DRV_IOCTL_IDENT: {\
+ int tmp = ModUtil_SetIdent(Data, (_ident));\
+ LEAVE('i', tmp); return tmp;\
+ }\
+ case DRV_IOCTL_VERSION: LEAVE('x', (_version)); return (_version);\
+ case DRV_IOCTL_LOOKUP:{\
+ int tmp = ModUtil_LookupString( _ioctls, (const char*)Data );\
+ LEAVE('i', tmp);\
+ return tmp;\
+ }
+
+/**
+ * \enum eTplDrv_Type
+ * \brief Driver Types returned by DRV_IOCTL_TYPE
+ */
+enum eTplDrv_Type {
+ DRV_TYPE_NULL, //!< NULL Type - Custom Interface
+ DRV_TYPE_MISC, //!< Miscelanious Compilant - Supports the core calls
+ DRV_TYPE_TERMINAL, //!< Terminal - see api_drv_terminal.h
+ DRV_TYPE_VIDEO, //!< Video - see api_drv_video.h
+ DRV_TYPE_SOUND, //!< Audio
+ DRV_TYPE_DISK, //!< Disk - see api_drv_disk.h
+ DRV_TYPE_KEYBOARD, //!< Keyboard - see api_drv_keyboard.h
+ DRV_TYPE_MOUSE, //!< Mouse
+ DRV_TYPE_JOYSTICK, //!< Joystick / Gamepad
+ DRV_TYPE_NETWORK //!< Network Device - see api_drv_network.h
+};
+
+#endif
--- /dev/null
+/**\r
+ * \file api_drv_disk.h\r
+ * \brief Disk Driver Interface Definitions\r
+ * \author John Hodge (thePowersGang)\r
+ * \r
+ * \section Nomeclature\r
+ * All addreses are 64-bit counts of bytes from the logical beginning of\r
+ * the disk unless explicitly stated.\r
+ * \r
+ * \section dirs VFS Layout\r
+ * Disk drivers have a flexible directory layout. The root directory can\r
+ * contain subdirectories, with the only conditions being that all nodes\r
+ * must support ::eTplDrv_IOCtl with DRV_IOCTL_TYPE returning DRV_TYPE_DISK.\r
+ * And all file nodes representing disk devices (or partitions) and implemeting\r
+ * ::eTplDisk_IOCtl fully\r
+ * \r
+ * \section files Files\r
+ * When a read or write occurs on a normal file in the disk driver it will\r
+ * read/write the represented device. The accesses need not be aligned to\r
+ * the block size, however aligned reads/writes should be handled specially\r
+ * to improve speed (this can be aided by using ::DrvUtil_ReadBlock and\r
+ * ::DrvUtil_WriteBlock)\r
+ */\r
+#ifndef _API_DRV_DISK_H\r
+#define _API_DRV_DISK_H\r
+\r
+#include <api_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplDisk_IOCtl\r
+ * \brief Common Disk IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplDisk_IOCtl {\r
+ /**\r
+ * ioctl(..., void)\r
+ * \brief Get the block size\r
+ * \return Size of a hardware block for this device\r
+ */\r
+ DISK_IOCTL_GETBLOCKSIZE = 4,\r
+ \r
+ /**\r
+ * ioctl(..., tTplDisk_CacheRegion *RegionInfo)\r
+ * \brief Sets the cache importantce and protocol for a section of\r
+ * memory.\r
+ * \param RegionInfo Pointer to a region information structure\r
+ * \return Boolean failure\r
+ */\r
+ DISK_IOCTL_SETCACHEREGION,\r
+ \r
+ /**\r
+ * ioctl(..., Uint64 *Info[2])\r
+ * \brief Asks the driver to precache a region of disk.\r
+ * \param Region 64-bit Address and Size pair describing the area to cache\r
+ * \return Number of blocks cached\r
+ */\r
+ DISK_IOCTL_PRECACHE,\r
+ \r
+ /**\r
+ * ioclt(..., Uint64 *Region[2])\r
+ * \brief Asks to driver to flush the region back to disk\r
+ * \param Region 64-bit Address and Size pair describing the area to flush\r
+ * \note If Region[0] == -1 then the entire disk's cache is flushed\r
+ * \return Number of blocks flushed (or 0 for entire disk)\r
+ */\r
+ DISK_IOCTL_FLUSH\r
+};\r
+\r
+/**\r
+ * \brief Describes the cache parameters of a region on the disk\r
+ */\r
+typedef struct sTplDisk_CacheRegion\r
+{\r
+ Uint64 Base; //!< Base of cache region\r
+ Uint64 Length; //!< Size of cache region\r
+ /**\r
+ * \brief Cache Protocol & Flags\r
+ * \r
+ * The low 4 bits denot the cache protocol to be used by the\r
+ * region (see ::eTplDisk_CacheProtocols for a list).\r
+ * The high 4 bits denote flags to apply to the cache (see\r
+ * ::eTplDisk_CacheFlags)\r
+ */\r
+ Uint8 Flags;\r
+ Uint8 Priority; //!< Lower is a higher proritory\r
+ /**\r
+ * \brief Maximum size of cache, in blocks\r
+ * \note If CacheSize is zero, the implemenation defined limit is used\r
+ */\r
+ Uint16 CacheSize;\r
+} tTplDisk_CacheRegion;\r
+\r
+/**\r
+ * \brief Cache protocols to use\r
+ */\r
+enum eTplDisk_CacheProtocols\r
+{\r
+ /**\r
+ * \brief Don't cache the region\r
+ */\r
+ \r
+ DISK_CACHEPROTO_DONTCACHE,\r
+ /**\r
+ * \brief Most recently used blocks cached\r
+ * \note This is the default action for undefined regions\r
+ */\r
+ DISK_CACHEPROTO_RECENTLYUSED,\r
+ /**\r
+ * \brief Cache the entire region in memory\r
+ * \r
+ * This is a faster version of setting Length to CacheSize*BlockSize\r
+ */\r
+ DISK_CACHEPROTO_FULLCACHE,\r
+ \r
+ /**\r
+ * \brief Cache only on demand\r
+ * \r
+ * Only cache when the ::DISK_IOCTL_PRECACHE IOCtl is used\r
+ */\r
+ DISK_CACHEPROTO_EXPLICIT\r
+};\r
+\r
+/**\r
+ * \brief Flags for the cache\r
+ */\r
+enum eTplDisk_CacheFlags\r
+{\r
+ /**\r
+ * \brief Write all changes to the region straight back to media\r
+ */\r
+ DISK_CACHEFLAG_WRITETHROUGH = 0x10\r
+};\r
+\r
+/**\r
+ * \brief IOCtl name strings\r
+ */\r
+#define DRV_DISK_IOCTLNAMES "get_block_size","set_cache_region","set_precache"\r
+\r
+/**\r
+ * \name Disk Driver Utilities\r
+ * \{\r
+ */\r
+\r
+/**\r
+ * \brief Callback function type used by DrvUtil_ReadBlock and DrvUtil_WriteBlock\r
+ * \param Address Zero based block number to read\r
+ * \param Count Number of blocks to read\r
+ * \param Buffer Destination for read blocks\r
+ * \param Argument Argument provided in ::DrvUtil_ReadBlock and ::DrvUtil_WriteBlock\r
+ */\r
+typedef Uint (*tDrvUtil_Read_Callback)(Uint64 Address, Uint Count, void *Buffer, Uint Argument);\r
+typedef Uint (*tDrvUtil_Write_Callback)(Uint64 Address, Uint Count, const void *Buffer, Uint Argument);\r
+\r
+/**\r
+ * \brief Reads a range from a block device using aligned reads\r
+ * \param Start Base byte offset\r
+ * \param Length Number of bytes to read\r
+ * \param Buffer Destination for read data\r
+ * \param ReadBlocks Callback function to read a sequence of blocks\r
+ * \param BlockSize Size of an individual block\r
+ * \param Argument An argument to pass to \a ReadBlocks\r
+ * \return Number of bytes read\r
+ */\r
+extern Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,\r
+ tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument);\r
+/**\r
+ * \brief Writes a range to a block device using aligned writes\r
+ * \param Start Base byte offset\r
+ * \param Length Number of bytes to write\r
+ * \param Buffer Destination for read data\r
+ * \param ReadBlocks Callback function to read a sequence of blocks\r
+ * \param WriteBlocks Callback function to write a sequence of blocks\r
+ * \param BlockSize Size of an individual block\r
+ * \param Argument An argument to pass to \a ReadBlocks and \a WriteBlocks\r
+ * \return Number of bytes written\r
+ */\r
+extern Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer,\r
+ tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks,\r
+ Uint64 BlockSize, Uint Argument);\r
+\r
+/**\r
+ * \}\r
+ */\r
+\r
+#endif\r
--- /dev/null
+/**\r
+ * \file api_drv_joystick.h\r
+ * \brief Joystick Driver Interface Definitions\r
+ * \author John Hodge (thePowersGang)\r
+ * \r
+ * \section dirs VFS Layout\r
+ * Joystick drivers define a single VFS node, that acts as a fixed size file.\r
+ * Reads from this file return the current state, writes are ignored.\r
+ *\r
+ * \section File Structure\r
+ * The device file must begin with a valid sJoystick_FileHeader structure.\r
+ * The file header is followed by \a NAxies instances of sJoystick_Axis, one\r
+ * for each axis.\r
+ * This is followed by \a NButtons boolean values (represented using \a Uint8),\r
+ * each representing the state of a single button (where 0 is unpressed,\r
+ * 0xFF is fully depressed - intermediate values are valid in the case of\r
+ * variable-pressure buttons)\r
+ */\r
+#ifndef _API_DRV_JOYSTICK_H\r
+#define _API_DRV_JOYSTICK_H\r
+\r
+#include <api_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplJoystick_IOCtl\r
+ * \brief Common Joystick IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplJoystick_IOCtl {\r
+ /**\r
+ * ioctl(..., tJoystick_Callback *Callback)\r
+ * \brief Sets the callback\r
+ * \note Can be called from kernel mode only\r
+ *\r
+ * Sets the callback that is called when a event occurs (button or axis\r
+ * change). This function pointer must be in kernel mode (although,\r
+ * kernel->user or kernel->ring3driver abstraction functions can be used)\r
+ * \r
+ * Axis events depend on the axis limit, if non-zero, the callback fires\r
+ * if the cursor position changes. Otherwise it fires when the axis value\r
+ * (cursor accelleration) changes.\r
+ */\r
+ JOY_IOCTL_SETCALLBACK = 4,\r
+\r
+ /**\r
+ * ioctl(..., int *Argument)\r
+ * \brief Set the argument passed as the first parameter to the callback\r
+ * \note Kernel mode only\r
+ */\r
+ JOY_IOCTL_SETCALLBACKARG,\r
+\r
+ /**\r
+ * ioctl(..., tJoystickNumValue *)\r
+ * \brief Set maximum value for sJoystick_Axis.CursorPos\r
+ * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
+ */\r
+ JOY_IOCTL_GETSETAXISLIMIT,\r
+\r
+ /**\r
+ * ioctl(..., tJoystickNumValue *)\r
+ * \brief Set the value of sJoystick_Axis.CursorPos\r
+ * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
+ */\r
+ JOY_IOCTL_GETSETAXISPOSITION,\r
+ \r
+ /**\r
+ * ioctl(..., tJoystickNumValue *)\r
+ * \brief Set axis flags\r
+ * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
+ * \todo Define flag values\r
+ */\r
+ JOY_IOCTL_GETSETAXISFLAGS,\r
+\r
+ /**\r
+ * ioctl(..., tJoystickNumValue *)\r
+ * \brief Set Button Flags\r
+ * \note If \a Value is equal to -1 (all bits set), the value is not changed\r
+ * \todo Define flag values\r
+ */\r
+ JOY_IOCTL_GETSETBUTTONFLAGS,\r
+};\r
+\r
+/**\r
+ * \brief Symbolic names for Joystick IOCtls\r
+ */\r
+#define DRV_JOY_IOCTLNAMES "set_callback", "set_callback_arg", "getset_axis_limit", "getset_axis_position", \\r
+ "getset_axis_flags", "getset_button_flags"\r
+\r
+// === TYPES ===\r
+typedef struct sJoystick_NumValue tJoystick_NumValue;\r
+typedef struct sJoystick_FileHeader tJoystick_FileHeader;\r
+typedef struct sJoystick_Axis tJoystick_Axis;\r
+\r
+/**\r
+ * \brief Number/Value pair for joystick IOCtls\r
+ */\r
+struct sJoystick_NumValue\r
+{\r
+ int Num; //!< Axis/Button number\r
+ int Value; //!< Value (see IOCtl defs for meaning)\r
+};\r
+\r
+/**\r
+ * \brief Callback type for JOY_IOCTL_SETCALLBACK\r
+ * \param Ident Ident value passed to JOY_IOCTL_SETCALLBACK\r
+ * \r
+ */\r
+typedef void (*tJoystick_Callback)(int Ident, int bIsAxis, int Num, int Delta);\r
+\r
+/**\r
+ * \struct sJoystick_FileHeader\r
+ * \brief Format of the joystick VFS node's first bytes\r
+ */\r
+struct sJoystick_FileHeader\r
+{\r
+ Uint16 NAxies; //!< Number of Axies\r
+ Uint16 NButtons; //!< Number of buttons\r
+};\r
+\r
+/**\r
+ * \brief Axis Definition in file data\r
+ *\r
+ * Describes the current state of an axis on the joystick.\r
+ * \a CursorPos is between zero and the current limit set by the\r
+ * JOY_IOCTL_GETSETAXISLIMIT IOCtl, while \a CurValue indicates the\r
+ * current position of the joystick axis. This is defined to be between\r
+ * \a MinValue and \a MaxValue.\r
+ */\r
+struct sJoystick_Axis\r
+{\r
+ Sint16 MinValue; //!< Minumum value for \a CurValue\r
+ Sint16 MaxValue; //!< Maximum value for \a CurValue\r
+ Sint16 CurValue; //!< Current value (joystick position)\r
+ Uint16 CursorPos; //!< Current state (cursor position)\r
+};\r
+\r
+/**\r
+ * \brief Macro to define a structure for a joystick's node\r
+ * \param _naxies Number of axies\r
+ * \param _nbuttons Number of buttons\r
+ * \note This just defines the structure, it's up to the driver to set the\r
+ * sJoystick_FileHeader.NAxies and sJoystick_FileHeader.NButtons fields.\r
+ */\r
+#define JOY_INFOSTRUCT(_naxies, _nbuttons) struct { \\r
+ Uint16 NAxies, NButtons;\\r
+ tJoystick_Axis Axies[_naxies];\\r
+ Uint16 Buttons[_nbuttons];\\r
+ }\r
+\r
+#endif\r
--- /dev/null
+/**\r
+ * \file api_drv_keyboard.h\r
+ * \brief Keyboard Driver Interface Definitions\r
+ * \author John Hodge (thePowersGang)\r
+ * \r
+ * \section dirs VFS Layout\r
+ * Keyboard drivers consist of only a single node, which is a normal file\r
+ * node with a size of zero. All reads and writes to this node are ignored\r
+ * (tVFS_Node.Read and tVFS_Node.Write are NULL)\r
+ */\r
+#ifndef _API_DRV_KEYBOARD_H\r
+#define _API_DRV_KEYBOARD_H\r
+\r
+#include <api_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplKeyboard_IOCtl\r
+ * \brief Common Keyboard IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplKeyboard_IOCtl {\r
+ /**\r
+ * ioctl(..., int *Rate)\r
+ * \brief Get/Set Repeat Rate\r
+ * \param Rate New repeat rate (pointer)\r
+ * \return Current/New Repeat rate\r
+ * \r
+ * Gets/Set the repeat rate (actually the time in miliseconds between\r
+ * repeats) of a held down key.\r
+ * If the rate is set to zero, repeating will be disabled.\r
+ */\r
+ KB_IOCTL_REPEATRATE = 4,\r
+ \r
+ /**\r
+ * ioctl(..., int *Delay)\r
+ * \brief Get/Set Repeat Delay\r
+ * \param Delay New repeat delay (pointer)\r
+ * \return Current/New repeat delay\r
+ * \r
+ * Gets/Set the time in miliseconds before a key starts repeating\r
+ * after a key is pressed.\r
+ * Setting the delay to a negative number will cause the function to\r
+ * return -1\r
+ */\r
+ KB_IOCTL_REPEATDELAY,\r
+ \r
+ \r
+ /**\r
+ * ioctl(..., tKeybardCallback *Callback)\r
+ * \brief Sets the callback\r
+ * \note Can be called from kernel mode only\r
+ * \r
+ * Sets the function to be called when a key event occurs (press, release\r
+ * or repeat). This function pointer must be in kernel mode (although,\r
+ * kernel->user or kernel->ring3driver abstraction functions can be used)\r
+ *\r
+ * This function is called when a key is pressed, repeated or released.\r
+ * If the raw scancode is to be included with the key event, it should precede\r
+ * the event.\r
+ */\r
+ KB_IOCTL_SETCALLBACK\r
+};\r
+\r
+/**\r
+ * \brief Symbolic names for Keyboard IOCtls\r
+ */\r
+#define DRV_KEYBAORD_IOCTLNAMES "getset_repeat_rate", "getset_repeat_delay", "set_callback"\r
+\r
+/**\r
+ * \brief Callback type for KB_IOCTL_SETCALLBACK\r
+ * \param Key Key symbol (Unicode or eTplKeyboard_KeyCodes)\r
+ */\r
+typedef void (*tKeybardCallback)(Uint32 Key);\r
+\r
+/**\r
+ * \name Callback key flags\r
+ * \brief Flags for values passed to the callback\r
+ * \{\r
+ */\r
+#define KEY_CODEPOINT_MASK 0x3FFFFFFF\r
+#define KEY_ACTION_MASK 0xC0000000\r
+#define KEY_ACTION_PRESS 0x00000000 //!< Key pressed\r
+#define KEY_ACTION_RELEASE 0x40000000 //!< Key released\r
+#define KEY_ACTION_REFIRE 0x80000000 //!< Repeated key\r
+#define KEY_ACTION_RAWSYM 0xC0000000 //!< Raw key symbol (comes before a press/repeat/release)\r
+/**\r
+ * \}\r
+ */\r
+\r
+/**\r
+ * \brief Symbolic key codes\r
+ * \r
+ * These key codes represent non-pritable characters and are placed above\r
+ * the Unicode character space.\r
+ * If the using driver recieves a key code with the 31st bit set, it means\r
+ * that that key has been released.\r
+ */\r
+enum eTplKeyboard_KeyCodes {\r
+ KEY_ESC = 0x1B, //!< Escape Character\r
+ \r
+ KEY_NP_MASK = 0x20000000, //! Mask for non-printable characters\r
+ \r
+ /**\r
+ * \name Special Keys\r
+ * \brief These keys are usually used on their own\r
+ * \{\r
+ */\r
+ KEY_CAPSLOCK,\r
+ KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,\r
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, \r
+ KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,\r
+ KEY_NUMLOCK, KEY_SCROLLLOCK,\r
+ KEY_HOME, KEY_END, KEY_INS, KEY_DEL,\r
+ KEY_PAUSE, KEY_BREAK,\r
+ KEY_PGUP, KEY_PGDOWN,\r
+ KEY_KPENTER, KEY_KPSLASH, KEY_KPMINUS, KEY_KPPLUS, KEY_KPSTAR,\r
+ KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT,\r
+ KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL,\r
+ KEY_LWIN, KEY_RWIN,\r
+ KEY_MENU,\r
+ /**\r
+ * \}\r
+ */\r
+ \r
+ // Modifiers\r
+ /**\r
+ * \name Modifiers\r
+ * \brief These keye usually alter the character stream sent to the user\r
+ * \{\r
+ */\r
+ KEY_MODIFIERS = 0x30000000,\r
+ KEY_LCTRL, KEY_RCTRL,\r
+ KEY_LALT, KEY_RALT,\r
+ KEY_LSHIFT, KEY_RSHIFT,\r
+ /**\r
+ * \}\r
+ */\r
+};\r
+\r
+\r
+#endif\r
--- /dev/null
+/**\r
+ * \file api_drv_network.h\r
+ * \brief Network Interface Driver Interface Definitions\r
+ * \r
+ * \section dirs VFS Layout\r
+ * All network drivers must have the following basic VFS structure\r
+ * The root of the driver will only contain files that are named from zero\r
+ * upwards that represent the present network adapters that this driver\r
+ * controls. All VFS nodes must implement ::eTplDrv_IOCtl with\r
+ * DRV_IOCTL_TYPE returning DRV_TYPE_NETWORK.\r
+ * The adapter nodes must also implement ::eTplNetwork_IOCtl fully\r
+ * (unless it is noted in the ::eTplNetwork_IOCtl documentation that a\r
+ * call is optional)\r
+ * \r
+ * \section files Adapter Files\r
+ * \subsection Reading\r
+ * When an adapter file is read from, the driver will block the reading\r
+ * thread until a packet arrives (if there is not already an unhandled\r
+ * packet in the queue) this will then be read into the destination buffer.\r
+ * If the packet does not fit in the buffer, the end of it is discarded.\r
+ * Likewise, if the packet does not completely fill the buffer, the call\r
+ * will still read to the buffer and then return the size of the packet.\r
+ * \subsection Writing\r
+ * When an adapter is written to, the data written is encoded as a packet\r
+ * and sent, if the data is not the correct size to be sent (if the packet\r
+ * is too small, or if it is too large) -1 should be returned and the packet\r
+ * will not be sent.\r
+ */\r
+#ifndef _API_DRV_NETWORK_H\r
+#define _API_DRV_NETWORK_H\r
+\r
+#include <api_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplNetwork_IOCtl\r
+ * \brief Common Network IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplNetwork_IOCtl {\r
+ /**\r
+ * ioctl(..., Uint8 *MAC[6])\r
+ * \brief Get the MAC address of the interface\r
+ * \return 1 on success, 0 if the file is the root, -1 on error\r
+ * \r
+ * Copies the six byte Media Access Control (MAC) address of the\r
+ * adapter to the \a MAC array.\r
+ */\r
+ NET_IOCTL_GETMAC = 4\r
+};\r
+\r
+/**\r
+ * \brief IOCtl name strings for use with eTplDrv_IOCtl.DRV_IOCTL_LOOKUP\r
+ */\r
+#define DRV_NETWORK_IOCTLNAMES "get_mac_addr"\r
+\r
+#endif\r
--- /dev/null
+/**\r
+ * \file api_drv_terminal.h\r
+ * \brief Terminal Driver Interface Definitions\r
+*/\r
+#ifndef _API_DRV_TERMINAL_H\r
+#define _API_DRV_TERMINAL_H\r
+\r
+#include <api_drv_common.h>\r
+\r
+/**\r
+ * \brief Common Terminal IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplTerminal_IOCtl {\r
+ /**\r
+ * ioctl(..., int *mode)\r
+ * \brief Get/Set the current video mode type\r
+ * \param mode Pointer to an integer with the new mode number (or NULL)\r
+ * If \a mode is non-NULL the current terminal mode is changed/updated\r
+ * to the mode indicated by \a *mode\r
+ * \note See ::eTplTerminal_Modes\r
+ * \return Current/new terminal mode\r
+ */\r
+ TERM_IOCTL_MODETYPE = 4,\r
+ \r
+ /**\r
+ * ioctl(..., int *width)\r
+ * \brief Get/set the display width\r
+ * \param width Pointer to an integer containing the new width (or NULL)\r
+ * \return Current/new width\r
+ * \r
+ * If \a width is non-NULL the current width is updated (but is not\r
+ * applied until ::TERM_IOCTL_MODETYPE is called with \a mode non-NULL.\r
+ */\r
+ TERM_IOCTL_WIDTH,\r
+ \r
+ /**\r
+ * ioctl(..., int *height)\r
+ * \brief Get/set the display height\r
+ * \param height Pointer to an integer containing the new height\r
+ * \return Current height\r
+ * \r
+ * If \a height is non-NULL the current height is updated (but is not\r
+ * applied until ::TERM_IOCTL_MODETYPE is called with a non-NULL \a mode.\r
+ */\r
+ TERM_IOCTL_HEIGHT,\r
+ \r
+ /**\r
+ * ioctl(..., tTerm_IOCtl_Mode *info)\r
+ * \brief Queries the current driver about it's native modes\r
+ * \param info A pointer to a ::tTerm_IOCtl_Mode with \a ID set to\r
+ * the mode index (or NULL)\r
+ * \return Number of modes\r
+ * \r
+ * If \a info is NULL, the number of avaliable vative display modes\r
+ * is returned. These display modes will have sequential ID numbers\r
+ * from zero up to this value.\r
+ * \r
+ * \note The id field of \a info is not for use with ::TERM_IOCTL_MODETYPE\r
+ * This field is just for indexing the mode to get its information.\r
+ */\r
+ TERM_IOCTL_QUERYMODE,\r
+ \r
+ /**\r
+ * ioctl(...)\r
+ * \brief Forces the current terminal to be shown\r
+ */\r
+ TERM_IOCTL_FORCESHOW,\r
+ \r
+ /**\r
+ * ioctl(..., tVideo_IOCtl_Pos *pos)\r
+ * \brief Returns the current text cursor position\r
+ * \param pos New cursor position. If NULL, the position is not changed\r
+ * \return Cursor position (as X+Y*Width)\r
+ */\r
+ TERM_IOCTL_GETSETCURSOR,\r
+ \r
+ /**\r
+ * ioctl(..., tVideo_IOCtl_Bitmap *Bmp)\r
+ * \brief Set the video cursor bitmap\r
+ * \param Bmp New bitmap (if NULL, the current bitmap is removed)\r
+ * \return Boolean failure\r
+ */\r
+ TERM_IOCTL_SETCURSORBITMAP,\r
+};\r
+\r
+/**\r
+ * \brief Virtual Terminal Mode\r
+ * Describes a VTerm mode to the caller of ::TERM_IOCTL_QUERYMODE\r
+ */\r
+typedef struct sTerm_IOCtl_Mode\r
+{\r
+ short ID; //!< Zero Based index of mode\r
+ short DriverID; //!< Driver's ID number (from ::tVideo_IOCtl_Mode)\r
+ Uint16 Height; //!< Height\r
+ Uint16 Width; //!< Width\r
+ Uint8 Depth; //!< Bits per cell\r
+ struct {\r
+ unsigned bText: 1; //!< Text Mode marker\r
+ unsigned unused: 7;\r
+ };\r
+} tTerm_IOCtl_Mode;\r
+\r
+/**\r
+ * \brief Terminal Modes\r
+ */\r
+enum eTplTerminal_Modes {\r
+ /**\r
+ * \brief UTF-8 Text Mode\r
+ * Any writes to the terminal file are treated as UTF-8 encoded\r
+ * strings and reads will also return UTF-8 strings.\r
+ */\r
+ TERM_MODE_TEXT,\r
+ \r
+ /**\r
+ * \brief 32bpp Framebuffer\r
+ * Writes to the terminal file will write to the framebuffer.\r
+ * Reads will return UTF-32 characters\r
+ */\r
+ TERM_MODE_FB,\r
+ \r
+ /**\r
+ * \brief 32bpp 2D Accellerated mode\r
+ * Writes to the terminal file will be read as a command stream\r
+ * defined in ::eTplTerminal_2D_Commands\r
+ */\r
+ TERM_MODE_2DACCEL,\r
+ \r
+ /**\r
+ * \brief OpenGL 2D/3D\r
+ * Writes to the terminal file will send 3D commands\r
+ * Reads will return UTF-32 characters\r
+ * \note May or may not stay in the spec\r
+ */\r
+ TERM_MODE_3D,\r
+ \r
+ /**\r
+ * \brief Number of terminal modes\r
+ */\r
+ NUM_TERM_MODES\r
+};\r
+\r
+/**\r
+ * \brief 2D Command IDs\r
+ * \todo Complete this structure\r
+ * \r
+ * Command IDs for when the terminal type is eTplTerminal_Modes.TERM_MODE_2DACCEL\r
+ */\r
+enum eTplTerminal_2D_Commands\r
+{\r
+ /**\r
+ * \brief No Operation - Used for padding\r
+ */\r
+ TERM_2DCMD_NOP,\r
+ \r
+ /**\r
+ * (Uint16 X, Y, W, H, Uint32 Data[])\r
+ * \brief Blits a bitmap to the display\r
+ * \param X,Y Coordinates of Top-Left corner\r
+ * \param W,H Dimensions\r
+ * \param Data 32-bpp pixel data\r
+ */\r
+ TERM_2DCMD_PUSH\r
+};\r
+\r
+#endif\r
--- /dev/null
+/**\r
+ * \file api_drv_video.h\r
+ * \brief Video Driver Interface Definitions\r
+ * \note For AcessOS Version 1\r
+ * \r
+ * Video drivers extend the common driver interface api_drv_common.h\r
+ * and must support the IOCtl numbers defined in this file to be\r
+ * compatable with Acess (drivers may implement more IOCtls above\r
+ * DRV_IOCTL_USERMIN).\r
+ * \r
+ * \section IOCtls\r
+ * As said, a compatable driver must implement these calls correctly,\r
+ * but they may choose not to allow direct user access to the framebuffer.\r
+ * \r
+ * \section Screen Contents\r
+ * Writes to the driver's file while in component colour modes\r
+ * must correspond to a change of the contents of the screen. The framebuffer\r
+ * must start at offset 0 in the file.\r
+ * Reading from the screen must either return zero, or read from the\r
+ * framebuffer.\r
+ * \r
+ * \section Mode Support\r
+ * All video drivers must support text output for every resolution (hardware\r
+ * accelerated or software), and at least the _BLIT and _FILL 2D operations\r
+ * (these may be implemented specifically for text mode).\r
+ */\r
+#ifndef _API_DRV_VIDEO_H\r
+#define _API_DRV_VIDEO_H\r
+\r
+#include <api_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplVideo_IOCtl\r
+ * \brief Common Video IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplVideo_IOCtl {\r
+ /**\r
+ * ioctl(..., int *mode)\r
+ * \brief Get/Set Mode\r
+ * \return Current mode ID or -1 on error\r
+ * \r
+ * If \a mode is non-NULL, the current video mode is set to \a *mode.\r
+ * This updated ID is then returned to the user.\r
+ */\r
+ VIDEO_IOCTL_GETSETMODE = 4,\r
+ \r
+ /**\r
+ * ioctl(..., tVideo_IOCtl_Mode *info)\r
+ * \brief Find a matching mode\r
+ * \return 1 if a mode was found, 0 otherwise\r
+ * \r
+ * Using avaliable modes matching the \a bpp and \a flags fields\r
+ * set the \a id, \a width and \a heights fields to the closest\r
+ * matching mode.\r
+ */\r
+ VIDEO_IOCTL_FINDMODE,\r
+ \r
+ /**\r
+ * ioctl(..., tVideo_IOCtl_Mode *info)\r
+ * \brief Get mode info\r
+ * \return 1 if the mode exists, 0 otherwise\r
+ * \r
+ * Set \a info's fields to the mode specified by the \a id field.\r
+ */\r
+ VIDEO_IOCTL_MODEINFO,\r
+ \r
+ /**\r
+ * ioctl(..., int *NewFormat)\r
+ * \brief Switches between Text, Framebuffer and 3D modes\r
+ * \param NewFormat Pointer to the new format code (see eTplVideo_BufFormats)\r
+ * \return Original format\r
+ * \r
+ * Enabes and disables the video text mode, changing the behavior of\r
+ * writes to the device file.\r
+ */\r
+ VIDEO_IOCTL_SETBUFFORMAT,\r
+ \r
+ /**\r
+ * ioctl(..., tVideo_IOCtl_Pos *pos)\r
+ * \brief Sets the cursor position\r
+ * \return Boolean success\r
+ * \r
+ * Set the text mode cursor position (if it is supported)\r
+ * If the \a pos is set to (-1,-1) the cursor is hidden, otherwise\r
+ * \a pos MUST be within the current screen size (as given by the\r
+ * current mode's tVideo_IOCtl_Mode.width and tVideo_IOCtl_Mode.height\r
+ * fields).\r
+ */\r
+ VIDEO_IOCTL_SETCURSOR,\r
+ \r
+ /**\r
+ * ioctl(..., tVideo_IOCtl_Bitmap *Image)\r
+ * \brief Sets the cursor image\r
+ * \return Boolean success\r
+ *\r
+ * Sets the graphics mode cursor image\r
+ */\r
+ VIDEO_IOCTL_SETCURSORBITMAP\r
+};\r
+\r
+/**\r
+ * \brief Symbolic names for Video IOCtls (#4 onwards)\r
+ */\r
+#define DRV_VIDEO_IOCTLNAMES "getset_mode", "find_mode", "mode_info", "set_buf_format", "set_cursor", "set_cursor_bitmap"\r
+\r
+/**\r
+ * \brief Mode Structure used in IOCtl Calls\r
+ * \r
+ * Defines a video mode supported by (or requested of) this driver (depending\r
+ * on what ioctl call is used)\r
+ */\r
+typedef struct sVideo_IOCtl_Mode\r
+{\r
+ short id; //!< Mode ID\r
+ Uint16 width; //!< Width\r
+ Uint16 height; //!< Height\r
+ Uint8 bpp; //!< Bits per pixel\r
+ Uint8 flags; //!< Mode Flags (none defined, should be zero)\r
+} tVideo_IOCtl_Mode;\r
+\r
+/**\r
+ * \brief Buffer Format Codes\r
+ */\r
+enum eTplVideo_BufFormats\r
+{\r
+ /**\r
+ * \brief Text Mode\r
+ * \r
+ * The device file presents itself as an array of ::tVT_Char\r
+ * each describing a character cell on the screen.\r
+ * These cells are each \a giVT_CharWidth pixels wide and\r
+ * \a giVT_CharHeight high.\r
+ */\r
+ VIDEO_BUFFMT_TEXT,\r
+ /**\r
+ * \brief Framebuffer Mode\r
+ * \r
+ * The device file presents as an array of 32-bpp pixels describing\r
+ * the entire screen. The format of a single pixel is in xRGB format\r
+ * (top 8 bits ignored, next 8 bits red, next 8 bits green and\r
+ * the bottom 8 bits blue)\r
+ */\r
+ VIDEO_BUFFMT_FRAMEBUFFER,\r
+ /**\r
+ * \brief 2D Accelerated Mode\r
+ * \r
+ * The device file acts as a character device, accepting a stream of\r
+ * commands described in eTplVideo_2DCommands when written to.\r
+ */\r
+ VIDEO_BUFFMT_2DSTREAM,\r
+ /**\r
+ * \brief 3D Accelerated Mode\r
+ * \r
+ * The device file acts as a character device, accepting a stream of\r
+ * commands described in eTplVideo_3DCommands when written to.\r
+ */\r
+ VIDEO_BUFFMT_3DSTREAM\r
+};\r
+\r
+/**\r
+ * \brief 2D Accellerated Video Commands\r
+ * \r
+ * Commands passed in the command stream for ::VIDEO_BUFFMT_2DSTREAM\r
+ */\r
+enum eTplVideo_2DCommands\r
+{\r
+ /**\r
+ * \brief No Operation\r
+ */\r
+ VIDEO_2DOP_NOP,\r
+ /**\r
+ * \brief Fill a region\r
+ * \param X Uint16 - Leftmost pixels of the region\r
+ * \param Y Uint16 - Topmost pixels of the region\r
+ * \param W Uint16 - Width of the region\r
+ * \param H Uint16 - Height of the region\r
+ * \param Colour Uint32 - Value to fill with\r
+ */\r
+ VIDEO_2DOP_FILL,\r
+ /**\r
+ * \brief Copy a region from one part of the framebuffer to another\r
+ * \param DestX Uint16 - Leftmost pixels of the destination\r
+ * \param DestY Uint16 - Topmost pixels of the destination\r
+ * \param SrcX Uint16 - Leftmost pixels of the source\r
+ * \param SrcY Uint16 - Topmost pixels of the source\r
+ * \param Width Uint16 - Width of the region\r
+ * \param Height Uint16 - Height of the region\r
+ */\r
+ VIDEO_2DOP_BLIT,\r
+\r
+\r
+ /**\r
+ * \brief Copy a region from video memory to the framebuffer\r
+ */\r
+ VIDEO_2DOP_BLITBUF,\r
+\r
+ /**\r
+ * \brief Copy and scale a region from video memory to the framebuffer\r
+ */\r
+ VIDEO_2DOP_BLITSCALEBUF,\r
+\r
+ NUM_VIDEO_2DOPS\r
+};\r
+\r
+/**\r
+ * \brief Describes a position in the video framebuffer\r
+ */\r
+typedef struct sVideo_IOCtl_Pos\r
+{\r
+ Sint16 x; //!< X Coordinate\r
+ Sint16 y; //!< Y Coordinate\r
+} tVideo_IOCtl_Pos;\r
+\r
+/**\r
+ * \brief Bitmap object (out of band image)\r
+ */\r
+typedef struct sVideo_IOCtl_Bitmap\r
+{\r
+ Sint16 W; //!< Width of image\r
+ Sint16 H; //!< Height of image\r
+ Sint16 XOfs; //!< X Offset of center\r
+ Sint16 YOfs; //!< Y Offset of center\r
+ Uint32 Data[]; //!< Image data (ARGB array)\r
+} tVideo_IOCtl_Bitmap;\r
+\r
+/**\r
+ * \brief Virtual Terminal Representation of a character\r
+ */\r
+typedef struct sVT_Char\r
+{\r
+ Uint32 Ch; //!< UTF-32 Character\r
+ union {\r
+ struct {\r
+ Uint16 BGCol; //!< 12-bit Foreground Colour\r
+ Uint16 FGCol; //!< 12-bit Background Colour\r
+ };\r
+ Uint32 Colour; //!< Compound colour for ease of access\r
+ };\r
+} tVT_Char;\r
+\r
+/**\r
+ * \name Basic builtin colour definitions\r
+ * \{\r
+ */\r
+#define VT_COL_BLACK 0x0000\r
+#define VT_COL_GREY 0x0888\r
+#define VT_COL_LTGREY 0x0CCC\r
+#define VT_COL_WHITE 0x0FFF\r
+/**\r
+ * \}\r
+ */\r
+\r
+//! \brief Defines the width of a rendered character\r
+extern int giVT_CharWidth;\r
+//! \brief Defines the height of a rendered character\r
+extern int giVT_CharHeight;\r
+/**\r
+ * \name Font rendering\r
+ * \{\r
+ */\r
+/**\r
+ * \brief Driver helper that renders a character to a buffer\r
+ * \param Codepoint Unicode character to render\r
+ * \param Buffer Buffer to render to\r
+ * \param Depth Bit depth of the destination buffer\r
+ * \param Pitch Number of bytes per line\r
+ * \param BGC 32-bit Background Colour\r
+ * \param FGC 32-bit Foreground Colour\r
+ * \r
+ * This function is provided to help video drivers to support a simple\r
+ * text mode by keeping the character rendering abstracted from the driver,\r
+ * easing the driver development and reducing code duplication.\r
+ */\r
+extern void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC);\r
+/**\r
+ * \fn Uint32 VT_Colour12to24(Uint16 Col12)\r
+ * \brief Converts a colour from 12bpp to 24bpp\r
+ * \param Col12 12-bpp input colour\r
+ * \return Expanded 32-bpp (24-bit colour) version of \a Col12\r
+ */\r
+extern Uint32 VT_Colour12to24(Uint16 Col12);\r
+/**\r
+ * \brief Converts a colour from 12bpp to 14bpp\r
+ * \param Col12 12-bpp input colour\r
+ * \return 15 bits per pixel value\r
+ */\r
+extern Uint16 VT_Colour12to15(Uint16 Col12);\r
+/**\r
+ * \brief Converts a colour from 12bpp to 32bpp\r
+ * \param Col12 12-bpp input colour\r
+ * \param Depth Desired bit depth\r
+ * \return \a Depth bit number, denoting Col12\r
+ * \r
+ * Expands the source colour into a \a Depth bits per pixel representation.\r
+ * The colours are expanded with preference to Green, Blue and Red in that order\r
+ * (so, green gets the first spare pixel, blue gets the next, and red never gets\r
+ * the spare). \n\r
+ * The final bit of each component is used to fill the lower bits of the output.\r
+ */\r
+extern Uint32 VT_Colour12toN(Uint16 Col12, int Depth);\r
+/**\r
+ * \}\r
+ */\r
+\r
+/**\r
+ * \brief Maximum cursor width for using the DrvUtil software cursor\r
+ */\r
+#define DRVUTIL_MAX_CURSOR_W 32\r
+/**\r
+ * \brief Maximum cursor width for using the DrvUtil software cursor\r
+ */\r
+#define DRVUTIL_MAX_CURSOR_H 32\r
+\r
+/**\r
+ * \brief Framebuffer information used by all DrvUtil_Video functions\r
+ */\r
+typedef struct sDrvUtil_Video_BufInfo\r
+{\r
+ /**\r
+ * \name Framebuffer state\r
+ * \{\r
+ */\r
+ /**\r
+ * \brief Framebuffer virtual address\r
+ */\r
+ void *Framebuffer;\r
+ /**\r
+ * \brief Bytes between the start of each line\r
+ */\r
+ int Pitch;\r
+ /**\r
+ * \brief Number of pixels in each line\r
+ */\r
+ short Width;\r
+ /**\r
+ * \brief Total number of lines\r
+ */\r
+ short Height;\r
+ /**\r
+ * \brief Bit depth of the framebuffer\r
+ */\r
+ short Depth;\r
+ /*\r
+ * \}\r
+ */\r
+ \r
+ /**\r
+ * \brief Buffer write format\r
+ */\r
+ short BufferFormat;\r
+ \r
+ /**\r
+ * \name Software cursor controls\r
+ * \{\r
+ */\r
+ /**\r
+ * \brief X coordinate of the cursor\r
+ */\r
+ short CursorX;\r
+ /**\r
+ * \brief Y coordinate of the cursor\r
+ */\r
+ short CursorY;\r
+\r
+ /**\r
+ * \brief Cursor bitmap\r
+ */\r
+ tVideo_IOCtl_Bitmap *CursorBitmap;\r
+ /*\r
+ * \}\r
+ */\r
+\r
+ /*\r
+ * \name Internal fields\r
+ * \{\r
+ */\r
+\r
+ /**\r
+ * \brief Buffer to store the area under the cursor\r
+ */\r
+ void *CursorSaveBuf;\r
+ \r
+ int CursorReadX; //!< X offset in cursor bitmap corresponding to \a CursorDestX\r
+ int CursorReadY; //!< Same as \a CursorReadX but for Y\r
+ int CursorRenderW; //!< Width of rendered cursor\r
+ int CursorRenderH; //!< Height of rendered cursor\r
+ int CursorDestX; //!< X coordinate Destination for rendered cursor\r
+ int CursorDestY; //!< Y coordinate destination for rendered cursor\r
+\r
+ /*\r
+ * \}\r
+ */\r
+} tDrvUtil_Video_BufInfo;\r
+\r
+/**\r
+ * \brief Handlers for eTplVideo_2DCommands\r
+ */\r
+typedef struct sDrvUtil_Video_2DHandlers\r
+{\r
+ /**\r
+ * \brief No Operation, Ignored\r
+ * \see VIDEO_2DOP_NOP\r
+ */\r
+ void *Nop;\r
+ /**\r
+ * \brief Fill a buffer region\r
+ * \param X Lefthand edge\r
+ * \param Y Top edge\r
+ * \param W Width\r
+ * \param H Height\r
+ * \param Colour Colour to fill with\r
+ * \see VIDEO_2DOP_FILL\r
+ */\r
+ void (*Fill)(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);\r
+ /**\r
+ * \brief Fill a buffer region\r
+ * \param DestX Lefthand edge of destination\r
+ * \param DestY Top edge of destination\r
+ * \param SrcX Lefthand edge of source\r
+ * \param SrcY Top edge of source\r
+ * \param W Width\r
+ * \param H Height\r
+ * \see VIDEO_2DOP_BLIT\r
+ */\r
+ void (*Blit)(void *Ent, Uint16 DestX, Uint16 DestY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);\r
+} tDrvUtil_Video_2DHandlers;\r
+\r
+/**\r
+ * \brief Handle a 2D operation stream for a driver\r
+ * \param Ent Value to pass to handlers\r
+ * \param Buffer Stream buffer\r
+ * \param Length Length of stream\r
+ * \param Handlers Handlers to use for the stream\r
+ * \param SizeofHandlers Size of \a tDrvUtil_Video_2DHandlers according\r
+ * to the driver. Used as version control and error avoidence.\r
+ */\r
+extern int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length,\r
+ tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);\r
+\r
+/**\r
+ * \brief Perform write operations to a LFB\r
+ * \param FBInfo Framebuffer descriptor, see type for details\r
+ * \param Offset Offset provided by VFS call\r
+ * \param Length Length provided by VFS call\r
+ * \param Src Data from VFS call\r
+ * \return Number of bytes written\r
+ *\r
+ * Handles all write modes in software, using the VT font calls for rendering.\r
+ * \note Calls the cursor clear and redraw if the cursor area is touched\r
+ */\r
+extern int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Src);\r
+\r
+/**\r
+ * \name Software cursor rendering\r
+ * \{\r
+ */\r
+/**\r
+ * \brief Set the cursor bitmap for a buffer\r
+ * \param Buf Framebuffer descriptor\r
+ * \param Bitmap New cursor bitmap\r
+ */\r
+extern int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);\r
+/**\r
+ * \brief Render the cursor at (\a X, \a Y)\r
+ * \param Buf Framebuffer descriptor, see type for details\r
+ * \param X X coord of the cursor\r
+ * \param Y Y coord of the cursor\r
+ */\r
+extern void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);\r
+/**\r
+ * \brief Removes the rendered cursor from the screen\r
+ * \param Buf Framebuffer descriptor, see type for details\r
+ */\r
+extern void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);\r
+\r
+/**\r
+ * \brief Text mode cursor image\r
+ */\r
+extern tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor;\r
+/**\r
+ * \}\r
+ */\r
+#endif\r
--- /dev/null
+/**
+ * \file apidoc/arch_x86.h
+ * \brief x86(-64) Specific Functions
+ * \author John Hodge (thePowersGang)
+ *
+ * \section toc Table of Contents
+ * - \ref portio "Port IO"
+ * - \ref dma "DMA - Direct Memory Access"
+ *
+ * \section portio Port IO
+ * The x86 architecture has two memory spaces, the first is the system
+ * memory accessable using standard loads and stores. The second is the
+ * 16-bit IO Bus. This bus is accessed using the \a in and \a out opcodes
+ * and is used to configure devices attached to the system.
+ * A driver should not use \a in and \a out directly, but instead use
+ * the provided \a in* and \a out* functions to access the IO Bus.
+ * This allows the kernel to run a driver in userspace if requested without
+ * the binary needing to be altered.
+ *
+ * \section dma DMA - Direct Memory Access
+ */
+
+/**
+ * \name IO Bus Access
+ * \{
+ */
+extern Uint8 inb(Uint16 Port); //!< Read 1 byte from the IO Bus
+extern Uint16 inw(Uint16 Port); //!< Read 2 bytes from the IO Bus
+extern Uint32 inl(Uint16 Port); //!< Read 4 bytes from the IO Bus
+extern Uint64 inq(Uint16 Port); //!< Read 8 bytes from the IO Bus\
+
+extern void outb(Uint16 Port, Uint8 Value); //!< Write 1 byte to the IO Bus
+extern void outw(Uint16 Port, Uint16 Value); //!< Write 2 bytes to the IO Bus
+extern void outl(Uint16 Port, Uint32 Value); //!< Write 4 bytes to the IO Bus
+extern void outq(Uint16 Port, Uint64 Value); //!< Write 8 bytes to the IO Bus
+/**
+ * \}
+ */
--- /dev/null
+/**
+ * \file apidoc_mainpage.h
+ * \brief API Documentation Home Page
+ * \author John Hodge (thePowersGang)
+ *
+ * \mainpage Acess 2 Kernel API Documentation
+ *
+ * \section intro Introduction
+ * These documents attempt to describe the standard Acess 2 (and hopefully
+ * future versions) Kernel mode API.
+ * The documentation covers filesystem drivers, binary formats and the
+ * various device driver interface standards.
+ *
+ * \section index "Sections"
+ * - \ref modules.h "Module Definitions"
+ * - Describes how a module is defined in Acess
+ * - \ref binary.h "Binary Formats"
+ * - Explains how to register a new binary format with the kernel
+ * - \ref vfs.h "VFS - The Virtual File System"
+ * - The VFS is the core of Acess's driver architecture
+ * - \ref drivers "Device Drivers"
+ * - Describes how drivers should use the VFS to expose themselves to the
+ * user.
+ * - Drivers for specific types of hardware must behave in the specific
+ * way described here.
+ *
+ * \page drivers Device Drivers
+ *
+ * \section toc Contents
+ * - \ref drvintro "Introduction"
+ * - \ref drv_misc "Miscelanious Devices"
+ * - \ref drv_video "Video Drivers"
+ *
+ * \section drvintro Introduction
+ * All Acess2 device drivers communicate with user-level (and other parts
+ * of the greater kernel) via the VFS. They register themselves in a similar
+ * way to how filesystem drivers do, however instead of registering with
+ * the VFS core, they register with a special filesystem driver called the
+ * \ref fs_devfs.h "Device Filesystem" (devfs). The DevFS provides the
+ * ::DevFS_AddDevice function that takes a ::tDevFS_Driver structure as
+ * an agument. This structure specifies the driver's name and its root
+ * VFS node. This node is used to provide the user access to the
+ * driver's functions via IOCtl calls and Reading or Writing to the driver
+ * file. Drivers are also able to expose a readonly buffer by using
+ * \ref fs_sysfs.h "ProcDev", usually to provide state information or device
+ * capabilities for the the user.
+ *
+ * The device driver interfaces are all based on the core specifcation
+ * in api_drv_common.h (Common Device Driver definitions).
+ * The following subsections define the various specific types of driver
+ * interfaces. These definitions only define the bare minimum of what the
+ * driver must implement, if the driver author so wants to, they can add
+ * IOCtl calls and/or files (where allowed by the type specifcation) to
+ * their device's VFS layout.
+ *
+ * \subsection drv_misc Miscelanious Devices
+ * If a device type does not have a specifcation for it, the driver can
+ * identify itself as a miscelanious device by returning DRV_TYPE_MISC
+ * from \ref DRV_IOCTL_TYPE.
+ * A misc device must at least implement the IOCtl calls defined in the
+ * \ref api_drv_common.h "Common Device Driver definitions", allowing it
+ * to be identified easily by the user and for interfacing programs to
+ * utilise the DRV_IOCTL_LOOKUP call.
+ *
+ * \subsection drv_video Video Devices
+ * Video drivers are based on a framebuffer model (unless in 3D mode,
+ * which is not yet fully standardised, so should be ignored).
+ * The driver will contain only one VFS node, that exposes the video
+ * framebuffer (this may not be the true framebuffer, to allow for double-buffering)
+ * to the user. See the full documentation in api_drv_video.h for the
+ * complete specifcation.
+ *
+ * \subsection drv_disk Disk/Storage Devices
+ * Storage devices present themselves as a linear collection of bytes.
+ * Reads and writes to the device need not be aligned to the stated block
+ * size, but it is suggested that users of a storage device file align
+ * their accesses to block boundaries.
+ * The functions DrvUtil_ReadBlock and DrvUtil_WriteBlock are provided
+ * to storage drivers to assist in handling non-alinged reads and writes.
+ *
+ * \see api_drv_common.h Common Spec.
+ * \see api_drv_video.h Video Device Spec.
+ * \see api_drv_keyboard.h Keyboard Device Spec.
+ * \see api_drv_disk.h Disk/Storage Device Spec.
+ * \see api_drv_network.h Network Device Spec.
+ * \see api_drv_terminal.h Virtual Terminal Spec.
+ */
--- /dev/null
+/**
+ * \file binary.h
+ * \brief Binary Loader Definitions
+ * \author John Hodge (thePowersGang)
+ */
+#ifndef _BINARY_H
+#define _BINARY_H
+
+// === TYPES ===
+/**
+ * \brief Representation of a section in a binary file
+ *
+ * Tells the binary loader where the page data resides on disk and where
+ * to load it to (relative to the binary base). Once the data is read,
+ * the \a Physical field contains the physical address of the page.
+ */
+typedef struct sBinarySection
+{
+ Uint64 Offset; //!< File offset of the section
+ tVAddr Virtual; //!< Virtual load address
+ size_t FileSize; //!< Number of bytes to load from the file
+ size_t MemSize; //!< Number of bytes in memory
+ Uint Flags; //!< Load Flags
+} tBinarySection;
+
+/**
+ * \brief Flags for ::tBinarySection.Flags
+ * \name Binary Section Flags
+ * \{
+ */
+//! \brief Read-only
+#define BIN_SECTFLAG_RO 0x0001
+//! \brief Executable
+#define BIN_SECTFLAG_EXEC 0x0002
+/**
+ * \}
+ */
+
+/**
+ * \brief Defines a binary file
+ *
+ * This structure defines and maintains the state of a binary during and
+ * after loading.
+ * Before the binary is loaded into memory (when it has just been returned
+ * from tBinaryType.Load) the \a Pages array will contain the file offsets
+ * to the page data in the \a Physical fields (or -1 for uninitialised
+ * data) and the \a Size fields define how much data is stored in-file
+ * for the page (to allow partial pages to be loaded from disk)
+ * Once the binary is loaded (NOTE: Drivers do not need to know about this,
+ * it is here for information only) the \a Physical fields now contain the
+ * physical addresses of the pages filled with the data. The \a Virtual
+ * fields contain the preferred virtual address of the pages (a given
+ * process may have these pages mapped to a different location).
+ */
+typedef struct sBinary
+{
+ struct sBinary *Next; //!< Pointer used by the kernel
+
+ tMount MountID; //!< Mount ID
+ tInode Inode; //!< Inode (Used for fast reopen)
+
+ /**
+ * \brief Interpreter used to load the file
+ * \note This can be either requested by the individual file, or a per-driver option
+ */
+ const char *Interpreter;
+ /**
+ * \brief Entrypoint of the binary (at requested base);
+ */
+ tVAddr Entry;
+ /**
+ * \brief File's requested load base
+ */
+ tVAddr Base;
+ /**
+ * \brief Number of times this binary has been mapped
+ */
+ int ReferenceCount;
+ /**
+ * \brief Number of sections defined in the file
+ */
+ int NumSections;
+ /**
+ * \brief Array of sections defined by this binary
+ * \note Contains \a NumSections entries
+ */
+ tBinarySection LoadSections[];
+} tBinary;
+
+/**
+ * \brief Binary type definition
+ *
+ * This structure is used to define a loader for a specific binary type
+ * so that the kernel's core binary loader can understand that type.
+ * The tBinaryType.Relocate and tBinaryType.GetSymbol need only be non-NULL
+ * if the binary type is to be used for kernel modules, otherwise it will
+ * only be able to load binaries for user space.
+ */
+typedef struct sBinaryType
+{
+ /**
+ * \brief Pointer used by the kernel
+ * \note Should not be touched by the driver (initialise to NULL)
+ */
+ struct sBinaryType *Next;
+ /**
+ * \brief Identifying DWord
+ *
+ * If he first 32-bits of the file match this value (when ANDed with
+ * tBinaryType.Mask), this binary loader will be used to load the file.
+ */
+ Uint32 Ident;
+ Uint32 Mask; //!< Mask value for tBinaryType.Ident
+ const char *Name; //!< Name of this executable type (for debug purpouses)
+ /**
+ * \brief Read a binary from a file
+ * \param FD VFS File handle to file to load
+ * \return Pointer to a ::tBinary that describes how to load the file
+ *
+ * This function reads a binary file and returns a ::tBinary pointer
+ * that tells the core binary loader how to read the data from the file
+ * and where to map it to.
+ */
+ tBinary *(*Load)(int FD);
+
+ /**
+ * \brief Prepares a mapped binary for execution at this address
+ * \param Base Binary loaded in memory
+ * \return Boolean Success
+ * \note This pointer can be NULL, but then the binary cannot be used
+ * to load a kernel module.
+ *
+ * tBinaryType.Relocate takes a binary that was loaded according to
+ * tBinaryType.Load and prepares it to run at the address it is
+ * loaded to, attempting to satisfy any external unresolved symbols
+ * required, if a symbol cannot be located, the function will return
+ * zero.
+ */
+ int (*Relocate)(void *Base);
+
+ /**
+ * \brief Gets a symbol's address from a loaded binary
+ * \note The binary pointed to by \a Base may not have been through
+ * tBinaryType.Relocate at this time, so the driver should
+ * accomodate this.
+ */
+ int (*GetSymbol)(void *Base, const char *Name, Uint *Dest);
+} tBinaryType;
+
+/**
+ * \brief Registers an interpreter path with the binary loader
+ * \param Path Path to the requested interpreter (need not be a "true" path)
+ * \return Pointer to the cached string
+ *
+ * Speeds up checking if the intepreter is loaded in the kernel by allowing
+ * the search to use pointer comparisons instead of string comparisons.
+ */
+extern char *Binary_RegInterp(char *Path);
+
+/**
+ * \brief Registers a binary type with the kernel's loader
+ * \param Type Pointer to the loader's type structure
+ * \return Boolean success
+ * \note The structure \a Type must be persistant (usually it will be a
+ * constant global variable)
+ *
+ * This function tells the binary loader about a new file type, and gives
+ * it the functions to read the type into a ::tBinary structure, relocate
+ * it and to find the value of symbols defined within the binary.
+ */
+extern int Binary_RegisterType(tBinaryType *Type);
+
+#endif
--- /dev/null
+/*
+ * Acess 2
+ * binary_ext.h
+ * - Exported Symbols from the binary loader
+ */
+#ifndef _BINARY_EXT_H
+#define _BINARY_EXT_H
+
+// === FUNCTIONS ===
+extern void *Binary_LoadFile(const char *Path);
+extern void *Binary_LoadKernel(const char *Path);
+extern Uint Binary_Relocate(void *Mem);
+extern void Binary_Unload(void *Base);
+extern int Binary_GetSymbol(const char *Name, Uint *Dest);
+extern Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val);
+
+#endif
--- /dev/null
+/**\r
+ * \file drv_pci.h\r
+ * \brief PCI Bus Driver\r
+ * \author John Hodge (thePowersGang)\r
+ */\r
+#ifndef _DRV_PCI_H\r
+#define _DRV_PCI_H\r
+\r
+/**\r
+ * \brief PCI Class Codes\r
+ */\r
+enum ePCIClasses\r
+{\r
+ PCI_CLASS_PRE20 = 0x00,\r
+ PCI_CLASS_STORAGE,\r
+ PCI_CLASS_NETWORK,\r
+ PCI_CLASS_DISPLAY,\r
+ PCI_CLASS_MULTIMEDIA,\r
+ PCI_CLASS_MEMORY,\r
+ PCI_CLASS_BRIDGE,\r
+ PCI_CLASS_COMM,\r
+ PCI_CLASS_PREPH,\r
+ PCI_CLASS_INPUT,\r
+ PCI_CLASS_DOCKING,\r
+ PCI_CLASS_PROCESSORS,\r
+ PCI_CLASS_SERIALBUS,\r
+ PCI_CLASS_MISC = 0xFF\r
+};\r
+\r
+enum ePCIOverClasses\r
+{\r
+ PCI_OC_PCIBRIDGE = 0x060400,\r
+ PCI_OC_SCSI = 0x010000\r
+};\r
+\r
+typedef int tPCIDev;\r
+\r
+/**\r
+ * \brief Count PCI Devices\r
+ * \r
+ * Counts the number of devices with specified Vendor and Device IDs\r
+ */\r
+extern int PCI_CountDevices(Uint16 VendorID, Uint16 DeviceID);\r
+extern tPCIDev PCI_GetDevice(Uint16 VendorID, Uint16 DeviceID, int index);\r
+/**\r
+ * \param ClassCode (Class:SubClass:PI)\r
+ */\r
+extern tPCIDev PCI_GetDeviceByClass(Uint32 ClassCode, Uint32 Mask, tPCIDev prev);\r
+\r
+extern int PCI_GetDeviceInfo(tPCIDev id, Uint16 *Vendor, Uint16 *Device, Uint32 *Class);\r
+extern int PCI_GetDeviceVersion(tPCIDev id, Uint8 *Revision);\r
+extern int PCI_GetDeviceSubsys(tPCIDev id, Uint16 *SubsystemVendor, Uint16 *SubsystemID);\r
+\r
+extern Uint32 PCI_ConfigRead(tPCIDev id, int Offset, int Size);\r
+extern void PCI_ConfigWrite(tPCIDev id, int Offset, int Size, Uint32 Value);\r
+\r
+extern Uint8 PCI_GetIRQ(tPCIDev id);\r
+extern Uint32 PCI_GetBAR(tPCIDev id, int BAR);\r
+//extern Uint16 PCI_AssignPort(tPCIDev id, int bar, int count);\r
+\r
+#endif\r
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv_pci_int.h
+ * - PCI internal definitions
+ */
+#ifndef _DRV_PCI_INT_H
+#define _DRV_PCI_INT_H
+
+#include <acess.h>
+
+extern Uint32 PCI_CfgReadDWord(Uint32 Addr);
+extern void PCI_CfgWriteDWord(Uint32 Addr, Uint32 data);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2
+ * errno.h
+ */
+#ifndef _ERRNO_H
+#define _ERRNO_H
+
+enum eErrorNums
+{
+ EOK,
+
+ ENOSYS, // Invalid Instruction
+ EINVAL, // Invalid Paramater
+ ENOMEM, // No free memory
+ EACCES, // Not permitted
+ ENOTFOUND, // Item not found
+ EREADONLY, // Read only
+ ENOTIMPL, // Not implemented
+ ENOENT, // No entry?
+ EEXIST, // Already exists
+ ENFILE, // Too many open files
+ ENOTDIR, // Not a directory
+
+ EALREADY, // Operation was a NOP
+ EINTERNAL, // Internal Error
+
+ NUM_ERRS
+};
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * events.h
+ * - Thread Events
+ */
+#ifndef _EVENTS_H_
+#define _EVENTS_H_
+
+#include <threads.h>
+
+#define THREAD_EVENT_VFS 0x00000001
+#define THREAD_EVENT_IPCMSG 0x00000002
+#define THREAD_EVENT_SIGNAL 0x00000004
+#define THREAD_EVENT_TIMER 0x00000008
+
+// === FUNCTIONS ===
+extern void Threads_PostEvent(tThread *Thread, Uint32 EventMask);
+extern Uint32 Threads_WaitEvents(Uint32 EventMask);
+
+#endif
+
--- /dev/null
+/**
+ * \file fs_devfs.h
+ * \brief Acess Device Filesystem interface
+ * \author John Hodge (thePowersGang)
+ */
+#ifndef _FS_DEVFS_H
+#define _FS_DEVFS_H
+#include <vfs.h>
+
+// === TYPES ===
+/**
+ * \brief DevFS driver definition
+ */
+typedef struct sDevFS_Driver
+{
+ struct sDevFS_Driver *Next; //!< Set to NULL by drivers (used internally)
+ const char *Name; //!< Name of the driver file/folder (must be unique)
+ tVFS_Node RootNode; //!< Root node of driver
+} tDevFS_Driver;
+
+// === FUNCTIONS ===
+/**
+ * \fn int DevFS_AddDevice(tDevFS_Driver *Device)
+ * \brief Registers a device in the Device Filesystem
+ * \param Device Pointer to a persistant structure that represents the driver
+ * \return Boolean success
+ */
+extern int DevFS_AddDevice(tDevFS_Driver *Device);
+
+/**
+ * \brief Unregisters a device with the Device Filesystem
+ */
+extern void DevFS_DelDevice(tDevFS_Driver *Device);
+
+#endif
--- /dev/null
+/*
+ * Acess2
+ * - SysFS Export Header
+ */
+/**
+ * \file fs_sysfs.h
+ * \brief ProcDev/SysFS Interface
+ * \author John Hodge (thePowersGang)
+ *
+ *
+ */
+#ifndef _FS_SYSFS_H_
+#define _FS_SYSFS_H_
+
+/**
+ * \brief Registers a file in the SysFS tree
+ * \param Path Path relative to the SysFS root (no .. or .)
+ * \param Data File buffer address
+ * \param Length Length of the file buffer
+ * \return An ID number to refer to the file, or -1 on error
+ * \note \a Data must be maintained until ::SysFS_UpdateFile is called
+ * with a different buffer, or ::SysFS_RemoveFile is called.
+ */
+extern int SysFS_RegisterFile(const char *Path, const char *Data, int Length);
+
+/**
+ * \brief Updates the size/pointer associated with a SysFD file
+ * \param ID Number returned by ::SysFS_RegisterFile
+ * \param Data New buffer address
+ * \param Length New length of the file
+ * \return Boolean Success
+ */
+extern int SysFS_UpdateFile(int ID, const char *Data, int Length);
+
+/**
+ * \brief Removes a file from the SysFS tree
+ * \param ID Number returned by ::SysFS_RegisterFile
+ */
+extern int SysFS_RemoveFile(int ID);
+
+#endif
--- /dev/null
+/**
+ * Acess2
+ * - By John Hodge (thePowersGang)
+ *
+ * include/hal_proc.h
+ * - HAL Process management functions
+ *
+ */
+#ifndef _HAL_PROC_H_
+#define _HAL_PROC_H_
+/**
+ * \file hal_proc.h
+ * \brief Achitecture defined thread/process management functions
+ */
+
+#include <threads_int.h>
+
+/**
+ * \brief Initialise the architecture dependent side of threading
+ */
+extern void ArchThreads_Init(void);
+/**
+ * \brief Start preemptive multithreading (if needed)
+ */
+extern void Proc_Start(void);
+/**
+ * \brief Called just before a thread is freed
+ */
+extern void Proc_ClearThread(tThread *Thread);
+/**
+ * \brief Called just before a process is freed
+ */
+extern void Proc_ClearProcess(tProcess *Process);
+/**
+ * \brief Get the ID of this CPU
+ * \return Zero based CPU ID
+ */
+extern int GetCPUNum(void);
+/**
+ * \brief Create a copy of the current process
+ * \param Flags Options for the clone
+ * \return ID of the new thread/process
+ */
+extern tTID Proc_Clone(Uint Flags);
+/**
+ * \brief Create a new kernel thread for the process
+ * \param Fnc Thread root function
+ * \param Ptr Argument to pass the root function
+ * \return ID of new thread
+ */
+extern tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr );
+/**
+ * \brief Start a user task
+ * \param Entrypoint User entrypoint
+ * \param Base Base of executable (argument for ld-acess)
+ * \param ArgC Number of arguments when the program was invoked
+ * \param ArgV Heap allocated arguments and environment (two NULL terminated lists)
+ * \param DataSize Size of the \a ArgV buffer in bytes
+ * \note This function should free \a ArgV
+ */
+extern void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) NORETURN;
+/**
+ * \brief Call the fault handler for a thread
+ * \param Thread Thread that is at fault :)
+ */
+extern void Proc_CallFaultHandler(tThread *Thread);
+/**
+ * \brief Dump the CPU state for a thread
+ */
+extern void Proc_DumpThreadCPUState(tThread *Thread);
+/**
+ * \brief Select a new task and run it, suspending this
+ */
+extern void Proc_Reschedule(void);
+
+/**
+ * \brief Clear the user's memory space back to the minimum required to run
+ */
+extern void MM_ClearUser(void);
+/**
+ * \brief Dump the address space to the debug channel
+ * \param Start First address
+ * \param End Last address
+ */
+extern void MM_DumpTables(tVAddr Start, tVAddr End);
+
+/**
+ * \brief Check if a buffer is valid (and all user if originally user)
+ * \param Addr Base address
+ * \param Size Size of the buffer in bytes
+ * \return Boolean valid (0: invalid, non-0: Valid)
+ */
+extern int MM_IsValidBuffer(tVAddr Addr, size_t Size);
+#endif
--- /dev/null
+/**
+ * Acess2 Kernel
+ * heap.h
+ * - Dynamic Memory Allocation exports
+ */
+
+#ifndef _HEAP_H_
+#define _HEAP_H_
+
+extern void *Heap_Allocate(const char *File, int Line, size_t Bytes);
+extern void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
+extern void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
+extern void Heap_Deallocate(void *Ptr);
+extern int Heap_IsHeapAddr(void *Ptr);
+extern void Heap_Validate(void);
+
+#define malloc(size) Heap_Allocate(_MODULE_NAME_"/"__FILE__, __LINE__, (size))
+#define calloc(num,size) Heap_AllocateZero(_MODULE_NAME_"/"__FILE__, __LINE__, (num)*(size))
+#define realloc(ptr,size) Heap_Reallocate(_MODULE_NAME_"/"__FILE__, __LINE__, (ptr), (size))
+#define free(ptr) Heap_Deallocate((ptr))
+#define IsHeap(ptr) Heap_IsHeapAddr((ptr))
+
+#define strdup(Str) _strdup(_MODULE_NAME_"/"__FILE__, __LINE__, (Str))
+
+#endif
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * heap_int.h
+ * - Internal Heap Header
+ */
+#ifndef _HEAP_INT_H
+#define _HEAP_INT_H
+
+typedef struct {
+ Uint Size;
+ int ValidSize;
+ const char *File;
+ int Line;
+ Uint Magic;
+ tTime AllocateTime;
+ char Data[];
+} tHeapHead;
+
+typedef struct {
+ Uint Magic;
+ tHeapHead *Head;
+ tHeapHead NextHead[]; // Array to make it act like an element, but have no size and refer to the next block
+} tHeapFoot;
+
+#endif
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * init.h
+ */
+#ifndef _INIT_H
+#define _INIT_H
+
+extern void Arch_LoadBootModules(void);
+extern void StartupPrint(const char *String);
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - IO Cache
+ *
+ * By thePowersGang (John Hodge)
+ */
+/**
+ * \file iocache.h
+ * \brief I/O Caching Helper Subsystem
+ *
+ * The IO Cache abstracts caching of disk sectors away from the device
+ * driver to reduce code duplication and allow a central location for
+ * disk cache management that can be flushed simply by the kernel, without
+ * having to ask each driver to do it indivitually.
+ */
+#ifndef _IOCHACHE_H_
+#define _IOCHACHE_H_
+
+// === TYPES ===
+/**
+ * \brief IO Cache Handle
+ */
+typedef struct sIOCache tIOCache;
+/**
+ * \brief Write Callback
+ *
+ * Called to write a sector back to the device
+ */
+typedef int (*tIOCache_WriteCallback)(Uint32 ID, Uint64 Sector, void *Buffer);
+
+// === CONSTANTS ===
+/**
+ * \brief I/O Cache handling modes
+ */
+enum eIOCache_Modess {
+ /**
+ * \brief Writeback
+ *
+ * Transparently writes data straight to the device when the cache
+ * is written to.
+ */
+ IOCACHE_WRITEBACK,
+ /**
+ * \brief Delay Write
+ *
+ * Only writes when instructed to (by ::IOCache_Flush) or when a
+ * cached sector is being reallocated.
+ */
+ IOCACHE_DELAYWRITE,
+ /**
+ * \brief Virtual - No Writes
+ *
+ * Changes to the cache contents are only reflected in memory,
+ * any calls to ::IOCache_Flush will silently return without doing
+ * anything and if a sector is reallocated, all changes will be lost
+ */
+ IOCACHE_VIRTUAL
+};
+
+// === FUNCTIONS ===
+/**
+ * \brief Creates a new IO Cache
+ * \param Write Function to call to write a sector to the device
+ * \param ID ID to pass to \a Write
+ * \param SectorSize Size of a cached sector
+ * \param CacheSize Maximum number of objects that can be in the cache at one time
+ */
+tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize );
+
+/**
+ * \brief Reads from a cached sector
+ * \param Cache Cache handle returned by ::IOCache_Create
+ * \param Sector Sector's ID number
+ * \param Buffer Destination for the data read
+ * \return 1 if the data was read, 0 if the sector is not cached, -1 on error
+ */
+ int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer );
+
+/**
+ * \brief Adds a sector to the cache
+ * \param Cache Cache handle returned by ::IOCache_Create
+ * \param Sector Sector's ID number
+ * \param Buffer Data to cache
+ * \return 1 on success, 0 if the sector is already cached, -1 on error
+ */
+ int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer );
+
+/**
+ * \brief Writes to a cached sector
+ * \param Cache Cache handle returned by ::IOCache_Create
+ * \param Sector Sector's ID number
+ * \param Buffer Data to write to the cache
+ * \return 1 if the data was read, 0 if the sector is not cached, -1 on error
+ *
+ * If the sector is in the cache, it is updated.
+ * Wether the Write callback is called depends on the selected caching
+ * behaviour.
+ */
+ int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer );
+
+/**
+ * \brief Flush altered sectors out to the device
+ * \param Cache Cache handle returned by ::IOCache_Create
+ *
+ * This will call the cache's write callback on each altered sector
+ * to flush the write cache.
+ */
+void IOCache_Flush( tIOCache *Cache );
+
+/**
+ * \brief Flushes the cache and then removes it
+ * \param Cache Cache handle returned by ::IOCache_Create
+ * \note After this is called \a Cache is no longer valid
+ *
+ * IOCache_Destroy writes all changed sectors to the device and then
+ * deallocates the cache for other systems to use.
+ */
+void IOCache_Destroy( tIOCache *Cache );
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge
+ *
+ * include/keyvalue.h
+ * - Key/Value pair parsing
+ */
+#ifndef _ACESS_KEYVALUE_H_
+#define _ACESS_KEYVALUE_H_
+
+typedef struct sKeyVal_ParseRules tKeyVal_ParseRules;
+typedef struct sKeyVal_int_Rule tKeyVal_int_Rule;
+typedef void (*tKeyVal_UnkCb)(char *String);
+typedef void (*tKeyVal_KeyCb)(const char *Key, char *Value);
+
+/**
+ * \brief Handling rule for a key
+ */
+struct sKeyVal_int_Rule
+{
+ const char *Key;
+ const char *Type; // Acess printf format, with 'F' being a tKeyVal_KeyCb
+ void *Data;
+};
+
+struct sKeyVal_ParseRules
+{
+ /**
+ * \brief Function to call when no match is found
+ */
+ tKeyVal_UnkCb Unknown;
+ tKeyVal_int_Rule Rules[];
+};
+
+/**
+ * \brief Parse a NULL terminated list of strings as Key/Value pairs
+ * \param Rules Parsing rules
+ * \param Strings Input string list
+ */
+extern int KeyVal_ParseNull(tKeyVal_ParseRules *Rules, char **Strings);
+
+#endif
+
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * mboot.h
+ */
+#ifndef _MBOOT_H
+#define _MBOOT_H
+
+#define MULTIBOOT_MAGIC 0x2BADB002
+
+// === TYPES ===
+typedef struct {
+ Uint32 Flags;
+ Uint32 LowMem;
+ Uint32 HighMem;
+ Uint32 BootDevice;
+ Uint32 CommandLine;
+ Uint32 ModuleCount;
+ Uint32 Modules;
+ Uint32 SymbolInfo[4]; // #32 UNUSED
+ Uint32 MMapLength;
+ Uint32 MMapAddr; // #40
+} tMBoot_Info;
+
+typedef struct {
+ Uint32 Start;
+ Uint32 End;
+ Uint32 String;
+ Uint32 Resvd;
+} tMBoot_Module;
+
+typedef struct {
+ Uint32 Size; // (May be at offset -4)
+ Uint64 Base;
+ Uint64 Length;
+ Uint32 Type; //1:RAM,Else Reserved
+} __attribute__ ((packed)) tMBoot_MMapEnt;
+
+#endif
--- /dev/null
+/*
+ * AcessOS 2
+ * - Module Loader
+ */
+/**
+ * \file modules.h
+ * \brief Module Handling and Loader Definitions
+ * \author John Hodge (thePowersGang)
+ *
+ * This file serves two pourposes. First it defines the format for native
+ * Acess2 modules and the functions to create them.
+ * Second, it defines the structure and register function for new module
+ * loaders, allowing Acess to understand many different module / driver
+ * formats.
+ *
+ * Modules are defined by including this file in the module's main source
+ * file and using the ::MODULE_DEFINE macro to create the module header.
+ *
+ * To register a new module loader with the kernel, the loader module must
+ * create and populate an instance of ::tModuleLoader then pass it to
+ * ::Module_RegisterLoader
+ */
+#ifndef _MODULE_H
+#define _MODULE_H
+
+/**
+ * \brief Module header magic value
+ */
+#define MODULE_MAGIC ('A'|('M'<<8)|('D'<<16)|('\2'<<24))
+
+/**
+ * \def MODULE_ARCH_ID
+ * \brief Architecture ID
+ */
+// IA32 - Architecture 1
+#if ARCHDIR == x86
+# define MODULE_ARCH_ID 1
+// IA64 - Architecture 2
+#elif ARCHDIR == x86_64
+# define MODULE_ARCH_ID 2
+#else
+# error "Unknown architecture when determining MODULE_ARCH_ID ('" #ARCHDIR "')"
+#endif
+
+/**
+ * \brief Define a module
+ * \param _flags Module Flags
+ * \param _ver Module Version
+ * \param _ident Unique Module Name
+ * \param _entry Module initialiser / entrypoint
+ * \param _deinit Module cleanup / unloader
+ * \param _deps NULL terminated list of this's module's dependencies
+ * Contains the identifiers of the required modules.
+ */
+#define MODULE_DEFINE(_flags,_ver,_ident,_entry,_deinit,_deps...) \
+ const char *EXPAND_CONCAT(_DriverDeps_,_ident)[]={_deps};\
+ tModule __attribute__ ((section ("KMODULES"),unused))\
+ EXPAND_CONCAT(_DriverInfo_,_ident)=\
+ {MODULE_MAGIC,MODULE_ARCH_ID,_flags,_ver,NULL,EXPAND_STR(_ident),\
+ _entry,_deinit,EXPAND_CONCAT(_DriverDeps_,_ident)}
+
+/**
+ * \brief Module header
+ * \note There is no reason for a module to touch this structure beyond
+ * using ::MODULE_DEFINE to create it.
+ */
+typedef struct sModule
+{
+ Uint32 Magic; //!< Identifying magic value (See ::MODULE_MAGIC)
+ Uint8 Arch; //!< Achitecture ID (See ::MODULE_ARCH_ID)
+ Uint8 Flags; //!< Module Flags
+ Uint16 Version; //!< Module Version in Major.Minor 8.8 form
+ struct sModule *Next; //!< Next module in list (not to be touched by the driver)
+ const char *Name; //!< Module Name/Identifier
+ int (*Init)(char **Arguments); //!< Module initialiser / entrypoint
+ void (*Deinit)(void); //!< Cleanup Function
+ const char **Dependencies; //!< NULL terminated list of dependencies
+} PACKED tModule;
+
+/**
+ * \brief Return values for tModule.Init
+ */
+enum eModuleErrors
+{
+ MODULE_ERR_OK, //!< No Error
+ MODULE_ERR_MISC, //!< Misc Error
+ MODULE_ERR_NOTNEEDED, //!< Module not needed
+ MODULE_ERR_MALLOC, //!< Error with malloc/realloc/calloc
+
+ MODULE_ERR_BADMODULE, //!< Bad module (only used by loader)
+ MODULE_ERR_MAX //!< Maximum defined error code
+};
+
+/**
+ * \brief Module Loader definition
+ *
+ * Allows a module to extend the loader to recognise other module types
+ * E.g. EDI, UDI, Windows, Linux, ...
+ */
+typedef struct sModuleLoader
+{
+ struct sModuleLoader *Next; //!< Kernel Only - Next loader in list
+ char *Name; //!< Friendly name for the loader
+ int (*Detector)(void *Base); //!< Simple detector function
+ int (*Loader)(void *Base); //!< Initialises the module
+ int (*Unloader)(void *Base); //!< Calls module's cleanup
+} PACKED tModuleLoader;
+
+/**
+ * \brief Registers a tModuleLoader with the kernel
+ * \param Loader Pointer to loader structure (must be persistent)
+ * \return Boolean Success
+ */
+extern int Module_RegisterLoader(tModuleLoader *Loader);
+
+/**
+ * \brief Initialises (if needed) a named module
+ * \param Name Module name to initialise
+ * \return -1 on not existing, 0 if the module initialised (or if it was already initialised)
+ */
+extern int Module_EnsureLoaded(const char *Name);
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * mutex.h
+ * - Mutual Exclusion syncronisation primitive
+ */
+#ifndef _MUTEX_H
+#define _MUTEX_H
+
+#include <acess.h>
+
+typedef struct sMutex tMutex;
+
+struct sMutex
+{
+ tShortSpinlock Protector; //!< Protector for the lock strucure
+ const char *Name; //!< Human-readable name
+ struct sThread *volatile Owner; //!< Owner of the lock (set upon getting the lock)
+ struct sThread *Waiting; //!< Waiting threads
+ struct sThread *LastWaiting; //!< Waiting threads
+};
+
+/**
+ * \brief Acquire a heavy mutex
+ * \param Mutex Mutex to acquire
+ * \return zero on success, -1 if terminated
+ *
+ * This type of mutex checks if the mutex is avaliable, and acquires it
+ * if it is. Otherwise, the current thread is added to the mutex's wait
+ * queue and the thread suspends. When the holder of the mutex completes,
+ * the oldest thread (top thread) on the queue is given the lock and
+ * restarted.
+ */
+extern int Mutex_Acquire(tMutex *Mutex);
+
+/**
+ * \brief Release a held mutex
+ * \param Mutex Mutex to release
+ * \note Releasing a non-held mutex has no effect
+ */
+extern void Mutex_Release(tMutex *Mutex);
+
+/**
+ * \brief Is this mutex locked?
+ * \param Mutex Mutex pointer
+ */
+extern int Mutex_IsLocked(tMutex *Mutex);
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * semaphore.h
+ * - Semaphore syncronisation primitive
+ */
+#ifndef _SEMAPHORE_H
+#define _SEMAPHORE_H
+
+#include <acess.h>
+
+/**
+ * \brief Semaphore typedef
+ */
+typedef struct sSemaphore tSemaphore;
+
+/**
+ * \brief Semaphore structure
+ */
+struct sSemaphore {
+ tShortSpinlock Protector; //!< Protector for the lock strucure
+ const char *ModName; //!< Human-readable module name
+ const char *Name; //!< Human-readable name
+ volatile int Value; //!< Current value
+ volatile int MaxValue; //!< Maximum value (signal will wait if it will go over this)
+
+ struct sThread *Waiting; //!< Waiting threads
+ struct sThread *LastWaiting; //!< Waiting threads
+ struct sThread *Signaling; //!< Waiting threads (from Semaphore_Signal)
+ struct sThread *LastSignaling; //!< Last waiting thread (from Semaphore_Signal)
+};
+
+/**
+ * \brief Initialise the semaphore
+ * \param Sem Semaphore structure to initialsie
+ * \param InitValue Initial value of the semaphore
+ * \param MaxValue Maximum value for the semaphore
+ * \param Module Module name
+ * \param Name Symbolic name
+ * \note Not always needed, as initialising to 0 is valid, but it is preferred
+ * if all semaphores have \a Name set
+ *
+ * \note \a Module and \a Name must be avaliable for as long as the semaphore is used
+ */
+extern void Semaphore_Init(tSemaphore *Sem, int InitValue, int MaxValue, const char *Module, const char *Name);
+/**
+ * \brief Acquire items from the semaphore
+ * \param Sem Semaphore structure to use
+ * \param MaxToTake Maximum number of items to take off the list (if zero, as much as possible is taken)
+ * \return Number of items fetched
+ * \retval 0 Semaphore interrupted (signal/message)
+ * \retval -1 Unspecified error
+ */
+extern int Semaphore_Wait(tSemaphore *Sem, int MaxToTake);
+/**
+ * \brief Add an "item" to the semaphore
+ * \param Sem Semaphore to use
+ * \param AmmountToAdd Number of items to add
+ * \return Actual number of items added
+ * \retval 0 Semaphore interrupted
+ * \retval -1 Unspecified error
+ */
+extern int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd);
+/**
+ * \brief Get the number of items waiting in a semaphore
+ * \param Sem Semaphore to use
+ * \return Number of waiting items
+ */
+extern int Semaphore_GetValue(tSemaphore *Sem);
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * Signal List
+ */
+#ifndef _SIGNAL_H_
+#define _SIGNAL_H_
+
+enum eSignals {
+ SIGKILL,
+ SIGSTOP,
+ SIGCONT,
+ SIGCHLD,
+ NSIG
+};
+
+#endif
--- /dev/null
+/*
+ * Acess2
+ * syscalls.h
+ * - System Call List
+ *
+ * NOTE: Generated from Kernel/syscalls.lst
+ */
+#ifndef _SYSCALLS_H
+#define _SYSCALLS_H
+
+#define SYS_EXIT 0 // Kill this thread
+#define SYS_CLONE 1 // Create a new thread
+#define SYS_KILL 2 // Send a signal
+#define SYS_SETFAULTHANDLER 3 // Set signal Handler
+#define SYS_YIELD 4 // Yield remainder of timestamp
+#define SYS_SLEEP 5 // Sleep until messaged or signaled
+#define SYS_WAITEVENT 6 // Wait for an event
+#define SYS_WAITTID 7 // Wait for a thread to do something
+#define SYS_SETNAME 8 // Sets the name of the current thread
+#define SYS_GETNAME 9 // Gets the name of a thread
+#define SYS_GETTID 10 // Get current thread ID
+#define SYS_GETPID 11 // Get current thread group ID
+#define SYS_SETPRI 12 // Set process priority
+#define SYS_SENDMSG 13 // Send an IPC message
+#define SYS_GETMSG 14 // Recieve an IPC message
+#define SYS_GETTIME 15 // Get the current timestamp
+#define SYS_SPAWN 16 // Spawn a new process
+#define SYS_EXECVE 17 // Replace the current process
+#define SYS_LOADBIN 18 // Load a binary into the current address space
+#define SYS_UNLOADBIN 19 // Unload a loaded binary
+#define SYS_LOADMOD 20 // Load a module into the kernel
+#define SYS_GETPHYS 32 // Get the physical address of a page
+#define SYS_MAP 33 // Map a physical address
+#define SYS_ALLOCATE 34 // Allocate a page
+#define SYS_UNMAP 35 // Unmap a page
+#define SYS_PREALLOC 36 // Preallocate a page
+#define SYS_SETFLAGS 37 // Set a page's flags
+#define SYS_SHAREWITH 38 // Share a page with another thread
+#define SYS_GETUID 39 // Get current User ID
+#define SYS_GETGID 40 // Get current Group ID
+#define SYS_SETUID 41 // Set current user ID
+#define SYS_SETGID 42 // Set current Group ID
+#define SYS_OPEN 64 // Open a file
+#define SYS_REOPEN 65 // Close a file and reuse its handle
+#define SYS_CLOSE 66 // Close a file
+#define SYS_READ 67 // Read from an open file
+#define SYS_WRITE 68 // Write to an open file
+#define SYS_IOCTL 69 // Perform an IOCtl Call
+#define SYS_SEEK 70 // Seek to a new position in the file
+#define SYS_READDIR 71 // Read from an open directory
+#define SYS_OPENCHILD 72 // Open a child entry in a directory
+#define SYS_GETACL 73 // Get an ACL Value
+#define SYS_SETACL 74 // Set an ACL Value
+#define SYS_FINFO 75 // Get file information
+#define SYS_MKDIR 76 // Create a new directory
+#define SYS_LINK 77 // Create a new link to a file
+#define SYS_SYMLINK 78 // Create a symbolic link
+#define SYS_UNLINK 79 // Delete a file
+#define SYS_TELL 80 // Return the current file position
+#define SYS_CHDIR 81 // Change current directory
+#define SYS_GETCWD 82 // Get current directory
+#define SYS_MOUNT 83 // Mount a filesystem
+#define SYS_SELECT 84 // Wait for file handles
+
+#define NUM_SYSCALLS 85
+#define SYS_DEBUG 0x100
+
+#ifndef __ASSEMBLER__
+static const char *cSYSCALL_NAMES[] = {
+ "SYS_EXIT",
+ "SYS_CLONE",
+ "SYS_KILL",
+ "SYS_SETFAULTHANDLER",
+ "SYS_YIELD",
+ "SYS_SLEEP",
+ "SYS_WAITEVENT",
+ "SYS_WAITTID",
+ "SYS_SETNAME",
+ "SYS_GETNAME",
+ "SYS_GETTID",
+ "SYS_GETPID",
+ "SYS_SETPRI",
+ "SYS_SENDMSG",
+ "SYS_GETMSG",
+ "SYS_GETTIME",
+ "SYS_SPAWN",
+ "SYS_EXECVE",
+ "SYS_LOADBIN",
+ "SYS_UNLOADBIN",
+ "SYS_LOADMOD",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "SYS_GETPHYS",
+ "SYS_MAP",
+ "SYS_ALLOCATE",
+ "SYS_UNMAP",
+ "SYS_PREALLOC",
+ "SYS_SETFLAGS",
+ "SYS_SHAREWITH",
+ "SYS_GETUID",
+ "SYS_GETGID",
+ "SYS_SETUID",
+ "SYS_SETGID",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "SYS_OPEN",
+ "SYS_REOPEN",
+ "SYS_CLOSE",
+ "SYS_READ",
+ "SYS_WRITE",
+ "SYS_IOCTL",
+ "SYS_SEEK",
+ "SYS_READDIR",
+ "SYS_OPENCHILD",
+ "SYS_GETACL",
+ "SYS_SETACL",
+ "SYS_FINFO",
+ "SYS_MKDIR",
+ "SYS_LINK",
+ "SYS_SYMLINK",
+ "SYS_UNLINK",
+ "SYS_TELL",
+ "SYS_CHDIR",
+ "SYS_GETCWD",
+ "SYS_MOUNT",
+ "SYS_SELECT",
+
+ ""
+};
+#endif
+
+#endif
--- /dev/null
+; Acess2
+; System Calls List
+;
+
+%define SYS_EXIT 0 ;Kill this thread
+%define SYS_CLONE 1 ;Create a new thread
+%define SYS_KILL 2 ;Send a signal
+%define SYS_SETFAULTHANDLER 3 ;Set signal Handler
+%define SYS_YIELD 4 ;Yield remainder of timestamp
+%define SYS_SLEEP 5 ;Sleep until messaged or signaled
+%define SYS_WAITEVENT 6 ;Wait for an event
+%define SYS_WAITTID 7 ;Wait for a thread to do something
+%define SYS_SETNAME 8 ;Sets the name of the current thread
+%define SYS_GETNAME 9 ;Gets the name of a thread
+%define SYS_GETTID 10 ;Get current thread ID
+%define SYS_GETPID 11 ;Get current thread group ID
+%define SYS_SETPRI 12 ;Set process priority
+%define SYS_SENDMSG 13 ;Send an IPC message
+%define SYS_GETMSG 14 ;Recieve an IPC message
+%define SYS_GETTIME 15 ;Get the current timestamp
+%define SYS_SPAWN 16 ;Spawn a new process
+%define SYS_EXECVE 17 ;Replace the current process
+%define SYS_LOADBIN 18 ;Load a binary into the current address space
+%define SYS_UNLOADBIN 19 ;Unload a loaded binary
+%define SYS_LOADMOD 20 ;Load a module into the kernel
+%define SYS_GETPHYS 32 ;Get the physical address of a page
+%define SYS_MAP 33 ;Map a physical address
+%define SYS_ALLOCATE 34 ;Allocate a page
+%define SYS_UNMAP 35 ;Unmap a page
+%define SYS_PREALLOC 36 ;Preallocate a page
+%define SYS_SETFLAGS 37 ;Set a page's flags
+%define SYS_SHAREWITH 38 ;Share a page with another thread
+%define SYS_GETUID 39 ;Get current User ID
+%define SYS_GETGID 40 ;Get current Group ID
+%define SYS_SETUID 41 ;Set current user ID
+%define SYS_SETGID 42 ;Set current Group ID
+%define SYS_OPEN 64 ;Open a file
+%define SYS_REOPEN 65 ;Close a file and reuse its handle
+%define SYS_CLOSE 66 ;Close a file
+%define SYS_READ 67 ;Read from an open file
+%define SYS_WRITE 68 ;Write to an open file
+%define SYS_IOCTL 69 ;Perform an IOCtl Call
+%define SYS_SEEK 70 ;Seek to a new position in the file
+%define SYS_READDIR 71 ;Read from an open directory
+%define SYS_OPENCHILD 72 ;Open a child entry in a directory
+%define SYS_GETACL 73 ;Get an ACL Value
+%define SYS_SETACL 74 ;Set an ACL Value
+%define SYS_FINFO 75 ;Get file information
+%define SYS_MKDIR 76 ;Create a new directory
+%define SYS_LINK 77 ;Create a new link to a file
+%define SYS_SYMLINK 78 ;Create a symbolic link
+%define SYS_UNLINK 79 ;Delete a file
+%define SYS_TELL 80 ;Return the current file position
+%define SYS_CHDIR 81 ;Change current directory
+%define SYS_GETCWD 82 ;Get current directory
+%define SYS_MOUNT 83 ;Mount a filesystem
+%define SYS_SELECT 84 ;Wait for file handles
--- /dev/null
+/*
+ * Acess2 Kernel
+ */
+#ifndef _THREADS_H_
+#define _THREADS_H_
+
+#include <arch.h>
+#include <signal.h>
+//#include <proc.h>
+
+enum eFaultNumbers
+{
+ FAULT_MISC,
+ FAULT_PAGE,
+ FAULT_ACCESS,
+ FAULT_DIV0,
+ FAULT_OPCODE,
+ FAULT_FLOAT
+};
+
+#define GETMSG_IGNORE ((void*)-1)
+
+typedef struct sThread tThread;
+
+// === FUNCTIONS ===
+extern void Threads_SetFaultHandler(Uint Handler);
+
+extern int Threads_SetUID(tUID ID);
+extern int Threads_SetGID(tUID ID);
+extern tTID Threads_WaitTID(int TID, int *Status);
+
+
+extern int *Threads_GetMaxFD(void);
+extern char **Threads_GetCWD(void);
+extern char **Threads_GetChroot(void);
+
+extern int Proc_SendMessage(Uint Dest, int Length, void *Data);
+extern int Proc_GetMessage(Uint *Source, void *Buffer);
+
+#endif
--- /dev/null
+/*
+ * Internal Threading header
+ * - Only for use by stuff that needs access to the thread type.
+ */
+#ifndef _THREADS_INT_H_
+#define _THREADS_INT_H_
+
+#include <threads.h>
+#include <proc.h>
+
+
+typedef struct sProcess tProcess;
+
+/**
+ * \brief IPC Message
+ */
+typedef struct sMessage
+{
+ struct sMessage *Next; //!< Next message in thread's inbox
+ tTID Source; //!< Source thread ID
+ Uint Length; //!< Length of message data in bytes
+ Uint8 Data[]; //!< Message data
+} tMsg;
+
+/**
+ * \brief Process state
+ */
+struct sProcess
+{
+ tPID PID;
+ int nThreads;
+
+ tUID UID; //!< User ID
+ tGID GID; //!< User and Group
+ tMemoryState MemState;
+
+ int MaxFD;
+ char *CurrentWorkingDir;
+ char *RootDir;
+};
+
+/**
+ * \brief Core threading structure
+ *
+ */
+struct sThread
+{
+ // --- threads.c's
+ /**
+ * \brief Next thread in current list
+ * \note Required to be first for linked list hacks to work
+ */
+ struct sThread *Next;
+ struct sThread *GlobalNext; //!< Next thread in global list
+ struct sThread *GlobalPrev; //!< Previous thread in global list
+ tShortSpinlock IsLocked; //!< Thread's spinlock
+ volatile int Status; //!< Thread Status
+ void *WaitPointer; //!< What (Mutex/Thread/other) is the thread waiting on
+ int RetStatus; //!< Return Status
+
+ tTID TID; //!< Thread ID
+ struct sProcess *Process; //!< Thread Group / Process
+ struct sThread *Parent; //!< Parent Thread
+ char *ThreadName; //!< Name of thread
+
+ // --- arch/proc.c's responsibility
+ //! Kernel Stack Base
+ tVAddr KernelStack;
+
+ //! State on task switch
+ tTaskState SavedState;
+
+ // --- threads.c's
+ int CurFaultNum; //!< Current fault number, 0: none
+ tVAddr FaultHandler; //!< Fault Handler
+
+ tMsg * volatile Messages; //!< Message Queue
+ tMsg *LastMessage; //!< Last Message (speeds up insertion)
+
+ int Quantum, Remaining; //!< Quantum Size and remaining timesteps
+ int Priority; //!< Priority - 0: Realtime, higher means less time
+
+ int _errno;
+
+ volatile int CurCPU;
+
+ bool bInstrTrace;
+
+ // --- event.c
+ Uint32 EventState;
+};
+
+
+enum {
+ THREAD_STAT_NULL, // Invalid process
+ THREAD_STAT_ACTIVE, // Running and schedulable process
+ THREAD_STAT_SLEEPING, // Message Sleep
+ THREAD_STAT_MUTEXSLEEP, // Mutex Sleep
+ THREAD_STAT_SEMAPHORESLEEP, // Semaphore Sleep
+ THREAD_STAT_QUEUESLEEP, // Queue
+ THREAD_STAT_EVENTSLEEP, // Event sleep
+ THREAD_STAT_WAITING, // ??? (Waiting for a thread)
+ THREAD_STAT_PREINIT, // Being created
+ THREAD_STAT_ZOMBIE, // Died/Killed, but parent not informed
+ THREAD_STAT_DEAD, // Awaiting burial (free)
+ THREAD_STAT_BURIED // If it's still on the list here, something's wrong
+};
+static const char * const casTHREAD_STAT[] = {
+ "THREAD_STAT_NULL",
+ "THREAD_STAT_ACTIVE",
+ "THREAD_STAT_SLEEPING",
+ "THREAD_STAT_MUTEXSLEEP",
+ "THREAD_STAT_SEMAPHORESLEEP",
+ "THREAD_STAT_QUEUESLEEP",
+ "THREAD_STAT_EVENTSLEEP",
+ "THREAD_STAT_WAITING",
+ "THREAD_STAT_PREINIT",
+ "THREAD_STAT_ZOMBIE",
+ "THREAD_STAT_DEAD",
+ "THREAD_STAT_BURIED"
+};
+
+// === GLOBALS ===
+extern BOOL gaThreads_NoTaskSwitch[MAX_CPUS];
+extern tShortSpinlock glThreadListLock;
+
+// === FUNCTIONS ===
+extern tThread *Proc_GetCurThread(void);
+
+extern tThread *Threads_GetThread(Uint TID);
+extern void Threads_SetPriority(tThread *Thread, int Pri);
+extern int Threads_Wake(tThread *Thread);
+extern void Threads_Kill(tThread *Thread, int Status);
+extern void Threads_AddActive(tThread *Thread);
+extern tThread *Threads_RemActive(void);
+extern void Threads_Delete(tThread *Thread);
+extern tThread *Threads_GetNextToRun(int CPU, tThread *Last);
+
+extern tThread *Threads_CloneTCB(Uint Flags);
+extern tThread *Threads_CloneThreadZero(void);
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * timers.h
+ * - Kernel timers
+ */
+#ifndef _KERNEL_TIMERS_H_
+#define _KERNEL_TIMERS_H_
+/**
+ * \file timers.h
+ * \brief Kernel timers
+ */
+
+typedef struct sTimer tTimer;
+
+/**
+ * \brief Timer callback function
+ */
+typedef void (tTimerCallback)(void *);
+
+/**
+ * \brief Creates a one-shot timer
+ * \param Delta Period of the timer
+ * \param Callback Function to call each time
+ * \param Argument Argument to pass to the callback
+ */
+extern tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument);
+/**
+ * \brief Removed an active timer
+ */
+extern void Time_RemoveTimer(tTimer *Timer);
+/**
+ * \brief Wait for a period of milliseconds
+ */
+extern void Time_Delay(int Delay);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 Core
+ *
+ * include/tpl_mm_phys_bitmap.h
+ * Physical Memory Manager Template
+ */
+/*
+ * Bitmap Edition
+ *
+ * Uses 4.125+PtrSize bytes per page
+ */
+
+#define MM_PAGE_REFCOUNTS MM_PMM_BASE
+#define MM_PAGE_NODES (MM_PMM_BASE+(MM_MAXPHYSPAGE*sizeof(Uint32)))
+#define MM_PAGE_BITMAP (MM_PAGE_NODES+(MM_MAXPHYSPAGE*sizeof(void*)))
+
+#define PAGE_BITMAP_FREE(__pg) (gaPageBitmaps[(__pg)/32] & (1LL << ((__pg)&31)))
+#define PAGE_BITMAP_SETFREE(__pg) do{gaPageBitmaps[(__pg)/32] |= (1LL << ((__pg)&31));}while(0)
+#define PAGE_BITMAP_SETUSED(__pg) do{gaPageBitmaps[(__pg)/32] &= ~(1LL << ((__pg)&31));}while(0)
+
+// === PROTOTYPES ===
+//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
+//tPAddr MM_AllocPhysRange(int Num, int Bits);
+//tPAddr MM_AllocPhys(void);
+//void MM_RefPhys(tPAddr PAddr);
+//void MM_DerefPhys(tPAddr PAddr);
+ int MM_int_GetRangeID( tPAddr Addr );
+ int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length );
+void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap);
+
+// === GLOBALS ===
+tMutex glPhysicalPages;
+void **gapPageNodes = (void*)MM_PAGE_NODES; //!< Associated VFS Node for each page
+Uint32 *gaiPageReferences = (void*)MM_PAGE_REFCOUNTS; // Reference Counts
+Uint32 *gaPageBitmaps = (void*)MM_PAGE_BITMAP; // Used bitmap (1 == avail)
+Uint64 giMaxPhysPage = 0; // Maximum Physical page
+ int gbPMM_Init = 0;
+ int giPhysFirstFree;
+ int giPhysLastFree;
+ int giPhysNumFree;
+
+// === CODE ===
+/**
+ * \brief Initialise the physical memory manager with a passed memory map
+ */
+void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap)
+{
+ int mapIndex = 0;
+ tPAddr rangeStart, rangeLen;
+
+ if( MM_PAGE_BITMAP + (MM_MAXPHYSPAGE/8) > MM_PMM_END ) {
+ Log_KernelPanic("PMM", "Config Error, PMM cannot fit data in allocated range");
+ }
+
+ giMaxPhysPage = MaxRAMPage;
+
+// for( i = 0; i < MM_RANGE_MAX; i ++ )
+// gaiPhysRangeFirstFree[i] = -1;
+ giPhysFirstFree = -1;
+
+ while( MM_int_GetMapEntry(MemoryMap, mapIndex++, &rangeStart, &rangeLen) )
+ {
+ tVAddr bitmap_page;
+
+ LOG("Range %i, %P to %P", mapIndex-1, rangeStart, rangeLen);
+ rangeStart /= PAGE_SIZE;
+ rangeLen /= PAGE_SIZE;
+
+ giPhysNumFree += rangeLen;
+
+ LOG("rangeStart = 0x%x, rangeLen = 0x%x", rangeStart, rangeLen);
+
+ if( giPhysFirstFree == -1 || giPhysFirstFree > rangeStart )
+ giPhysFirstFree = rangeStart;
+
+ if( giPhysLastFree < rangeStart + rangeLen )
+ giPhysLastFree = rangeStart + rangeLen;
+
+ LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree);
+
+ bitmap_page = (tVAddr)&gaPageBitmaps[rangeStart/32];
+ bitmap_page &= ~(PAGE_SIZE-1);
+
+ // Only need to allocate bitmaps
+ if( !MM_GetPhysAddr( bitmap_page ) ) {
+ if( !MM_Allocate( bitmap_page ) ) {
+ Log_KernelPanic("PMM", "Out of memory during init, this is bad");
+ return ;
+ }
+// memset( (void*)bitmap_page, 0, (rangeStart/8) & ~(PAGE_SIZE-1) );
+ memset( (void*)bitmap_page, 0, PAGE_SIZE );
+ }
+
+ // Align to 32 pages
+ for( ; (rangeStart & 31) && rangeLen > 0; rangeStart++, rangeLen-- ) {
+ gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31);
+ LOG("gaPageBitmaps[%i] = 0x%x", rangeStart/32, gaPageBitmaps[rangeStart/32]);
+ }
+ // Mark blocks of 32 as avail
+ for( ; rangeLen > 31; rangeStart += 32, rangeLen -= 32 ) {
+ gaPageBitmaps[rangeStart / 32] = -1;
+ }
+ // Mark the tail
+ for( ; rangeLen > 0; rangeStart ++, rangeLen -- ) {
+ gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31);
+ }
+ }
+
+ gbPMM_Init = 1;
+
+ LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree);
+ LEAVE('-');
+}
+
+/**
+ * \brief Allocate a contiguous range of physical pages with a maximum
+ * bit size of \a MaxBits
+ * \param Pages Number of pages to allocate
+ * \param MaxBits Maximum size of the physical address
+ * \note If \a MaxBits is <= 0, any sized address is used (with preference
+ * to higher addresses)
+ */
+tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
+{
+ tPAddr addr, ret;
+ int nFree = 0, i;
+
+ ENTER("iPages iBits", Pages, MaxBits);
+
+ Mutex_Acquire(&glPhysicalPages);
+
+ // Check if there is enough in the range
+ if(giPhysNumFree >= Pages)
+ {
+ LOG("{0x%x -> 0x%x}", giPhysFirstFree, giPhysLastFree);
+ // Do a cheap scan, scanning upwards from the first free page in
+ // the range
+ nFree = 0;
+ addr = giPhysFirstFree;
+ while( addr <= giPhysLastFree )
+ {
+ #if USE_SUPER_BITMAP
+ // Check the super bitmap
+ if( gaSuperBitmap[addr / (32*32)] == 0 )
+ {
+ LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr += (32*32);
+ addr &= ~(32*32-1); // (1LL << 6+6) - 1
+ continue;
+ }
+ #endif
+ LOG("gaPageBitmaps[%i] = 0x%x", addr/32, gaPageBitmaps[addr/32]);
+ // Check page block (32 pages)
+ if( gaPageBitmaps[addr / 32] == 0) {
+ LOG("nFree = %i = 0 (block) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr += 32;
+ addr &= ~31;
+ continue;
+ }
+ // Check individual page
+ if( !(gaPageBitmaps[addr / 32] & (1LL << (addr & 31))) )
+ {
+ LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr ++;
+ continue;
+ }
+ nFree ++;
+ addr ++;
+ LOG("nFree(%i) == %i (1x%x)", nFree, Pages, addr);
+ if(nFree == Pages)
+ break;
+ }
+ LOG("nFree = %i", nFree);
+ // If we don't find a contiguous block, nFree will not be equal
+ // to Num, so we set it to zero and do the expensive lookup.
+ if(nFree != Pages) nFree = 0;
+ }
+
+ if( !nFree )
+ {
+#if 0
+ // Oops. ok, let's do an expensive check (scan down the list
+ // until a free range is found)
+ nFree = 1;
+ addr = gaiPhysRangeLastFree[ rangeID ];
+ // TODO
+#endif
+ Mutex_Release(&glPhysicalPages);
+ // TODO: Page out
+ // ATM. Just Warning
+ Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
+ Log_Warning("PMM",
+ "Out of memory (unable to fulfil request for %i pages)",
+ Pages
+ );
+ LEAVE('i', 0);
+ return 0;
+ }
+ LOG("nFree = %i, addr = 0x%08x", nFree, (addr-Pages) << 12);
+
+ // Mark pages as allocated
+ addr -= Pages;
+ for( i = 0; i < Pages; i++, addr++ )
+ {
+ // Mark as used
+ PAGE_BITMAP_SETUSED(addr);
+ // Maintain first possible free
+ giPhysNumFree --;
+ if(addr == giPhysFirstFree)
+ giPhysFirstFree += 1;
+
+ LOG("if( MM_GetPhysAddr( %p ) )", &gaiPageReferences[addr]);
+ // Mark as referenced if the reference count page is valid
+ if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) ) {
+ gaiPageReferences[addr] = 1;
+ }
+ }
+ ret = addr - Pages; // Save the return address
+ LOG("ret = %x", ret);
+
+ #if TRACE_ALLOCS
+ LogF("MM_AllocPhysRange: %P (%i pages)\n", ret, Pages);
+ if(Pages > 1) {
+ LogF(" also");
+ for(i = 1; i < Pages; i++)
+ LogF(" %P", ret+i);
+ LogF("\n");
+ }
+ #endif
+
+ #if USE_SUPER_BITMAP
+ // Update super bitmap
+ Pages += addr & (32-1);
+ addr &= ~(32-1);
+ Pages = (Pages + (32-1)) & ~(32-1);
+ for( i = 0; i < Pages/32; i++ )
+ {
+ if( gaPageBitmaps[ addr / 32 ] + 1 == 0 )
+ gaSuperBitmap[addr / (32*32)] |= 1LL << ((addr / 32) & 31);
+ }
+ #endif
+
+ Mutex_Release(&glPhysicalPages);
+ LEAVE('x', ret << 12);
+ return ret << 12;
+}
+
+/**
+ * \brief Allocate a single physical page, with no preference as to address size.
+ */
+tPAddr MM_AllocPhys(void)
+{
+ int i;
+
+ if( !gbPMM_Init )
+ {
+ // Hack to allow allocation during setup
+ for(i = 0; i < NUM_STATIC_ALLOC; i++) {
+ if( gaiStaticAllocPages[i] ) {
+ tPAddr ret = gaiStaticAllocPages[i];
+ gaiStaticAllocPages[i] = 0;
+ Log("MM_AllocPhys: Return %x, static alloc %i", ret, i);
+ return ret;
+ }
+ }
+
+ tPAddr ret = 0;
+ for( ret = 0; ret < giMaxPhysPage; ret ++ )
+ {
+ if( !MM_GetPhysAddr( (tVAddr)&gaPageBitmaps[ret/32] ) ) {
+ ret += PAGE_SIZE*8;
+ continue ;
+ }
+ if( gaPageBitmaps[ret/32] == 0 ) {
+ ret += 32-1;
+ continue ;
+ }
+ if( gaPageBitmaps[ret/32] & (1 << (ret&31)) ) {
+ gaPageBitmaps[ret/32] &= ~(1 << (ret&31));
+ return ret * PAGE_SIZE;
+ }
+ }
+ Log_Error("PMM", "MM_AllocPhys failed duing init");
+ return 0;
+ }
+ #if TRACE_ALLOCS
+ Log("AllocPhys by %p", __builtin_return_address(0));
+ #endif
+
+ return MM_AllocPhysRange(1, -1);
+}
+
+/**
+ * \brief Reference a physical page
+ */
+void MM_RefPhys(tPAddr PAddr)
+{
+ tPAddr page = PAddr / PAGE_SIZE;
+ tVAddr refpage = (tVAddr)&gaiPageReferences[page] & ~(PAGE_SIZE-1);
+
+ if( page >= giMaxPhysPage ) return ;
+
+ if( PAGE_BITMAP_FREE(page) )
+ {
+ // Allocate
+ PAGE_BITMAP_SETUSED(page);
+ #if USE_SUPER_BITMAP
+ if( gaPageBitmaps[page / 32] == 0 )
+ gaSuperBitmap[page / (32*32)] &= ~(1LL << ((page / 32) & 31));
+ #endif
+ if( MM_GetPhysAddr( refpage ) )
+ gaiPageReferences[page] = 1;
+ }
+ else
+ {
+ // Reference again
+ if( !MM_GetPhysAddr( refpage ) )
+ {
+ int pages_per_page, basepage, i;
+ if( MM_Allocate(refpage) == 0 ) {
+ // Out of memory, can this be resolved?
+ // TODO: Reclaim memory
+ Log_Error("PMM", "Out of memory (MM_RefPhys)");
+ return ;
+ }
+ pages_per_page = PAGE_SIZE/sizeof(*gaiPageReferences);
+ basepage = page & ~(pages_per_page-1);
+ for( i = 0; i < pages_per_page; i ++ ) {
+ if( PAGE_BITMAP_FREE(basepage+i) )
+ gaiPageReferences[basepage+i] = 0;
+ else
+ gaiPageReferences[basepage+i] = 1;
+ }
+ gaiPageReferences[page] = 2;
+ }
+ else
+ gaiPageReferences[ page ] ++;
+ }
+}
+
+int MM_GetRefCount(tPAddr PAddr)
+{
+ PAddr >>= 12;
+ if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) {
+ return gaiPageReferences[PAddr];
+ }
+
+ if( gaPageBitmaps[ PAddr / 32 ] & (1LL << (PAddr&31)) ) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Dereference a physical page
+ */
+void MM_DerefPhys(tPAddr PAddr)
+{
+ Uint64 page = PAddr >> 12;
+
+ if( PAddr >> 12 > giMaxPhysPage ) return ;
+
+ ENTER("PPAddr", PAddr);
+
+ if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) )
+ {
+ if( gaiPageReferences[page] > 0 )
+ gaiPageReferences[ page ] --;
+ if( gaiPageReferences[ page ] == 0 ) {
+ gaPageBitmaps[ page / 32 ] |= 1 << (page&31);
+ // TODO: Catch when all pages in this range have been dereferenced
+ }
+ }
+ else
+ gaPageBitmaps[ page / 32 ] |= 1 << (page&31);
+ // Clear node if needed
+ if( MM_GetPhysAddr( (tVAddr)&gapPageNodes[page] ) ) {
+ gapPageNodes[page] = NULL;
+ // TODO: Catch when all pages in this range are not using nodes
+ }
+
+ // Update the free counts if the page was freed
+ if( gaPageBitmaps[ page / 32 ] & (1LL << (page&31)) )
+ {
+ giPhysNumFree ++;
+ if( giPhysFirstFree == -1 || giPhysFirstFree > page )
+ giPhysFirstFree = page;
+ if( giPhysLastFree < page )
+ giPhysLastFree = page;
+ }
+
+ #if USE_SUPER_BITMAP
+ // If the bitmap entry is not zero, set the bit free in the super bitmap
+ if(gaPageBitmaps[ page / 32 ] != 0 ) {
+ gaSuperBitmap[page / (32*32)] |= 1LL << ((page / 32) & 31);
+ }
+ #endif
+ LEAVE('-');
+}
+
+int MM_SetPageNode(tPAddr PAddr, void *Node)
+{
+ tPAddr page = PAddr >> 12;
+ tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1);
+
+ if( !MM_GetRefCount(PAddr) ) return 1;
+
+ if( !MM_GetPhysAddr(node_page) ) {
+ if( !MM_Allocate(node_page) )
+ return -1;
+ memset( (void*)node_page, 0, PAGE_SIZE );
+ }
+
+ gapPageNodes[page] = Node;
+ return 0;
+}
+
+int MM_GetPageNode(tPAddr PAddr, void **Node)
+{
+ if( !MM_GetRefCount(PAddr) ) return 1;
+ PAddr >>= 12;
+
+ if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) {
+ *Node = NULL;
+ return 0;
+ }
+
+ *Node = gapPageNodes[PAddr];
+ return 0;
+}
+
--- /dev/null
+/*
+ * Acess2 Core
+ *
+ * include/tpl_mm_phys.h
+ * Physical Memory Manager Template
+ */
+#define DEBUG 0
+
+/**
+ * \file tpl_mm_phys_stack.h
+ * \brief Template physical memory manager
+ *
+ * An extensible physical memory manager
+ *
+ * Usage: Requires NUM_MM_PHYS_RANGES to be set to the number of address
+ * "classes" wanted.
+ * MAX_PHYS_PAGES - Used to calculate structure sizes
+ * PADDR_TYPE
+ */
+
+// === PROTOTYPES ===
+//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot);
+//tPAddr MM_AllocPhysRange(int Num, int Bits);
+//tPAddr MM_AllocPhys(void);
+//void MM_RefPhys(tPAddr PAddr);
+//void MM_DerefPhys(tPAddr PAddr);
+ int MM_int_GetRangeID( tPAddr Addr );
+ int MM_int_IsPhysUnavail( tPageNum Page );
+
+// === GLOBALS ===
+tMutex glPhysicalPages;
+//Uint64 *gaPageStacks[NUM_MM_PHYS_RANGES]; // Page stacks
+ int giPageStackSizes[NUM_MM_PHYS_RANGES]; // Points to the first unused slot
+Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
+Uint64 giMaxPhysPage = 0; // Maximum Physical page
+
+// === CODE ===
+/**
+ * \brief Initialise the physical memory map using a Multiboot 1 map
+ */
+void MM_Tpl_InitPhys(int MaxRAMPage)
+{
+ const int PAGE_SIZE = 0x1000;
+ const int pagesPerPageOnStack = PAGE_SIZE / sizeof(gaPageStack[0]);
+ int i;
+ tPageNum page;
+
+// ENTER("pMBoot=%p", MBoot);
+
+ giMaxPhysPage = MaxRAMPage;
+
+ tPAddr page = 0;
+ for( i = 0; i < NUM_MM_PHYS_RANGES; i ++ )
+ {
+ for( ; page < giPageRangeMax[i] && page < giMaxPhysPage; page ++ )
+ {
+ int rangeSize;
+
+ rangeSize = MM_int_IsPhysUnavail(page);
+ if( rangeSize > 0 ) {
+ page += rangeSize;
+ continue;
+ }
+ // Page is avaliable for use
+
+ // Check if the page stack is allocated
+ tVAddr stack_page = &gaPageStacks[i][giPageStackSizes[i]&~pagesPerPageOnStack];
+ if( !MM_GetPhysAddr( stack_page ) ) {
+ MM_Map( stack_page, page*PAGE_SIZE );
+ }
+ else {
+ gaPageStacks[i][ giPageStackSizes[i] ] = page;
+ giPageStackSizes[i] ++;
+ }
+ }
+ }
+
+ LEAVE('-');
+}
+
+/**
+ * \brief Allocate a contiguous range of physical pages with a maximum
+ * bit size of \a MaxBits
+ * \param Pages Number of pages to allocate
+ * \param MaxBits Maximum size of the physical address
+ * \note If \a MaxBits is <= 0, any sized address is used (with preference
+ * to higher addresses)
+ */
+tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
+{
+ tPAddr addr, ret;
+ int rangeID;
+ int nFree = 0, i;
+
+ ENTER("iPages iBits", Pages, MaxBits);
+
+ if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case
+ rangeID = MM_PHYS_MAX;
+ else
+ rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 );
+
+ LOG("rangeID = %i", rangeID);
+
+ Mutex_Acquire(&glPhysicalPages);
+
+ // Check if the range actually has any free pages
+ while(giPageStackSizes[rangeID] == 0 && rangeID)
+ rangeID --;
+
+ LOG("rangeID = %i", rangeID);
+
+ // What the? Oh, man. No free pages
+ if(giPageStackSizes[rangeID] == 0) {
+ Mutex_Release(&glPhysicalPages);
+ // TODO: Page out / attack the cache
+ // ATM. Just Warning
+ Warning(" MM_AllocPhysRange: Out of free pages");
+ Log_Warning("Arch",
+ "Out of memory (unable to fulfil request for %i pages), zero remaining",
+ Pages
+ );
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Check if there is enough in the range
+ if(giPhysRangeFree[rangeID] >= Pages)
+ {
+ LOG("{%i,0x%x -> 0x%x}",
+ giPhysRangeFree[rangeID],
+ giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID]
+ );
+ // Do a cheap scan, scanning upwards from the first free page in
+ // the range
+ nFree = 0;
+ addr = giPhysRangeFirst[ rangeID ];
+ while( addr <= giPhysRangeLast[ rangeID ] )
+ {
+ //Log(" MM_AllocPhysRange: addr = 0x%x", addr);
+ // Check the super bitmap
+ if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) {
+ LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr += 1LL << (6+6);
+ addr &= ~0xFFF; // (1LL << 6+6) - 1
+ continue;
+ }
+ // Check page block (64 pages)
+ if( gaMainBitmap[addr >> 6] + 1 == 0) {
+ LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr += 1LL << (6);
+ addr &= ~0x3F;
+ continue;
+ }
+ // Check individual page
+ if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) {
+ LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr);
+ nFree = 0;
+ addr ++;
+ continue;
+ }
+ nFree ++;
+ addr ++;
+ LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr);
+ if(nFree == Pages)
+ break;
+ }
+ LOG("nFree = %i", nFree);
+ // If we don't find a contiguous block, nFree will not be equal
+ // to Num, so we set it to zero and do the expensive lookup.
+ if(nFree != Pages) nFree = 0;
+ }
+
+ if( !nFree )
+ {
+ // Oops. ok, let's do an expensive check (scan down the list
+ // until a free range is found)
+ nFree = 1;
+ addr = giPhysRangeLast[ rangeID ];
+ // TODO
+ Mutex_Release(&glPhysicalPages);
+ // TODO: Page out
+ // ATM. Just Warning
+ Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages);
+ Log_Warning("Arch",
+ "Out of memory (unable to fulfil request for %i pages)",
+ Pages
+ );
+ LEAVE('i', 0);
+ return 0;
+ }
+ LOG("nFree = %i, addr = 0x%08x", nFree, addr);
+
+ // Mark pages as allocated
+ addr -= Pages;
+ for( i = 0; i < Pages; i++, addr++ )
+ {
+ gaMainBitmap[addr >> 6] |= 1LL << (addr & 63);
+ rangeID = MM_int_GetRangeID(addr << 12);
+ giPhysRangeFree[ rangeID ] --;
+ LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]);
+ if(addr == giPhysRangeFirst[ rangeID ])
+ giPhysRangeFirst[ rangeID ] += 1;
+ }
+ ret = addr; // Save the return address
+
+ // Update super bitmap
+ Pages += addr & (64-1);
+ addr &= ~(64-1);
+ Pages = (Pages + (64-1)) & ~(64-1);
+ for( i = 0; i < Pages/64; i++ )
+ {
+ if( gaMainBitmap[ addr >> 6 ] + 1 == 0 )
+ gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63);
+ }
+
+ Mutex_Release(&glPhysicalPages);
+ LEAVE('x', ret << 12);
+ return ret << 12;
+}
+
+/**
+ * \brief Allocate a single physical page, with no preference as to address
+ * size.
+ */
+tPAddr MM_AllocPhys(void)
+{
+ int i;
+
+ // Hack to allow allocation during setup
+ for(i = 0; i < NUM_STATIC_ALLOC; i++) {
+ if( gaiStaticAllocPages[i] ) {
+ tPAddr ret = gaiStaticAllocPages[i];
+ gaiStaticAllocPages[i] = 0;
+ Log("MM_AllocPhys: Return %x, static alloc %i", ret, i);
+ return ret;
+ }
+ }
+
+ return MM_AllocPhysRange(1, -1);
+}
+
+/**
+ * \brief Reference a physical page
+ */
+void MM_RefPhys(tPAddr PAddr)
+{
+ Uint64 page = PAddr >> 12;
+
+ if( PAddr >> 12 > giMaxPhysPage ) return ;
+
+ if( gaMainBitmap[ page >> 6 ] & (1LL << (page&63)) )
+ {
+ // Reference again
+ gaMultiBitmap[ page >> 6 ] |= 1LL << (page&63);
+ gaiPageReferences[ page ] ++;
+ }
+ else
+ {
+ // Allocate
+ gaMainBitmap[page >> 6] |= 1LL << (page&63);
+ if( gaMainBitmap[page >> 6 ] + 1 == 0 )
+ gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63);
+ }
+}
+
+/**
+ * \brief Dereference a physical page
+ */
+void MM_DerefPhys(tPAddr PAddr)
+{
+ Uint64 page = PAddr >> 12;
+
+ if( PAddr >> 12 > giMaxPhysPage ) return ;
+
+ if( gaMultiBitmap[ page >> 6 ] & (1LL << (page&63)) ) {
+ gaiPageReferences[ page ] --;
+ if( gaiPageReferences[ page ] == 1 )
+ gaMultiBitmap[ page >> 6 ] &= ~(1LL << (page&63));
+ if( gaiPageReferences[ page ] == 0 )
+ gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63));
+ }
+ else
+ gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63));
+
+ // Update the free counts if the page was freed
+ if( !(gaMainBitmap[ page >> 6 ] & (1LL << (page&63))) )
+ {
+ int rangeID;
+ rangeID = MM_int_GetRangeID( PAddr );
+ giPhysRangeFree[ rangeID ] ++;
+ if( giPhysRangeFirst[rangeID] > page )
+ giPhysRangeFirst[rangeID] = page;
+ if( giPhysRangeLast[rangeID] < page )
+ giPhysRangeLast[rangeID] = page;
+ }
+
+ // If the bitmap entry is not -1, unset the bit in the super bitmap
+ if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) {
+ gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63));
+ }
+}
+
+/**
+ * \brief Takes a physical address and returns the ID of its range
+ * \param Addr Physical address of page
+ * \return Range ID from eMMPhys_Ranges
+ */
+int MM_int_GetRangeID( tPAddr Addr )
+{
+ if(Addr >> 32)
+ return MM_PHYS_MAX;
+ else if(Addr >> 24)
+ return MM_PHYS_32BIT;
+ else if(Addr >> 20)
+ return MM_PHYS_24BIT;
+ else if(Addr >> 16)
+ return MM_PHYS_20BIT;
+ else
+ return MM_PHYS_16BIT;
+}
--- /dev/null
+/*
+ * Acess2
+ * VFS Common Header
+ */
+/**
+ * \file vfs.h
+ * \brief Acess VFS Layer
+ *
+ * The Acess Virtual File System (VFS) provides abstraction of multiple
+ * physical filesystems, network storage and devices (both hardware and
+ * virtual) to the user.
+ *
+ * The core of the VFS is the concept of a \ref tVFS_Node "VFS Node".
+ * A VFS Node represents a "file" in the VFS tree, this can be any sort
+ * of file (an ordinary file, a directory, a symbolic link or a device)
+ * depending on the bits set in the \ref tVFS_Node.Flags Flags field.
+ * - For more information see "VFS Node Flags"
+ */
+#ifndef _VFS_H
+#define _VFS_H
+
+#include <acess.h>
+
+/**
+ * \brief Thread list datatype for VFS_Select
+ */
+typedef struct sVFS_SelectList tVFS_SelectList;
+
+typedef struct sVFS_NodeType tVFS_NodeType;
+
+/**
+ * \name tVFS_Node Flags
+ * \brief Flag values for tVFS_Node.Flags
+ * \{
+ */
+//! \todo Is this still needed
+#define VFS_FFLAG_READONLY 0x01 //!< Readonly File
+/**
+ * \brief Directory Flag
+ *
+ * This flag marks the tVFS_Node as describing a directory, and says
+ * that the tVFS_Node.FindDir, tVFS_Node.ReadDir, tVFS_Node.MkNod and
+ * tVFS_Node.Relink function pointers are valid.
+ * For a directory the tVFS_Node.Size field contains the number of files
+ * within the directory, or -1 for undetermined.
+ */
+#define VFS_FFLAG_DIRECTORY 0x02
+/**
+ * \brief Symbolic Link Flag
+ *
+ * Marks a file as a symbolic link
+ */
+#define VFS_FFLAG_SYMLINK 0x04
+/**
+ * \brief Set User ID Flag
+ *
+ * Allows an executable file to change it's executing user to the file's
+ * owner.
+ * In the case of a directory, it means that all immediate children will
+ * inherit the UID of the parent.
+ */
+#define VFS_FFLAG_SETUID 0x08
+/**
+ * \brief Set Group ID Flag
+ *
+ * Allows an executable file to change it's executing group to the file's
+ * owning group.
+ * In the case of a directory, it means that all immediate children will
+ * inherit the GID of the parent.
+ */
+#define VFS_FFLAG_SETGID 0x10
+
+/**
+ * \brief "Don't do Write-Back" Flag
+ *
+ * Stops the VFS from calling tVFS_Node.Write on dirty pages when a region
+ * is unmapped. Nice for read-only files and memory-only files (or
+ * pseudo-readonly filesystems)
+ */
+#define VFS_FFLAG_NOWRITEBACK
+/**
+ * \}
+ */
+
+/**
+ * \brief Represents a node (file or directory) in the VFS tree
+ *
+ * This structure provides the VFS with the functions required to read/write
+ * the file (or directory) that it represents.
+ */
+typedef struct sVFS_Node
+{
+ /**
+ * \name Identifiers
+ * \brief Fields used by the driver to identify what data this node
+ * corresponds to.
+ * \{
+ */
+ Uint64 Inode; //!< Inode ID - Must identify the node uniquely if tVFS_Driver.GetNodeFromINode is non-NULL
+ Uint ImplInt; //!< Implementation Usable Integer
+ void *ImplPtr; //!< Implementation Usable Pointer
+ /**
+ * \}
+ */
+
+ /**
+ * \name Node State
+ * \brief Stores the misc information about the node
+ * \{
+ */
+ int ReferenceCount; //!< Number of times the node is used
+
+ Uint64 Size; //!< File Size
+
+ Uint32 Flags; //!< File Flags
+
+ /**
+ * \brief Pointer to cached data (FS Specific)
+ * \note The Inode_* functions will free when the node is uncached
+ * this if needed
+ */
+ void *Data;
+
+ /**
+ * \brief Node mutex
+ * \note Provided for the Filesystem driver's use
+ */
+ tMutex Lock;
+
+ /**
+ * \}
+ */
+
+ /**
+ * \name Times
+ * \{
+ */
+ Sint64 ATime; //!< Last Accessed Time
+ Sint64 MTime; //!< Last Modified Time
+ Sint64 CTime; //!< Creation Time
+ /**
+ * \}
+ */
+
+ /**
+ * \name Access control
+ * \{
+ */
+ tUID UID; //!< ID of Owning User
+ tGID GID; //!< ID of Owning Group
+
+ int NumACLs; //!< Number of ACL entries
+ tVFS_ACL *ACLs; //!< Access Controll List pointer
+ /**
+ * \}
+ */
+
+ /**
+ * \name VFS_Select() fields
+ * \note Used by the VFS internals, drivers should use VFS_Mark*
+ * \{
+ */
+ int DataAvaliable;
+ tVFS_SelectList *ReadThreads; //!< Threads waiting to read
+ int BufferFull;
+ tVFS_SelectList *WriteThreads; //!< Threads waiting to write
+ int ErrorOccurred;
+ tVFS_SelectList *ErrorThreads; //!< Threads waiting for an error
+ /**
+ * \}
+ */
+
+ /**
+ * \name VFS_MMap() fields
+ * \{
+ */
+ void *MMapInfo;
+ /**
+ * \}
+ */
+
+ /**
+ * \brief Functions associated with the node
+ */
+ tVFS_NodeType *Type;
+} tVFS_Node;
+
+/**
+ * \brief Functions for a specific node type
+ */
+struct sVFS_NodeType
+{
+ /**
+ * \brief Debug name for the type
+ */
+ const char *TypeName;
+
+ /**
+ * \name Common Functions
+ * \brief Functions that are used no matter the value of .Flags
+ * \{
+ */
+ /**
+ * \brief Reference the node
+ * \param Node Pointer to this node
+ */
+ void (*Reference)(struct sVFS_Node *Node);
+ /**
+ * \brief Close (dereference) the node
+ * \param Node Pointer to this node
+ *
+ * Usually .Close is used to write any changes to the node back to
+ * the persistent storage.
+ */
+ void (*Close)(struct sVFS_Node *Node);
+
+ /**
+ * \brief Send an IO Control
+ * \param Node Pointer to this node
+ * \param Id IOCtl call number
+ * \param Data Pointer to data to pass to the driver
+ * \return Implementation defined
+ */
+ int (*IOCtl)(struct sVFS_Node *Node, int Id, void *Data);
+
+ /**
+ * \}
+ */
+
+ /**
+ * \name Buffer Functions
+ * \brief Functions for accessing a buffer-type file (normal file or
+ * symbolic link)
+ * \{
+ */
+
+ /**
+ * \brief Read from the file
+ * \param Node Pointer to this node
+ * \param Offset Byte offset in the file
+ * \param Length Number of bytes to read
+ * \param Buffer Destination for read data
+ * \return Number of bytes read
+ */
+ Uint64 (*Read)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+ /**
+ * \brief Write to the file
+ * \param Node Pointer to this node
+ * \param Offset Byte offser in the file
+ * \param Length Number of bytes to write
+ * \param Buffer Source of written data
+ * \return Number of bytes read
+ */
+ Uint64 (*Write)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+
+ /**
+ * \brief Map a region of a file into memory
+ * \param Node Pointer to this node
+ * \param Offset Start of the region (page aligned)
+ * \param Length Length of the region (page aligned)
+ * \param Dest Location to which to map
+ * \return Boolean Failure
+ * \note If NULL, the VFS implements it using .Read
+ */
+ int (*MMap)(struct sVFS_Node *Node, Uint64 Offset, int Length, void *Dest);
+
+ /**
+ * \}
+ */
+
+ /**
+ * \name Directory Functions
+ * \{
+ */
+ /**
+ * \brief Find an directory entry by name
+ * \param Node Pointer to this node
+ * \param Name Name of the file wanted
+ * \return Pointer to the requested node or NULL if it cannot be found
+ * \note The node returned must be accessable until ::tVFS_Node.Close
+ * is called and ReferenceCount reaches zero.
+ */
+ struct sVFS_Node *(*FindDir)(struct sVFS_Node *Node, const char *Name);
+
+ /**
+ * \brief Read from a directory
+ * \param Node Pointer to this node
+ * \param Pos Offset in the directory
+ * \return Pointer to the name of the item on the heap (will be freed
+ * by the caller). If the directory end has been reached, NULL
+ * will be returned.
+ * If an item is required to be skipped either &::NULLNode,
+ * ::VFS_SKIP or ::VFS_SKIPN(0...1023) will be returned.
+ */
+ char *(*ReadDir)(struct sVFS_Node *Node, int Pos);
+
+ /**
+ * \brief Create a node in a directory
+ * \param Node Pointer to this node
+ * \param Name Name of the new child
+ * \param Flags Flags to apply to the new child (directory or symlink)
+ * \return Zero on Success, non-zero on error (see errno.h)
+ */
+ int (*MkNod)(struct sVFS_Node *Node, const char *Name, Uint Flags);
+
+ /**
+ * \brief Relink (Rename/Remove) a file/directory
+ * \param Node Pointer to this node
+ * \param OldName Name of the item to move/delete
+ * \param NewName New name (or NULL if unlinking is wanted)
+ * \return Zero on Success, non-zero on error (see errno.h)
+ */
+ int (*Relink)(struct sVFS_Node *Node, const char *OldName, const char *NewName);
+
+ /**
+ * \brief Link a node to a name
+ * \param Node Pointer to this node (directory)
+ * \param Child Node to create a new link to
+ * \param NewName Name for the new link
+ * \retur Zeron on success, non-zero on error (see errno.h)
+ */
+ int (*Link)(struct sVFS_Node *Node, struct sVFS_Node *Child, const char *NewName);
+
+ /**
+ * \}
+ */
+};
+
+/**
+ * \brief VFS Driver (Filesystem) Definition
+ */
+typedef struct sVFS_Driver
+{
+ /**
+ * \brief Unique Identifier for this filesystem type
+ */
+ const char *Name;
+ /**
+ * \brief Flags applying to this driver
+ */
+ Uint Flags;
+
+ /**
+ * \brief Callback to mount a device
+ */
+ tVFS_Node *(*InitDevice)(const char *Device, const char **Options);
+ /**
+ * \brief Callback to unmount a device
+ */
+ void (*Unmount)(tVFS_Node *Node);
+ /**
+ * \brief Retrieve a VFS node from an inode
+ */
+ tVFS_Node *(*GetNodeFromINode)(tVFS_Node *RootNode, Uint64 InodeValue);
+ /**
+ * \brief Used internally (next driver in the chain)
+ */
+ struct sVFS_Driver *Next;
+} tVFS_Driver;
+
+// === GLOBALS ===
+//! \brief Maximum number of elements that can be skipped in one return
+#define VFS_MAXSKIP ((void*)1024)
+//! \brief Skip a single entry in readdir
+#define VFS_SKIP ((void*)1)
+//! \brief Skip \a n entries in readdir
+#define VFS_SKIPN(n) ((void*)(n))
+
+extern tVFS_Node NULLNode; //!< NULL VFS Node (Ignored/Skipped)
+/**
+ * \name Static ACLs
+ * \brief Simple ACLs to aid writing drivers
+ * \{
+ */
+extern tVFS_ACL gVFS_ACL_EveryoneRWX; //!< Everyone Read/Write/Execute
+extern tVFS_ACL gVFS_ACL_EveryoneRW; //!< Everyone Read/Write
+extern tVFS_ACL gVFS_ACL_EveryoneRX; //!< Everyone Read/Execute
+extern tVFS_ACL gVFS_ACL_EveryoneRO; //!< Everyone Read only
+/**
+ * \}
+ */
+
+// === FUNCTIONS ===
+/**
+ * \fn int VFS_AddDriver(tVFS_Driver *Info)
+ * \brief Registers the driver with the DevFS layer
+ * \param Info Driver information structure
+ */
+extern int VFS_AddDriver(tVFS_Driver *Info);
+/**
+ * \brief Get the information structure of a driver given its name
+ * \param Name Name of filesystem driver to find
+ */
+extern tVFS_Driver *VFS_GetFSByName(const char *Name);
+
+
+/**
+ * \brief Prepare a node for use
+ */
+extern void VFS_InitNode(tVFS_Node *Node);
+
+/**
+ * \brief Clean up a node, ready for deletion
+ * \note This should ALWAYS be called before a node is freed, as it
+ * cleans up VFS internal structures.
+ */
+extern void VFS_CleanupNode(tVFS_Node *Node);
+
+/**
+ * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group)
+ * \brief Transforms Unix Permssions into Acess ACLs
+ * \param Mode Unix RWXrwxRWX mask
+ * \param Owner UID of the file's owner
+ * \param Group GID of the file's owning group
+ * \return An array of 3 Acess ACLs
+ */
+extern tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group);
+
+/**
+ * \brief Flags fro \a TypeFlag of VFS_SelectNode
+ * \{
+ */
+#define VFS_SELECT_READ 0x01
+#define VFS_SELECT_WRITE 0x02
+#define VFS_SELECT_ERROR 0x04
+/**
+ * \}
+ */
+
+/**
+ * \brief Wait for an event on a node
+ * \param Node Node to wait on
+ * \param Type Type of wait
+ * \param Timeout Time to wait (NULL for infinite wait)
+ * \param Name Name to show in debug output
+ * \return Number of nodes that actioned (0 or 1)
+ */
+extern int VFS_SelectNode(tVFS_Node *Node, int Type, tTime *Timeout, const char *Name);
+
+/**
+ * \brief Change the full flag on a node
+ */
+extern int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
+/**
+ * \brief Alter the space avaliable flag on a node
+ */
+extern int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
+/**
+ * \brief Alter the error flags on a node
+ */
+extern int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
+
+// --- Node Cache --
+/**
+ * \name Node Cache
+ * \brief Functions to allow a node to be cached in memory by the VFS
+ *
+ * These functions store a node for the driver, to prevent it from having
+ * to re-generate the node on each call to FindDir. It also allows for
+ * fast cleanup when a filesystem is unmounted.
+ * \{
+ */
+/**
+ * \fn int Inode_GetHandle(void)
+ * \brief Gets a unique handle to the Node Cache
+ * \return A unique handle for use for the rest of the Inode_* functions
+ */
+extern int Inode_GetHandle(void);
+/**
+ * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode)
+ * \brief Gets an inode from the node cache
+ * \param Handle A handle returned by Inode_GetHandle()
+ * \param Inode Value of the Inode field of the ::tVFS_Node you want
+ * \return A pointer to the cached node
+ */
+extern tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode);
+/**
+ * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node)
+ * \brief Caches a node in the Node Cache
+ * \param Handle A handle returned by Inode_GetHandle()
+ * \param Node A pointer to the node to be cached (a copy is taken)
+ * \return A pointer to the node in the node cache
+ */
+extern tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node);
+/**
+ * \fn int Inode_UncacheNode(int Handle, Uint64 Inode)
+ * \brief Dereferences (and removes if needed) a node from the cache
+ * \param Handle A handle returned by Inode_GetHandle()
+ * \param Inode Value of the Inode field of the ::tVFS_Node you want to remove
+ */
+extern void Inode_UncacheNode(int Handle, Uint64 Inode);
+/**
+ * \fn void Inode_ClearCache(int Handle)
+ * \brief Clears the cache for a handle
+ * \param Handle A handle returned by Inode_GetHandle()
+ */
+extern void Inode_ClearCache(int Handle);
+
+/**
+ * \}
+ */
+
+#endif
--- /dev/null
+/**
+ * \file vfs_ext.h
+ * \brief Exported VFS Definitions
+ * \author John Hodge (thePowersGang)
+ */
+#ifndef _VFS_EXT_H
+#define _VFS_EXT_H
+
+//! Inode number type
+typedef Uint64 tInode;
+
+//! Mountpoint identifier type
+typedef Uint32 tMount;
+
+// === CONSTANTS ===
+//! Maximum size of a Memory Path generated by VFS_GetMemPath
+#define VFS_MEMPATH_SIZE (3 + (BITS/4)*2)
+/**
+ * \name Flags for VFS_Open
+ * \{
+ */
+//! Open for execution
+#define VFS_OPENFLAG_EXEC 0x01
+//! Open for reading
+#define VFS_OPENFLAG_READ 0x02
+//! Open for writing
+#define VFS_OPENFLAG_WRITE 0x04
+//! Do not resolve the final symbolic link
+#define VFS_OPENFLAG_NOLINK 0x40
+//! Create the file if it doesn't exist
+#define VFS_OPENFLAG_CREATE 0x80
+//! Open as a user
+#define VFS_OPENFLAG_USER 0x8000
+/**
+ * \}
+ */
+//! Marks a VFS handle as belonging to the kernel
+#define VFS_KERNEL_FLAG 0x40000000
+
+//! Architectual maximum number of file descriptors
+#define MAX_FILE_DESCS 128
+
+/**
+ * \brief VFS_Seek directions
+ */
+enum eVFS_SeekDirs
+{
+ SEEK_SET = 1, //!< Set the current file offset
+ SEEK_CUR = 0, //!< Seek relative to the current position
+ SEEK_END = -1 //!< Seek from the end of the file backwards
+};
+
+/**
+ * \name ACL Permissions
+ * \{
+ */
+/**
+ * \brief Readable
+ */
+#define VFS_PERM_READ 0x00000001
+/**
+ * \brief Writeable
+ */
+#define VFS_PERM_WRITE 0x00000002
+/**
+ * \brief Append allowed
+ */
+#define VFS_PERM_APPEND 0x00000004
+/**
+ * \brief Executable
+ */
+#define VFS_PERM_EXECUTE 0x00000008
+/**
+ * \brief All permissions granted
+ */
+#define VFS_PERM_ALL 0x7FFFFFFF // Mask for permissions
+/**
+ * \brief Denies instead of granting permissions
+ * \note Denials take precedence
+ */
+#define VFS_PERM_DENY 0x80000000 // Inverts permissions
+/**
+ * \}
+ */
+
+/**
+ * \brief MMap protection flags
+ * \{
+ */
+#define MMAP_PROT_READ 0x001 //!< Readable memory
+#define MMAP_PROT_WRITE 0x002 //!< Writable memory
+#define MMAP_PROT_EXEC 0x004 //!< Executable memory
+/**
+ * \}
+ */
+
+/**
+ * \brief MMap mapping flags
+ * \{
+ */
+#define MMAP_MAP_SHARED 0x001 //!< Shared with all other users of the FD
+#define MMAP_MAP_PRIVATE 0x002 //!< Local (COW) copy
+#define MMAP_MAP_FIXED 0x004 //!< Load to a fixed address
+#define MMAP_MAP_ANONYMOUS 0x008 //!< Not associated with a FD
+/**
+ * \}
+ */
+
+// -- System Call Structures ---
+/**
+ * \brief ACL Defintion Structure
+ */
+typedef struct sVFS_ACL
+{
+ struct {
+ unsigned Group: 1; //!< Group (as opposed to user) flag
+ unsigned ID: 31; //!< ID of Group/User (-1 for nobody/world)
+ };
+ struct {
+ unsigned Inv: 1; //!< Invert Permissions
+ unsigned Perms: 31; //!< Permission Flags
+ };
+} tVFS_ACL;
+
+/**
+ * \brief SYS_FINFO structure
+ */
+typedef struct sFInfo
+{
+ tMount mount; //!< Mountpoint ID
+ tInode inode; //!< Inode
+ Uint32 uid; //!< Owning User ID
+ Uint32 gid; //!< Owning Group ID
+ Uint32 flags; //!< File flags
+ Uint64 size; //!< File Size
+ Sint64 atime; //!< Last Accessed time
+ Sint64 mtime; //!< Last modified time
+ Sint64 ctime; //!< Creation time
+ Sint32 numacls; //!< Total number of ACL entries
+ tVFS_ACL acls[]; //!< ACL buffer (size is passed in the \a MaxACLs argument to VFS_FInfo)
+} PACKED tFInfo;
+
+/**
+ * \brief fd_set for select()
+ */
+typedef struct
+{
+ //! Bitmap of set file descriptors
+ Uint16 flags[MAX_FILE_DESCS/16];
+} fd_set;
+
+/**
+ * \brief Clear a descriptor flag in a fd_set
+ * \param fd File descriptor to clear
+ * \param fdsetp Set to modify
+ */
+#define FD_CLR(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&=~(1<<((fd)%16)))
+/**
+ * \brief Set a descriptor flag in a fd_set
+ * \param fd File descriptor to set
+ * \param fdsetp Set to modify
+ */
+#define FD_SET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]|=~(1<<((fd)%16)))
+/**
+ * \brief Test a descriptor flag in a fd_set
+ * \param fd File descriptor to test
+ * \param fdsetp Set to modify
+ */
+#define FD_ISSET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&(1<<((fd)%16)))
+
+// === FUNCTIONS ===
+/**
+ * \brief Initialise the VFS (called by system.c)
+ * \return Boolean Success
+ */
+extern int VFS_Init(void);
+
+/**
+ * \brief Open a file
+ * \param Path Absolute or relative path to the file
+ * \param Flags Flags defining how to open the file
+ * \return VFS Handle (an integer) or -1 if an error occured
+ * \note Calls VFS_OpenEx(Path, Flags, 0)
+ */
+extern int VFS_Open(const char *Path, Uint Flags);
+/**
+ * \brief Open a file
+ * \param Path Absolute or relative path to the file
+ * \param Flags Flags defining how to open the file
+ * \param Mode Mode for newly created file (POSIX compatability)
+ * \return VFS Handle (an integer) or -1 if an error occured
+ */
+extern int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode);
+/**
+ * \brief Opens a file via an open directory
+ * \note The file to open must be a direct child of the parent
+ * \param FD Parent Directory
+ * \param Name Child name
+ * \param Mode Open mode
+ * \return File handle (same as returned from VFS_Open)
+ */
+extern int VFS_OpenChild(int FD, const char *Name, Uint Mode);
+/**
+ * \brief Opens a file given a mount ID and an inode number
+ * \param Mount Mount ID returned by FInfo
+ * \param Inode Inode number from FInfo
+ * \param Mode Open mode (see VFS_Open)
+ * \return File handle (same as VFS_Open)
+ */
+extern int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode);
+
+/**
+ * \brief Close a currently open file
+ * \param FD Handle returned by ::VFS_Open
+ */
+extern void VFS_Close(int FD);
+
+/**
+ * \brief Get file information from an open file
+ * \param FD File handle returned by ::VFS_Open
+ * \param Dest Destination for the read information
+ * \param MaxACLs Number of ACL slots allocated in the \a Dest structure
+ * \return Boolean Success
+ *
+ * If the \a NumACLs is smaller than the number of ACLs the node has, only
+ * \a NumACLs will be copied into \a Dest, but the tFInfo.numacls field
+ * will be set to the true ammount of ACLs. It is up to the user to do with
+ * this information how they like.
+ */
+extern int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs);
+/**
+ * \brief Gets the permissions appling to a user/group.
+ * \param FD File handle returned by ::VFS_Open
+ * \param Dest ACL information structure to edit
+ * \return Boolean success
+ *
+ * This function sets the tVFS_ACL.Inv and tVFS_ACL.Perms fields to what
+ * permissions the user/group specied in tVFS_ACL.ID has on the file.
+ */
+extern int VFS_GetACL(int FD, tVFS_ACL *Dest);
+/**
+ * \brief Changes the user's current working directory
+ * \param Dest New working directory (either absolute or relative to the current)
+ * \return Boolean Success
+ */
+extern int VFS_ChDir(const char *Dest);
+/**
+ * \brief Change the current virtual root for the user
+ * \param New New virtual root (same as ::VFS_ChDir but cannot go
+ * above the current virtual root)
+ * \return Boolean success
+ */
+extern int VFS_ChRoot(const char *New);
+
+/**
+ * \brief Change the location of the current file pointer
+ * \param FD File handle returned by ::VFS_Open
+ * \param Offset Offset within the file to go to
+ * \param Whence A direction from ::eVFS_SeekDirs
+ * \return Boolean success
+ */
+extern int VFS_Seek(int FD, Sint64 Offset, int Whence);
+/**
+ * \brief Returns the current file pointer
+ * \param FD File handle returned by ::VFS_Open
+ * \return Current file position
+ */
+extern Uint64 VFS_Tell(int FD);
+
+/**
+ * \brief Reads data from a file
+ * \param FD File handle returned by ::VFS_Open
+ * \param Length Number of bytes to read from the file
+ * \param Buffer Destination of read data
+ * \return Number of read bytes
+ */
+extern Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer);
+/**
+ * \brief Writes data to a file
+ * \param FD File handle returned by ::VFS_Open
+ * \param Length Number of bytes to write to the file
+ * \param Buffer Source of written data
+ * \return Number of bytes written
+ */
+extern Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer);
+
+/**
+ * \brief Reads from a specific offset in the file
+ * \param FD File handle returned by ::VFS_Open
+ * \param Offset Byte offset in the file
+ * \param Length Number of bytes to read from the file
+ * \param Buffer Source of read data
+ * \return Number of bytes read
+ */
+extern Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer);
+/**
+ * \brief Writes to a specific offset in the file
+ * \param FD File handle returned by ::VFS_Open
+ * \param Offset Byte offset in the file
+ * \param Length Number of bytes to write to the file
+ * \param Buffer Source of written data
+ * \return Number of bytes written
+ */
+extern Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer);
+
+/**
+ * \brief Sends an IOCtl request to the driver
+ * \param FD File handle returned by ::VFS_Open
+ * \param ID IOCtl call ID (driver specific)
+ * \param Buffer Data pointer to send to the driver
+ * \return Driver specific response
+ */
+extern int VFS_IOCtl(int FD, int ID, void *Buffer);
+
+/**
+ * \brief Creates a VFS Memory path from a pointer and size
+ * \param Dest Destination for the created path
+ * \param Base Base of the memory file
+ * \param Length Length of the memory buffer
+ * \note A maximum of VFS_MEMPATH_SIZE bytes will be required in \a Dest
+ */
+extern void VFS_GetMemPath(char *Dest, void *Base, Uint Length);
+/**
+ * \brief Gets the canoical (true) path of a file
+ * \param Path File path (may contain symlinks, relative elements etc.)
+ * \return Absolute path with no symlinks
+ */
+extern char *VFS_GetTruePath(const char *Path);
+
+/**
+ * \brief Mounts a filesystem
+ * \param Device Device to mount
+ * \param MountPoint Location to mount
+ * \param Filesystem Filesystem to use
+ * \param Options Options string to pass the driver
+ * \return 1 on succes, -1 on error
+ */
+extern int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
+/**
+ * \brief Create a new directory
+ * \param Path Path to new directory (absolute or relative)
+ * \return Boolean success
+ * \note The parent of the directory must exist
+ */
+extern int VFS_MkDir(const char *Path);
+/**
+ * \brief Create a symbolic link
+ * \param Name Name of the symbolic link
+ * \param Link File the symlink points to
+ * \return Boolean success (\a Link is not tested for existence)
+ */
+extern int VFS_Symlink(const char *Name, const char *Link);
+/**
+ * \brief Read from a directory
+ * \param FD File handle returned by ::VFS_Open
+ * \param Dest Destination array for the file name (max 255 bytes)
+ * \return Boolean Success
+ */
+extern int VFS_ReadDir(int FD, char *Dest);
+/**
+ * \brief Wait for an aciton on a file descriptor
+ * \param MaxHandle Maximum set handle in \a *Handles
+ * \param ReadHandles Handles to wait for data on
+ * \param WriteHandles Handles to wait to write to
+ * \param ErrHandles Handle to wait for errors on
+ * \param Timeout Timeout for select() (if null, there is no timeout), if zero select() is non blocking
+ * \param ExtraEvents Extra event set to wait on
+ * \param IsKernel Use kernel handles as opposed to user handles
+ * \return Number of handles that actioned
+ */
+extern int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel);
+
+/**
+ * \brief Map a file into memory
+ * \param DestHint Suggested place for read data
+ * \param Length Size of area to map
+ * \param Protection Protection type (see `man mmap`)
+ * \param Flags Mapping flags
+ * \param FD File descriptor to load from
+ * \param Offset Start of region
+ */
+extern void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset);
+
+/**
+ * \brief Unmap memory allocated by VFS_MMap
+ * \param Addr Address of data to unmap
+ * \param Length Length of data
+ */
+extern int VFS_MUnmap(void *Addr, size_t Length);
+#endif
--- /dev/null
+/*
+ * Acess Micro - VFS Server Ver 1
+ */
+#ifndef _VFS_INT_H
+#define _VFS_INT_H
+
+#include "vfs.h"
+
+// === TYPES ===
+typedef struct sVFS_Mount {
+ struct sVFS_Mount *Next;
+ char *MountPoint;
+ size_t MountPointLen;
+ Uint32 Identifier;
+ char *Device;
+ char *Options;
+ tVFS_Driver *Filesystem;
+ tVFS_Node *RootNode;
+ char StrData[];
+} tVFS_Mount;
+
+typedef struct sVFS_Handle {
+ tVFS_Node *Node;
+ tVFS_Mount *Mount;
+ Uint64 Position;
+ Uint Mode;
+} tVFS_Handle;
+
+typedef struct sVFS_Proc {
+ struct sVFS_Proc *Next;
+ int ID;
+ int CwdLen;
+ Uint UID, GID;
+ char *Cwd;
+ int MaxHandles;
+ tVFS_Handle Handles[];
+} tVFS_Proc;
+
+typedef struct sVFS_MMapPage {
+ Uint64 FileOffset;
+ tPAddr PAddr;
+} tVFS_MMapPage;
+
+// === GLOBALS ===
+extern tVFS_Mount *gVFS_Mounts;
+
+// === PROTOTYPES ===
+// --- open.c ---
+extern char *VFS_GetAbsPath(const char *Path);
+extern tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint);
+extern tVFS_Handle *VFS_GetHandle(int FD);
+// --- acls.c ---
+extern int VFS_CheckACL(tVFS_Node *Node, Uint Permissions);
+// --- mount.c ---
+extern tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID);
+// --- dir.c ---
+extern int VFS_MkNod(const char *Path, Uint Flags);
+
+
+// --- VFS Helpers ---
+static inline void _CloseNode(tVFS_Node *Node)
+{
+ if(Node && Node->Type && Node->Type->Close)
+ Node->Type->Close( Node );
+}
+
+
+#endif
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - RAM Filesystem Coommon Structures
+ */
+#ifndef _RAMFS_H
+#define _RAMFS_H
+#include <vfs.h>
+
+typedef struct sRamFS_File {
+ struct sRamFS_File *Next;
+ struct sRamFS_File *Parent;
+ char Name[32];
+ tVFS_Node Node;
+ union {
+ struct sRamFS_File *FirstChild;
+ char *Bytes;
+ } Data;
+} tRamFS_File;
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * include/vfs_threads.h
+ * - Handle maintainance functions for the VFS used by threading code
+ */
+#ifndef _VFS_THREADS_H_
+#define _VFS_THREADS_H_
+
+// === FUNCTIONS ===
+extern void VFS_ReferenceUserHandles(void);
+extern void VFS_CloseAllUserHandles(void);
+
+extern void *VFS_SaveHandles(int NumFDs, int *FDs);
+extern void VFS_RestoreHandles(int NumFDs, void *Handles);
+extern void VFS_FreeSavedHandles(int NumFDs, void *Handles);
+
+#endif
--- /dev/null
+/**
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * workqueue.h
+ * - FIFO Queue
+ */
+#ifndef _WORKQUEUE_H_
+#define _WORKQUEUE_H_
+
+#include <acess.h>
+
+typedef struct sWorkqueue tWorkqueue;
+
+struct sWorkqueue
+{
+ tShortSpinlock Protector;
+ const char *Name;
+ int NextOffset;
+
+ void *Head;
+ void *Tail;
+ struct sThread *Sleeper;
+};
+
+extern void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset);
+extern void *Workqueue_GetWork(tWorkqueue *Queue);
+extern void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2
+ * Common Library Functions
+ */
+#include <acess.h>
+#include <hal_proc.h>
+
+// === CONSTANTS ===
+#define RANDOM_SEED 0xACE55052
+#define RANDOM_A 0x00731ADE
+#define RANDOM_C 12345
+#define RANDOM_SPRUCE 0xf12b039
+// Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec
+const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+#define UNIX_TO_2K ((30*365*3600*24) + (7*3600*24)) //Normal years + leap years
+
+// === PROTOTYPES ===
+#if 0
+ int atoi(const char *string);
+void itoa(char *buf, Uint64 num, int base, int minLength, char pad);
+ int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
+ int sprintf(char *__s, const char *__format, ...);
+#endif
+ int tolower(int c);
+#if 0
+ int strucmp(const char *Str1, const char *Str2);
+char *strchr(const char *__s, int __c);
+ int strpos(const char *Str, char Ch);
+ Uint8 ByteSum(void *Ptr, int Size);
+size_t strlen(const char *__s);
+char *strcpy(char *__str1, const char *__str2);
+char *strncpy(char *__str1, const char *__str2, size_t max);
+ int strcmp(const char *str1, const char *str2);
+ int strncmp(const char *str1, const char *str2, size_t num);
+char *_strdup(const char *File, int Line, const char *Str);
+char **str_split(const char *__str, char __ch);
+ int strpos8(const char *str, Uint32 Search);
+ int ReadUTF8(Uint8 *str, Uint32 *Val);
+ int WriteUTF8(Uint8 *str, Uint32 Val);
+ int DivUp(int num, int dem);
+Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year);
+void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
+ int rand(void);
+
+ int CheckString(char *String);
+ int CheckMem(void *Mem, int NumBytes);
+
+ int ModUtil_LookupString(char **Array, char *Needle);
+ int ModUtil_SetIdent(char *Dest, char *Value);
+
+ int Hex(char *Dest, size_t Size, const Uint8 *SourceData);
+ int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString);
+#endif
+
+// === EXPORTS ===
+EXPORT(atoi);
+EXPORT(itoa);
+EXPORT(vsnprintf);
+EXPORT(sprintf);
+EXPORT(tolower);
+EXPORT(strucmp);
+EXPORT(strchr);
+EXPORT(strpos);
+EXPORT(ByteSum);
+EXPORT(strlen);
+EXPORT(strcpy);
+EXPORT(strncpy);
+EXPORT(strcat);
+EXPORT(strncat);
+EXPORT(strcmp);
+EXPORT(strncmp);
+//EXPORT(strdup);
+EXPORT(_strdup); // Takes File/Line too
+EXPORT(str_split);
+EXPORT(strpos8);
+EXPORT(DivUp);
+EXPORT(ReadUTF8);
+EXPORT(WriteUTF8);
+EXPORT(timestamp);
+EXPORT(CheckString);
+EXPORT(CheckMem);
+EXPORT(ModUtil_LookupString);
+EXPORT(ModUtil_SetIdent);
+EXPORT(UnHex);
+EXPORT(SwapEndian16);
+EXPORT(SwapEndian32);
+EXPORT(memmove);
+
+// === CODE ===
+/**
+ * \brief Convert a string into an integer
+ */
+int atoi(const char *string)
+{
+ int ret = 0;
+ ParseInt(string, &ret);
+ return ret;
+}
+int ParseInt(const char *string, int *Val)
+{
+ int ret = 0;
+ int bNeg = 0;
+ const char *orig_string = string;
+
+ //Log("atoi: (string='%s')", string);
+
+ // Clear non-numeric characters
+ while( !('0' <= *string && *string <= '9') && *string != '-' ) string++;
+ if( *string == '-' ) {
+ bNeg = 1;
+ while( !('0' <= *string && *string <= '9') ) string++;
+ }
+
+ if(*string == '0')
+ {
+ string ++;
+ if(*string == 'x')
+ {
+ // Hex
+ string ++;
+ for( ;; string ++ )
+ {
+ if('0' <= *string && *string <= '9') {
+ ret *= 16;
+ ret += *string - '0';
+ }
+ else if('A' <= *string && *string <= 'F') {
+ ret *= 16;
+ ret += *string - 'A' + 10;
+ }
+ else if('a' <= *string && *string <= 'f') {
+ ret *= 16;
+ ret += *string - 'a' + 10;
+ }
+ else
+ break;
+ }
+ }
+ else // Octal
+ {
+ for( ; '0' <= *string && *string <= '7'; string ++ )
+ {
+ ret *= 8;
+ ret += *string - '0';
+ }
+ }
+ }
+ else // Decimal
+ {
+ for( ; '0' <= *string && *string <= '9'; string++)
+ {
+ ret *= 10;
+ ret += *string - '0';
+ }
+ // Error check
+ if( ret == 0 ) return 0;
+ }
+
+ if(bNeg) ret = -ret;
+
+ //Log("atoi: RETURN %i", ret);
+
+ if(Val) *Val = ret;
+
+ return string - orig_string;
+}
+
+static const char cUCDIGITS[] = "0123456789ABCDEF";
+/**
+ * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
+ * \brief Convert an integer into a character string
+ */
+void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
+{
+ char tmpBuf[64+1];
+ int pos=0, i;
+ Uint64 rem;
+
+ // Sanity check
+ if(!buf) return;
+
+ // Sanity Check
+ if(base > 16 || base < 2) {
+ buf[0] = 0;
+ return;
+ }
+
+ // Convert
+ while(num > base-1) {
+ num = DivMod64U(num, base, &rem); // Shift `num` and get remainder
+ tmpBuf[pos] = cUCDIGITS[ rem ];
+ pos++;
+ }
+ tmpBuf[pos++] = cUCDIGITS[ num ]; // Last digit of `num`
+
+ // Put in reverse
+ i = 0;
+ minLength -= pos;
+ while(minLength-- > 0) buf[i++] = pad;
+ while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters
+ buf[i] = 0;
+}
+
+/**
+ * \brief Append a character the the vsnprintf output
+ */
+#define PUTCH(c) _putch(c)
+#define GETVAL() do {\
+ if(isLongLong) val = va_arg(args, Uint64);\
+ else val = va_arg(args, unsigned int);\
+ }while(0)
+/**
+ * \brief VArg String Number Print Formatted
+ */
+int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args)
+{
+ char c, pad = ' ';
+ int minSize = 0, precision = -1, len;
+ char tmpBuf[34]; // For Integers
+ const char *p = NULL;
+ int isLongLong = 0;
+ Uint64 val;
+ size_t pos = 0;
+ // Flags
+ int bPadLeft = 0;
+
+ auto void _putch(char ch);
+
+ void _putch(char ch)
+ {
+ if(pos < __maxlen)
+ {
+ if(__s) __s[pos] = ch;
+ pos ++;
+ }
+ }
+
+ while((c = *__format++) != 0)
+ {
+ // Non control character
+ if(c != '%') { PUTCH(c); continue; }
+
+ c = *__format++;
+ if(c == '\0') break;
+
+ // Literal %
+ if(c == '%') { PUTCH('%'); continue; }
+
+ // Pointer - Done first for debugging
+ if(c == 'p') {
+ Uint ptr = va_arg(args, Uint);
+ PUTCH('*'); PUTCH('0'); PUTCH('x');
+ for( len = BITS/4; len --; )
+ PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] );
+ continue ;
+ }
+
+ // - Padding Side Flag
+ if(c == '-') {
+ bPadLeft = 1;
+ c = *__format++;
+ }
+
+ // - Padding
+ if(c == '0') {
+ pad = '0';
+ c = *__format++;
+ }
+ else
+ pad = ' ';
+
+ // - Minimum length
+ if(c == '*') { // Dynamic length
+ minSize = va_arg(args, unsigned int);
+ c = *__format++;
+ }
+ else if('1' <= c && c <= '9')
+ {
+ minSize = 0;
+ while('0' <= c && c <= '9')
+ {
+ minSize *= 10;
+ minSize += c - '0';
+ c = *__format++;
+ }
+ }
+ else
+ minSize = 0;
+
+ // - Precision
+ precision = -1;
+ if( c == '.' ) {
+ c = *__format++;
+
+ if(c == '*') { // Dynamic length
+ precision = va_arg(args, unsigned int);
+ c = *__format++;
+ }
+ else if('1' <= c && c <= '9')
+ {
+ precision = 0;
+ while('0' <= c && c <= '9')
+ {
+ precision *= 10;
+ precision += c - '0';
+ c = *__format++;
+ }
+ }
+ }
+
+ // - Default, Long or LongLong?
+ isLongLong = 0;
+ if(c == 'l') // Long is actually the default on x86
+ {
+ c = *__format++;
+ if(c == 'l') {
+ c = *__format++;
+ isLongLong = 1;
+ }
+ }
+
+ // - Now get the format code
+ p = tmpBuf;
+ switch(c)
+ {
+ case 'd':
+ case 'i':
+ GETVAL();
+ if( isLongLong && val >> 63 ) {
+ PUTCH('-');
+ val = -val;
+ }
+ else if( !isLongLong && val >> 31 ) {
+ PUTCH('-');
+ val = -(Sint32)val;
+ }
+ itoa(tmpBuf, val, 10, minSize, pad);
+ goto printString;
+ case 'u': // Unsigned
+ GETVAL();
+ itoa(tmpBuf, val, 10, minSize, pad);
+ goto printString;
+ case 'P': // Physical Address
+ PUTCH('0');
+ PUTCH('x');
+ if(sizeof(tPAddr) > 4) isLongLong = 1;
+ GETVAL();
+ itoa(tmpBuf, val, 16, minSize, pad);
+ goto printString;
+ case 'X': // Hex
+ if(BITS == 64)
+ isLongLong = 1; // TODO: Handle non-x86 64-bit archs
+ GETVAL();
+ itoa(tmpBuf, val, 16, minSize, pad);
+ goto printString;
+
+ case 'x': // Lower case hex
+ GETVAL();
+ itoa(tmpBuf, val, 16, minSize, pad);
+ goto printString;
+ case 'o': // Octal
+ GETVAL();
+ itoa(tmpBuf, val, 8, minSize, pad);
+ goto printString;
+ case 'b':
+ GETVAL();
+ itoa(tmpBuf, val, 2, minSize, pad);
+ goto printString;
+
+ case 'B': //Boolean
+ val = va_arg(args, unsigned int);
+ if(val) p = "True";
+ else p = "False";
+ goto printString;
+
+ // String - Null Terminated Array
+ case 's':
+ p = va_arg(args, char*); // Get Argument
+ if( !p || !CheckString(p) ) p = "(inval)"; // Avoid #PFs
+ printString:
+ if(!p) p = "(null)";
+ len = strlen(p);
+ if( !bPadLeft ) while(len++ < minSize) PUTCH(pad);
+ while(*p && precision--) PUTCH(*p++);
+ if( bPadLeft ) while(len++ < minSize) PUTCH(pad);
+ break;
+
+ case 'C': // Non-Null Terminated Character Array
+ p = va_arg(args, char*);
+ if( !CheckMem(p, minSize) ) continue; // No #PFs please
+ if(!p) goto printString;
+ while(minSize--) PUTCH(*p++);
+ break;
+
+ // Single Character
+ case 'c':
+ default:
+ GETVAL();
+ PUTCH( (Uint8)val );
+ break;
+ }
+ }
+
+ if(__s && pos != __maxlen)
+ __s[pos] = '\0';
+
+ return pos;
+}
+#undef PUTCH
+
+/**
+ */
+int sprintf(char *__s, const char *__format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, __format);
+ ret = vsnprintf(__s, -1, __format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/**
+ * \fn int tolower(int c)
+ * \brief Converts a character to lower case
+ */
+int tolower(int c)
+{
+ if('A' <= c && c <= 'Z')
+ return c - 'A' + 'a';
+ return c;
+}
+
+/**
+ * \fn int strucmp(const char *Str1, const char *Str2)
+ * \brief Compare \a Str1 and \a Str2 case-insensitively
+ */
+int strucmp(const char *Str1, const char *Str2)
+{
+ while(*Str1 && tolower(*Str1) == tolower(*Str2))
+ Str1++, Str2++;
+ return tolower(*Str1) - tolower(*Str2);
+}
+
+/**
+ * \brief Locate a byte in a string
+ */
+char *strchr(const char *__s, int __c)
+{
+ for( ; *__s; __s ++ )
+ {
+ if( *__s == __c ) return (char*)__s;
+ }
+ return NULL;
+}
+
+/**
+ * \fn int strpos(const char *Str, char Ch)
+ * \brief Search a string for an ascii character
+ */
+int strpos(const char *Str, char Ch)
+{
+ int pos;
+ for(pos=0;Str[pos];pos++)
+ {
+ if(Str[pos] == Ch) return pos;
+ }
+ return -1;
+}
+
+/**
+ * \fn Uint8 ByteSum(void *Ptr, int Size)
+ * \brief Adds the bytes in a memory region and returns the sum
+ */
+Uint8 ByteSum(const void *Ptr, int Size)
+{
+ Uint8 sum = 0;
+ const Uint8 *data = Ptr;
+ while(Size--) sum += *(data++);
+ return sum;
+}
+
+/**
+ * \fn size_t strlen(const char *__str)
+ * \brief Get the length of string
+ */
+size_t strlen(const char *__str)
+{
+ size_t ret = 0;
+ while(*__str++) ret++;
+ return ret;
+}
+
+/**
+ * \brief Copy a string to a new location
+ */
+char *strcpy(char *__str1, const char *__str2)
+{
+ while(*__str2)
+ *__str1++ = *__str2++;
+ *__str1 = '\0'; // Terminate String
+ return __str1;
+}
+
+/**
+ * \brief Copy a string to a new location
+ * \note Copies at most `max` chars
+ */
+char *strncpy(char *__str1, const char *__str2, size_t __max)
+{
+ while(*__str2 && __max-- >= 1)
+ *__str1++ = *__str2++;
+ if(__max)
+ *__str1 = '\0'; // Terminate String
+ return __str1;
+}
+
+/**
+ * \brief Append a string to another
+ */
+char *strcat(char *__dest, const char *__src)
+{
+ while(*__dest++);
+ __dest--;
+ while(*__src)
+ *__dest++ = *__src++;
+ *__dest = '\0';
+ return __dest;
+}
+
+/**
+ * \brief Append at most \a n chars to a string from another
+ * \note At most n+1 chars are written (the dest is always zero terminated)
+ */
+char *strncat(char *__dest, const char *__src, size_t n)
+{
+ while(*__dest++);
+ while(*__src && n-- >= 1)
+ *__dest++ = *__src++;
+ *__dest = '\0';
+ return __dest;
+}
+
+/**
+ * \fn int strcmp(const char *str1, const char *str2)
+ * \brief Compare two strings return the difference between
+ * the first non-matching characters.
+ */
+int strcmp(const char *str1, const char *str2)
+{
+ while(*str1 && *str1 == *str2)
+ str1++, str2++;
+ return *str1 - *str2;
+}
+
+/**
+ * \fn int strncmp(const char *Str1, const char *Str2, size_t num)
+ * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters
+ */
+int strncmp(const char *Str1, const char *Str2, size_t num)
+{
+ if(num == 0) return 0; // TODO: Check what should officially happen here
+ while(--num && *Str1 && *Str1 == *Str2)
+ Str1++, Str2++;
+ return *Str1-*Str2;
+}
+
+#if 0
+/**
+ * \fn char *strdup(const char *Str)
+ * \brief Duplicates a string
+ */
+char *strdup(const char *Str)
+{
+ char *ret;
+ ret = malloc(strlen(Str)+1);
+ if( !ret ) return NULL;
+ strcpy(ret, Str);
+ return ret;
+}
+#else
+
+/**
+ * \fn char *_strdup(const char *File, int Line, const char *Str)
+ * \brief Duplicates a string
+ */
+char *_strdup(const char *File, int Line, const char *Str)
+{
+ char *ret;
+ ret = Heap_Allocate(File, Line, strlen(Str)+1);
+ if( !ret ) return NULL;
+ strcpy(ret, Str);
+ return ret;
+}
+#endif
+
+/**
+ * \brief Split a string using the passed character
+ * \return NULL terminated array of strings on the heap
+ * \param __str String to split
+ * \param __ch Character to split by
+ */
+char **str_split(const char *__str, char __ch)
+{
+ int i, j;
+ int len = 1;
+ char **ret;
+ char *start;
+
+ for( i = 0; __str[i]; i++ )
+ {
+ if(__str[i] == __ch)
+ len ++;
+ }
+
+ ret = malloc( sizeof(char*)*(len+1) + (i + 1) );
+ if( !ret ) return NULL;
+
+ j = 1;
+ start = (char *)&ret[len+1];
+ ret[0] = start;
+ for( i = 0; __str[i]; i++ )
+ {
+ if(__str[i] == __ch) {
+ *start++ = '\0';
+ ret[j++] = start;
+ }
+ else {
+ *start++ = __str[i];
+ }
+ }
+ *start = '\0';
+ ret[j] = NULL;
+
+ return ret;
+}
+
+/**
+ * \fn int DivUp(int num, int dem)
+ * \brief Divide two numbers, rounding up
+ * \param num Numerator
+ * \param dem Denominator
+ */
+int DivUp(int num, int dem)
+{
+ return (num+dem-1)/dem;
+}
+
+/**
+ * \fn int strpos8(const char *str, Uint32 search)
+ * \brief Search a string for a UTF-8 character
+ */
+int strpos8(const char *str, Uint32 Search)
+{
+ int pos;
+ Uint32 val = 0;
+ for(pos=0;str[pos];pos++)
+ {
+ // ASCII Range
+ if(Search < 128) {
+ if(str[pos] == Search) return pos;
+ continue;
+ }
+ if(*(Uint8*)(str+pos) < 128) continue;
+
+ pos += ReadUTF8( (Uint8*)&str[pos], &val );
+ if(val == Search) return pos;
+ }
+ return -1;
+}
+
+/**
+ * \fn int ReadUTF8(Uint8 *str, Uint32 *Val)
+ * \brief Read a UTF-8 character from a string
+ */
+int ReadUTF8(const Uint8 *str, Uint32 *Val)
+{
+ *Val = 0xFFFD; // Assume invalid character
+
+ // ASCII
+ if( !(*str & 0x80) ) {
+ *Val = *str;
+ return 1;
+ }
+
+ // Middle of a sequence
+ if( (*str & 0xC0) == 0x80 ) {
+ return 1;
+ }
+
+ // Two Byte
+ if( (*str & 0xE0) == 0xC0 ) {
+ *Val = (*str & 0x1F) << 6; // Upper 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F); // Lower 6 Bits
+ return 2;
+ }
+
+ // Three Byte
+ if( (*str & 0xF0) == 0xE0 ) {
+ *Val = (*str & 0x0F) << 12; // Upper 4 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F) << 6; // Middle 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F); // Lower 6 Bits
+ return 3;
+ }
+
+ // Four Byte
+ if( (*str & 0xF1) == 0xF0 ) {
+ *Val = (*str & 0x07) << 18; // Upper 3 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits
+ str ++;
+ if( (*str & 0xC0) != 0x80) return -1; // Validity check
+ *Val |= (*str & 0x3F); // Lower 6 Bits
+ return 4;
+ }
+
+ // UTF-8 Doesn't support more than four bytes
+ return 4;
+}
+
+/**
+ * \fn int WriteUTF8(Uint8 *str, Uint32 Val)
+ * \brief Write a UTF-8 character sequence to a string
+ */
+int WriteUTF8(Uint8 *str, Uint32 Val)
+{
+ // ASCII
+ if( Val < 128 ) {
+ if( str ) {
+ *str = Val;
+ }
+ return 1;
+ }
+
+ // Two Byte
+ if( Val < 0x8000 ) {
+ if( str ) {
+ *str++ = 0xC0 | (Val >> 6);
+ *str++ = 0x80 | (Val & 0x3F);
+ }
+ return 2;
+ }
+
+ // Three Byte
+ if( Val < 0x10000 ) {
+ if( str ) {
+ *str++ = 0xE0 | (Val >> 12);
+ *str++ = 0x80 | ((Val >> 6) & 0x3F);
+ *str++ = 0x80 | (Val & 0x3F);
+ }
+ return 3;
+ }
+
+ // Four Byte
+ if( Val < 0x110000 ) {
+ if( str ) {
+ *str++ = 0xF0 | (Val >> 18);
+ *str++ = 0x80 | ((Val >> 12) & 0x3F);
+ *str++ = 0x80 | ((Val >> 6) & 0x3F);
+ *str++ = 0x80 | (Val & 0x3F);
+ }
+ return 4;
+ }
+
+ // UTF-8 Doesn't support more than four bytes
+ return 0;
+}
+
+/**
+ * \fn Uint64 timestamp(int sec, int mins, int hrs, int day, int month, int year)
+ * \brief Converts a date into an Acess Timestamp
+ */
+Sint64 timestamp(int sec, int min, int hrs, int day, int month, int year)
+{
+ int is_leap;
+ Sint64 stamp;
+
+ if( !(0 <= sec && sec < 60) ) return 0;
+ if( !(0 <= min && min < 60) ) return 0;
+ if( !(0 <= hrs && hrs < 24) ) return 0;
+ if( !(0 <= day && day < 31) ) return 0;
+ if( !(0 <= month && month < 12) ) return 0;
+
+ stamp = DAYS_BEFORE[month] + day;
+
+ // Every 4 years
+ // - every 100 years
+ // + every 400 years
+ is_leap = (year % 4 == 0) - (year % 100 == 0) + (year % 400 == 0);
+ ASSERT(is_leap == 0 || is_leap == 1);
+
+ if( is_leap && month > 1 ) // Leap year and after feb
+ stamp += 1;
+
+ // Get seconds before the first of specified year
+ year -= 2000; // Base off Y2K
+ // base year days + total leap year days
+ stamp += year*365 + (year/400) - (year/100) + (year/4);
+
+ stamp *= 3600*24;
+ stamp += UNIX_TO_2K;
+ stamp += sec;
+ stamp += min*60;
+ stamp += hrs*3600;
+
+ return stamp * 1000;
+}
+
+void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms)
+{
+ int is_leap = 0, i;
+
+ auto Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R);
+
+ Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R)
+ {
+ int sign = (N < 0) != (D < 0);
+ if(N < 0) N = -N;
+ if(D < 0) D = -D;
+ if(sign)
+ return -DivMod64U(N, D, (Uint64*)R);
+ else
+ return DivMod64U(N, D, (Uint64*)R);
+ }
+
+ // Get time
+ // TODO: Leap-seconds?
+ {
+ Sint64 rem;
+ TS = DivMod64( TS, 1000, &rem );
+ *ms = rem;
+ TS = DivMod64( TS, 60, &rem );
+ *sec = rem;
+ TS = DivMod64( TS, 60, &rem );
+ *mins = rem;
+ TS = DivMod64( TS, 24, &rem );
+ *hrs = rem;
+ }
+
+ // Adjust to Y2K
+ TS -= UNIX_TO_2K/(3600*24);
+
+ // Year (400 yr blocks) - (400/4-3) leap years
+ *year = 400 * DivMod64( TS, 365*400 + (400/4-3), &TS );
+ if( TS < 366 ) // First year in 400 is a leap
+ is_leap = 1;
+ else
+ {
+ // 100 yr blocks - 100/4-1 leap years
+ *year += 100 * DivMod64( TS, 365*100 + (100/4-1), &TS );
+ if( TS < 366 ) // First year in 100 isn't a leap
+ is_leap = 0;
+ else
+ {
+ *year += 4 * DivMod64( TS, 365*4 + 1, &TS );
+ if( TS < 366 ) // First year in 4 is a leap
+ is_leap = 1;
+ else
+ {
+ *year += DivMod64( TS, 356, &TS );
+ }
+ }
+ }
+ *year += 2000;
+
+ ASSERT(TS >= 0);
+
+ *day = 0;
+ // Month (if after the first of march, which is 29 Feb in a leap year)
+ if( is_leap && TS > DAYS_BEFORE[2] ) {
+ TS -= 1; // Shifts 29 Feb to 28 Feb
+ *day = 1;
+ }
+ // Get what month it is
+ for( i = 0; i < 12; i ++ ) {
+ if( TS < DAYS_BEFORE[i] )
+ break;
+ }
+ *month = i - 1;
+ // Get day
+ TS -= DAYS_BEFORE[i-1];
+ *day += TS; // Plus offset from leap handling above
+}
+
+/**
+ * \fn int rand()
+ * \brief Pseudo random number generator
+ */
+int rand(void)
+{
+ #if 0
+ static Uint state = RANDOM_SEED;
+ Uint old = state;
+ // Get the next state value
+ giRandomState = (RANDOM_A*state + RANDOM_C);
+ // Check if it has changed, and if it hasn't, change it
+ if(state == old) state += RANDOM_SPRUCE;
+ return state;
+ #else
+ // http://en.wikipedia.org/wiki/Xorshift
+ // 2010-10-03
+ static Uint32 x = 123456789;
+ static Uint32 y = 362436069;
+ static Uint32 z = 521288629;
+ static Uint32 w = 88675123;
+ Uint32 t;
+
+ t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ return w = w ^ (w >> 19) ^ t ^ (t >> 8);
+ #endif
+}
+
+/* *
+ * \name Memory Validation
+ * \{
+ */
+/**
+ * \brief Checks if a string resides fully in valid memory
+ */
+int CheckString(const char *String)
+{
+ tVAddr addr;
+ int bUser;
+
+ addr = (tVAddr)String;
+
+ if( !MM_GetPhysAddr( addr ) )
+ return 0;
+
+ // Check 1st page
+ bUser = MM_IsUser( addr );
+
+ while( *(char*)addr )
+ {
+ if( (addr & (PAGE_SIZE-1)) == 0 )
+ {
+ if(bUser && !MM_IsUser(addr) )
+ return 0;
+ if(!bUser && !MM_GetPhysAddr(addr) )
+ return 0;
+ }
+ addr ++;
+ }
+ return 1;
+}
+
+/**
+ * \brief Check if a sized memory region is valid memory
+ * \return Boolean success
+ */
+int CheckMem(const void *Mem, int NumBytes)
+{
+ return MM_IsValidBuffer( (tVAddr)Mem, NumBytes );
+}
+/* *
+ * \}
+ */
+
+/**
+ * \brief Search a string array for \a Needle
+ * \note Helper function for eTplDrv_IOCtl::DRV_IOCTL_LOOKUP
+ */
+int ModUtil_LookupString(const char **Array, const char *Needle)
+{
+ int i;
+ if( !CheckString(Needle) ) return -1;
+ for( i = 0; Array[i]; i++ )
+ {
+ if(strcmp(Array[i], Needle) == 0) return i;
+ }
+ return -1;
+}
+
+int ModUtil_SetIdent(char *Dest, const char *Value)
+{
+ if( !CheckMem(Dest, 32) ) return -1;
+ strncpy(Dest, Value, 32);
+ return 1;
+}
+
+int Hex(char *Dest, size_t Size, const Uint8 *SourceData)
+{
+ int i;
+ for( i = 0; i < Size; i ++ )
+ {
+ sprintf(Dest + i*2, "%02x", SourceData[i]);
+ }
+ return i*2;
+}
+
+/**
+ * \brief Convert a string of hexadecimal digits into a byte stream
+ */
+int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString)
+{
+ int i;
+
+ for( i = 0; i < DestSize*2; i += 2 )
+ {
+ Uint8 val = 0;
+
+ if(SourceString[i] == '\0') break;
+
+ if('0' <= SourceString[i] && SourceString[i] <= '9')
+ val |= (SourceString[i]-'0') << 4;
+ else if('A' <= SourceString[i] && SourceString[i] <= 'F')
+ val |= (SourceString[i]-'A'+10) << 4;
+ else if('a' <= SourceString[i] && SourceString[i] <= 'f')
+ val |= (SourceString[i]-'a'+10) << 4;
+
+ if(SourceString[i+1] == '\0') break;
+
+ if('0' <= SourceString[i+1] && SourceString[i+1] <= '9')
+ val |= (SourceString[i+1] - '0');
+ else if('A' <= SourceString[i+1] && SourceString[i+1] <= 'F')
+ val |= (SourceString[i+1] - 'A' + 10);
+ else if('a' <= SourceString[i+1] && SourceString[i+1] <= 'f')
+ val |= (SourceString[i+1] - 'a' + 10);
+
+ Dest[i/2] = val;
+ }
+ return i/2;
+}
+
+Uint16 SwapEndian16(Uint16 Val)
+{
+ return ((Val&0xFF)<<8) | ((Val>>8)&0xFF);
+}
+Uint32 SwapEndian32(Uint32 Val)
+{
+ return ((Val&0xFF)<<24) | ((Val&0xFF00)<<8) | ((Val>>8)&0xFF00) | ((Val>>24)&0xFF);
+}
+
+void *memmove(void *__dest, const void *__src, size_t len)
+{
+ size_t block_size;
+ char *dest = __dest;
+ const char *src = __src;
+ void *ret = __dest;
+
+ if( len == 0 || dest == src )
+ return dest;
+
+ if( (tVAddr)dest > (tVAddr)src + len )
+ return memcpy(dest, src, len);
+ if( (tVAddr)dest + len < (tVAddr)src )
+ return memcpy(dest, src, len);
+
+ // NOTE: Assumes memcpy works forward
+ if( (tVAddr)dest < (tVAddr)src )
+ return memcpy(dest, src, len);
+
+ if( (tVAddr)dest < (tVAddr)src )
+ block_size = (tVAddr)src - (tVAddr)dest;
+ else
+ block_size = (tVAddr)dest - (tVAddr)src;
+
+ block_size &= ~0xF;
+
+ while(len >= block_size)
+ {
+ memcpy(dest, src, block_size);
+ len -= block_size;
+ dest += block_size;
+ src += block_size;
+ }
+ memcpy(dest, src, len);
+ return ret;
+
+}
+
--- /dev/null
+/*
+ * Acess 2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * logging.c - Kernel Logging Service
+ */
+#include <acess.h>
+#include <adt.h>
+
+#define CACHE_MESSAGES 0
+#define PRINT_ON_APPEND 1
+#define USE_RING_BUFFER 1
+#define RING_BUFFER_SIZE 4096
+
+// === CONSTANTS ===
+enum eLogLevels
+{
+ LOG_LEVEL_KPANIC,
+ LOG_LEVEL_PANIC,
+ LOG_LEVEL_FATAL,
+ LOG_LEVEL_ERROR,
+ LOG_LEVEL_WARNING,
+ LOG_LEVEL_NOTICE,
+ LOG_LEVEL_LOG,
+ LOG_LEVEL_DEBUG,
+ NUM_LOG_LEVELS
+};
+const char *csaLevelColours[] = {
+ "\x1B[35m", "\x1B[34m", "\x1B[36m", "\x1B[31m",
+ "\x1B[33m", "\x1B[32m", "\x1B[0m", "\x1B[0m"
+ };
+const char *csaLevelCodes[] = {"k","p","f","e","w","n","l","d"};
+
+// === TYPES ===
+typedef struct sLogEntry
+{
+ struct sLogEntry *Next;
+ struct sLogEntry *LevelNext;
+ Sint64 Time;
+ int Level;
+ int Length;
+ char Ident[9];
+ char Data[];
+} tLogEntry;
+typedef struct sLogList
+{
+ tLogEntry *Head;
+ tLogEntry *Tail;
+} tLogList;
+
+// === PROTOTYPES ===
+void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args);
+static void Log_Int_PrintMessage(tLogEntry *Entry);
+//void Log_KernelPanic(const char *Ident, const char *Message, ...);
+//void Log_Panic(const char *Ident, const char *Message, ...);
+//void Log_Error(const char *Ident, const char *Message, ...);
+//void Log_Warning(const char *Ident, const char *Message, ...);
+//void Log_Notice(const char *Ident, const char *Message, ...);
+//void Log_Log(const char *Ident, const char *Message, ...);
+//void Log_Debug(const char *Ident, const char *Message, ...);
+
+// === EXPORTS ===
+EXPORT(Log_Panic);
+EXPORT(Log_Error);
+EXPORT(Log_Warning);
+EXPORT(Log_Notice);
+EXPORT(Log_Log);
+EXPORT(Log_Debug);
+
+// === GLOBALS ===
+tShortSpinlock glLogOutput;
+#if USE_RING_BUFFER
+Uint8 gaLog_RingBufferData[sizeof(tRingBuffer)+RING_BUFFER_SIZE];
+tRingBuffer *gpLog_RingBuffer = (void*)gaLog_RingBufferData;
+#else
+tMutex glLog;
+tLogList gLog;
+tLogList gLog_Levels[NUM_LOG_LEVELS];
+#endif
+
+// === CODE ===
+/**
+ * \brief Adds an event to the log
+ */
+void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args)
+{
+ int len;
+ tLogEntry *ent;
+ va_list args_tmp;
+
+ if( Level >= NUM_LOG_LEVELS ) return;
+
+ va_copy(args_tmp, Args);
+ len = vsnprintf(NULL, 256, Format, args_tmp);
+
+ //Log("len = %i", len);
+
+ #if USE_RING_BUFFER || !CACHE_MESSAGES
+ {
+ char buf[sizeof(tLogEntry)+len+1];
+ ent = (void*)buf;
+ #else
+ ent = malloc(sizeof(tLogEntry)+len+1);
+ #endif
+ ent->Time = now();
+ strncpy(ent->Ident, Ident, 8);
+ ent->Ident[8] = '\0';
+ ent->Level = Level;
+ ent->Length = len;
+ vsnprintf( ent->Data, len+1, Format, Args );
+
+ #if CACHE_MESSAGES
+ # if USE_RING_BUFFER
+ {
+ #define LOG_HDR_LEN (14+1+2+8+2)
+ char newData[ LOG_HDR_LEN + len + 2 + 1 ];
+ char _ident[9];
+ strncpy(_ident, Ident, 9);
+ sprintf( newData, "%014lli%s [%-8s] ",
+ ent->Time, csaLevelCodes[Level], Ident);
+ strcpy( newData + LOG_HDR_LEN, ent->Data );
+ strcpy( newData + LOG_HDR_LEN + len, "\r\n" );
+ gpLog_RingBuffer->Space = RING_BUFFER_SIZE; // Needed to init the buffer
+ RingBuffer_Write( gpLog_RingBuffer, newData, LOG_HDR_LEN + len + 2 );
+ }
+ # else
+ Mutex_Acquire( &glLog );
+
+ ent->Next = gLog.Tail;
+ if(gLog.Head)
+ gLog.Tail = ent;
+ else
+ gLog.Tail = gLog.Head = ent;
+
+ ent->LevelNext = gLog_Levels[Level].Tail;
+ if(gLog_Levels[Level].Head)
+ gLog_Levels[Level].Tail = ent;
+ else
+ gLog_Levels[Level].Tail = gLog_Levels[Level].Head = ent;
+
+ Mutex_Release( &glLog );
+ # endif
+ #endif
+
+ #if PRINT_ON_APPEND || !CACHE_MESSAGES
+ Log_Int_PrintMessage( ent );
+ #endif
+
+ #if USE_RING_BUFFER || !CACHE_MESSAGES
+ }
+ #endif
+}
+
+/**
+ * \brief Prints a log message to the debug console
+ */
+void Log_Int_PrintMessage(tLogEntry *Entry)
+{
+ SHORTLOCK( &glLogOutput );
+ LogF("%s%014lli%s [%-8s] %i - %s",
+ csaLevelColours[Entry->Level],
+ Entry->Time,
+ csaLevelCodes[Entry->Level],
+ Entry->Ident,
+ Threads_GetTID(),
+ Entry->Data
+ );
+ LogF("\x1B[0m\r\n"); // Separate in case Entry->Data is too long
+ SHORTREL( &glLogOutput );
+}
+
+/**
+ * \brief KERNEL PANIC!!!!
+ */
+void Log_KernelPanic(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_KPANIC, Message, args);
+ va_end(args);
+ Panic("Log_KernelPanic - %s", Ident);
+}
+
+/**
+ * \brief Panic Message - Driver Unrecoverable error
+ */
+void Log_Panic(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_PANIC, Message, args);
+ va_end(args);
+}
+
+/**
+ * \brief Error Message - Recoverable Error
+ */
+void Log_Error(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_ERROR, Message, args);
+ va_end(args);
+}
+
+/**
+ * \brief Warning Message - Something the user should know
+ */
+void Log_Warning(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_WARNING, Message, args);
+ va_end(args);
+}
+
+/**
+ * \brief Notice Message - Something the user might like to know
+ */
+void Log_Notice(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_NOTICE, Message, args);
+ va_end(args);
+}
+
+/**
+ * \brief Log Message - Possibly useful information
+ */
+void Log_Log(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_LOG, Message, args);
+ va_end(args);
+}
+
+/**
+ * \brief Debug Message - Only a developer would want this info
+ */
+void Log_Debug(const char *Ident, const char *Message, ...)
+{
+ va_list args;
+ va_start(args, Message);
+ Log_AddEvent(Ident, LOG_LEVEL_DEBUG, Message, args);
+ va_end(args);
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * messages.c
+ * - IPC Messages
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <threads.h>
+#include <threads_int.h>
+#include <errno.h>
+#include <events.h>
+
+// === CODE ===
+/**
+ * \fn int Proc_SendMessage(Uint Dest, int Length, void *Data)
+ * \brief Send an IPC message
+ * \param Dest Destination Thread
+ * \param Length Length of the message
+ * \param Data Message data
+ */
+int Proc_SendMessage(Uint Dest, int Length, void *Data)
+{
+ tThread *thread;
+ tMsg *msg;
+
+ ENTER("pErr iDest iLength pData", Err, Dest, Length, Data);
+
+ if(Length <= 0 || !Data) {
+ errno = -EINVAL;
+ LEAVE_RET('i', -1);
+ }
+
+ // TODO: Check message length against global/per-thread maximums
+ // TODO: Restrict queue length
+
+ // Get thread
+ thread = Threads_GetThread( Dest );
+
+ // Error check
+ if(!thread) LEAVE_RET('i', -1);
+
+ // Get Spinlock
+ SHORTLOCK( &thread->IsLocked );
+
+ // Check if thread is still alive
+ if(thread->Status == THREAD_STAT_DEAD) {
+ SHORTREL( &thread->IsLocked );
+ LEAVE_RET('i', -1);
+ }
+
+ // Create message
+ msg = malloc( sizeof(tMsg)+Length );
+ msg->Next = NULL;
+ msg->Source = Proc_GetCurThread()->TID;
+ msg->Length = Length;
+ memcpy(msg->Data, Data, Length);
+
+ // If there are already messages
+ if(thread->LastMessage) {
+ thread->LastMessage->Next = msg;
+ thread->LastMessage = msg;
+ } else {
+ thread->Messages = msg;
+ thread->LastMessage = msg;
+ }
+
+ SHORTREL(&thread->IsLocked);
+
+ // Wake the thread
+ LOG("Waking %p (%i %s)", thread, thread->TID, thread->ThreadName);
+ Threads_PostEvent( thread, THREAD_EVENT_IPCMSG );
+
+ LEAVE_RET('i', 0);
+}
+
+/**
+ * \fn int Proc_GetMessage(Uint *Source, void *Buffer)
+ * \brief Gets a message
+ * \param Source Where to put the source TID
+ * \param Buffer Buffer to place the message data (set to NULL to just get message length)
+ * \return Message length
+ */
+int Proc_GetMessage(Uint *Source, void *Buffer)
+{
+ int ret;
+ void *tmp;
+ tThread *cur = Proc_GetCurThread();
+
+ ENTER("pSource pBuffer", Source, Buffer);
+
+ // Check if queue has any items
+ if(!cur->Messages) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ SHORTLOCK( &cur->IsLocked );
+
+ if(Source) {
+ *Source = cur->Messages->Source;
+ LOG("*Source = %i", *Source);
+ }
+
+ // Get message length
+ if( !Buffer ) {
+ ret = cur->Messages->Length;
+ SHORTREL( &cur->IsLocked );
+ LEAVE('i', ret);
+ return ret;
+ }
+
+ // Get message
+ if(Buffer != GETMSG_IGNORE)
+ {
+ if( !CheckMem( Buffer, cur->Messages->Length ) )
+ {
+ LOG("Invalid buffer");
+ errno = -EINVAL;
+ SHORTREL( &cur->IsLocked );
+ LEAVE('i', -1);
+ return -1;
+ }
+ LOG("Copied to buffer");
+ memcpy(Buffer, cur->Messages->Data, cur->Messages->Length);
+ }
+ ret = cur->Messages->Length;
+
+ // Remove from list
+ tmp = cur->Messages;
+ cur->Messages = cur->Messages->Next;
+ if(cur->Messages == NULL) cur->LastMessage = NULL;
+
+ SHORTREL( &cur->IsLocked );
+
+ free(tmp); // Free outside of lock
+
+ LEAVE('i', ret);
+ return ret;
+}
--- /dev/null
+/*
+ * Acess2
+ * - Module Loader
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <modules.h>
+
+#define USE_EDI 0
+#define USE_UDI 0
+
+// === PROTOTYPES ===
+ int Module_int_Initialise(tModule *Module, const char *ArgString);
+void Modules_int_GetBuiltinArray(void);
+void Modules_LoadBuiltins(void);
+void Modules_SetBuiltinParams(const char *Name, char *ArgString);
+ int Modules_InitialiseBuiltin(const char *Name);
+// int Module_RegisterLoader(tModuleLoader *Loader);
+// int Module_LoadMem(void *Buffer, Uint Length, char *ArgString);
+// int Module_LoadFile(char *Path, char *ArgString);
+ int Module_int_ResolveDeps(tModule *Info);
+ int Module_IsLoaded(const char *Name);
+// int Module_EnsureLoaded(const char *Name);
+
+// === EXPORTS ===
+EXPORT(Module_RegisterLoader);
+
+// === IMPORTS ===
+#if USE_UDI
+extern int UDI_LoadDriver(void *Base);
+#endif
+extern void StartupPrint(const char *Str);
+extern tModule gKernelModules;
+extern tModule gKernelModulesEnd;
+
+// === GLOBALS ===
+ int giNumBuiltinModules = 0;
+tShortSpinlock glModuleSpinlock;
+tModule *gLoadedModules = NULL;
+tModuleLoader *gModule_Loaders = NULL;
+tModule *gLoadingModules = NULL;
+tModule **gapBuiltinModules = NULL;
+char **gasBuiltinModuleArgs;
+
+// === CODE ===
+/**
+ * \brief Initialises a module
+ * \param Module Pointer to the module header
+ * \param ArgString Comma separated list of module arguments
+ * \return Zero on success, eModuleErrors or -1 on error
+ * \retval -1 Returned if a dependency fails, or a circular dependency
+ * exists.
+ * \retval 0 Returned on success
+ * \retval >0 Error code form the module's initialisation function
+ */
+int Module_int_Initialise(tModule *Module, const char *ArgString)
+{
+ int i, j;
+ int ret;
+ const char **deps;
+ char **args;
+ tModule *mod;
+
+ ENTER("pModule", Module);
+ LOG("Module->Magic = 0x%x", Module->Magic);
+ if(Module->Magic != MODULE_MAGIC) {
+ Log_Warning(
+ "Module",
+ "Module %p is no a valid Acess2 module (0x%08x != 0x%08x)",
+ Module, Module->Magic, MODULE_MAGIC
+ );
+ LEAVE('i', MODULE_ERR_BADMODULE);
+ return MODULE_ERR_BADMODULE;
+ }
+ LOG("Module->Name = %p \"%s\"", Module->Name, Module->Name);
+
+ if(Module->Arch != MODULE_ARCH_ID) {
+ Log_Warning(
+ "Module",
+ "Module %p (%s) is for another architecture (%i)",
+ Module, Module->Name, Module->Arch
+ );
+ }
+
+ deps = Module->Dependencies;
+
+ // Check if the module has been loaded
+ for( mod = gLoadedModules; mod; mod = mod->Next )
+ {
+ if(mod == Module) LEAVE_RET('i', 0);
+ }
+
+ // Add to the "loading" (prevents circular deps)
+ Module->Next = gLoadingModules;
+ gLoadingModules = Module;
+
+ // Scan dependency list
+ for( j = 0; deps && deps[j]; j++ )
+ {
+ // Check if the module is already loaded
+ for( mod = gLoadedModules; mod; mod = mod->Next )
+ {
+ if(strcmp(deps[j], mod->Name) == 0)
+ break;
+ }
+ if( mod ) continue; // Dependency is loaded, check the rest
+
+ // Ok, check if it's loading
+ for( mod = gLoadingModules->Next; mod; mod = mod->Next )
+ {
+ if(strcmp(deps[j], mod->Name) == 0)
+ break;
+ }
+ if( mod ) {
+ Log_Warning("Module", "Circular dependency detected (%s and %s)",
+ mod->Name, Module->Name);
+ LEAVE_RET('i', -1);
+ }
+
+ // So, if it's not loaded, we better load it then
+ for( i = 0; i < giNumBuiltinModules; i ++ )
+ {
+ if( strcmp(deps[j], gapBuiltinModules[i]->Name) == 0 )
+ break;
+ }
+ if( i == giNumBuiltinModules ) {
+ Log_Warning("Module", "Dependency '%s' for module '%s' failed",
+ deps[j], Module->Name);
+ return -1;
+ }
+
+ // Dependency is not loaded, so load it
+ ret = Module_int_Initialise(
+ gapBuiltinModules[i],
+ gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL
+ );
+ if( ret )
+ {
+ // The only "ok" error is NOTNEEDED
+ if(ret != MODULE_ERR_NOTNEEDED)
+ LEAVE_RET('i', -1);
+ }
+ }
+
+ // All Dependencies OK? Initialise
+ StartupPrint(Module->Name);
+ Log_Log("Module", "Starting %p '%s' v%i.%i",
+ Module, Module->Name,
+ Module->Version >> 8, Module->Version & 0xFF
+ );
+
+ if( ArgString )
+ args = str_split( ArgString, ',' );
+ else
+ args = NULL;
+
+ ret = Module->Init(args);
+
+ if(args) free(args);
+
+ // Remove from loading list
+ gLoadingModules = gLoadingModules->Next;
+
+ if( ret != MODULE_ERR_OK ) {
+ switch(ret)
+ {
+ case MODULE_ERR_MISC:
+ Log_Warning("Module", "Unable to load, reason: Miscelanious");
+ break;
+ case MODULE_ERR_NOTNEEDED:
+ Log_Debug("Module", "Unable to load, reason: Module not needed");
+ break;
+ case MODULE_ERR_MALLOC:
+ Log_Warning("Module", "Unable to load, reason: Error in malloc/realloc/calloc, probably not good");
+ break;
+ default:
+ Log_Warning("Module", "Unable to load reason - Unknown code %i", ret);
+ break;
+ }
+ LEAVE_RET('i', ret);
+ return ret;
+ }
+ LOG("ret = %i", ret);
+
+ // Add to loaded list
+ SHORTLOCK( &glModuleSpinlock );
+ Module->Next = gLoadedModules;
+ gLoadedModules = Module;
+ SHORTREL( &glModuleSpinlock );
+
+ LEAVE_RET('i', 0);
+}
+
+/**
+ * \brief Scans the builtin modules and creates an array of them
+ */
+void Modules_int_GetBuiltinArray(void)
+{
+ int i;
+ tModule *module;
+
+ // Count
+ module = &gKernelModules;
+ i = 0;
+ while( (tVAddr)module < (tVAddr)&gKernelModulesEnd )
+ {
+ if(module->Magic == MODULE_MAGIC) {
+ i ++;
+ module ++;
+ }
+ else
+ module = (void*)( (tVAddr)module + 4 );
+ }
+
+ // Create
+ giNumBuiltinModules = i;
+ gasBuiltinModuleArgs = calloc( giNumBuiltinModules, sizeof(char*) );
+ gapBuiltinModules = malloc( giNumBuiltinModules * sizeof(tModule*) );
+
+
+ // Fill
+ module = &gKernelModules;
+ i = 0;
+ while( (tVAddr)module < (tVAddr)&gKernelModulesEnd )
+ {
+ if(module->Magic == MODULE_MAGIC) {
+ gapBuiltinModules[i] = module;
+ i ++;
+ module ++;
+ }
+ else
+ module = (void*)( (tVAddr)module + 4 );
+ }
+}
+
+/**
+ * \brief Initialises builtin modules
+ */
+void Modules_LoadBuiltins()
+{
+ int i;
+
+ if( !gapBuiltinModules )
+ Modules_int_GetBuiltinArray();
+
+ for( i = 0; i < giNumBuiltinModules; i++ )
+ {
+ Module_int_Initialise(
+ gapBuiltinModules[i],
+ (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL)
+ );
+ }
+
+ if( gasBuiltinModuleArgs != NULL )
+ free(gasBuiltinModuleArgs);
+}
+
+/**
+ * \brief Initialise a builtin module given it's name
+ *
+ * E.g. Used by VTerm to load an alternate video driver at runtime
+ */
+int Modules_InitialiseBuiltin(const char *Name)
+{
+ int i;
+
+ // Check if it's loaded
+ if( Module_IsLoaded(Name) )
+ return 0;
+
+ if( !gapBuiltinModules )
+ Modules_int_GetBuiltinArray();
+
+ for( i = 0; i < giNumBuiltinModules; i++ )
+ {
+ if( strcmp(gapBuiltinModules[i]->Name, Name) == 0 ) {
+ return Module_int_Initialise(gapBuiltinModules[i],
+ (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL)
+ );
+ }
+ }
+ return -1;
+}
+
+/**
+ * \brief Sets the parameters for a builtin module
+ */
+void Modules_SetBuiltinParams(const char *Name, char *ArgString)
+{
+ int i;
+
+ if( gasBuiltinModuleArgs == NULL )
+ {
+ Modules_int_GetBuiltinArray();
+ }
+
+ // I hate expensive scans
+ for( i = 0; i < giNumBuiltinModules; i++ )
+ {
+ if(strcmp( gapBuiltinModules[i]->Name, Name ) == 0) {
+ gasBuiltinModuleArgs[i] = ArgString;
+ return ;
+ }
+ }
+
+ Log_Warning("Modules", "Unknown builtin kernel module '%s'", Name);
+}
+
+/**
+ * \brief Registers a tModuleLoader with the kernel
+ * \param Loader Pointer to loader structure (must be persistent)
+ */
+int Module_RegisterLoader(tModuleLoader *Loader)
+{
+ if(!Loader) return 1;
+
+ Loader->Next = gModule_Loaders;
+ gModule_Loaders = Loader;
+
+ return 0;
+}
+
+/**
+ * \fn int Module_LoadMem(void *Buffer, Uint Length, char *ArgString)
+ * \brief Load a module from a memory location
+ */
+int Module_LoadMem(void *Buffer, Uint Length, const char *ArgString)
+{
+ char path[VFS_MEMPATH_SIZE];
+
+ VFS_GetMemPath(path, Buffer, Length);
+
+ return Module_LoadFile( path, ArgString );
+}
+
+/**
+ * \fn int Module_LoadFile(const char *Path, const char *ArgString)
+ * \brief Load a module from a file
+ */
+int Module_LoadFile(const char *Path, const char *ArgString)
+{
+ void *base;
+ tModule *info;
+
+ // Load Binary
+ base = Binary_LoadKernel(Path);
+
+ // Error check
+ if(base == NULL) {
+ Log_Warning("Module", "Module_LoadFile - Unable to load '%s'", Path);
+ return 0;
+ }
+
+ // Check for Acess Driver
+ if( Binary_FindSymbol(base, "DriverInfo", (Uint*)&info ) == 0 )
+ {
+ tModuleLoader *tmp;
+ for( tmp = gModule_Loaders; tmp; tmp = tmp->Next)
+ {
+ if( tmp->Detector(base) == 0 ) continue;
+
+ return tmp->Loader(base);
+ }
+
+ #if USE_EDI
+ // Check for EDI Driver
+ if( Binary_FindSymbol(base, "driver_init", NULL ) != 0 )
+ {
+ return Module_InitEDI( base ); // And intialise
+ }
+ #endif
+
+ // Unknown module type?, return error
+ Binary_Unload(base);
+ #if USE_EDI
+ Log_Warning("Module", "Module '%s' has neither a Module Info struct, nor an EDI entrypoint", Path);
+ #else
+ Log_Warning("Module", "Module '%s' does not have a Module Info struct", Path);
+ #endif
+ return 0;
+ }
+
+ // Initialise (and register)
+ if( Module_int_Initialise( info, ArgString ) )
+ {
+ Binary_Unload(base);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \fn int Module_int_ResolveDeps(tModule *Info)
+ * \brief Resolves the dependencies
+ * \todo Implement
+ * \note Currently does not resolve the dependencies, just checks them
+ */
+int Module_int_ResolveDeps(tModule *Info)
+{
+ const char **names = Info->Dependencies;
+
+ // Walk dependencies array
+ for( ; *names; names++ )
+ {
+ // Check if the module is loaded
+ if( !Module_IsLoaded(*names) ) {
+ Log_Warning("Module", "Module `%s' requires `%s', which is not loaded\n", Info->Name, *names);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * \fn int Module_IsLoaded(const char *Name)
+ * \brief Checks if a module is loaded
+ * \param Name Name of module to find
+ */
+int Module_IsLoaded(const char *Name)
+{
+ tModule *mod = gLoadedModules;
+
+ // Scan loaded list
+ for( ; mod; mod = mod->Next )
+ {
+ // If found, return true
+ if(strcmp(mod->Name, Name) == 0)
+ return 1;
+ }
+ // not found - return false
+ return 0;
+}
+
+/**
+ * \brief Load a module if needed
+ */
+int Module_EnsureLoaded(const char *Name)
+{
+ if( Module_IsLoaded(Name) )
+ return 0;
+
+ if( Modules_InitialiseBuiltin(Name) == 0 )
+ return 0;
+
+ // TODO: Load from a file?
+
+ return -1;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * mutex.c
+ * - Mutexes
+ */
+#include <acess.h>
+#include <threads_int.h>
+#include <mutex.h>
+
+// === PROTOTYPES ===
+#if 0
+ int Mutex_Acquire(tMutex *Mutex);
+void Mutex_Release(tMutex *Mutex);
+ int Mutex_IsLocked(tMutex *Mutex);
+#endif
+
+// === CODE ===
+//
+// Acquire mutex (see mutex.h for documentation)
+//
+int Mutex_Acquire(tMutex *Mutex)
+{
+ tThread *us = Proc_GetCurThread();
+
+ // Get protector
+ SHORTLOCK( &Mutex->Protector );
+
+// Log("Mutex_Acquire: (%p)", Mutex);
+
+ // Check if the lock is already held
+ if( Mutex->Owner ) {
+ SHORTLOCK( &glThreadListLock );
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_MUTEXSLEEP;
+ us->WaitPointer = Mutex;
+
+ // - Add to waiting
+ if(Mutex->LastWaiting) {
+ Mutex->LastWaiting->Next = us;
+ Mutex->LastWaiting = us;
+ }
+ else {
+ Mutex->Waiting = us;
+ Mutex->LastWaiting = us;
+ }
+
+ #if DEBUG_TRACE_STATE
+ Log("%p (%i %s) waiting on mutex %p",
+ us, us->TID, us->ThreadName, Mutex);
+ #endif
+
+ #if 0
+ {
+ int i = 0;
+ tThread *t;
+ for( t = Mutex->Waiting; t; t = t->Next, i++ )
+ Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i,
+ t, t->TID, t->ThreadName);
+ }
+ #endif
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Mutex->Protector );
+ while(us->Status == THREAD_STAT_MUTEXSLEEP) Threads_Yield();
+ // We're only woken when we get the lock
+ us->WaitPointer = NULL;
+ }
+ // Ooh, let's take it!
+ else {
+ Mutex->Owner = us;
+ SHORTREL( &Mutex->Protector );
+ }
+
+ #if 0
+ extern tMutex glPhysAlloc;
+ if( Mutex != &glPhysAlloc )
+ LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0));
+ #endif
+
+ return 0;
+}
+
+// Release a mutex
+void Mutex_Release(tMutex *Mutex)
+{
+ SHORTLOCK( &Mutex->Protector );
+ //Log("Mutex_Release: (%p)", Mutex);
+ if( Mutex->Waiting ) {
+ Mutex->Owner = Mutex->Waiting; // Set owner
+ Mutex->Waiting = Mutex->Waiting->Next; // Next!
+ // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
+ if( Mutex->LastWaiting == Mutex->Owner )
+ Mutex->LastWaiting = NULL;
+
+ // Wake new owner
+ if( Mutex->Owner->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(Mutex->Owner);
+ }
+ else {
+ Mutex->Owner = NULL;
+ }
+ SHORTREL( &Mutex->Protector );
+
+ #if 0
+ extern tMutex glPhysAlloc;
+ if( Mutex != &glPhysAlloc )
+ LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0));
+ #endif
+}
+
+// Check if a mutex is locked
+int Mutex_IsLocked(tMutex *Mutex)
+{
+ return Mutex->Owner != NULL;
+}
+
+// === EXPORTS ===
+EXPORT(Mutex_Acquire);
+EXPORT(Mutex_Release);
+EXPORT(Mutex_IsLocked);
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * semaphore.c
+ * - Semaphores
+ */
+#include <acess.h>
+#include <semaphore.h>
+#include <threads_int.h>
+
+#define SEMAPHORE_DEBUG 0 // Debug semaphores
+
+// === CODE ===
+//
+// Initialise a semaphore
+//
+void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
+{
+ memset(Sem, 0, sizeof(tSemaphore));
+ Sem->Value = Value;
+ Sem->ModName = Module;
+ Sem->Name = Name;
+ Sem->MaxValue = MaxValue;
+}
+//
+// Wait for items to be avaliable
+//
+int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
+{
+ tThread *us;
+ int taken;
+ if( MaxToTake < 0 ) {
+ Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
+ MaxToTake, Sem, Sem->Name);
+ }
+
+ SHORTLOCK( &Sem->Protector );
+
+ // Check if there's already items avaliable
+ if( Sem->Value > 0 )
+ {
+ // Take what we need
+ if( MaxToTake && Sem->Value > MaxToTake )
+ taken = MaxToTake;
+ else
+ taken = Sem->Value;
+ Sem->Value -= taken;
+ }
+ else
+ {
+ SHORTLOCK( &glThreadListLock );
+
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_SEMAPHORESLEEP;
+ us->WaitPointer = Sem;
+ us->RetStatus = MaxToTake; // Use RetStatus as a temp variable
+
+ // - Add to waiting
+ if(Sem->LastWaiting) {
+ Sem->LastWaiting->Next = us;
+ Sem->LastWaiting = us;
+ }
+ else {
+ Sem->Waiting = us;
+ Sem->LastWaiting = us;
+ }
+
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) waiting on semaphore %p %s:%s",
+ us, us->TID, us->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ SHORTREL( &Sem->Protector ); // Release first to make sure it is released
+ SHORTREL( &glThreadListLock );
+ while( us->Status == THREAD_STAT_SEMAPHORESLEEP )
+ {
+ Threads_Yield();
+ if(us->Status == THREAD_STAT_SEMAPHORESLEEP)
+ Log_Warning("Threads", "Semaphore %p %s:%s re-schedulued while asleep",
+ Sem, Sem->ModName, Sem->Name);
+ }
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name);
+ #endif
+ // We're only woken when there's something avaliable (or a signal arrives)
+ us->WaitPointer = NULL;
+
+ taken = us->RetStatus;
+
+ // Get the lock again
+ SHORTLOCK( &Sem->Protector );
+ }
+
+ // While there is space, and there are thread waiting
+ // wake the first thread and give it what it wants (or what's left)
+ while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
+ {
+ int given;
+ tThread *toWake = Sem->Signaling;
+
+ Sem->Signaling = Sem->Signaling->Next;
+ // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
+ if( Sem->Signaling == NULL )
+ Sem->LastSignaling = NULL;
+
+ // Figure out how much to give
+ if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
+ given = toWake->RetStatus;
+ else
+ given = Sem->MaxValue - Sem->Value;
+ Sem->Value -= given;
+
+
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) woken by wait on %p %s:%s",
+ toWake, toWake->TID, toWake->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ // Save the number we gave to the thread's status
+ toWake->RetStatus = given;
+
+ // Wake the sleeper
+ SHORTLOCK( &glThreadListLock );
+ if( toWake->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(toWake);
+ SHORTREL( &glThreadListLock );
+ }
+ SHORTREL( &Sem->Protector );
+
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("Semaphore %p %s:%s took %i by wait",
+ Sem, Sem->ModName, Sem->Name, taken);
+ #endif
+
+ return taken;
+}
+
+//
+// Add items to a semaphore
+//
+int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
+{
+ int given;
+ int added;
+
+ if( AmmountToAdd < 0 ) {
+ Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
+ AmmountToAdd, Sem, Sem->Name);
+ }
+ SHORTLOCK( &Sem->Protector );
+
+ // Check if we have to block
+ if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
+ {
+ tThread *us;
+ #if 0
+ Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name);
+ Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue);
+ #endif
+
+ SHORTLOCK( &glThreadListLock );
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_SEMAPHORESLEEP;
+ us->WaitPointer = Sem;
+ us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable
+
+ // - Add to waiting
+ if(Sem->LastSignaling) {
+ Sem->LastSignaling->Next = us;
+ Sem->LastSignaling = us;
+ }
+ else {
+ Sem->Signaling = us;
+ Sem->LastSignaling = us;
+ }
+
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) signaling semaphore %p %s:%s",
+ us, us->TID, us->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Sem->Protector );
+ while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
+ // We're only woken when there's something avaliable
+ us->WaitPointer = NULL;
+
+ added = us->RetStatus;
+
+ // Get the lock again
+ SHORTLOCK( &Sem->Protector );
+ }
+ // Non blocking
+ else
+ {
+ // Figure out how much we need to take off
+ if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
+ added = Sem->MaxValue - Sem->Value;
+ else
+ added = AmmountToAdd;
+ Sem->Value += added;
+ }
+
+ // While there are items avaliable, and there are thread waiting
+ // wake the first thread and give it what it wants (or what's left)
+ while( Sem->Value && Sem->Waiting )
+ {
+ tThread *toWake = Sem->Waiting;
+
+ // Remove thread from list (double ended, so clear LastWaiting if needed)
+ Sem->Waiting = Sem->Waiting->Next;
+ if( Sem->Waiting == NULL )
+ Sem->LastWaiting = NULL;
+
+ // Figure out how much to give to woken thread
+ // - Requested count is stored in ->RetStatus
+ if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
+ given = toWake->RetStatus;
+ else
+ given = Sem->Value;
+ Sem->Value -= given;
+
+ // Save the number we gave to the thread's status
+ toWake->RetStatus = given;
+
+ if(toWake->bInstrTrace)
+ Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) woken by signal on %p %s:%s",
+ toWake, toWake->TID, toWake->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ // Wake the sleeper
+ if( toWake->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(toWake);
+ else
+ Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
+ }
+ SHORTREL( &Sem->Protector );
+
+ return added;
+}
+
+//
+// Get the current value of a semaphore
+//
+int Semaphore_GetValue(tSemaphore *Sem)
+{
+ return Sem->Value;
+}
+
+// === EXPORTS ===
+EXPORT(Semaphore_Init);
+EXPORT(Semaphore_Wait);
+EXPORT(Semaphore_Signal);
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * syscalls.c
+ */
+#define DEBUG 0
+
+#include <acess.h>
+#include <syscalls.h>
+#include <proc.h>
+#include <hal_proc.h>
+#include <errno.h>
+#include <threads.h>
+#include <events.h>
+
+#define CHECK_NUM_NULLOK(v,size) \
+ if((v)&&!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;}
+#define CHECK_STR_NULLOK(v) \
+ if((v)&&!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;}
+#define CHECK_NUM_NONULL(v,size) \
+ if(!(v)||!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;}
+#define CHECK_STR_NONULL(v) \
+ if(!(v)||!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;}
+#define CHECK_STR_ARRAY(arr) do {\
+ int i;\
+ char **tmp = (char**)arr; \
+ CHECK_NUM_NONULL( tmp, sizeof(char**) ); \
+ for(i=0;tmp[i];i++) { \
+ CHECK_STR_NONULL( tmp[i] ); \
+ CHECK_NUM_NONULL( &tmp[i+1], sizeof(char*) ); \
+ }\
+ if(tmp[i]) break;\
+} while(0)
+
+// === IMPORTS ===
+extern Uint Binary_Load(const char *file, Uint *entryPoint);
+
+// === PROTOTYPES ===
+void SyscallHandler(tSyscallRegs *Regs);
+ int Syscall_ValidString(const char *Addr);
+ int Syscall_Valid(int Size, const void *Addr);
+
+// === CODE ===
+// TODO: Do sanity checking on arguments, ATM the user can really fuck with the kernel
+void SyscallHandler(tSyscallRegs *Regs)
+{
+ Uint64 ret = 0;
+ Uint err = -EOK;
+ int callNum = Regs->Num;
+
+ #if DEBUG < 2
+ if(callNum != SYS_READ && callNum != SYS_WRITE) {
+ #endif
+ ENTER("iThread iNum", Threads_GetTID(), callNum);
+ if(callNum < NUM_SYSCALLS)
+ LOG("Syscall %s", cSYSCALL_NAMES[callNum]);
+ LOG("Arg1: 0x%x, Arg2: 0x%x, Arg3: 0x%x, Arg4: 0x%x", Regs->Arg1, Regs->Arg2, Regs->Arg3, Regs->Arg4);
+ #if DEBUG < 2
+ }
+ #endif
+
+ switch(Regs->Num)
+ {
+ // -- Exit the current thread
+ case SYS_EXIT: Threads_Exit(0, Regs->Arg1); break;
+
+ // -- Put the current thread to sleep
+ case SYS_SLEEP: Threads_Sleep(); break;
+
+ // -- Yield current timeslice
+ case SYS_YIELD: Threads_Yield(); break;
+
+ // -- Set Error Handler
+ case SYS_SETFAULTHANDLER:
+ Threads_SetFaultHandler(Regs->Arg1);
+ break;
+
+ // -- Clone the current thread
+ case SYS_CLONE:
+ // Call clone system call
+ ret = Proc_Clone(Regs->Arg1);
+ break;
+
+ // -- Send a signal
+ case SYS_KILL:
+ err = -ENOSYS;
+ ret = -1;
+ break;
+
+ // -- Wait fr an event
+ case SYS_WAITEVENT:
+ // Message mask
+ ret = Threads_WaitEvents(Regs->Arg1);
+ break;
+
+ // -- Wait for a thread
+ case SYS_WAITTID:
+ // Sanity Check (Status can be NULL)
+ CHECK_NUM_NULLOK( (int*)Regs->Arg2, sizeof(int) );
+ // TID, *Status
+ ret = Threads_WaitTID(Regs->Arg1, (int*)Regs->Arg2);
+ break;
+
+ // -- Get the physical address of a page
+ case SYS_GETPHYS:
+ ret = MM_GetPhysAddr(Regs->Arg1);
+ break;
+
+ // -- Map an address
+ case SYS_MAP: MM_Map(Regs->Arg1, Regs->Arg2); break;
+
+ // -- Allocate an address
+ case SYS_ALLOCATE: ret = MM_Allocate(Regs->Arg1); break;
+
+ // -- Unmap an address
+ case SYS_UNMAP: MM_Deallocate(Regs->Arg1); break;
+
+ // -- Get Thread/Process IDs
+ case SYS_GETTID: ret = Threads_GetTID(); break;
+ case SYS_GETPID: ret = Threads_GetPID(); break;
+
+ // -- Get User/Group IDs
+ case SYS_GETUID: ret = Threads_GetUID(); break;
+ case SYS_GETGID: ret = Threads_GetGID(); break;
+
+ // -- Set User/Group IDs
+ case SYS_SETUID: ret = Threads_SetUID(Regs->Arg1); break;
+ case SYS_SETGID: ret = Threads_SetGID(Regs->Arg1); break;
+
+ // -- Send Message
+ case SYS_SENDMSG:
+ CHECK_NUM_NONULL( (void*)Regs->Arg3, Regs->Arg2 );
+ // Destination, Size, *Data
+ ret = Proc_SendMessage(Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3);
+ break;
+ // -- Check for messages
+ case SYS_GETMSG:
+ CHECK_NUM_NULLOK( (Uint*)Regs->Arg1, sizeof(Uint) );
+ // NOTE: Can't do range checking as we don't know the size
+ // - Should be done by Proc_GetMessage
+ if( Regs->Arg2 && Regs->Arg2 != -1 && !MM_IsUser(Regs->Arg2) ) {
+ err = -EINVAL; ret = -1; break;
+ }
+ // *Source, *Data
+ ret = Proc_GetMessage((Uint*)Regs->Arg1, (void*)Regs->Arg2);
+ break;
+
+ // -- Get the current timestamp
+ case SYS_GETTIME:
+ ret = now();
+ break;
+
+ // -- Set the thread's name
+ case SYS_SETNAME:
+ CHECK_STR_NONULL( (char*) Regs->Arg1);
+ Threads_SetName( (char*)Regs->Arg1 );
+ break;
+
+ // ---
+ // Binary Control
+ // ---
+ // -- Create a new process
+ case SYS_SPAWN:
+ CHECK_STR_NONULL((const char*)Regs->Arg1);
+ CHECK_STR_ARRAY((const char**)Regs->Arg2);
+ CHECK_STR_ARRAY((const char**)Regs->Arg3);
+ CHECK_NUM_NONULL((void*)Regs->Arg5, Regs->Arg4*sizeof(int));
+ ret = Proc_SysSpawn(
+ (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3,
+ Regs->Arg4, (int*)Regs->Arg5
+ );
+ break;
+ // -- Replace the current process with another
+ case SYS_EXECVE:
+ CHECK_STR_NONULL((char*)Regs->Arg1);
+ CHECK_STR_ARRAY( (char**)Regs->Arg2 );
+ if( Regs->Arg3 )
+ CHECK_STR_ARRAY( (char**)Regs->Arg3 );
+ LEAVE('s', "Assuming 0");
+ // Path, **Argv, **Envp, DataSize (=0 to tell it to create a copy)
+ ret = Proc_Execve(
+ (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3,
+ 0
+ );
+ break;
+ // -- Load a binary into the current process
+ case SYS_LOADBIN:
+ CHECK_STR_NONULL( (char*)Regs->Arg1 );
+ CHECK_NUM_NONULL( (Uint*)Regs->Arg2, sizeof(Uint) );
+ // Path, *Entrypoint
+ ret = Binary_Load((char*)Regs->Arg1, (Uint*)Regs->Arg2);
+ break;
+
+ // ---
+ // Virtual Filesystem
+ // ---
+ case SYS_OPEN:
+ CHECK_STR_NONULL( (char*)Regs->Arg1 );
+ LOG("VFS_Open(\"%s\", 0x%x)", (char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER);
+ ret = VFS_Open((char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER);
+ break;
+
+ case SYS_CLOSE:
+ LOG("VFS_Close(%i)", Regs->Arg1);
+ VFS_Close( Regs->Arg1 );
+ break;
+
+ case SYS_SEEK:
+ #if BITS == 64
+ ret = VFS_Seek( Regs->Arg1, Regs->Arg2, Regs->Arg3 );
+ #else
+ ret = VFS_Seek( Regs->Arg1, Regs->Arg2|(((Uint64)Regs->Arg3)<<32), Regs->Arg4 );
+ #endif
+ break;
+
+ case SYS_TELL:
+ ret = VFS_Tell( Regs->Arg1 );
+ break;
+
+ case SYS_WRITE:
+ CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
+ ret = VFS_Write( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 );
+ break;
+
+ case SYS_READ:
+ CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
+ ret = VFS_Read( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 );
+ break;
+
+ case SYS_FINFO:
+ CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tFInfo) + Regs->Arg3*sizeof(tVFS_ACL) );
+ // FP, Dest, MaxACLs
+ ret = VFS_FInfo( Regs->Arg1, (void*)Regs->Arg2, Regs->Arg3 );
+ break;
+
+ // Get ACL Value
+ case SYS_GETACL:
+ CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tVFS_ACL) );
+ ret = VFS_GetACL( Regs->Arg1, (void*)Regs->Arg2 );
+ break;
+
+ // Read Directory
+ case SYS_READDIR:
+ // TODO: What if the filename is longer?
+ // Maybe force it to be a 256 byte buffer
+ CHECK_NUM_NONULL( (void*)Regs->Arg2, 256 );
+ ret = VFS_ReadDir( Regs->Arg1, (void*)Regs->Arg2 );
+ break;
+
+ // Open a file that is a entry in an open directory
+ case SYS_OPENCHILD:
+ CHECK_STR_NONULL( (char*)Regs->Arg2 );
+ ret = VFS_OpenChild( Regs->Arg1, (char*)Regs->Arg2, Regs->Arg3 | VFS_OPENFLAG_USER);
+ break;
+
+ // Change Directory
+ case SYS_CHDIR:
+ CHECK_STR_NONULL( (const char*)Regs->Arg1 );
+ ret = VFS_ChDir( (const char*)Regs->Arg1 );
+ break;
+
+ // IO Control
+ case SYS_IOCTL:
+ // All sanity checking should be done by the driver
+ if( Regs->Arg3 && !MM_IsUser(Regs->Arg3) ) {
+ err = -EINVAL; ret = -1; break;
+ }
+ ret = VFS_IOCtl( Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3 );
+ break;
+
+ // Mount a filesystem
+ case SYS_MOUNT:
+ // Only root can mount filesystems
+ if(Threads_GetUID() != 0) {
+ err = -EACCES;
+ ret = -1;
+ break;
+ }
+ // Sanity check the paths
+ if(!Syscall_ValidString((char*)Regs->Arg1)
+ || !Syscall_ValidString((char*)Regs->Arg2)
+ || !Syscall_ValidString((char*)Regs->Arg3)
+ || !Syscall_ValidString((char*)Regs->Arg4) ) {
+ err = -EINVAL;
+ ret = -1;
+ break;
+ }
+ ret = VFS_Mount(
+ (char*)Regs->Arg1, // Device
+ (char*)Regs->Arg2, // Mount point
+ (char*)Regs->Arg3, // Filesystem
+ (char*)Regs->Arg4 // Options
+ );
+ break;
+
+ // Wait on a set of handles
+ case SYS_SELECT:
+ // Sanity checks
+ if( (Regs->Arg2 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg2))
+ || (Regs->Arg3 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg3))
+ || (Regs->Arg4 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg4))
+ || (Regs->Arg5 && !Syscall_Valid(sizeof(tTime), (void*)Regs->Arg5)) )
+ {
+ err = -EINVAL;
+ ret = -1;
+ break;
+ }
+ // Perform the call
+ ret = VFS_Select(
+ Regs->Arg1, // Max handle
+ (fd_set *)Regs->Arg2, // Read
+ (fd_set *)Regs->Arg3, // Write
+ (fd_set *)Regs->Arg4, // Errors
+ (tTime *)Regs->Arg5, // Timeout
+ (Uint32)Regs->Arg6, // Extra wakeup events
+ 0 // User handles
+ );
+ break;
+
+ // -- Debug
+ //#if DEBUG_BUILD
+ case SYS_DEBUG:
+ CHECK_STR_NONULL( (char*)Regs->Arg1 );
+ LogF("Log: %08lli [%i] ", now(), Threads_GetTID());
+ LogF((const char*)Regs->Arg1,
+ Regs->Arg2, Regs->Arg3, Regs->Arg4, Regs->Arg5, Regs->Arg6);
+ LogF("\r\n");
+ break;
+ //#endif
+
+ // -- Default (Return Error)
+ default:
+ Log_Warning("Syscalls", "Unknown System Call %i", Regs->Num);
+ if(Regs->Num < NUM_SYSCALLS)
+ Log_Warning("Syscall", " named '%s'", cSYSCALL_NAMES[Regs->Num]);
+ err = -ENOSYS;
+ ret = -1;
+ break;
+ }
+
+ if(err == 0) err = errno;
+
+ if(err != 0) {
+ LOG("ID: %i, Return errno = %i", Regs->Num, err);
+ }
+
+ #if BITS < 64
+ Regs->Return = ret&0xFFFFFFFF;
+ Regs->RetHi = ret >> 32;
+ #else
+ Regs->Return = ret;
+ #endif
+ Regs->Error = err;
+ #if DEBUG
+ # if DEBUG < 2
+ if( callNum != SYS_READ && callNum != SYS_WRITE ) {
+ # endif
+ LOG("err = %i", err);
+ if( callNum == SYS_EXECVE )
+ LOG("Actual %i", ret);
+ else
+ LEAVE('x', ret);
+ # if DEBUG < 2
+ }
+ # endif
+ #endif
+}
+
+/**
+ * \fn int Syscall_ValidString(const char *Addr)
+ * \brief Checks if a memory address contains a valid string
+ */
+int Syscall_ValidString(const char *Addr)
+{
+ // Check if the memory is user memory
+ if(!MM_IsUser( (tVAddr) Addr)) return 0;
+
+ return CheckString( Addr );
+}
+
+/**
+ * \fn int Syscall_Valid(int Size, const void *Addr)
+ * \brief Checks if a memory address is valid
+ */
+int Syscall_Valid(int Size, const void *Addr)
+{
+ if(!MM_IsUser( (tVAddr)Addr )) return 0;
+
+ return CheckMem( Addr, Size );
+}
--- /dev/null
+
+0
+SYS_EXIT Kill this thread
+SYS_CLONE Create a new thread
+SYS_KILL Send a signal
+SYS_SETFAULTHANDLER Set signal Handler
+SYS_YIELD Yield remainder of timestamp
+SYS_SLEEP Sleep until messaged or signaled
+SYS_WAITEVENT Wait for an event
+SYS_WAITTID Wait for a thread to do something
+
+SYS_SETNAME Sets the name of the current thread
+SYS_GETNAME Gets the name of a thread
+SYS_GETTID Get current thread ID
+SYS_GETPID Get current thread group ID
+SYS_SETPRI Set process priority
+
+SYS_SENDMSG Send an IPC message
+SYS_GETMSG Recieve an IPC message
+
+SYS_GETTIME Get the current timestamp
+
+SYS_SPAWN Spawn a new process
+SYS_EXECVE Replace the current process
+SYS_LOADBIN Load a binary into the current address space
+SYS_UNLOADBIN Unload a loaded binary
+SYS_LOADMOD Load a module into the kernel
+
+32
+SYS_GETPHYS Get the physical address of a page
+SYS_MAP Map a physical address
+SYS_ALLOCATE Allocate a page
+SYS_UNMAP Unmap a page
+SYS_PREALLOC Preallocate a page
+SYS_SETFLAGS Set a page's flags
+SYS_SHAREWITH Share a page with another thread
+
+SYS_GETUID Get current User ID
+SYS_GETGID Get current Group ID
+SYS_SETUID Set current user ID
+SYS_SETGID Set current Group ID
+
+64
+SYS_OPEN Open a file
+SYS_REOPEN Close a file and reuse its handle
+SYS_CLOSE Close a file
+SYS_READ Read from an open file
+SYS_WRITE Write to an open file
+SYS_IOCTL Perform an IOCtl Call
+SYS_SEEK Seek to a new position in the file
+SYS_READDIR Read from an open directory
+SYS_OPENCHILD Open a child entry in a directory
+SYS_GETACL Get an ACL Value
+SYS_SETACL Set an ACL Value
+SYS_FINFO Get file information
+SYS_MKDIR Create a new directory
+SYS_LINK Create a new link to a file
+SYS_SYMLINK Create a symbolic link
+SYS_UNLINK Delete a file
+SYS_TELL Return the current file position
+SYS_CHDIR Change current directory
+SYS_GETCWD Get current directory
+SYS_MOUNT Mount a filesystem
+SYS_SELECT Wait for file handles
--- /dev/null
+/*
+ * Acess 2 Kernel
+ * - By John Hodge (thePowersGang)
+ * system.c
+ * - Architecture Independent System Init
+ */
+#define DEBUG 0
+#include <acess.h>
+
+// === IMPORTS ===
+extern void Arch_LoadBootModules(void);
+extern int Modules_LoadBuiltins(void);
+extern void Modules_SetBuiltinParams(char *Name, char *ArgString);
+extern void Debug_SetKTerminal(const char *File);
+
+// === PROTOTYPES ===
+void System_Init(char *Commandline);
+void System_ParseCommandLine(char *ArgString);
+void System_ExecuteCommandLine(void);
+void System_ParseVFS(char *Arg);
+void System_ParseModuleArgs(char *Arg);
+void System_ParseSetting(char *Arg);
+
+// === GLOBALS ===
+const char *gsInitBinary = "/Acess/SBin/init";
+char *argv[32];
+ int argc;
+
+// === CODE ===
+void System_Init(char *CommandLine)
+{
+ // Parse Kernel's Command Line
+ System_ParseCommandLine(CommandLine);
+
+ // Initialise modules
+ Log_Log("Config", "Initialising builtin modules...");
+ Modules_LoadBuiltins();
+ Arch_LoadBootModules();
+
+ System_ExecuteCommandLine();
+
+ // - Execute the Config Script
+ Log_Log("Config", "Spawning init '%s'", gsInitBinary);
+ Proc_Spawn(gsInitBinary);
+
+ // Set the debug to be echoed to the terminal
+ Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)");
+ Debug_SetKTerminal("/Devices/VTerm/7");
+}
+
+/**
+ * \fn void System_ParseCommandLine(char *ArgString)
+ * \brief Parses the kernel's command line and sets the environment
+ */
+void System_ParseCommandLine(char *ArgString)
+{
+ int i;
+ char *str;
+
+ Log_Log("Config", "Kernel Invocation (%p) \"%s\"", ArgString, ArgString);
+
+ // --- Get Arguments ---
+ str = ArgString;
+ for( argc = 0; argc < 32; argc++ )
+ {
+ // Eat Whitespace
+ while(*str == ' ') str++;
+ // Check for the end of the string
+ if(*str == '\0') { argc--; break;}
+ argv[argc] = str;
+ if(*str == '"') {
+ while(*str && !(*str == '"' && str[-1] != '\\'))
+ str ++;
+ }
+ else {
+ while(*str && *str != ' ')
+ str++;
+ }
+ if(*str == '\0') break; // Check for EOS
+ *str = '\0'; // Cap off argument string
+ str ++; // and increment the string pointer
+ }
+ if(argc < 32)
+ argc ++; // Count last argument
+
+ // --- Parse Arguments (Pass 1) ---
+ for( i = 1; i < argc; i++ )
+ {
+ switch(argv[i][0])
+ {
+ // --- VFS ---
+ // Ignored on this pass
+ case '/':
+ break;
+
+ // --- Module Paramaters ---
+ // -VTerm:Width=640,Height=480,Scrollback=2
+ case '-':
+ System_ParseModuleArgs( argv[i] );
+ break;
+ // --- Config Options ---
+ // SCRIPT=/Acess/Conf/BootConf.cfg
+ default:
+ System_ParseSetting( argv[i] );
+ break;
+ }
+ }
+}
+
+void System_ExecuteCommandLine(void)
+{
+ int i;
+ if(argc > 0)
+ LOG("Invocation '%s'", argv[0]);
+ for( i = 1; i < argc; i++ )
+ {
+ LOG("argv[%i] = '%s'", i, argv[i]);
+ switch(argv[i][0])
+ {
+ // --- VFS ---
+ // Mount /System=ext2:/Devices/ATA/A1
+ // Symlink /Acess=/System/Acess2
+ case '/':
+ System_ParseVFS( argv[i] );
+ break;
+ }
+ }
+}
+
+/**
+ * \fn void System_ParseVFS(char *Arg)
+ */
+void System_ParseVFS(char *Arg)
+{
+ char *value;
+ int fd;
+
+ value = Arg;
+ // Search for the '=' token
+ while( *value && *value != '=' )
+ value++;
+
+ // Check if the equals was found
+ if( *value == '\0' ) {
+ Log_Warning("Config", "Expected '=' in the string '%s'", Arg);
+ return ;
+ }
+
+ // Edit string
+ *value = '\0'; value ++;
+
+ // Check assignment type
+ // - Symbolic Link <link>=<destination>
+ if(value[0] == '/')
+ {
+ Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value);
+ VFS_Symlink(Arg, value);
+ }
+ // - Mount <mountpoint>=<fs>:<device>
+ else
+ {
+ char *dev = value;
+ // Find colon
+ while(*dev && *dev != ':') dev++;
+ if(*dev) {
+ *dev = '\0';
+ dev++; // Eat ':'
+ }
+ // Create Mountpoint
+ if( (fd = VFS_Open(Arg, 0)) == -1 ) {
+ Log_Log("Config", "Creating directory '%s'", Arg, value);
+ VFS_MkDir( Arg );
+ } else {
+ VFS_Close(fd);
+ }
+ // Mount
+ Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value);
+ VFS_Mount(dev, Arg, value, "");
+ }
+}
+
+/**
+ * \brief Parse a module argument string
+ * \param Arg Argument string
+ */
+void System_ParseModuleArgs(char *Arg)
+{
+ char *name, *args;
+ int i;
+
+ // Remove '-'
+ name = Arg + 1;
+
+ // Find the start of the args
+ i = strpos(name, ':');
+ if( i == -1 ) {
+ Log_Warning("Config", "Module spec with no arguments");
+ #if 1
+ return ;
+ #else
+ i = strlen(name);
+ args = name + i;
+ #endif
+ }
+ else {
+ name[i] = '\0';
+ args = name + i + 1;
+ }
+
+ Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args);
+ Modules_SetBuiltinParams(name, args);
+}
+
+/**
+ * \fn void System_ParseSetting(char *Arg)
+ */
+void System_ParseSetting(char *Arg)
+{
+ char *value;
+ value = Arg;
+
+ // Search for the '=' token
+ while( *value && *value != '=' )
+ value++;
+
+ // Check for boolean/flag (no '=')
+ if(*value == '\0')
+ {
+ //if(strcmp(Arg, "") == 0) {
+ //} else {
+ Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg);
+ //}
+ }
+ else
+ {
+ *value = '\0'; // Remove '='
+ value ++; // and eat it's position
+
+ if(strcmp(Arg, "INIT") == 0) {
+ Log_Log("Config", "Init binary: '%s'", value);
+ if(strlen(value) == 0)
+ gsInitBinary = NULL;
+ else
+ gsInitBinary = value;
+ }
+ else {
+ Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg);
+ }
+
+ }
+}
+
--- /dev/null
+/*
+ * Acess2
+ * threads.c
+ * - Common Thread Control
+ */
+#include <acess.h>
+#include <threads.h>
+#include <threads_int.h>
+#include <errno.h>
+#include <hal_proc.h>
+#include <semaphore.h>
+#include <vfs_threads.h> // VFS Handle maintainence
+
+// Configuration
+#define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
+#define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
+
+// --- Schedulers ---
+#define SCHED_UNDEF 0
+#define SCHED_LOTTERY 1 // Lottery scheduler
+#define SCHED_RR_SIM 2 // Single Queue Round Robin
+#define SCHED_RR_PRI 3 // Multi Queue Round Robin
+// Set scheduler type
+#define SCHEDULER_TYPE SCHED_RR_PRI
+
+// === CONSTANTS ===
+#define DEFAULT_QUANTUM 5
+#define DEFAULT_PRIORITY 5
+#define MIN_PRIORITY 10
+
+// === IMPORTS ===
+
+// === TYPE ===
+typedef struct
+{
+ tThread *Head;
+ tThread *Tail;
+} tThreadList;
+
+// === PROTOTYPES ===
+void Threads_Init(void);
+#if 0
+void Threads_Delete(tThread *Thread);
+ int Threads_SetName(const char *NewName);
+#endif
+char *Threads_GetName(tTID ID);
+#if 0
+void Threads_SetPriority(tThread *Thread, int Pri);
+tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
+ int Threads_WaitTID(int TID, int *status);
+tThread *Threads_GetThread(Uint TID);
+#endif
+tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread);
+void Threads_int_AddToList(tThreadList *List, tThread *Thread);
+#if 0
+void Threads_Exit(int TID, int Status);
+void Threads_Kill(tThread *Thread, int Status);
+void Threads_Yield(void);
+void Threads_Sleep(void);
+ int Threads_Wake(tThread *Thread);
+void Threads_AddActive(tThread *Thread);
+tThread *Threads_RemActive(void);
+#endif
+void Threads_ToggleTrace(int TID);
+void Threads_Fault(int Num);
+void Threads_SegFault(tVAddr Addr);
+#if 0
+ int Threads_GetPID(void);
+ int Threads_GetTID(void);
+tUID Threads_GetUID(void);
+tGID Threads_GetGID(void);
+ int Threads_SetUID(Uint *Errno, tUID ID);
+ int Threads_SetGID(Uint *Errno, tUID ID);
+#endif
+void Threads_Dump(void);
+void Threads_DumpActive(void);
+
+// === GLOBALS ===
+// -- Core Thread --
+struct sProcess gProcessZero = {
+ };
+// Only used for the core kernel
+tThread gThreadZero = {
+ .Status = THREAD_STAT_ACTIVE, // Status
+ .ThreadName = (char*)"ThreadZero", // Name
+ .Quantum = DEFAULT_QUANTUM, // Default Quantum
+ .Remaining = DEFAULT_QUANTUM, // Current Quantum
+ .Priority = DEFAULT_PRIORITY // Number of tickets
+ };
+// -- Processes --
+// --- Locks ---
+tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
+// --- Current State ---
+volatile int giNumActiveThreads = 0; // Number of threads on the active queue
+volatile Uint giNextTID = 1; // Next TID to allocate
+// --- Thread Lists ---
+tThread *gAllThreads = NULL; // All allocated threads
+tThreadList gSleepingThreads; // Sleeping Threads
+ int giNumCPUs = 1; // Number of CPUs
+BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF)
+// --- Scheduler Types ---
+#if SCHEDULER_TYPE == SCHED_LOTTERY
+const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0};
+volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
+tThreadList gActiveThreads; // Currently Running Threads
+#elif SCHEDULER_TYPE == SCHED_RR_SIM
+tThreadList gActiveThreads; // Currently Running Threads
+#elif SCHEDULER_TYPE == SCHED_RR_PRI
+tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level
+#else
+# error "Unkown scheduler type"
+#endif
+
+// === CODE ===
+/**
+ * \fn void Threads_Init(void)
+ * \brief Initialse the thread list
+ */
+void Threads_Init(void)
+{
+ ArchThreads_Init();
+
+ Log_Debug("Threads", "Offsets of tThread");
+ Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
+ Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
+
+ // Create Initial Task
+// #if SCHEDULER_TYPE == SCHED_RR_PRI
+// gaActiveThreads[gThreadZero.Priority].Head = &gThreadZero;
+// gaActiveThreads[gThreadZero.Priority].Tail = &gThreadZero;
+// #else
+// gActiveThreads.Head = &gThreadZero;
+// gActiveThreads.Tail = &gThreadZero;
+// #endif
+
+ gAllThreads = &gThreadZero;
+ giNumActiveThreads = 1;
+ gThreadZero.Process = &gProcessZero;
+
+ Proc_Start();
+}
+
+void Threads_Delete(tThread *Thread)
+{
+ // Set to dead
+ Thread->Status = THREAD_STAT_BURIED;
+
+ // Clear out process state
+ Proc_ClearThread(Thread);
+
+ Thread->Process->nThreads --;
+ if( Thread->Process->nThreads == 0 )
+ {
+ tProcess *proc = Thread->Process;
+ // VFS Cleanup
+ VFS_CloseAllUserHandles();
+ // Architecture cleanup
+ Proc_ClearProcess( proc );
+ // VFS Configuration strings
+ if( proc->CurrentWorkingDir)
+ free( proc->CurrentWorkingDir );
+ if( proc->RootDir )
+ free( proc->RootDir );
+ // Process descriptor
+ free( proc );
+ }
+
+ // Free name
+ if( IsHeap(Thread->ThreadName) )
+ free(Thread->ThreadName);
+
+ // Remove from global list
+ // TODO: Lock this too
+ if( Thread == gAllThreads )
+ gAllThreads = Thread->GlobalNext;
+ else
+ Thread->GlobalPrev->GlobalNext = Thread->GlobalNext;
+
+ free(Thread);
+}
+
+/**
+ * \fn void Threads_SetName(const char *NewName)
+ * \brief Sets the current thread's name
+ * \param NewName New name for the thread
+ * \return Boolean Failure
+ */
+int Threads_SetName(const char *NewName)
+{
+ tThread *cur = Proc_GetCurThread();
+ char *oldname = cur->ThreadName;
+
+ // NOTE: There is a possibility of non-thread safety here
+ // A thread could read the current name pointer before it is zeroed
+
+ cur->ThreadName = NULL;
+
+ if( IsHeap(oldname) ) free( oldname );
+ cur->ThreadName = strdup(NewName);
+
+ Log_Debug("Threads", "Thread renamed to '%s'", NewName);
+
+ return 0;
+}
+
+/**
+ * \fn char *Threads_GetName(int ID)
+ * \brief Gets a thread's name
+ * \param ID Thread ID (-1 indicates current thread)
+ * \return Pointer to name
+ * \retval NULL Failure
+ */
+char *Threads_GetName(tTID ID)
+{
+ if(ID == -1) {
+ return Proc_GetCurThread()->ThreadName;
+ }
+ return Threads_GetThread(ID)->ThreadName;
+}
+
+/**
+ * \fn void Threads_SetPriority(tThread *Thread, int Pri)
+ * \brief Sets the priority of a task
+ * \param Thread Thread to update ticket count (NULL means current thread)
+ * \param Pri New priority
+ */
+void Threads_SetPriority(tThread *Thread, int Pri)
+{
+ // Get current thread
+ if(Thread == NULL) Thread = Proc_GetCurThread();
+ // Bounds checking
+ // - If < 0, set to lowest priority
+ // - Minumum priority is actualy a high number, 0 is highest
+ if(Pri < 0) Pri = MIN_PRIORITY;
+ if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
+
+ // Do we actually have to do anything?
+ if( Pri == Thread->Priority ) return;
+
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ if( Thread != Proc_GetCurThread() )
+ {
+ SHORTLOCK( &glThreadListLock );
+ // Remove from old priority
+ Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread );
+ // And add to new
+ Threads_int_AddToList( &gaActiveThreads[Pri], Thread );
+ Thread->Priority = Pri;
+ SHORTREL( &glThreadListLock );
+ }
+ else
+ Thread->Priority = Pri;
+ #else
+ // If this isn't the current thread, we need to lock
+ if( Thread != Proc_GetCurThread() )
+ {
+ SHORTLOCK( &glThreadListLock );
+
+ #if SCHEDULER_TYPE == SCHED_LOTTERY
+ giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
+ # if DEBUG_TRACE_TICKETS
+ Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
+ giFreeTickets,
+ caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
+ # endif
+ #endif
+ Thread->Priority = Pri;
+ SHORTREL( &glThreadListLock );
+ }
+ else
+ Thread->Priority = Pri;
+ #endif
+
+ #if DEBUG_TRACE_STATE
+ Log("Threads_SetPriority: %p(%i %s) pri set %i",
+ Thread, Thread->TID, Thread->ThreadName,
+ Pri);
+ #endif
+}
+
+/**
+ * \brief Clone the TCB of the current thread
+ * \param Flags Flags for something... (What is this for?)
+ */
+tThread *Threads_CloneTCB(Uint Flags)
+{
+ tThread *cur, *new;
+ cur = Proc_GetCurThread();
+
+ // Allocate and duplicate
+ new = malloc(sizeof(tThread));
+ if(new == NULL) { errno = -ENOMEM; return NULL; }
+ memcpy(new, cur, sizeof(tThread));
+
+ new->CurCPU = -1;
+ new->Next = NULL;
+ memset( &new->IsLocked, 0, sizeof(new->IsLocked));
+ new->Status = THREAD_STAT_PREINIT;
+ new->RetStatus = 0;
+
+ // Get Thread ID
+ new->TID = giNextTID++;
+ new->Parent = cur;
+ new->bInstrTrace = 0;
+
+ // Clone Name
+ new->ThreadName = strdup(cur->ThreadName);
+
+ // Set Thread Group ID (PID)
+ if(Flags & CLONE_VM) {
+ tProcess *newproc, *oldproc;
+ oldproc = cur->Process;
+ new->Process = malloc( sizeof(struct sProcess) );
+ newproc = new->Process;
+ newproc->PID = new->TID;
+ newproc->UID = oldproc->UID;
+ newproc->GID = oldproc->GID;
+ newproc->MaxFD = oldproc->MaxFD;
+ if( oldproc->CurrentWorkingDir )
+ newproc->CurrentWorkingDir = strdup( oldproc->CurrentWorkingDir );
+ else
+ newproc->CurrentWorkingDir = NULL;
+ if( oldproc->RootDir )
+ newproc->RootDir = strdup( oldproc->RootDir );
+ else
+ newproc->RootDir = NULL;
+ newproc->nThreads = 1;
+ // Reference all handles in the VFS
+ VFS_ReferenceUserHandles();
+ }
+ else {
+ new->Process->nThreads ++;
+ }
+
+ // Messages are not inherited
+ new->Messages = NULL;
+ new->LastMessage = NULL;
+
+ // Set State
+ new->Remaining = new->Quantum = cur->Quantum;
+ new->Priority = cur->Priority;
+ new->_errno = 0;
+
+ // Set Signal Handlers
+ new->CurFaultNum = 0;
+ new->FaultHandler = cur->FaultHandler;
+
+ // Maintain a global list of threads
+ SHORTLOCK( &glThreadListLock );
+ new->GlobalPrev = NULL; // Protect against bugs
+ new->GlobalNext = gAllThreads;
+ gAllThreads->GlobalPrev = new;
+ gAllThreads = new;
+ SHORTREL( &glThreadListLock );
+
+ return new;
+}
+
+/**
+ * \brief Clone the TCB of the kernel thread
+ */
+tThread *Threads_CloneThreadZero(void)
+{
+ tThread *new;
+
+ // Allocate and duplicate
+ new = malloc(sizeof(tThread));
+ if(new == NULL) {
+ return NULL;
+ }
+ memcpy(new, &gThreadZero, sizeof(tThread));
+
+ new->Process->nThreads ++;
+
+ new->CurCPU = -1;
+ new->Next = NULL;
+ memset( &new->IsLocked, 0, sizeof(new->IsLocked));
+ new->Status = THREAD_STAT_PREINIT;
+ new->RetStatus = 0;
+
+ // Get Thread ID
+ new->TID = giNextTID++;
+ new->Parent = 0;
+
+ // Clone Name
+ new->ThreadName = NULL;
+
+ // Messages are not inherited
+ new->Messages = NULL;
+ new->LastMessage = NULL;
+
+ // Set State
+ new->Remaining = new->Quantum = DEFAULT_QUANTUM;
+ new->Priority = DEFAULT_PRIORITY;
+ new->bInstrTrace = 0;
+
+ // Set Signal Handlers
+ new->CurFaultNum = 0;
+ new->FaultHandler = 0;
+
+ // Maintain a global list of threads
+ SHORTLOCK( &glThreadListLock );
+ new->GlobalPrev = NULL; // Protect against bugs
+ new->GlobalNext = gAllThreads;
+ gAllThreads->GlobalPrev = new;
+ gAllThreads = new;
+ SHORTREL( &glThreadListLock );
+
+ return new;
+}
+
+/**
+ * \brief Wait for a task to change state
+ * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
+ * \param Status Thread return status
+ * \return TID of child that changed state
+ */
+tTID Threads_WaitTID(int TID, int *Status)
+{
+ // Any Child
+ if(TID == -1) {
+ Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child");
+ return -1;
+ }
+
+ // Any peer/child thread
+ if(TID == 0) {
+ Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling");
+ return -1;
+ }
+
+ // TGID = abs(TID)
+ if(TID < -1) {
+ Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID");
+ return -1;
+ }
+
+ // Specific Thread
+ if(TID > 0) {
+ tThread *t = Threads_GetThread(TID);
+ tTID ret;
+
+ // Wait for the thread to die!
+ // TODO: Handle child also being suspended if wanted
+ while(t->Status != THREAD_STAT_ZOMBIE) {
+ Threads_Sleep();
+ Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
+ Threads_GetTID(), t->TID, t->Status);
+ }
+
+ // Set return status
+ ret = t->TID;
+ switch(t->Status)
+ {
+ case THREAD_STAT_ZOMBIE:
+ // Kill the thread
+ t->Status = THREAD_STAT_DEAD;
+ // TODO: Child return value?
+ if(Status) *Status = t->RetStatus;
+ // add to delete queue
+ Threads_Delete( t );
+ break;
+ default:
+ if(Status) *Status = -1;
+ break;
+ }
+ return ret;
+ }
+
+ return -1;
+}
+
+/**
+ * \brief Gets a thread given its TID
+ * \param TID Thread ID
+ * \return Thread pointer
+ */
+tThread *Threads_GetThread(Uint TID)
+{
+ tThread *thread;
+
+ // Search global list
+ for(thread = gAllThreads;
+ thread;
+ thread = thread->GlobalNext)
+ {
+ if(thread->TID == TID)
+ return thread;
+ }
+
+ Log("Unable to find TID %i on main list\n", TID);
+
+ return NULL;
+}
+
+/**
+ * \brief Deletes an entry from a list
+ * \param List Pointer to the list head
+ * \param Thread Thread to find
+ * \return \a Thread
+ */
+tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread)
+{
+ tThread *ret, *prev = NULL;
+
+ for(ret = List->Head;
+ ret && ret != Thread;
+ prev = ret, ret = ret->Next
+ );
+
+ // Is the thread on the list
+ if(!ret) {
+ //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List);
+ return NULL;
+ }
+
+ if( !prev ) {
+ List->Head = Thread->Next;
+ //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List);
+ }
+ else {
+ prev->Next = Thread->Next;
+ //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev);
+ }
+ if( Thread->Next == NULL )
+ List->Tail = prev;
+
+ return Thread;
+}
+
+void Threads_int_AddToList(tThreadList *List, tThread *Thread)
+{
+ if( List->Head )
+ List->Tail->Next = Thread;
+ else
+ List->Head = Thread;
+ List->Tail = Thread;
+ Thread->Next = NULL;
+}
+
+/**
+ * \brief Exit the current process (or another?)
+ * \param TID Thread ID to kill
+ * \param Status Exit status
+ */
+void Threads_Exit(int TID, int Status)
+{
+ if( TID == 0 )
+ Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
+ else
+ Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
+
+ // Halt forever, just in case
+ for(;;) HALT();
+}
+
+/**
+ * \fn void Threads_Kill(tThread *Thread, int Status)
+ * \brief Kill a thread
+ * \param Thread Thread to kill
+ * \param Status Status code to return to the parent
+ */
+void Threads_Kill(tThread *Thread, int Status)
+{
+ tMsg *msg;
+ int isCurThread = Thread == Proc_GetCurThread();
+
+ // TODO: Disown all children?
+ #if 1
+ {
+ tThread *child;
+ // TODO: I should keep a .Children list
+ for(child = gAllThreads;
+ child;
+ child = child->GlobalNext)
+ {
+ if(child->Parent == Thread)
+ child->Parent = &gThreadZero;
+ }
+ }
+ #endif
+
+ ///\note Double lock is needed due to overlap of lock areas
+
+ // Lock thread (stop us recieving messages)
+ SHORTLOCK( &Thread->IsLocked );
+
+ // Clear Message Queue
+ while( Thread->Messages )
+ {
+ msg = Thread->Messages->Next;
+ free( Thread->Messages );
+ Thread->Messages = msg;
+ }
+
+ // Lock thread list
+ SHORTLOCK( &glThreadListLock );
+
+ switch(Thread->Status)
+ {
+ case THREAD_STAT_PREINIT: // Only on main list
+ break;
+
+ // Currently active thread
+ case THREAD_STAT_ACTIVE:
+ if( Thread != Proc_GetCurThread() )
+ {
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ tThreadList *list = &gaActiveThreads[Thread->Priority];
+ #else
+ tThreadList *list = &gActiveThreads;
+ #endif
+ if( Threads_int_DelFromQueue( list, Thread ) )
+ {
+ }
+ else
+ {
+ Log_Warning("Threads",
+ "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list",
+ Thread, Thread->TID, Thread->ThreadName
+ );
+ }
+ #if SCHEDULER_TYPE == SCHED_LOTTERY
+ giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ];
+ #endif
+ }
+ // Ensure that we are not rescheduled
+ Thread->Remaining = 0; // Clear Remaining Quantum
+ Thread->Quantum = 0; // Clear Quantum to indicate dead thread
+
+ // Update bookkeeping
+ giNumActiveThreads --;
+ break;
+ // Kill it while it sleeps!
+ case THREAD_STAT_SLEEPING:
+ if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) )
+ {
+ Log_Warning("Threads",
+ "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list",
+ Thread, Thread->TID, Thread->ThreadName
+ );
+ }
+ break;
+
+ // Brains!... You cannot kill something that is already dead
+ case THREAD_STAT_ZOMBIE:
+ Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it",
+ Thread, Thread->TID, Thread->ThreadName);
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Thread->IsLocked );
+ return ;
+
+ default:
+ Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)",
+ Thread->Status);
+ break;
+ }
+
+ // Save exit status
+ Thread->RetStatus = Status;
+
+ SHORTREL( &Thread->IsLocked );
+
+ Thread->Status = THREAD_STAT_ZOMBIE;
+ SHORTREL( &glThreadListLock );
+ // TODO: Send something like SIGCHLD
+ Threads_Wake( Thread->Parent );
+
+ Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
+
+ // And, reschedule
+ if(isCurThread)
+ {
+ for( ;; )
+ Proc_Reschedule();
+ }
+}
+
+/**
+ * \brief Yield remainder of the current thread's timeslice
+ */
+void Threads_Yield(void)
+{
+// Log("Threads_Yield: by %p", __builtin_return_address(0));
+ Proc_Reschedule();
+}
+
+/**
+ * \fn void Threads_Sleep(void)
+ * \brief Take the current process off the run queue
+ */
+void Threads_Sleep(void)
+{
+ tThread *cur = Proc_GetCurThread();
+
+ // Acquire Spinlock
+ SHORTLOCK( &glThreadListLock );
+
+ // Don't sleep if there is a message waiting
+ if( cur->Messages ) {
+ SHORTREL( &glThreadListLock );
+ return;
+ }
+
+ // Remove us from running queue
+ Threads_RemActive();
+ // Mark thread as sleeping
+ cur->Status = THREAD_STAT_SLEEPING;
+
+ // Add to Sleeping List (at the top)
+ Threads_int_AddToList( &gSleepingThreads, cur );
+
+ #if DEBUG_TRACE_STATE
+ Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
+ #endif
+
+ // Release Spinlock
+ SHORTREL( &glThreadListLock );
+
+ while(cur->Status != THREAD_STAT_ACTIVE) {
+ Proc_Reschedule();
+ if( cur->Status != THREAD_STAT_ACTIVE )
+ Log("%i - Huh? why am I up? zzzz...", cur->TID);
+ }
+}
+
+
+/**
+ * \brief Wakes a sleeping/waiting thread up
+ * \param Thread Thread to wake
+ * \return Boolean Failure (Returns ERRNO)
+ * \warning This should ONLY be called with task switches disabled
+ */
+int Threads_Wake(tThread *Thread)
+{
+ if(!Thread)
+ return -EINVAL;
+
+ switch(Thread->Status)
+ {
+ case THREAD_STAT_ACTIVE:
+ Log("Threads_Wake - Waking awake thread (%i)", Thread->TID);
+ return -EALREADY;
+
+ case THREAD_STAT_SLEEPING:
+ SHORTLOCK( &glThreadListLock );
+ // Remove from sleeping queue
+ Threads_int_DelFromQueue(&gSleepingThreads, Thread);
+
+ SHORTREL( &glThreadListLock );
+ Threads_AddActive( Thread );
+
+ #if DEBUG_TRACE_STATE
+ Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
+ #endif
+ return -EOK;
+
+ case THREAD_STAT_SEMAPHORESLEEP: {
+ tSemaphore *sem;
+ tThread *th, *prev=NULL;
+
+ sem = Thread->WaitPointer;
+
+ SHORTLOCK( &sem->Protector );
+
+ // Remove from sleeping queue
+ for( th = sem->Waiting; th; prev = th, th = th->Next )
+ if( th == Thread ) break;
+ if( th )
+ {
+ if(prev)
+ prev->Next = Thread->Next;
+ else
+ sem->Waiting = Thread->Next;
+ if(sem->LastWaiting == Thread)
+ sem->LastWaiting = prev;
+ }
+ else
+ {
+ prev = NULL;
+ for( th = sem->Signaling; th; prev = th, th = th->Next )
+ if( th == Thread ) break;
+ if( !th ) {
+ Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
+ Thread, Thread->TID, Thread->ThreadName,
+ sem, sem->ModName, sem->Name);
+ return -EINTERNAL;
+ }
+
+ if(prev)
+ prev->Next = Thread->Next;
+ else
+ sem->Signaling = Thread->Next;
+ if(sem->LastSignaling == Thread)
+ sem->LastSignaling = prev;
+ }
+
+ Thread->RetStatus = 0; // It didn't get anything
+ Threads_AddActive( Thread );
+
+ #if DEBUG_TRACE_STATE
+ Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
+ #endif
+ SHORTREL( &sem->Protector );
+ } return -EOK;
+
+ case THREAD_STAT_WAITING:
+ Warning("Threads_Wake - Waiting threads are not currently supported");
+ return -ENOTIMPL;
+
+ case THREAD_STAT_DEAD:
+ Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID);
+ return -ENOTIMPL;
+
+ default:
+ Warning("Threads_Wake - Unknown process status (%i)\n", Thread->Status);
+ return -EINTERNAL;
+ }
+}
+
+/**
+ * \brief Wake a thread given the TID
+ * \param TID Thread ID to wake
+ * \return Boolean Faulure (errno)
+ */
+int Threads_WakeTID(tTID TID)
+{
+ tThread *thread = Threads_GetThread(TID);
+ int ret;
+ if(!thread)
+ return -ENOENT;
+ ret = Threads_Wake( thread );
+ //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread);
+ return ret;
+}
+
+void Threads_ToggleTrace(int TID)
+{
+ tThread *thread = Threads_GetThread(TID);
+ if(!thread) return ;
+ thread->bInstrTrace = !thread->bInstrTrace;
+}
+
+/**
+ * \brief Adds a thread to the active queue
+ */
+void Threads_AddActive(tThread *Thread)
+{
+ SHORTLOCK( &glThreadListLock );
+
+ if( Thread->Status == THREAD_STAT_ACTIVE ) {
+ tThread *cur = Proc_GetCurThread();
+ Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
+ __builtin_return_address(0),
+ GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
+ SHORTREL( &glThreadListLock );
+ return ;
+ }
+
+ // Set state
+ Thread->Status = THREAD_STAT_ACTIVE;
+// Thread->CurCPU = -1;
+ // Add to active list
+ {
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ tThreadList *list = &gaActiveThreads[Thread->Priority];
+ #else
+ tThreadList *list = &gActiveThreads;
+ #endif
+ Threads_int_AddToList( list, Thread );
+ }
+
+ // Update bookkeeping
+ giNumActiveThreads ++;
+
+ #if SCHEDULER_TYPE == SCHED_LOTTERY
+ {
+ int delta;
+ // Only change the ticket count if the thread is un-scheduled
+ if(Thread->CurCPU != -1)
+ delta = 0;
+ else
+ delta = caiTICKET_COUNTS[ Thread->Priority ];
+
+ giFreeTickets += delta;
+ # if DEBUG_TRACE_TICKETS
+ Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]",
+ GetCPUNum(), Thread, Thread->TID, Thread->ThreadName,
+ giFreeTickets, delta
+ );
+ # endif
+ }
+ #endif
+
+ SHORTREL( &glThreadListLock );
+}
+
+/**
+ * \brief Removes the current thread from the active queue
+ * \warning This should ONLY be called with the lock held
+ * \return Current thread pointer
+ */
+tThread *Threads_RemActive(void)
+{
+ #if 0
+ tThread *ret = Proc_GetCurThread();
+
+ if( !IS_LOCKED(&glThreadListLock) ) {
+ Log_KernelPanic("Threads", "Threads_RemActive called without lock held");
+ return NULL;
+ }
+
+ // Delete from active queue
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) )
+ #else
+ if( !Threads_int_DelFromQueue(&gActiveThreads, ret) )
+ #endif
+ {
+ Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue",
+ ret, ret->TID, ret->ThreadName
+ );
+ return NULL;
+ }
+
+ ret->Next = NULL;
+ ret->Remaining = 0;
+
+ giNumActiveThreads --;
+ // no need to decrement tickets, scheduler did it for us
+
+ #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
+ Log("CPU%i %p (%i %s) removed, giFreeTickets = %i [nc]",
+ GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
+ #endif
+
+ return ret;
+ #else
+ return Proc_GetCurThread();
+ #endif
+}
+
+/**
+ * \fn void Threads_SetFaultHandler(Uint Handler)
+ * \brief Sets the signal handler for a signal
+ */
+void Threads_SetFaultHandler(Uint Handler)
+{
+ //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler);
+ Proc_GetCurThread()->FaultHandler = Handler;
+}
+
+/**
+ * \fn void Threads_Fault(int Num)
+ * \brief Calls a fault handler
+ */
+void Threads_Fault(int Num)
+{
+ tThread *thread = Proc_GetCurThread();
+
+ if(!thread) return ;
+
+ Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
+
+ switch(thread->FaultHandler)
+ {
+ case 0: // Panic?
+ Threads_Kill(thread, -1);
+ HALT();
+ return ;
+ case 1: // Dump Core?
+ Threads_Kill(thread, -1);
+ HALT();
+ return ;
+ }
+
+ // Double Fault? Oh, F**k
+ if(thread->CurFaultNum != 0) {
+ Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
+ Threads_Kill(thread, -1); // For now, just kill
+ HALT();
+ }
+
+ thread->CurFaultNum = Num;
+
+ Proc_CallFaultHandler(thread);
+}
+
+/**
+ * \fn void Threads_SegFault(tVAddr Addr)
+ * \brief Called when a Segment Fault occurs
+ */
+void Threads_SegFault(tVAddr Addr)
+{
+ tThread *cur = Proc_GetCurThread();
+ cur->bInstrTrace = 0;
+ Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
+ MM_DumpTables(0, USER_MAX);
+ Threads_Fault( 1 );
+ //Threads_Exit( 0, -1 );
+}
+
+// --- Process Structure Access Functions ---
+tPID Threads_GetPID(void)
+{
+ return Proc_GetCurThread()->Process->PID;
+}
+tTID Threads_GetTID(void)
+{
+ return Proc_GetCurThread()->TID;
+}
+tUID Threads_GetUID(void)
+{
+ return Proc_GetCurThread()->Process->UID;
+}
+tGID Threads_GetGID(void)
+{
+ return Proc_GetCurThread()->Process->GID;
+}
+
+int Threads_SetUID(tUID ID)
+{
+ tThread *t = Proc_GetCurThread();
+ if( t->Process->UID != 0 ) {
+ errno = -EACCES;
+ return -1;
+ }
+ Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID);
+ t->Process->UID = ID;
+ return 0;
+}
+
+int Threads_SetGID(tGID ID)
+{
+ tThread *t = Proc_GetCurThread();
+ if( t->Process->UID != 0 ) {
+ errno = -EACCES;
+ return -1;
+ }
+ Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID);
+ t->Process->GID = ID;
+ return 0;
+}
+
+// --- Per-thread storage ---
+int *Threads_GetErrno(void)
+{
+ return &Proc_GetCurThread()->_errno;
+}
+
+// --- Configuration ---
+int *Threads_GetMaxFD(void)
+{
+ return &Proc_GetCurThread()->Process->MaxFD;
+}
+char **Threads_GetChroot(void)
+{
+ return &Proc_GetCurThread()->Process->RootDir;
+}
+char **Threads_GetCWD(void)
+{
+ return &Proc_GetCurThread()->Process->CurrentWorkingDir;
+}
+// ---
+
+/**
+ * \fn void Threads_Dump(void)
+ */
+void Threads_DumpActive(void)
+{
+ tThread *thread;
+ tThreadList *list;
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ int i;
+ #endif
+
+ Log("Active Threads: (%i reported)", giNumActiveThreads);
+
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ for( i = 0; i < MIN_PRIORITY+1; i++ )
+ {
+ list = &gaActiveThreads[i];
+ #else
+ list = &gActiveThreads;
+ #endif
+ for(thread=list->Head;thread;thread=thread->Next)
+ {
+ Log(" %p %i (%i) - %s (CPU %i)",
+ thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU);
+ if(thread->Status != THREAD_STAT_ACTIVE)
+ Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)",
+ thread->Status, THREAD_STAT_ACTIVE);
+ Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
+ Log(" KStack 0x%x", thread->KernelStack);
+ if( thread->bInstrTrace )
+ Log(" Tracing Enabled");
+ Proc_DumpThreadCPUState(thread);
+ }
+
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ }
+ #endif
+}
+
+/**
+ * \fn void Threads_Dump(void)
+ * \brief Dumps a list of currently running threads
+ */
+void Threads_Dump(void)
+{
+ tThread *thread;
+
+ Log("--- Thread Dump ---");
+ Threads_DumpActive();
+
+ Log("All Threads:");
+ for(thread=gAllThreads;thread;thread=thread->GlobalNext)
+ {
+ Log(" %p %i (%i) - %s (CPU %i)",
+ thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU);
+ Log(" State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]);
+ switch(thread->Status)
+ {
+ case THREAD_STAT_MUTEXSLEEP:
+ Log(" Mutex Pointer: %p", thread->WaitPointer);
+ break;
+ case THREAD_STAT_SEMAPHORESLEEP:
+ Log(" Semaphore Pointer: %p", thread->WaitPointer);
+ Log(" Semaphore Name: %s:%s",
+ ((tSemaphore*)thread->WaitPointer)->ModName,
+ ((tSemaphore*)thread->WaitPointer)->Name
+ );
+ break;
+ case THREAD_STAT_ZOMBIE:
+ Log(" Return Status: %i", thread->RetStatus);
+ break;
+ default: break;
+ }
+ Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
+ Log(" KStack 0x%x", thread->KernelStack);
+ if( thread->bInstrTrace )
+ Log(" Tracing Enabled");
+ Proc_DumpThreadCPUState(thread);
+ }
+}
+
+/**
+ * \brief Gets the next thread to run
+ * \param CPU Current CPU
+ * \param Last The thread the CPU was running
+ */
+tThread *Threads_GetNextToRun(int CPU, tThread *Last)
+{
+ tThread *thread;
+
+ // If this CPU has the lock, we must let it complete
+ if( CPU_HAS_LOCK( &glThreadListLock ) )
+ return Last;
+
+ // Don't change threads if the current CPU has switches disabled
+ if( gaThreads_NoTaskSwitch[CPU] )
+ return Last;
+
+ // Lock thread list
+ SHORTLOCK( &glThreadListLock );
+
+ // Make sure the current (well, old) thread is marked as de-scheduled
+ if(Last) Last->CurCPU = -1;
+
+ // No active threads, just take a nap
+ if(giNumActiveThreads == 0) {
+ SHORTREL( &glThreadListLock );
+ #if DEBUG_TRACE_TICKETS
+ Log("No active threads");
+ #endif
+ return NULL;
+ }
+
+ #if 0
+ #if SCHEDULER_TYPE != SCHED_RR_PRI
+ // Special case: 1 thread
+ if(giNumActiveThreads == 1) {
+ if( gActiveThreads.Head->CurCPU == -1 )
+ gActiveThreads.Head->CurCPU = CPU;
+
+ SHORTREL( &glThreadListLock );
+
+ if( gActiveThreads.Head->CurCPU == CPU )
+ return gActiveThreads.Head;
+
+ return NULL; // CPU has nothing to do
+ }
+ #endif
+ #endif
+
+ // Allow the old thread to be scheduled again
+ if( Last ) {
+ if( Last->Status == THREAD_STAT_ACTIVE ) {
+ tThreadList *list;
+ #if SCHEDULER_TYPE == SCHED_LOTTERY
+ giFreeTickets += caiTICKET_COUNTS[ Last->Priority ];
+ # if DEBUG_TRACE_TICKETS
+ LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n",
+ CPU, Last, Last->TID, Last->ThreadName, giFreeTickets,
+ caiTICKET_COUNTS[ Last->Priority ]);
+ # endif
+ #endif
+
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ list = &gaActiveThreads[ Last->Priority ];
+ #else
+ list = &gActiveThreads;
+ #endif
+ // Add to end of list
+ Threads_int_AddToList( list, Last );
+ }
+ #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
+ else
+ LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
+ CPU, Last, Last->TID, Last->ThreadName, Last->Status);
+ #endif
+ Last->CurCPU = -1;
+ }
+
+ // ---
+ // Lottery Scheduler
+ // ---
+ #if SCHEDULER_TYPE == SCHED_LOTTERY
+ {
+ int ticket, number;
+ # if 1
+ number = 0;
+ for(thread = gActiveThreads.Head; thread; thread = thread->Next)
+ {
+ if(thread->Status != THREAD_STAT_ACTIVE)
+ Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i",
+ thread, thread->TID, thread->ThreadName, thread->Status);
+ if(thread->Next == thread) {
+ Panic("Bookkeeping fail - %p %i(%s) loops back on itself",
+ thread, thread->TID, thread->ThreadName, thread->Status);
+ }
+ number += caiTICKET_COUNTS[ thread->Priority ];
+ }
+ if(number != giFreeTickets) {
+ Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i",
+ giFreeTickets, number, CPU);
+ }
+ # endif
+
+ // No free tickets (all tasks delegated to cores)
+ if( giFreeTickets == 0 ) {
+ SHORTREL(&glThreadListLock);
+ return NULL;
+ }
+
+ // Get the ticket number
+ ticket = number = rand() % giFreeTickets;
+
+ // Find the next thread
+ for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next )
+ {
+ if( caiTICKET_COUNTS[ thread->Priority ] > number) break;
+ number -= caiTICKET_COUNTS[ thread->Priority ];
+ }
+
+ // If we didn't find a thread, something went wrong
+ if(thread == NULL)
+ {
+ number = 0;
+ for(thread=gActiveThreads;thread;thread=thread->Next) {
+ if(thread->CurCPU >= 0) continue;
+ number += caiTICKET_COUNTS[ thread->Priority ];
+ }
+ Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)",
+ giFreeTickets, number);
+ }
+
+ // Remove
+ if(prev)
+ prev->Next = thread->Next;
+ else
+ gActiveThreads.Head = thread->Next;
+ if(!thread->Next)
+ gActiveThreads.Tail = prev;
+
+ giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ];
+ # if DEBUG_TRACE_TICKETS
+ LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n",
+ CPU, thread, thread->TID, thread->ThreadName,
+ giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]);
+ # endif
+ }
+
+ // ---
+ // Priority based round robin scheduler
+ // ---
+ #elif SCHEDULER_TYPE == SCHED_RR_PRI
+ {
+ int i;
+ thread = NULL;
+ for( i = 0; i < MIN_PRIORITY + 1; i ++ )
+ {
+ if( !gaActiveThreads[i].Head )
+ continue ;
+
+ thread = gaActiveThreads[i].Head;
+
+ // Remove from head
+ gaActiveThreads[i].Head = thread->Next;
+ if(!thread->Next)
+ gaActiveThreads[i].Tail = NULL;
+ thread->Next = NULL;
+ break;
+ }
+
+ // Anything to do?
+ if( !thread ) {
+ SHORTREL(&glThreadListLock);
+ return NULL;
+ }
+ if( thread->Status != THREAD_STAT_ACTIVE ) {
+ LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName);
+ }
+ }
+ #elif SCHEDULER_TYPE == SCHED_RR_SIM
+ {
+ // Get the next thread off the list
+ thread = gActiveThreads.Head;
+ gActiveThreads.Head = thread->Next;
+ if(!thread->Next)
+ gaActiveThreads.Tail = NULL;
+ thread->Next = NULL;
+
+ // Anything to do?
+ if( !thread ) {
+ SHORTREL(&glThreadListLock);
+ return NULL;
+ }
+ }
+ #else
+ # error "Unimplemented scheduling algorithm"
+ #endif
+
+ // Make the new thread non-schedulable
+ thread->CurCPU = CPU;
+ thread->Remaining = thread->Quantum;
+
+ SHORTREL( &glThreadListLock );
+
+ return thread;
+}
+
+// === EXPORTS ===
+EXPORT(Threads_GetUID);
+EXPORT(Threads_GetGID);
--- /dev/null
+/*
+ * Acess 2
+ * - By John Hodge (thePowersGang)
+ *
+ * Timer Code
+ */
+#include <acess.h>
+#include <timers.h>
+#include <events.h>
+#include <hal_proc.h> // Proc_GetCurThread
+
+// === CONSTANTS ===
+#define NUM_TIMERS 8
+
+// === TYPEDEFS ===
+struct sTimer {
+ tTimer *Next;
+ Sint64 FiresAfter;
+ void (*Callback)(void*);
+ void *Argument;
+};
+
+// === PROTOTYPES ===
+void Timer_CallTimers(void);
+
+// === GLOBALS ===
+volatile Uint64 giTicks = 0;
+volatile Sint64 giTimestamp = 0;
+volatile Uint64 giPartMiliseconds = 0;
+tTimer *gTimers; // TODO: Replace by a ring-list timer
+
+// === CODE ===
+/**
+ * \fn void Timer_CallTimers()
+ */
+void Timer_CallTimers()
+{
+ while( gTimers && gTimers->FiresAfter < now() )
+ {
+ tTimer *next;
+
+ if( gTimers->Callback )
+ gTimers->Callback(gTimers->Argument);
+ else
+ Threads_PostEvent(gTimers->Argument, THREAD_EVENT_TIMER);
+
+ next = gTimers->Next;
+ free(gTimers);
+ gTimers = next;
+ }
+}
+
+/**
+ * \brief Schedule an action
+ */
+tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument)
+{
+ tTimer *ret;
+ tTimer *t, *p;
+
+ if(Callback == NULL)
+ Argument = Proc_GetCurThread();
+
+ // TODO: Use a pool instead?
+ ret = malloc(sizeof(tTimer));
+
+ ret->Callback = Callback;
+ ret->FiresAfter = now() + Delta;
+ ret->Argument = Argument;
+
+ // Add into list (sorted)
+ for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
+ {
+ if( t->FiresAfter > ret->FiresAfter ) break;
+ }
+ ret->Next = t;
+ p->Next = ret;
+
+ return ret;
+}
+
+/**
+ * \brief Delete a timer
+ */
+void Time_RemoveTimer(tTimer *Timer)
+{
+ tTimer *t, *p;
+ for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next )
+ {
+ if( t == Timer )
+ {
+ p->Next = t->Next;
+ free(Timer);
+ return ;
+ }
+ }
+}
+
+/**
+ * \fn void Time_Delay(int Delay)
+ * \brief Delay for a small ammount of time
+ */
+void Time_Delay(int Delay)
+{
+// tTime dest = now() + Delay;
+// while(dest > now()) Threads_Yield();
+ Time_CreateTimer(Delay, NULL, NULL);
+ Threads_WaitEvents(THREAD_EVENT_TIMER);
+}
+
+// === EXPORTS ===
+EXPORT(Time_CreateTimer);
+EXPORT(Time_RemoveTimer);
+EXPORT(Time_Delay);
--- /dev/null
+/*
+ * Acess Micro VFS
+ */
+#include <acess.h>
+#include "vfs.h"
+#include "vfs_int.h"
+
+// === GLOBALS ===
+tVFS_ACL gVFS_ACL_EveryoneRWX = { {1,-1}, {0,VFS_PERM_ALL} };
+tVFS_ACL gVFS_ACL_EveryoneRW = { {1,-1}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE} };
+tVFS_ACL gVFS_ACL_EveryoneRX = { {1,-1}, {0,VFS_PERM_READ|VFS_PERM_EXECUTE} };
+tVFS_ACL gVFS_ACL_EveryoneRO = { {1,-1}, {0,VFS_PERM_READ} };
+
+// === CODE ===
+/**
+ * \fn int VFS_CheckACL(tVFS_Node *Node, Uint Permissions)
+ * \brief Checks the permissions on a file
+ */
+int VFS_CheckACL(tVFS_Node *Node, Uint Permissions)
+{
+ int i;
+ int uid = Threads_GetUID();
+ int gid = Threads_GetGID();
+
+ // Root can do anything
+ if(uid == 0) return 1;
+
+ // Root only file?, fast return
+ if( Node->NumACLs == 0 ) {
+ Log("VFS_CheckACL - %p inaccesable, NumACLs = 0, uid=%i", Node, uid);
+ return 0;
+ }
+
+ // Check Deny Permissions
+ for(i=0;i<Node->NumACLs;i++)
+ {
+ if(!Node->ACLs[i].Inv) continue; // Ignore ALLOWs
+ if(Node->ACLs[i].ID != 0x7FFFFFFF)
+ {
+ if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue;
+ if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue;
+ }
+
+ //Log("Deny %x", Node->ACLs[i].Perms);
+
+ if(Node->ACLs[i].Perms & Permissions) {
+ Log("VFS_CheckACL - %p inaccesable, %x denied",
+ Node, Node->ACLs[i].Perms & Permissions);
+ return 0;
+ }
+ }
+
+ // Check for allow permissions
+ for(i=0;i<Node->NumACLs;i++)
+ {
+ if(Node->ACLs[i].Inv) continue; // Ignore DENYs
+ if(Node->ACLs[i].ID != 0x7FFFFFFF)
+ {
+ if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue;
+ if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue;
+ }
+
+ //Log("Allow %x", Node->ACLs[i].Perms);
+
+ if((Node->ACLs[i].Perms & Permissions) == Permissions) return 1;
+ }
+
+ Log("VFS_CheckACL - %p inaccesable, %x not allowed", Node, Permissions);
+ return 0;
+}
+/**
+ * \fn int VFS_GetACL(int FD, tVFS_ACL *Dest)
+ */
+int VFS_GetACL(int FD, tVFS_ACL *Dest)
+{
+ int i;
+ tVFS_Handle *h = VFS_GetHandle(FD);
+
+ // Error check
+ if(!h) {
+ return -1;
+ }
+
+ // Root can do anything
+ if(Dest->Group == 0 && Dest->ID == 0) {
+ Dest->Inv = 0;
+ Dest->Perms = -1;
+ return 1;
+ }
+
+ // Root only file?, fast return
+ if( h->Node->NumACLs == 0 ) {
+ Dest->Inv = 0;
+ Dest->Perms = 0;
+ return 0;
+ }
+
+ // Check Deny Permissions
+ for(i=0;i<h->Node->NumACLs;i++)
+ {
+ if(h->Node->ACLs[i].Group != Dest->Group) continue;
+ if(h->Node->ACLs[i].ID != Dest->ID) continue;
+
+ Dest->Inv = h->Node->ACLs[i].Inv;
+ Dest->Perms = h->Node->ACLs[i].Perms;
+ return 1;
+ }
+
+
+ Dest->Inv = 0;
+ Dest->Perms = 0;
+ return 0;
+}
+
+/**
+ * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group)
+ * \brief Converts UNIX permissions to three Acess ACL entries
+ */
+tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group)
+{
+ tVFS_ACL *ret = malloc(sizeof(tVFS_ACL)*3);
+
+ // Error Check
+ if(!ret) return NULL;
+
+ // Owner
+ ret[0].Group = 0; ret[0].ID = Owner;
+ ret[0].Inv = 0; ret[0].Perms = 0;
+ if(Mode & 0400) ret[0].Perms |= VFS_PERM_READ;
+ if(Mode & 0200) ret[0].Perms |= VFS_PERM_WRITE;
+ if(Mode & 0100) ret[0].Perms |= VFS_PERM_EXECUTE;
+
+ // Group
+ ret[1].Group = 1; ret[1].ID = Group;
+ ret[1].Inv = 0; ret[1].Perms = 0;
+ if(Mode & 0040) ret[1].Perms |= VFS_PERM_READ;
+ if(Mode & 0020) ret[1].Perms |= VFS_PERM_WRITE;
+ if(Mode & 0010) ret[1].Perms |= VFS_PERM_EXECUTE;
+
+ // Global
+ ret[2].Group = 1; ret[2].ID = -1;
+ ret[2].Inv = 0; ret[2].Perms = 0;
+ if(Mode & 0004) ret[2].Perms |= VFS_PERM_READ;
+ if(Mode & 0002) ret[2].Perms |= VFS_PERM_WRITE;
+ if(Mode & 0001) ret[2].Perms |= VFS_PERM_EXECUTE;
+
+ // Return buffer
+ return ret;
+}
+
+// === EXPORTS ===
+// --- Variables ---
+EXPORTV(gVFS_ACL_EveryoneRWX);
+EXPORTV(gVFS_ACL_EveryoneRW);
+EXPORTV(gVFS_ACL_EveryoneRX);
+// --- Functions ---
+EXPORT(VFS_UnixToAcessACL);
--- /dev/null
+/*
+ * Acess2 VFS
+ * - Directory Management Functions
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <vfs.h>
+#include <vfs_int.h>
+
+// === IMPORTS ===
+extern tVFS_Mount *gRootMount;
+
+// === PROTOTYPES ===
+#if 0
+ int VFS_MkDir(const char *Path);
+ int VFS_MkNod(const char *Path, Uint Flags);
+ int VFS_Symlink(const char *Name, const char *Link);
+#endif
+
+// === CODE ===
+/**
+ * \fn int VFS_MkDir(char *Path)
+ * \brief Create a new node
+ * \param Path Path of directory to create
+ */
+int VFS_MkDir(const char *Path)
+{
+ return VFS_MkNod(Path, VFS_FFLAG_DIRECTORY);
+}
+
+/**
+ * \fn int VFS_MkNod(char *Path, Uint Flags)
+ * \brief Create a new node in a directory
+ * \param Path Path of new node
+ * \param Flags Flags to apply to the node
+ */
+int VFS_MkNod(const char *Path, Uint Flags)
+{
+ char *absPath, *name;
+ int pos = 0, oldpos = 0;
+ int next = 0;
+ tVFS_Node *parent;
+ int ret;
+
+ ENTER("sPath xFlags", Path, Flags);
+
+ absPath = VFS_GetAbsPath(Path);
+ LOG("absPath = '%s'", absPath);
+
+ while( (next = strpos(&absPath[pos+1], '/')) != -1 ) {
+ LOG("next = %i", next);
+ pos += next+1;
+ LOG("pos = %i", pos);
+ oldpos = pos;
+ }
+ absPath[oldpos] = '\0'; // Mutilate path
+ name = &absPath[oldpos+1];
+
+ LOG("absPath = '%s', name = '%s'", absPath, name);
+
+ // Check for root
+ if(absPath[0] == '\0')
+ parent = VFS_ParsePath("/", NULL, NULL);
+ else
+ parent = VFS_ParsePath(absPath, NULL, NULL);
+
+ LOG("parent = %p", parent);
+
+ if(!parent) {
+ LEAVE('i', -1);
+ return -1; // Error Check
+ }
+
+ // Permissions Check
+ if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) {
+ _CloseNode(parent);
+ free(absPath);
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ LOG("parent = %p", parent);
+
+ if(!parent->Type || !parent->Type->MkNod) {
+ Warning("VFS_MkNod - Directory has no MkNod method");
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Create node
+ ret = parent->Type->MkNod(parent, name, Flags);
+
+ // Free allocated string
+ free(absPath);
+
+ // Free Parent
+ _CloseNode(parent);
+
+ // Error Check
+ if(ret == 0) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \fn int VFS_Symlink(const char *Name, const char *Link)
+ * \brief Creates a symlink called \a Name to \a Link
+ * \param Name Name of symbolic link
+ * \param Link Destination of symbolic link
+ */
+int VFS_Symlink(const char *Name, const char *Link)
+{
+ char *realLink;
+ int fp;
+
+ ENTER("sName sLink", Name, Link);
+
+ // Get absolue path name
+ realLink = VFS_GetAbsPath( Link );
+ if(!realLink) {
+ Log_Warning("VFS", "Path '%s' is badly formed", Link);
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ LOG("realLink = '%s'", realLink);
+
+ // Make node
+ if( VFS_MkNod(Name, VFS_FFLAG_SYMLINK) != 0 ) {
+ Log_Warning("VFS", "Unable to create link node '%s'", Name);
+ free(realLink);
+ LEAVE('i', -2);
+ return -2; // Make link node
+ }
+
+ // Write link address
+ fp = VFS_Open(Name, VFS_OPENFLAG_WRITE|VFS_OPENFLAG_NOLINK);
+ VFS_Write(fp, strlen(realLink), realLink);
+ VFS_Close(fp);
+
+ free(realLink);
+
+ LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \fn int VFS_ReadDir(int FD, char *Dest)
+ * \brief Read from a directory
+ */
+int VFS_ReadDir(int FD, char *Dest)
+{
+ tVFS_Handle *h = VFS_GetHandle(FD);
+ char *tmp;
+
+ //ENTER("ph pDest", h, Dest);
+
+ if(!h || !h->Node->Type || !h->Node->Type->ReadDir) {
+ //LEAVE('i', 0);
+ return 0;
+ }
+
+ if(h->Node->Size != -1 && h->Position >= h->Node->Size) {
+ //LEAVE('i', 0);
+ return 0;
+ }
+
+ do {
+ tmp = h->Node->Type->ReadDir(h->Node, h->Position);
+ if((Uint)tmp < (Uint)VFS_MAXSKIP)
+ h->Position += (Uint)tmp;
+ else
+ h->Position ++;
+ } while(tmp != NULL && (Uint)tmp < (Uint)VFS_MAXSKIP);
+
+ //LOG("tmp = '%s'", tmp);
+
+ if(!tmp) {
+ //LEAVE('i', 0);
+ return 0;
+ }
+
+ strcpy(Dest, tmp);
+ free(tmp);
+
+ //LEAVE('i', 1);
+ return 1;
+}
--- /dev/null
+/*
+ * Acess 2
+ * Device Filesystem (DevFS)
+ * - vfs/fs/devfs.c
+ */
+#include <acess.h>
+#include <vfs.h>
+#include <fs_devfs.h>
+
+// === PROTOTYPES ===
+#if 0
+ int DevFS_AddDevice(tDevFS_Driver *Device);
+void DevFS_DelDevice(tDevFS_Driver *Device);
+#endif
+tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options);
+char *DevFS_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name);
+
+// === GLOBALS ===
+tVFS_Driver gDevFS_Info = {
+ "devfs", 0, DevFS_InitDevice, NULL, NULL
+ };
+tVFS_NodeType gDevFS_DirType = {
+ .TypeName = "DevFS-Dir",
+ .ReadDir = DevFS_ReadDir,
+ .FindDir = DevFS_FindDir
+ };
+tVFS_Node gDevFS_RootNode = {
+ .Size = 0,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Type = &gDevFS_DirType
+ };
+tDevFS_Driver *gDevFS_Drivers = NULL;
+ int giDevFS_NextID = 1;
+tShortSpinlock glDevFS_ListLock;
+
+// === CODE ===
+/**
+ * \fn int DevFS_AddDevice(tDevFS_Driver *Device)
+ */
+int DevFS_AddDevice(tDevFS_Driver *Device)
+{
+ int ret = 0;
+ tDevFS_Driver *dev;
+
+ SHORTLOCK( &glDevFS_ListLock );
+
+ // Check if the device is already registered or the name is taken
+ for( dev = gDevFS_Drivers; dev; dev = dev->Next )
+ {
+ if(dev == Device) break;
+ if(strcmp(dev->Name, Device->Name) == 0) break;
+ }
+
+ if(dev) {
+ if(dev == Device)
+ Log_Warning("DevFS", "Device %p '%s' attempted to register itself twice",
+ dev, dev->Name);
+ else
+ Log_Warning("DevFS", "Device %p attempted to register '%s' which was owned by %p",
+ Device, dev->Name, dev);
+ ret = 0; // Error
+ }
+ else {
+ Device->Next = gDevFS_Drivers;
+ gDevFS_Drivers = Device;
+ gDevFS_RootNode.Size ++;
+ ret = giDevFS_NextID ++;
+ }
+ SHORTREL( &glDevFS_ListLock );
+
+ return ret;
+}
+
+/**
+ * \brief Delete a device from the DevFS folder
+ */
+void DevFS_DelDevice(tDevFS_Driver *Device)
+{
+ tDevFS_Driver *prev = NULL, *dev;
+
+ SHORTLOCK( &glDevFS_ListLock );
+ // Search list for device
+ for(dev = gDevFS_Drivers;
+ dev && dev != Device;
+ prev = dev, dev = dev->Next
+ );
+
+ // Check if it was found
+ if(dev)
+ {
+ if(prev)
+ prev->Next = Device->Next;
+ else
+ gDevFS_Drivers = Device->Next;
+ }
+ else
+ Log_Warning("DevFS", "Attempted to unregister device %p '%s' which was not registered",
+ Device, Device->Name);
+
+ SHORTREL( &glDevFS_ListLock );
+}
+
+/**
+ * \brief Initialise the DevFS and detect double-mounting, or just do nothing
+ * \note STUB
+ */
+tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options)
+{
+ return &gDevFS_RootNode;
+}
+
+/**
+ * \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos)
+ */
+char *DevFS_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tDevFS_Driver *dev;
+
+ if(Pos < 0) return NULL;
+
+ for(dev = gDevFS_Drivers;
+ dev && Pos--;
+ dev = dev->Next
+ );
+
+ if(dev)
+ return strdup(dev->Name);
+ else
+ return NULL;
+}
+
+/**
+ * \fn tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name)
+ * \brief Get an entry from the devices directory
+ */
+tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name)
+{
+ tDevFS_Driver *dev;
+
+ //ENTER("pNode sName", Node, Name);
+
+ for(dev = gDevFS_Drivers;
+ dev;
+ dev = dev->Next
+ )
+ {
+ //LOG("dev = %p", dev);
+ //LOG("dev->Name = '%s'", dev->Name);
+ if(strcmp(dev->Name, Name) == 0) {
+ //LEAVE('p', &dev->RootNode);
+ return &dev->RootNode;
+ }
+ }
+
+ //LEAVE('n');
+ return NULL;
+}
+
+// --- EXPORTS ---
+EXPORT(DevFS_AddDevice);
+EXPORT(DevFS_DelDevice);
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - Root Filesystem Driver
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <vfs.h>
+#include <vfs_ramfs.h>
+
+// === CONSTANTS ===
+#define MAX_FILES 64
+#define MAX_FILE_SIZE 1024
+
+// === PROTOTYPES ===
+tVFS_Node *Root_InitDevice(const char *Device, const char **Options);
+ int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name);
+char *Root_ReadDir(tVFS_Node *Node, int Pos);
+Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+tRamFS_File *Root_int_AllocFile(void);
+
+// === GLOBALS ===
+tVFS_Driver gRootFS_Info = {
+ "rootfs", 0, Root_InitDevice, NULL, NULL
+ };
+tRamFS_File RootFS_Files[MAX_FILES];
+tVFS_ACL RootFS_DirACLs[3] = {
+ {{0,0}, {0,VFS_PERM_ALL}}, // Owner (Root)
+ {{1,0}, {0,VFS_PERM_ALL}}, // Group (Root)
+ {{0,-1}, {0,VFS_PERM_ALL^VFS_PERM_WRITE}} // World (Nobody)
+};
+tVFS_ACL RootFS_FileACLs[3] = {
+ {{0,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Owner (Root)
+ {{1,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Group (Root)
+ {{0,-1}, {0,VFS_PERM_READ}} // World (Nobody)
+};
+tVFS_NodeType gRootFS_DirType = {
+ .TypeName = "RootFS-Dir",
+ .ReadDir = Root_ReadDir,
+ .FindDir = Root_FindDir,
+ .MkNod = Root_MkNod
+};
+tVFS_NodeType gRootFS_FileType = {
+ .TypeName = "RootFS-File",
+ .Read = Root_Read,
+ .Write = Root_Write,
+};
+
+// === CODE ===
+/**
+ * \brief Initialise the root filesystem
+ */
+tVFS_Node *Root_InitDevice(const char *Device, const char **Options)
+{
+ tRamFS_File *root;
+ if(strcmp(Device, "root") != 0) {
+ return NULL;
+ }
+
+ // Create Root Node
+ root = &RootFS_Files[0];
+
+ root->Node.ImplPtr = root;
+
+ root->Node.CTime
+ = root->Node.MTime
+ = root->Node.ATime = now();
+ root->Node.NumACLs = 3;
+ root->Node.ACLs = RootFS_DirACLs;
+
+ root->Node.Type = &gRootFS_DirType;
+
+ return &root->Node;
+}
+
+/**
+ * \fn int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
+ * \brief Create an entry in the root directory
+ */
+int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
+{
+ tRamFS_File *parent = Node->ImplPtr;
+ tRamFS_File *child;
+ tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild;
+
+ ENTER("pNode sName xFlags", Node, Name, Flags);
+
+ LOG("%i > %i", strlen(Name)+1, sizeof(child->Name));
+ if(strlen(Name) + 1 > sizeof(child->Name))
+ LEAVE_RET('i', 0);
+
+ // Find last child, while we're at it, check for duplication
+ for( child = parent->Data.FirstChild; child; prev = child, child = child->Next )
+ {
+ if(strcmp(child->Name, Name) == 0) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ }
+
+ child = Root_int_AllocFile();
+ memset(child, 0, sizeof(tRamFS_File));
+
+ strcpy(child->Name, Name);
+
+ child->Parent = parent;
+ child->Next = NULL;
+ child->Data.FirstChild = NULL;
+
+ child->Node.ImplPtr = child;
+ child->Node.Flags = Flags;
+ child->Node.NumACLs = 3;
+ child->Node.Size = 0;
+
+ if(Flags & VFS_FFLAG_DIRECTORY)
+ {
+ child->Node.ACLs = RootFS_DirACLs;
+ child->Node.Type = &gRootFS_DirType;
+ } else {
+ if(Flags & VFS_FFLAG_SYMLINK)
+ child->Node.ACLs = RootFS_DirACLs;
+ else
+ child->Node.ACLs = RootFS_FileACLs;
+ child->Node.Type = &gRootFS_FileType;
+ }
+
+ prev->Next = child;
+
+ parent->Node.Size ++;
+
+ LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \fn tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name)
+ * \brief Find an entry in the filesystem
+ */
+tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name)
+{
+ tRamFS_File *parent = Node->ImplPtr;
+ tRamFS_File *child = parent->Data.FirstChild;
+
+ //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name);
+
+ for(;child;child = child->Next)
+ {
+ //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name);
+ if(strcmp(child->Name, Name) == 0) return &child->Node;
+ }
+
+ return NULL;
+}
+
+/**
+ * \fn char *Root_ReadDir(tVFS_Node *Node, int Pos)
+ * \brief Get an entry from the filesystem
+ */
+char *Root_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tRamFS_File *parent = Node->ImplPtr;
+ tRamFS_File *child = parent->Data.FirstChild;
+
+ for( ; child && Pos--; child = child->Next ) ;
+
+ if(child) return strdup(child->Name);
+
+ return NULL;
+}
+
+/**
+ * \fn Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from a file in the root directory
+ */
+Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tRamFS_File *file = Node->ImplPtr;
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ if(Offset > Node->Size) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ if(Length > Node->Size) Length = Node->Size;
+
+ if(Offset+Length > Node->Size)
+ Length = Node->Size - Offset;
+
+ memcpy(Buffer, file->Data.Bytes+Offset, Length);
+ LOG("Buffer = '%.*s'", (int)Length, Buffer);
+
+ LEAVE('i', Length);
+ return Length;
+}
+
+/**
+ * \fn Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Write to a file in the root directory
+ */
+Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tRamFS_File *file = Node->ImplPtr;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ if(Offset > Node->Size) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ if(Offset + Length > MAX_FILE_SIZE)
+ {
+ Length = MAX_FILE_SIZE - Offset;
+ }
+
+ LOG("Buffer = '%.*s'", (int)Length, Buffer);
+
+ // Check if buffer needs to be expanded
+ if(Offset + Length > Node->Size)
+ {
+ void *tmp = realloc( file->Data.Bytes, Offset + Length );
+ if(tmp == NULL) {
+ Warning("Root_Write - Increasing buffer size failed");
+ LEAVE('i', -1);
+ return -1;
+ }
+ file->Data.Bytes = tmp;
+ Node->Size = Offset + Length;
+ LOG("Expanded buffer to %i bytes", (int)Node->Size);
+ }
+
+ memcpy(file->Data.Bytes+Offset, Buffer, Length);
+ LOG("File - '%.*s'", Node->Size, file->Data.Bytes);
+
+ LEAVE('i', Length);
+ return Length;
+}
+
+/**
+ * \fn tRamFS_File *Root_int_AllocFile(void)
+ * \brief Allocates a file from the pool
+ */
+tRamFS_File *Root_int_AllocFile(void)
+{
+ int i;
+ for( i = 0; i < MAX_FILES; i ++ )
+ {
+ if( RootFS_Files[i].Name[0] == '\0' )
+ {
+ return &RootFS_Files[i];
+ }
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ * Acess2 VFS
+ * - AllocHandle, GetHandle
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <mm_virt.h>
+#include "vfs.h"
+#include "vfs_int.h"
+#include "vfs_ext.h"
+#include <threads.h> // GetMaxFD
+#include <vfs_threads.h> // Handle maintainance
+
+// === CONSTANTS ===
+#define MAX_KERNEL_FILES 128
+
+// === PROTOTYPES ===
+#if 0
+tVFS_Handle *VFS_GetHandle(int FD);
+#endif
+ int VFS_AllocHandle(int FD, tVFS_Node *Node, int Mode);
+
+// === GLOBALS ===
+tVFS_Handle *gaUserHandles = (void*)MM_PPD_HANDLES;
+tVFS_Handle *gaKernelHandles = (void*)MM_KERNEL_VFS;
+
+// === CODE ===
+/**
+ * \fn tVFS_Handle *VFS_GetHandle(int FD)
+ * \brief Gets a pointer to the handle information structure
+ */
+tVFS_Handle *VFS_GetHandle(int FD)
+{
+ tVFS_Handle *h;
+
+ //Log_Debug("VFS", "VFS_GetHandle: (FD=0x%x)", FD);
+
+ if(FD < 0) return NULL;
+
+ if(FD & VFS_KERNEL_FLAG) {
+ FD &= (VFS_KERNEL_FLAG - 1);
+ if(FD >= MAX_KERNEL_FILES) return NULL;
+ h = &gaKernelHandles[ FD ];
+ } else {
+ if(FD >= *Threads_GetMaxFD()) return NULL;
+ h = &gaUserHandles[ FD ];
+ }
+
+ if(h->Node == NULL) return NULL;
+ //Log_Debug("VFS", "VFS_GetHandle: RETURN %p", h);
+ return h;
+}
+
+int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
+{
+ int i;
+
+ // Check for a user open
+ if(bIsUser)
+ {
+ int max_handles = *Threads_GetMaxFD();
+ // Allocate Buffer
+ if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
+ {
+ Uint addr, size;
+ size = max_handles * sizeof(tVFS_Handle);
+ for(addr = 0; addr < size; addr += 0x1000)
+ {
+ if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) )
+ {
+ Warning("OOM - VFS_AllocHandle");
+ Threads_Exit(0, 0xFF); // Terminate user
+ }
+ }
+ memset( gaUserHandles, 0, size );
+ }
+ // Get a handle
+ for( i = 0; i < max_handles; i ++ )
+ {
+ if(gaUserHandles[i].Node) continue;
+ gaUserHandles[i].Node = Node;
+ gaUserHandles[i].Position = 0;
+ gaUserHandles[i].Mode = Mode;
+ return i;
+ }
+ }
+ else
+ {
+ // Allocate space if not already
+ if( MM_GetPhysAddr( (tVAddr)gaKernelHandles ) == 0 )
+ {
+ Uint addr, size;
+ size = MAX_KERNEL_FILES * sizeof(tVFS_Handle);
+ for(addr = 0; addr < size; addr += 0x1000)
+ {
+ if( !MM_Allocate( (tVAddr)gaKernelHandles + addr ) )
+ {
+ Panic("OOM - VFS_AllocHandle");
+ Threads_Exit(0, 0xFF); // Terminate application (get some space back)
+ }
+ }
+ memset( gaKernelHandles, 0, size );
+ }
+ // Get a handle
+ for(i=0;i<MAX_KERNEL_FILES;i++)
+ {
+ if(gaKernelHandles[i].Node) continue;
+ gaKernelHandles[i].Node = Node;
+ gaKernelHandles[i].Position = 0;
+ gaKernelHandles[i].Mode = Mode;
+ return i|VFS_KERNEL_FLAG;
+ }
+ }
+
+ return -1;
+}
+
+void VFS_ReferenceUserHandles(void)
+{
+ int i;
+ int max_handles = *Threads_GetMaxFD();
+
+ // Check if this process has any handles
+ if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
+ return ;
+
+ for( i = 0; i < max_handles; i ++ )
+ {
+ tVFS_Handle *h;
+ h = &gaUserHandles[i];
+ if( !h->Node )
+ continue ;
+ if( h->Node->Type && h->Node->Type->Reference )
+ h->Node->Type->Reference( h->Node );
+ }
+}
+
+void VFS_CloseAllUserHandles(void)
+{
+ int i;
+ int max_handles = *Threads_GetMaxFD();
+
+ // Check if this process has any handles
+ if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
+ return ;
+
+ for( i = 0; i < max_handles; i ++ )
+ {
+ tVFS_Handle *h;
+ h = &gaUserHandles[i];
+ if( !h->Node )
+ continue ;
+ if( h->Node->Type && h->Node->Type->Close )
+ h->Node->Type->Close( h->Node );
+ }
+}
+
+/**
+ * \brief Take a backup of a set of file descriptors
+ */
+void *VFS_SaveHandles(int NumFDs, int *FDs)
+{
+ tVFS_Handle *ret;
+ int i;
+ int max_handles = *Threads_GetMaxFD();
+
+ // Check if this process has any handles
+ if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 )
+ return NULL;
+
+ // Allocate
+ ret = malloc( NumFDs * sizeof(tVFS_Handle) );
+ if( !ret )
+ return NULL;
+
+ if( NumFDs > max_handles )
+ NumFDs = max_handles;
+
+ // Take copies of the handles
+ for( i = 0; i < NumFDs; i ++ )
+ {
+ tVFS_Handle *h;
+ if( FDs == NULL )
+ h = &gaUserHandles[i];
+ else {
+ h = VFS_GetHandle(FDs[i] & (VFS_KERNEL_FLAG - 1));
+ if(!h) {
+ Log_Warning("VFS", "VFS_SaveHandles - Invalid FD %i",
+ FDs[i] & (VFS_KERNEL_FLAG - 1) );
+ free(ret);
+ return NULL;
+ }
+ }
+ memcpy( &ret[i], h, sizeof(tVFS_Handle) );
+
+ // Reference node
+ if( !h->Node )
+ continue ;
+ if( h->Node->Type && h->Node->Type->Reference )
+ h->Node->Type->Reference( h->Node );
+ }
+
+ return ret;
+}
+
+void VFS_RestoreHandles(int NumFDs, void *Handles)
+{
+ tVFS_Handle *handles = Handles;
+ int i;
+
+ // NULL = nothing to do
+ if( !Handles )
+ return ;
+
+ // Check if there is already a set of handles
+ if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) != 0 )
+ return ;
+
+
+ // Allocate user handle area
+ {
+ Uint addr, size;
+ int max_handles = *Threads_GetMaxFD();
+ size = max_handles * sizeof(tVFS_Handle);
+ for(addr = 0; addr < size; addr += 0x1000)
+ {
+ if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) )
+ {
+ Warning("OOM - VFS_AllocHandle");
+ Threads_Exit(0, 0xFF); // Terminate user
+ }
+ }
+ memset( gaUserHandles, 0, size );
+ }
+
+ // Restore handles
+ memcpy( gaUserHandles, handles, NumFDs * sizeof(tVFS_Handle) );
+ // Reference when copied
+ for( i = 0; i < NumFDs; i ++ )
+ {
+ tVFS_Handle *h = &handles[i];
+
+ if( !h->Node )
+ continue ;
+ if( h->Node->Type && h->Node->Type->Reference )
+ h->Node->Type->Reference( h->Node );
+ }
+}
+
+void VFS_FreeSavedHandles(int NumFDs, void *Handles)
+{
+ tVFS_Handle *handles = Handles;
+ int i;
+
+ // NULL = nothing to do
+ if( !Handles )
+ return ;
+
+ // Dereference all saved nodes
+ for( i = 0; i < NumFDs; i ++ )
+ {
+ tVFS_Handle *h = &handles[i];
+
+ if( !h->Node )
+ continue ;
+ if( h->Node->Type && h->Node->Type->Close )
+ h->Node->Type->Close( h->Node );
+ }
+ free( Handles );
+}
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - File IO Passthru's
+ */
+#define DEBUG 0
+#include <acess.h>
+#include "vfs.h"
+#include "vfs_int.h"
+
+// === CODE ===
+/**
+ * \fn Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer)
+ * \brief Read data from a node (file)
+ */
+Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer)
+{
+ tVFS_Handle *h;
+ Uint64 ret;
+
+ ENTER("iFD XLength pBuffer", FD, Length, Buffer);
+
+ h = VFS_GetHandle(FD);
+ if(!h) LEAVE_RET('i', -1);
+
+ if( !(h->Mode & VFS_OPENFLAG_READ) || h->Node->Flags & VFS_FFLAG_DIRECTORY )
+ LEAVE_RET('i', -1);
+
+ if(!h->Node->Type || !h->Node->Type->Read) LEAVE_RET('i', 0);
+
+ ret = h->Node->Type->Read(h->Node, h->Position, Length, Buffer);
+ if(ret == -1) LEAVE_RET('i', -1);
+
+ h->Position += ret;
+ LEAVE('X', ret);
+ return ret;
+}
+
+/**
+ * \fn Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read data from a given offset (atomic)
+ */
+Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tVFS_Handle *h;
+ Uint64 ret;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ if( !(h->Mode & VFS_OPENFLAG_READ) ) return -1;
+ if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1;
+
+ if( !h->Node->Type || !h->Node->Type->Read) {
+ Warning("VFS_ReadAt - Node %p, does not have a read method", h->Node);
+ return 0;
+ }
+ ret = h->Node->Type->Read(h->Node, Offset, Length, Buffer);
+ if(ret == -1) return -1;
+ return ret;
+}
+
+/**
+ * \fn Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer)
+ * \brief Read data from a node (file)
+ */
+Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer)
+{
+ tVFS_Handle *h;
+ Uint64 ret;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1;
+ if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1;
+
+ if( !h->Node->Type || !h->Node->Type->Write ) return 0;
+
+ ret = h->Node->Type->Write(h->Node, h->Position, Length, Buffer);
+ if(ret == -1) return -1;
+
+ h->Position += ret;
+ return ret;
+}
+
+/**
+ * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
+ * \brief Write data to a file at a given offset
+ */
+Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tVFS_Handle *h;
+ Uint64 ret;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1;
+ if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1;
+
+ if(!h->Node->Type || !h->Node->Type->Write) return 0;
+ ret = h->Node->Type->Write(h->Node, Offset, Length, Buffer);
+
+ if(ret == -1) return -1;
+ return ret;
+}
+
+/**
+ * \fn Uint64 VFS_Tell(int FD)
+ * \brief Returns the current file position
+ */
+Uint64 VFS_Tell(int FD)
+{
+ tVFS_Handle *h;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ return h->Position;
+}
+
+/**
+ * \fn int VFS_Seek(int FD, Sint64 Offset, int Whence)
+ * \brief Seek to a new location
+ * \param FD File descriptor
+ * \param Offset Where to go
+ * \param Whence From where
+ */
+int VFS_Seek(int FD, Sint64 Offset, int Whence)
+{
+ tVFS_Handle *h;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ //Log_Debug("VFS", "VFS_Seek: (fd=0x%x, Offset=0x%llx, Whence=%i)",
+ // FD, Offset, Whence);
+
+ // Set relative to current position
+ if(Whence == 0) {
+ h->Position += Offset;
+ return 0;
+ }
+
+ // Set relative to end of file
+ if(Whence < 0) {
+ if( h->Node->Size == -1 ) return -1;
+
+ h->Position = h->Node->Size - Offset;
+ return 0;
+ }
+
+ // Set relative to start of file
+ h->Position = Offset;
+ return 0;
+}
+
+/**
+ * \fn int VFS_IOCtl(int FD, int ID, void *Buffer)
+ * \brief Call an IO Control on a file
+ */
+int VFS_IOCtl(int FD, int ID, void *Buffer)
+{
+ tVFS_Handle *h;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ if(!h->Node->Type || !h->Node->Type->IOCtl) return -1;
+ return h->Node->Type->IOCtl(h->Node, ID, Buffer);
+}
+
+/**
+ * \fn int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
+ * \brief Retrieve file information
+ * \return Number of ACLs stored
+ */
+int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs)
+{
+ tVFS_Handle *h;
+ int max;
+
+ h = VFS_GetHandle(FD);
+ if(!h) return -1;
+
+ if( h->Mount )
+ Dest->mount = h->Mount->Identifier;
+ else
+ Dest->mount = 0;
+ Dest->inode = h->Node->Inode;
+ Dest->uid = h->Node->UID;
+ Dest->gid = h->Node->GID;
+ Dest->size = h->Node->Size;
+ Dest->atime = h->Node->ATime;
+ Dest->ctime = h->Node->MTime;
+ Dest->mtime = h->Node->CTime;
+ Dest->numacls = h->Node->NumACLs;
+
+ Dest->flags = 0;
+ if(h->Node->Flags & VFS_FFLAG_DIRECTORY) Dest->flags |= 0x10;
+ if(h->Node->Flags & VFS_FFLAG_SYMLINK) Dest->flags |= 0x20;
+
+ max = (MaxACLs < h->Node->NumACLs) ? MaxACLs : h->Node->NumACLs;
+ memcpy(&Dest->acls, h->Node->ACLs, max*sizeof(tVFS_ACL));
+
+ return max;
+}
+
+// === EXPORTS ===
+EXPORT(VFS_Read);
+EXPORT(VFS_Write);
+EXPORT(VFS_ReadAt);
+EXPORT(VFS_WriteAt);
+EXPORT(VFS_IOCtl);
+EXPORT(VFS_Seek);
+EXPORT(VFS_Tell);
--- /dev/null
+/*
+ * Acess 2
+ * Virtual File System
+ */
+#include <acess.h>
+#include <fs_sysfs.h>
+#include <threads.h>
+#include <vfs.h>
+#include <vfs_int.h>
+#include <vfs_ext.h>
+
+// === IMPORTS ===
+extern tVFS_Driver gRootFS_Info;
+extern tVFS_Driver gDevFS_Info;
+
+// === PROTOTYPES ===
+#if 0
+ int VFS_Init(void);
+char *VFS_GetTruePath(const char *Path);
+void VFS_GetMemPath(char *Dest, void *Base, Uint Length);
+tVFS_Driver *VFS_GetFSByName(const char *Name);
+ int VFS_AddDriver(tVFS_Driver *Info);
+#endif
+void VFS_UpdateDriverFile(void);
+
+// === EXPORTS ===
+EXPORT(VFS_AddDriver);
+
+// === GLOBALS ===
+tVFS_Node NULLNode = {0};
+tShortSpinlock slDriverListLock;
+tVFS_Driver *gVFS_Drivers = NULL;
+char *gsVFS_DriverFile = NULL;
+ int giVFS_DriverFileID = 0;
+
+char *gsVFS_MountFile = NULL;
+ int giVFS_MountFileID = 0;
+
+// === CODE ===
+/**
+ * \fn int VFS_Init(void)
+ * \brief Initialises the VFS for use by the kernel and user
+ */
+int VFS_Init(void)
+{
+ // Core Drivers
+ gVFS_Drivers = &gRootFS_Info;
+ gVFS_Drivers->Next = &gDevFS_Info;
+ VFS_UpdateDriverFile();
+
+ // Register with SysFS
+ giVFS_MountFileID = SysFS_RegisterFile("VFS/Mounts", NULL, 0);
+ giVFS_DriverFileID = SysFS_RegisterFile("VFS/Drivers", NULL, 0);
+
+ if( VFS_Mount("root", "/", "rootfs", "") != 0 ) {
+ Log_KernelPanic("VFS", "Unable to mount root (Where the **** is rootfs?)");
+ return -1;
+ }
+ VFS_MkDir("/Devices");
+ VFS_MkDir("/Mount");
+ VFS_Mount("dev", "/Devices", "devfs", "");
+
+ Log_Debug("VFS", "Setting max files");
+ *Threads_GetMaxFD() = 32;
+ return 0;
+}
+
+/**
+ * \fn char *VFS_GetTruePath(const char *Path)
+ * \brief Gets the true path (non-symlink) of a file
+ */
+char *VFS_GetTruePath(const char *Path)
+{
+ tVFS_Node *node;
+ char *ret, *tmp;
+
+ tmp = VFS_GetAbsPath(Path);
+ if(tmp == NULL) return NULL;
+ //Log(" VFS_GetTruePath: tmp = '%s'", tmp);
+ node = VFS_ParsePath(tmp, &ret, NULL);
+ free(tmp);
+ //Log(" VFS_GetTruePath: node=%p, ret='%s'", node, ret);
+
+ if(!node) return NULL;
+ if(node->Type->Close) node->Type->Close(node);
+
+ return ret;
+}
+
+/**
+ * \fn void VFS_GetMemPath(char *Dest, void *Base, Uint Length)
+ * \brief Create a VFS memory pointer path
+ */
+void VFS_GetMemPath(char *Dest, void *Base, Uint Length)
+{
+ Dest[0] = '$';
+ itoa( &Dest[1], (tVAddr)Base, 16, BITS/4, '0' );
+ Dest[BITS/4+1] = ':';
+ itoa( &Dest[BITS/4+2], Length, 16, BITS/4, '0' );
+ Dest[BITS/2+2] = '\0';
+}
+
+/**
+ * \fn tVFS_Driver *VFS_GetFSByName(const char *Name)
+ * \brief Gets a filesystem structure given a name
+ */
+tVFS_Driver *VFS_GetFSByName(const char *Name)
+{
+ tVFS_Driver *drv = gVFS_Drivers;
+
+ for(;drv;drv=drv->Next)
+ {
+// Log("strcmp('%s' (%p), '%s') == 0?", drv->Name, drv->Name, Name);
+ if(strcmp(drv->Name, Name) == 0)
+ return drv;
+ }
+ return NULL;
+}
+
+/**
+ * \fn int VFS_AddDriver(tVFS_Driver *Info)
+ */
+int VFS_AddDriver(tVFS_Driver *Info)
+{
+ if(!Info) return -1;
+
+ SHORTLOCK( &slDriverListLock );
+ Info->Next = gVFS_Drivers;
+ gVFS_Drivers = Info;
+ SHORTREL( &slDriverListLock );
+
+ VFS_UpdateDriverFile();
+
+ return 0;
+}
+
+/**
+ * \fn void VFS_UpdateDriverFile(void)
+ * \brief Updates the driver list file
+ */
+void VFS_UpdateDriverFile(void)
+{
+ tVFS_Driver *drv;
+ int len = 0;
+ char *buf;
+
+ // Format:
+ // <name>\n
+ for( drv = gVFS_Drivers; drv; drv = drv->Next )
+ {
+ len += 1 + strlen(drv->Name);
+ }
+ buf = malloc(len+1);
+ len = 0;
+ for( drv = gVFS_Drivers; drv; drv = drv->Next )
+ {
+ strcpy( &buf[len], drv->Name );
+ len += strlen(drv->Name);
+ buf[len++] = '\n';
+ }
+ buf[len] = '\0';
+
+ SysFS_UpdateFile( giVFS_DriverFileID, buf, len );
+ if(gsVFS_DriverFile) free(gsVFS_DriverFile);
+ gsVFS_DriverFile = buf;
+}
--- /dev/null
+/*
+ * Acess 2
+ * Virtual File System
+ * - Memory Pseudo Files
+ */
+#include <acess.h>
+#include <vfs.h>
+
+// === PROTOTYPES ===
+tVFS_Node *VFS_MemFile_Create(const char *Path);
+void VFS_MemFile_Close(tVFS_Node *Node);
+Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+
+// === GLOBALS ===
+tVFS_NodeType gVFS_MemFileType = {
+ .Close = VFS_MemFile_Close,
+ .Read = VFS_MemFile_Read,
+ .Write = VFS_MemFile_Write
+ };
+
+// === CODE ===
+/**
+ * \fn tVFS_Node *VFS_MemFile_Create(const char *Path)
+ */
+tVFS_Node *VFS_MemFile_Create(const char *Path)
+{
+ Uint base, size;
+ const char *str = Path;
+ tVFS_Node *ret;
+
+ str++; // Eat '$'
+
+ // Read Base address
+ base = 0;
+ for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ )
+ {
+ base *= 16;
+ if('A' <= *str && *str <= 'F')
+ base += *str - 'A' + 10;
+ else
+ base += *str - '0';
+ }
+
+ // Check separator
+ if(*str++ != ':') return NULL;
+
+ // Read buffer size
+ size = 0;
+ for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ )
+ {
+ size *= 16;
+ if('A' <= *str && *str <= 'F')
+ size += *str - 'A' + 10;
+ else
+ size += *str - '0';
+ }
+
+ // Check for NULL byte
+ if(*str != '\0') return NULL;
+
+ // Allocate and fill node
+ ret = malloc(sizeof(tVFS_Node));
+ memset(ret, 0, sizeof(tVFS_Node));
+
+ // State
+ ret->ImplPtr = (void*)base;
+ ret->Size = size;
+
+ // ACLs
+ ret->NumACLs = 1;
+ ret->ACLs = &gVFS_ACL_EveryoneRWX;
+
+ // Functions
+ ret->Type = &gVFS_MemFileType;
+
+ return ret;
+}
+
+/**
+ * \fn void VFS_MemFile_Close(tVFS_Node *Node)
+ * \brief Dereference and clean up a memory file
+ */
+void VFS_MemFile_Close(tVFS_Node *Node)
+{
+ Node->ReferenceCount --;
+ if( Node->ReferenceCount == 0 ) {
+ Node->ImplPtr = NULL;
+ free(Node);
+ }
+}
+
+/**
+ * \fn Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from a memory file
+ */
+Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ // Check for use of free'd file
+ if(Node->ImplPtr == NULL) return 0;
+
+ // Check for out of bounds read
+ if(Offset > Node->Size) return 0;
+
+ // Truncate data read if needed
+ if(Offset + Length > Node->Size)
+ Length = Node->Size - Offset;
+
+ // Copy Data
+ memcpy(Buffer, (Uint8*)Node->ImplPtr + Offset, Length);
+
+ return Length;
+}
+
+/**
+ * \fn Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Write to a memory file
+ */
+Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ // Check for use of free'd file
+ if(Node->ImplPtr == NULL) return 0;
+
+ // Check for out of bounds read
+ if(Offset > Node->Size) return 0;
+
+ // Truncate data read if needed
+ if(Offset + Length > Node->Size)
+ Length = Node->Size - Offset;
+
+ // Copy Data
+ memcpy((Uint8*)Node->ImplPtr + Offset, Buffer, Length);
+
+ return Length;
+}
--- /dev/null
+/*
+ * Acess2 Kernel VFS
+ * - By John Hodge (thePowersGang)
+ *
+ * mmap.c
+ * - VFS_MMap support
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <vfs.h>
+#include <vfs_ext.h>
+#include <vfs_int.h>
+
+#define MMAP_PAGES_PER_BLOCK 16
+
+// === STRUCTURES ===
+typedef struct sVFS_MMapPageBlock tVFS_MMapPageBlock;
+struct sVFS_MMapPageBlock
+{
+ tVFS_MMapPageBlock *Next;
+ Uint64 BaseOffset; // Must be a multiple of MMAP_PAGES_PER_BLOCK*PAGE_SIZE
+ tPAddr PhysAddrs[MMAP_PAGES_PER_BLOCK];
+};
+
+// === CODE ===
+void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset)
+{
+ tVFS_Handle *h;
+ tVAddr mapping_dest, mapping_base;
+ int npages, pagenum;
+ tVFS_MMapPageBlock *pb, *prev;
+
+ ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
+
+ if( Flags & MMAP_MAP_ANONYMOUS )
+ Offset = (tVAddr)DestHint & 0xFFF;
+
+ npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE;
+ pagenum = Offset / PAGE_SIZE;
+
+ mapping_base = (tVAddr)DestHint;
+ mapping_dest = mapping_base & ~(PAGE_SIZE-1);
+
+ // TODO: Locate space for the allocation
+
+ // Handle anonymous mappings
+ if( Flags & MMAP_MAP_ANONYMOUS )
+ {
+ size_t ofs = 0;
+ LOG("%i pages anonymous to %p", npages, mapping_dest);
+ for( ; npages --; mapping_dest += PAGE_SIZE, ofs += PAGE_SIZE )
+ {
+ if( MM_GetPhysAddr(mapping_dest) ) {
+ // TODO: Set flags to COW if needed (well, if shared)
+ MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW);
+ LOG("clear from %p, %i bytes", (void*)(mapping_base + ofs),
+ PAGE_SIZE - (mapping_base & (PAGE_SIZE-1))
+ );
+ memset( (void*)(mapping_base + ofs), 0, PAGE_SIZE - (mapping_base & (PAGE_SIZE-1)));
+ }
+ else {
+ LOG("New empty page");
+ // TODO: Map a COW zero page instead
+ if( !MM_Allocate(mapping_dest) ) {
+ // TODO: Error
+ Log_Warning("VFS", "VFS_MMap: Anon alloc to %p failed", mapping_dest);
+ }
+ memset((void*)mapping_dest, 0, PAGE_SIZE);
+ LOG("Anon map to %p", mapping_dest);
+ }
+ }
+ LEAVE_RET('p', (void*)mapping_base);
+ }
+
+ h = VFS_GetHandle(FD);
+ if( !h || !h->Node ) LEAVE_RET('n', NULL);
+
+ LOG("h = %p", h);
+
+ Mutex_Acquire( &h->Node->Lock );
+
+ // Search for existing mapping for each page
+ // - Sorted list of 16 page blocks
+ for(
+ pb = h->Node->MMapInfo, prev = NULL;
+ pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK < pagenum;
+ prev = pb, pb = pb->Next
+ );
+
+ LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0);
+
+ // - Allocate a block if needed
+ if( !pb || pb->BaseOffset > pagenum )
+ {
+ void *old_pb = pb;
+ pb = malloc( sizeof(tVFS_MMapPageBlock) );
+ if(!pb) {
+ Mutex_Release( &h->Node->Lock );
+ LEAVE_RET('n', NULL);
+ }
+ pb->Next = old_pb;
+ pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK;
+ memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs));
+ if(prev)
+ prev->Next = pb;
+ else
+ h->Node->MMapInfo = pb;
+ }
+
+ // - Map (and allocate) pages
+ while( npages -- )
+ {
+ if( MM_GetPhysAddr(mapping_dest) == 0 )
+ {
+ if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 )
+ {
+ tVFS_NodeType *nt = h->Node->Type;
+ if( !nt )
+ {
+ // TODO: error
+ }
+ else if( nt->MMap )
+ nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest);
+ else
+ {
+ int read_len;
+ // Allocate pages and read data
+ if( MM_Allocate(mapping_dest) == 0 ) {
+ // TODO: Unwrap
+ Mutex_Release( &h->Node->Lock );
+ LEAVE('n');
+ return NULL;
+ }
+ // TODO: Clip read length
+ read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest);
+// if( read_len != PAGE_SIZE ) {
+// memset( (void*)(mapping_dest+read_len), 0, PAGE_SIZE-read_len );
+// }
+ }
+ pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest );
+ MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node );
+ MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
+ LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
+ pb->PhysAddrs[pagenum - pb->BaseOffset]);
+ }
+ else
+ {
+ MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] );
+ MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
+ LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
+ pb->PhysAddrs[pagenum - pb->BaseOffset]);
+ }
+ h->Node->ReferenceCount ++;
+
+ // Set flags
+ if( !(Protection & MMAP_PROT_WRITE) ) {
+ MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO);
+ }
+ else {
+ MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
+ }
+
+ if( Protection & MMAP_PROT_EXEC ) {
+ MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC);
+ }
+ else {
+ MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC);
+ }
+ }
+ else
+ {
+ LOG("Flag update on %p", mapping_dest);
+ if( (MM_GetFlags(mapping_dest) & MM_PFLAG_RO) && (Protection & MMAP_PROT_WRITE) )
+ {
+ MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
+ }
+ }
+ if( Flags & MMAP_MAP_PRIVATE )
+ MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW);
+ pagenum ++;
+ mapping_dest += PAGE_SIZE;
+
+ // Roll on to next block if needed
+ if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK)
+ {
+ if( pb->Next && pb->Next->BaseOffset == pagenum )
+ pb = pb->Next;
+ else
+ {
+ tVFS_MMapPageBlock *oldpb = pb;
+ pb = malloc( sizeof(tVFS_MMapPageBlock) );
+ pb->Next = oldpb->Next;
+ pb->BaseOffset = pagenum;
+ memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs));
+ oldpb->Next = pb;
+ }
+ pagenum = 0;
+ }
+ }
+
+ Mutex_Release( &h->Node->Lock );
+
+ LEAVE('p', mapping_base);
+ return (void*)mapping_base;
+}
+
+int VFS_MUnmap(void *Addr, size_t Length)
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Acess Micro - VFS Server version 1
+ */
+#include <acess.h>
+#include <vfs.h>
+#include <vfs_int.h>
+#include <fs_sysfs.h>
+
+// === IMPORTS ===
+extern int giVFS_MountFileID;
+extern char *gsVFS_MountFile;
+
+// === PROTOTYPES ===
+#if 0
+ int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
+#endif
+void VFS_UpdateMountFile(void);
+
+// === GLOBALS ===
+tMutex glVFS_MountList;
+tVFS_Mount *gVFS_Mounts;
+tVFS_Mount *gVFS_RootMount = NULL;
+Uint32 giVFS_NextMountIdent = 1;
+
+// === CODE ===
+/**
+ * \brief Mount a device
+ * \param Device Device string to mount
+ * \param MountPoint Destination for the mount
+ * \param Filesystem Filesystem to use for the mount
+ * \param Options Options to be passed to the filesystem
+ * \return -1 on Invalid FS, -2 on No Mem, 0 on success
+ *
+ * Mounts the filesystem on \a Device at \a MountPoint using the driver
+ * \a Filesystem. The options in the string \a Options is passed to the
+ * driver's mount.
+ */
+int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options)
+{
+ tVFS_Mount *mnt;
+ tVFS_Driver *fs;
+ int deviceLen = strlen(Device);
+ int mountLen = strlen(MountPoint);
+ int argLen = strlen(Options);
+
+ // Get the filesystem
+ fs = VFS_GetFSByName(Filesystem);
+ if(!fs) {
+ Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem);
+ return -1;
+ }
+
+ // Create mount information
+ mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 );
+ if(!mnt) {
+ return -2;
+ }
+
+ // HACK: Forces VFS_ParsePath to fall back on root
+ if(mountLen == 1 && MountPoint[0] == '/')
+ mnt->MountPointLen = 0;
+ else
+ mnt->MountPointLen = mountLen;
+
+ // Fill Structure
+ mnt->Filesystem = fs;
+
+ mnt->Device = &mnt->StrData[0];
+ memcpy( mnt->Device, Device, deviceLen+1 );
+
+ mnt->MountPoint = &mnt->StrData[deviceLen+1];
+ memcpy( mnt->MountPoint, MountPoint, mountLen+1 );
+
+ mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1];
+ memcpy( mnt->Options, Options, argLen+1 );
+
+ // Initialise Volume
+ mnt->RootNode = fs->InitDevice(Device, NULL); //&ArgString);
+ if(!mnt->RootNode) {
+ free(mnt);
+ return -2;
+ }
+
+ mnt->Identifier = giVFS_NextMountIdent++;
+ #if 0
+ // Ensure identifiers don't repeat
+ // - Only a problem if there have been 4 billion mounts
+ while( giVFS_NextMountIdent == 0 || VFS_GetMountByIdent(giVFS_NextMountIdent) )
+ giVFS_NextMountIdent ++;
+ #endif
+
+ // Set root
+ if(!gVFS_RootMount) gVFS_RootMount = mnt;
+
+ // Add to mount list
+ Mutex_Acquire( &glVFS_MountList );
+ {
+ tVFS_Mount *tmp;
+ mnt->Next = NULL;
+ if(gVFS_Mounts) {
+ for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next );
+ tmp->Next = mnt;
+ }
+ else {
+ gVFS_Mounts = mnt;
+ }
+ }
+ Mutex_Release( &glVFS_MountList );
+
+ Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem);
+
+ VFS_UpdateMountFile();
+
+ return 0;
+}
+
+/**
+ * \brief Gets a mount point given the identifier
+ */
+tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID)
+{
+ tVFS_Mount *mnt;
+ for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
+ {
+ if(mnt->Identifier == MountID)
+ return mnt;
+ }
+ return NULL;
+}
+
+/**
+ * \brief Updates the mount file buffer
+ *
+ * Updates the ProcFS mounts file buffer to match the current mounts list.
+ */
+void VFS_UpdateMountFile(void)
+{
+ int len = 0;
+ char *buf;
+ tVFS_Mount *mnt;
+
+ // Format:
+ // <device>\t<location>\t<type>\t<options>\n
+
+ for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
+ {
+ len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint)
+ + strlen(mnt->Filesystem->Name) + strlen(mnt->Options);
+ }
+
+ buf = malloc( len + 1 );
+ len = 0;
+ for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
+ {
+ strcpy( &buf[len], mnt->Device );
+ len += strlen(mnt->Device);
+ buf[len++] = '\t';
+
+ strcpy( &buf[len], mnt->MountPoint );
+ len += strlen(mnt->MountPoint);
+ buf[len++] = '\t';
+
+ strcpy( &buf[len], mnt->Filesystem->Name );
+ len += strlen(mnt->Filesystem->Name);
+ buf[len++] = '\t';
+
+ strcpy( &buf[len], mnt->Options );
+ len += strlen(mnt->Options);
+ buf[len++] = '\n';
+ }
+ buf[len] = 0;
+
+ SysFS_UpdateFile( giVFS_MountFileID, buf, len );
+ if( gsVFS_MountFile ) free( gsVFS_MountFile );
+ gsVFS_MountFile = buf;
+}
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - File IO Passthru's
+ */
+#include <acess.h>
+#include "vfs.h"
+#include "vfs_int.h"
+
+// === TYPES ===
+typedef struct sCachedInode {
+ struct sCachedInode *Next;
+ tVFS_Node Node;
+} tCachedInode;
+typedef struct sInodeCache {
+ struct sInodeCache *Next;
+ int Handle;
+ tCachedInode *FirstNode; // Sorted List
+ Uint64 MaxCached; // Speeds up Searching
+} tInodeCache;
+
+// === PROTOTYPES ===
+tInodeCache *Inode_int_GetFSCache(int Handle);
+
+// === GLOBALS ===
+ int gVFS_NextInodeHandle = 1;
+tShortSpinlock glVFS_InodeCache;
+tInodeCache *gVFS_InodeCache = NULL;
+
+// === CODE ===
+/**
+ * \fn int Inode_GetHandle()
+ */
+int Inode_GetHandle()
+{
+ tInodeCache *ent;
+
+ ent = malloc( sizeof(tInodeCache) );
+ ent->MaxCached = 0;
+ ent->Handle = gVFS_NextInodeHandle++;
+ ent->Next = NULL; ent->FirstNode = NULL;
+
+ // Add to list
+ SHORTLOCK( &glVFS_InodeCache );
+ ent->Next = gVFS_InodeCache;
+ gVFS_InodeCache = ent;
+ SHORTREL( &glVFS_InodeCache );
+
+ return gVFS_NextInodeHandle-1;
+}
+
+/**
+ * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode)
+ * \brief Gets a node from the cache
+ */
+tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode)
+{
+ tInodeCache *cache;
+ tCachedInode *ent;
+
+ cache = Inode_int_GetFSCache(Handle);
+ if(!cache) return NULL;
+
+ if(Inode > cache->MaxCached) return NULL;
+
+ // Search Cache
+ ent = cache->FirstNode;
+ for( ; ent; ent = ent->Next )
+ {
+ if(ent->Node.Inode < Inode) continue;
+ if(ent->Node.Inode > Inode) return NULL;
+ ent->Node.ReferenceCount ++;
+ return &ent->Node;
+ }
+
+ return NULL; // Should never be reached
+}
+
+/**
+ * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node)
+ */
+tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node)
+{
+ tInodeCache *cache;
+ tCachedInode *newEnt, *ent, *prev;
+
+ cache = Inode_int_GetFSCache(Handle);
+ if(!cache) return NULL;
+
+ if(Node->Inode > cache->MaxCached)
+ cache->MaxCached = Node->Inode;
+
+ // Search Cache
+ ent = cache->FirstNode;
+ prev = (tCachedInode*) &cache->FirstNode;
+ for( ; ent; prev = ent, ent = ent->Next )
+ {
+ if(ent->Node.Inode < Node->Inode) continue;
+ if(ent->Node.Inode == Node->Inode) {
+ ent->Node.ReferenceCount ++;
+ return &ent->Node;
+ }
+ break;
+ }
+
+ // Create new entity
+ newEnt = malloc(sizeof(tCachedInode));
+ newEnt->Next = ent;
+ memcpy(&newEnt->Node, Node, sizeof(tVFS_Node));
+ prev->Next = newEnt;
+
+ return &newEnt->Node;
+}
+
+/**
+ * \fn void Inode_UncacheNode(int Handle, Uint64 Inode)
+ * \brief Dereferences/Removes a cached node
+ */
+void Inode_UncacheNode(int Handle, Uint64 Inode)
+{
+ tInodeCache *cache;
+ tCachedInode *ent, *prev;
+
+ cache = Inode_int_GetFSCache(Handle);
+ if(!cache) return ;
+
+ if(Inode > cache->MaxCached) return ;
+
+ // Search Cache
+ ent = cache->FirstNode;
+ prev = (tCachedInode*) &cache->FirstNode; // Special case removal
+ for( ; ent; prev = ent, ent = ent->Next )
+ {
+ if(ent->Node.Inode < Inode) continue;
+ if(ent->Node.Inode > Inode) return;
+ ent->Node.ReferenceCount --;
+ // Check if node needs to be freed
+ if(ent->Node.ReferenceCount == 0)
+ {
+ prev->Next = ent->Next;
+ if(ent->Node.Inode == cache->MaxCached)
+ {
+ if(ent != cache->FirstNode)
+ cache->MaxCached = prev->Node.Inode;
+ else
+ cache->MaxCached = 0;
+ }
+
+ free(ent);
+ }
+ return ;
+ }
+
+ return ;
+}
+
+/**
+ * \fn void Inode_ClearCache(int Handle)
+ * \brief Removes a cache
+ */
+void Inode_ClearCache(int Handle)
+{
+ tInodeCache *cache;
+ tInodeCache *prev = NULL;
+ tCachedInode *ent, *next;
+
+ // Find the cache
+ for(
+ cache = gVFS_InodeCache;
+ cache && cache->Handle < Handle;
+ prev = cache, cache = cache->Next
+ );
+ if(!cache || cache->Handle != Handle) return;
+
+ // Search Cache
+ ent = cache->FirstNode;
+ while( ent )
+ {
+ ent->Node.ReferenceCount = 1;
+ next = ent->Next;
+
+ if(ent->Node.Type && ent->Node.Type->Close)
+ ent->Node.Type->Close( &ent->Node );
+ free(ent);
+
+ ent = next;
+ }
+
+ // Free Cache
+ if(prev == NULL)
+ gVFS_InodeCache = cache->Next;
+ else
+ prev->Next = cache->Next;
+ free(cache);
+}
+
+/**
+ * \fn tInodeCache *Inode_int_GetFSCache(int Handle)
+ * \brief Gets a cache given it's handle
+ */
+tInodeCache *Inode_int_GetFSCache(int Handle)
+{
+ tInodeCache *cache = gVFS_InodeCache;
+ // Find Cache
+ for( ; cache; cache = cache->Next )
+ {
+ if(cache->Handle > Handle) continue;
+ if(cache->Handle < Handle) {
+ Warning("Inode_int_GetFSCache - Handle %i not in cache\n", Handle);
+ return NULL;
+ }
+ break;
+ }
+ if(!cache) {
+ Warning("Inode_int_GetFSCache - Handle %i not in cache [NULL]\n", Handle);
+ return NULL;
+ }
+
+ return cache;
+}
--- /dev/null
+/*
+ * Acess2 VFS
+ * - Open, Close and ChDir
+ */
+#define DEBUG 0
+#include <acess.h>
+#include "vfs.h"
+#include "vfs_int.h"
+#include "vfs_ext.h"
+#include <threads.h>
+
+// === CONSTANTS ===
+#define OPEN_MOUNT_ROOT 1
+#define MAX_PATH_SLASHES 256
+#define MAX_NESTED_LINKS 4
+#define MAX_PATH_LEN 255
+
+// === IMPORTS ===
+extern tVFS_Mount *gVFS_RootMount;
+extern int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode);
+extern tVFS_Node *VFS_MemFile_Create(const char *Path);
+
+// === PROTOTYPES ===
+ int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode );
+
+// === CODE ===
+/**
+ * \fn char *VFS_GetAbsPath(const char *Path)
+ * \brief Create an absolute path from a relative one
+ */
+char *VFS_GetAbsPath(const char *Path)
+{
+ char *ret;
+ int pathLen = strlen(Path);
+ char *pathComps[MAX_PATH_SLASHES];
+ char *tmpStr;
+ int iPos = 0;
+ int iPos2 = 0;
+ const char *chroot = *Threads_GetChroot();
+ int chrootLen;
+ const char *cwd = *Threads_GetCWD();
+ int cwdLen;
+
+ ENTER("sPath", Path);
+
+ // Memory File
+ if(Path[0] == '$') {
+ ret = malloc(strlen(Path)+1);
+ if(!ret) {
+ Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
+ return NULL;
+ }
+ strcpy(ret, Path);
+ LEAVE('p', ret);
+ return ret;
+ }
+
+ // - Fetch ChRoot
+ if( chroot == NULL )
+ chroot = "";
+ chrootLen = strlen(chroot);
+
+ // Check if the path is already absolute
+ if(Path[0] == '/') {
+ ret = malloc(chrootLen + pathLen + 1);
+ if(!ret) {
+ Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
+ return NULL;
+ }
+ strcpy(ret + chrootLen, Path);
+ }
+ else {
+ if(cwd == NULL) {
+ cwd = "/";
+ cwdLen = 1;
+ }
+ else {
+ cwdLen = strlen(cwd);
+ }
+ // Prepend the current directory
+ ret = malloc(chrootLen + cwdLen + 1 + pathLen + 1 );
+ strcpy(ret+chrootLen, cwd);
+ ret[cwdLen] = '/';
+ strcpy(ret+chrootLen+cwdLen+1, Path);
+ //Log("ret = '%s'", ret);
+ }
+
+ // Parse Path
+ pathComps[iPos++] = tmpStr = ret+chrootLen+1;
+ while(*tmpStr)
+ {
+ if(*tmpStr++ == '/')
+ {
+ pathComps[iPos++] = tmpStr;
+ if(iPos == MAX_PATH_SLASHES) {
+ LOG("Path '%s' has too many elements", Path);
+ free(ret);
+ LEAVE('n');
+ return NULL;
+ }
+ }
+ }
+ pathComps[iPos] = NULL;
+
+ // Cleanup
+ iPos2 = iPos = 0;
+ while(pathComps[iPos])
+ {
+ tmpStr = pathComps[iPos];
+ // Always Increment iPos
+ iPos++;
+ // ..
+ if(tmpStr[0] == '.' && tmpStr[1] == '.' && (tmpStr[2] == '/' || tmpStr[2] == '\0') )
+ {
+ if(iPos2 != 0)
+ iPos2 --;
+ continue;
+ }
+ // .
+ if(tmpStr[0] == '.' && (tmpStr[1] == '/' || tmpStr[1] == '\0') )
+ {
+ continue;
+ }
+ // Empty
+ if(tmpStr[0] == '/' || tmpStr[0] == '\0')
+ {
+ continue;
+ }
+
+ // Set New Position
+ pathComps[iPos2] = tmpStr;
+ iPos2++;
+ }
+ pathComps[iPos2] = NULL;
+
+ // Build New Path
+ iPos2 = chrootLen + 1; iPos = 0;
+ ret[0] = '/';
+ while(pathComps[iPos])
+ {
+ tmpStr = pathComps[iPos];
+ while(*tmpStr && *tmpStr != '/')
+ {
+ ret[iPos2++] = *tmpStr;
+ tmpStr++;
+ }
+ ret[iPos2++] = '/';
+ iPos++;
+ }
+ if(iPos2 > 1)
+ ret[iPos2-1] = 0;
+ else
+ ret[iPos2] = 0;
+
+ // Prepend the chroot
+ if(chrootLen)
+ memcpy( ret, chroot, chrootLen );
+
+ LEAVE('s', ret);
+// Log_Debug("VFS", "VFS_GetAbsPath: RETURN '%s'", ret);
+ return ret;
+}
+
+/**
+ * \fn char *VFS_ParsePath(const char *Path, char **TruePath)
+ * \brief Parses a path, resolving sysmlinks and applying permissions
+ */
+tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint)
+{
+ tVFS_Mount *mnt, *longestMount;
+ int cmp, retLength = 0;
+ int ofs, nextSlash;
+ int iNestedLinks = 0;
+ tVFS_Node *curNode, *tmpNode;
+ char *tmp;
+ char path_buffer[MAX_PATH_LEN+1];
+
+ ENTER("sPath pTruePath", Path, TruePath);
+
+ // HACK: Memory File
+ if(Threads_GetUID() == 0 && Path[0] == '$') {
+ if(TruePath) {
+ *TruePath = malloc(strlen(Path)+1);
+ strcpy(*TruePath, Path);
+ }
+ curNode = VFS_MemFile_Create(Path);
+ if(MountPoint) {
+ *MountPoint = NULL;
+ }
+ LEAVE('p', curNode);
+ return curNode;
+ }
+
+restart_parse:
+ // For root we always fast return
+ if(Path[0] == '/' && Path[1] == '\0') {
+ if(TruePath) {
+ *TruePath = malloc( gVFS_RootMount->MountPointLen+1 );
+ strcpy(*TruePath, gVFS_RootMount->MountPoint);
+ }
+ if(MountPoint) *MountPoint = gVFS_RootMount;
+ LEAVE('p', gVFS_RootMount->RootNode);
+ return gVFS_RootMount->RootNode;
+ }
+
+ // Check if there is anything mounted
+ if(!gVFS_Mounts) {
+ Log_Error("VFS", "VFS_ParsePath - No filesystems mounted");
+ return NULL;
+ }
+
+ // Find Mountpoint
+ longestMount = gVFS_RootMount;
+ for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
+ {
+ // Quick Check
+ if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0')
+ continue;
+ // Length Check - If the length is smaller than the longest match sofar
+ if(mnt->MountPointLen < longestMount->MountPointLen) continue;
+ // String Compare
+ cmp = strcmp(Path, mnt->MountPoint);
+
+ #if OPEN_MOUNT_ROOT
+ // Fast Break - Request Mount Root
+ if(cmp == 0) {
+ if(TruePath) {
+ *TruePath = malloc( mnt->MountPointLen+1 );
+ strcpy(*TruePath, mnt->MountPoint);
+ }
+ if(MountPoint)
+ *MountPoint = mnt;
+ LEAVE('p', mnt->RootNode);
+ return mnt->RootNode;
+ }
+ #endif
+ // Not a match, continue
+ if(cmp != '/') continue;
+ longestMount = mnt;
+ }
+
+ // Save to shorter variable
+ mnt = longestMount;
+
+ LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint);
+
+ // Initialise String
+ if(TruePath)
+ {
+ // Assumes that the resultant path (here) will not be > strlen(Path) + 1
+ *TruePath = malloc( strlen(Path) + 1 );
+ strcpy(*TruePath, mnt->MountPoint);
+ retLength = mnt->MountPointLen;
+ }
+
+ curNode = mnt->RootNode;
+ curNode->ReferenceCount ++;
+ // Parse Path
+ ofs = mnt->MountPointLen+1;
+ for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; ofs += nextSlash + 1)
+ {
+ char pathEle[nextSlash+1];
+
+ // Empty String
+ if(nextSlash == 0) continue;
+
+ memcpy(pathEle, &Path[ofs], nextSlash);
+ pathEle[nextSlash] = 0;
+
+ // Check permissions on root of filesystem
+ if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) {
+ //Log("Permissions fail on '%s'", Path);
+ goto _error;
+ }
+
+ // Check if the node has a FindDir method
+ if( !curNode->Type->FindDir )
+ {
+ //Log("FindDir fail on '%s'", Path);
+ goto _error;
+ }
+ LOG("FindDir{=%p}(%p, '%s')", curNode->Type->FindDir, curNode, pathEle);
+ // Get Child Node
+ tmpNode = curNode->Type->FindDir(curNode, pathEle);
+ LOG("tmpNode = %p", tmpNode);
+ _CloseNode( curNode );
+ curNode = tmpNode;
+
+ // Error Check
+ if(!curNode) {
+ LOG("Node '%s' not found in dir '%s'", pathEle, Path);
+ goto _error;
+ }
+
+ // Handle Symbolic Links
+ if(curNode->Flags & VFS_FFLAG_SYMLINK) {
+ if(TruePath) {
+ free(*TruePath);
+ *TruePath = NULL;
+ }
+ if(!curNode->Type || !curNode->Type->Read) {
+ Log_Warning("VFS", "VFS_ParsePath - Read of symlink node %p'%s' is NULL",
+ curNode, Path);
+ goto _error;
+ }
+
+ if(iNestedLinks > MAX_NESTED_LINKS) {
+ Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded");
+ goto _error;
+ }
+
+ // Parse Symlink Path
+ // - Just update the path variable and restart the function
+ // > Count nested symlinks and limit to some value (counteracts loops)
+ {
+ int remlen = strlen(Path) - (ofs + nextSlash);
+ if( curNode->Size + remlen > MAX_PATH_LEN ) {
+ Log_Warning("VFS", "VFS_ParsePath - Symlinked path too long");
+ goto _error;
+ }
+ curNode->Type->Read( curNode, 0, curNode->Size, path_buffer );
+ path_buffer[ curNode->Size ] = '\0';
+ LOG("path_buffer = '%s'", path_buffer);
+ strcat(path_buffer, Path + ofs+nextSlash);
+
+ Path = path_buffer;
+// Log_Debug("VFS", "VFS_ParsePath: Symlink translated to '%s'", Path);
+ iNestedLinks ++;
+ }
+
+ // EVIL: Goto :)
+ LOG("Symlink -> '%s', restart", Path);
+ goto restart_parse;
+ }
+
+ // Handle Non-Directories
+ if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
+ {
+ Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory");
+ goto _error;
+ }
+
+ // Check if path needs extending
+ if(!TruePath) continue;
+
+ // Increase buffer space
+ tmp = realloc( *TruePath, retLength + strlen(pathEle) + 1 + 1 );
+ // Check if allocation succeeded
+ if(!tmp) {
+ Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
+ goto _error;
+ }
+ *TruePath = tmp;
+ // Append to path
+ (*TruePath)[retLength] = '/';
+ strcpy(*TruePath+retLength+1, pathEle);
+
+ LOG("*TruePath = '%s'", *TruePath);
+
+ // - Extend Path
+ retLength += nextSlash + 1;
+ }
+
+ // Check final finddir call
+ if( !curNode->Type || !curNode->Type->FindDir ) {
+ Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path);
+ goto _error;
+ }
+
+ // Get last node
+ LOG("FindDir(%p, '%s')", curNode, &Path[ofs]);
+ tmpNode = curNode->Type->FindDir(curNode, &Path[ofs]);
+ LOG("tmpNode = %p", tmpNode);
+ // Check if file was found
+ if(!tmpNode) {
+ LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
+ goto _error;
+ }
+ _CloseNode( curNode );
+
+ if(TruePath)
+ {
+ // Increase buffer space
+ tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1);
+ // Check if allocation succeeded
+ if(!tmp) {
+ Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
+ goto _error;
+ }
+ *TruePath = tmp;
+ // Append to path
+ (*TruePath)[retLength] = '/';
+ strcpy(*TruePath + retLength + 1, &Path[ofs]);
+ // - Extend Path
+ //retLength += strlen(tmpNode->Name) + 1;
+ }
+
+ if( MountPoint ) {
+ *MountPoint = mnt;
+ }
+
+ LEAVE('p', tmpNode);
+ return tmpNode;
+
+_error:
+ _CloseNode( curNode );
+
+ if(TruePath && *TruePath) {
+ free(*TruePath);
+ *TruePath = NULL;
+ }
+ LEAVE('n');
+ return NULL;
+}
+
+/**
+ * \brief Create and return a handle number for the given node and mode
+ */
+int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode )
+{
+ int i;
+
+ ENTER("pNode pMount xMode", Node, Mount, Mode);
+
+ i = 0;
+ i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
+ i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
+ i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
+
+ LOG("i = 0b%b", i);
+
+ // Permissions Check
+ if( !VFS_CheckACL(Node, i) ) {
+ _CloseNode( Node );
+ Log_Log("VFS", "VFS_int_CreateHandle: Permissions Failed");
+ errno = EACCES;
+ LEAVE_RET('i', -1);
+ }
+
+ i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), Node, Mode );
+ if( i < 0 ) {
+ Log_Notice("VFS", "VFS_int_CreateHandle: Out of handles");
+ errno = ENFILE;
+ LEAVE_RET('i', -1);
+ }
+
+ VFS_GetHandle(i)->Mount = Mount;
+
+ LEAVE_RET('x', i);
+}
+
+/**
+ * \fn int VFS_Open(const char *Path, Uint Mode)
+ * \brief Open a file
+ */
+int VFS_Open(const char *Path, Uint Flags)
+{
+ return VFS_OpenEx(Path, Flags, 0);
+}
+
+int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode)
+{
+ tVFS_Node *node;
+ tVFS_Mount *mnt;
+ char *absPath;
+
+ ENTER("sPath xFlags oMode", Path, Flags);
+
+ // Get absolute path
+ absPath = VFS_GetAbsPath(Path);
+ if(absPath == NULL) {
+ Log_Warning("VFS", "VFS_Open: Path expansion failed '%s'", Path);
+ LEAVE_RET('i', -1);
+ }
+ LOG("absPath = \"%s\"", absPath);
+
+ // Parse path and get mount point
+ node = VFS_ParsePath(absPath, NULL, &mnt);
+
+ // Create file if requested and it doesn't exist
+ if( !node && (Flags & VFS_OPENFLAG_CREATE) )
+ {
+ // TODO: Translate `Mode` into ACL and node flags
+ // Get parent, create node
+ VFS_MkNod(absPath, 0);
+ node = VFS_ParsePath(absPath, NULL, &mnt);
+ }
+
+ // Free generated path
+ free(absPath);
+
+ // Check for error
+ if(!node)
+ {
+ LOG("Cannot find node");
+ errno = ENOENT;
+ LEAVE_RET('i', -1);
+ }
+
+ // Check for symlinks
+ if( !(Flags & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) )
+ {
+ char tmppath[node->Size+1];
+ if( node->Size > MAX_PATH_LEN ) {
+ Log_Warning("VFS", "VFS_Open - Symlink is too long (%i)", node->Size);
+ LEAVE_RET('i', -1);
+ }
+ if( !node->Type || !node->Type->Read ) {
+ Log_Warning("VFS", "VFS_Open - No read method on symlink");
+ LEAVE_RET('i', -1);
+ }
+ // Read symlink's path
+ node->Type->Read( node, 0, node->Size, tmppath );
+ tmppath[ node->Size ] = '\0';
+ _CloseNode( node );
+ // Open the target
+ node = VFS_ParsePath(tmppath, NULL, &mnt);
+ if(!node) {
+ LOG("Cannot find symlink target node (%s)", tmppath);
+ errno = ENOENT;
+ LEAVE_RET('i', -1);
+ }
+ }
+
+ LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Flags));
+}
+
+
+/**
+ * \brief Open a file from an open directory
+ */
+int VFS_OpenChild(int FD, const char *Name, Uint Mode)
+{
+ tVFS_Handle *h;
+ tVFS_Node *node;
+
+ ENTER("xFD sName xMode", FD, Name, Mode);
+
+ // Get handle
+ h = VFS_GetHandle(FD);
+ if(h == NULL) {
+ Log_Warning("VFS", "VFS_OpenChild - Invalid file handle 0x%x", FD);
+ errno = EINVAL;
+ LEAVE_RET('i', -1);
+ }
+
+ // Check for directory
+ if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
+ Log_Warning("VFS", "VFS_OpenChild - Passed handle is not a directory");
+ errno = ENOTDIR;
+ LEAVE_RET('i', -1);
+ }
+
+ // Sanity check
+ if( !h->Node->Type || !h->Node->Type->FindDir ) {
+ Log_Error("VFS", "VFS_OpenChild - Node does not have a type/is missing FindDir");
+ errno = ENOTDIR;
+ LEAVE_RET('i', -1);
+ }
+
+ // Find Child
+ node = h->Node->Type->FindDir(h->Node, Name);
+ if(!node) {
+ errno = ENOENT;
+ LEAVE_RET('i', -1);
+ }
+
+ LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode));
+}
+
+int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode)
+{
+ tVFS_Mount *mnt;
+ tVFS_Node *node;
+
+ ENTER("iMount XInode xMode", Mount, Inode, Mode);
+
+ // Get mount point
+ mnt = VFS_GetMountByIdent(Mount);
+ if( !mnt ) {
+ LOG("Mount point ident invalid");
+ errno = ENOENT;
+ LEAVE_RET('i', -1);
+ }
+
+ // Does the filesystem support this?
+ if( !mnt->Filesystem->GetNodeFromINode ) {
+ LOG("Filesystem does not support inode accesses");
+ errno = ENOENT;
+ LEAVE_RET('i', -1);
+ }
+
+ // Get node
+ node = mnt->Filesystem->GetNodeFromINode(mnt->RootNode, Inode);
+ if( !node ) {
+ LOG("Unable to find inode");
+ errno = ENOENT;
+ LEAVE_RET('i', -1);
+ }
+
+ LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Mode));
+}
+
+/**
+ * \fn void VFS_Close(int FD)
+ * \brief Closes an open file handle
+ */
+void VFS_Close(int FD)
+{
+ tVFS_Handle *h;
+
+ // Get handle
+ h = VFS_GetHandle(FD);
+ if(h == NULL) {
+ Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD);
+ return;
+ }
+
+ #if VALIDATE_VFS_FUNCTIPONS
+ if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) {
+ Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)",
+ h->Node, h->Node->Close);
+ return ;
+ }
+ #endif
+
+ _CloseNode(h->Node);
+
+ h->Node = NULL;
+}
+
+/**
+ * \brief Change current working directory
+ */
+int VFS_ChDir(const char *Dest)
+{
+ char *buf;
+ int fd;
+ tVFS_Handle *h;
+
+ // Create Absolute
+ buf = VFS_GetAbsPath(Dest);
+ if(buf == NULL) {
+ Log_Notice("VFS", "VFS_ChDir: Path expansion failed");
+ return -1;
+ }
+
+ // Check if path exists
+ fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
+ if(fd == -1) {
+ Log_Notice("VFS", "VFS_ChDir: Path is invalid");
+ return -1;
+ }
+
+ // Get node so we can check for directory
+ h = VFS_GetHandle(fd);
+ if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
+ Log("VFS_ChDir: Path is not a directory");
+ VFS_Close(fd);
+ return -1;
+ }
+
+ // Close file
+ VFS_Close(fd);
+
+ {
+ char **cwdptr = Threads_GetCWD();
+ // Free old working directory
+ if( *cwdptr ) free( *cwdptr );
+ // Set new
+ *cwdptr = buf;
+ }
+
+ Log("Updated CWD to '%s'", buf);
+
+ return 1;
+}
+
+/**
+ * \fn int VFS_ChRoot(char *New)
+ * \brief Change current root directory
+ */
+int VFS_ChRoot(const char *New)
+{
+ char *buf;
+ int fd;
+ tVFS_Handle *h;
+
+ if(New[0] == '/' && New[1] == '\0')
+ return 1; // What a useless thing to ask!
+
+ // Create Absolute
+ buf = VFS_GetAbsPath(New);
+ if(buf == NULL) {
+ LOG("Path expansion failed");
+ return -1;
+ }
+
+ // Check if path exists
+ fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
+ if(fd == -1) {
+ LOG("Path is invalid");
+ return -1;
+ }
+
+ // Get node so we can check for directory
+ h = VFS_GetHandle(fd);
+ if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
+ LOG("Path is not a directory");
+ VFS_Close(fd);
+ return -1;
+ }
+
+ // Close file
+ VFS_Close(fd);
+
+ // Update
+ {
+ char **chroot_ptr = Threads_GetChroot();
+ if( *chroot_ptr ) free( *chroot_ptr );
+ *chroot_ptr = buf;
+ }
+
+ LOG("Updated Root to '%s'", buf);
+
+ return 1;
+}
+
+// === EXPORTS ===
+EXPORT(VFS_Open);
+EXPORT(VFS_Close);
--- /dev/null
+/*
+ * Acess2 VFS
+ * - By thePowersGang (John Hodge)
+ *
+ * select.c
+ * - Implements the select() system call (and supporting code)
+ *
+ * TODO: Implment timeouts (via an alarm event?)
+ * TODO: Remove malloc for read/write queues
+ */
+#define DEBUG 0
+#include <acess.h>
+#include "vfs.h"
+#include "vfs_int.h"
+#include "vfs_ext.h"
+#include <semaphore.h>
+#include <threads.h>
+#include <events.h>
+
+// === CONSTANTS ===
+#define NUM_THREADS_PER_ALLOC 4
+
+// === IMPORTS ===
+extern tThread *Proc_GetCurThread(void);
+
+// === TYPES ===
+typedef struct sVFS_SelectListEnt tVFS_SelectListEnt;
+
+// === STRUCTURES ===
+struct sVFS_SelectListEnt
+{
+ tVFS_SelectListEnt *Next;
+ tThread *Threads[NUM_THREADS_PER_ALLOC];
+};
+
+// NOTE: Typedef is in vfs.h
+struct sVFS_SelectList
+{
+ tMutex Lock;
+ tVFS_SelectListEnt FirstEnt;
+};
+
+// === PROTOTYPES ===
+// int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout);
+// int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel);
+// int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull);
+// int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable);
+// int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState);
+ int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed);
+ int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
+ int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel);
+ int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed);
+void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread);
+void VFS_int_Select_SignalAll(tVFS_SelectList *List);
+
+// === GLOBALS ===
+
+// === FUNCTIONS ===
+int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
+{
+ tThread *thisthread = Proc_GetCurThread();
+ int ret, type;
+
+ ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
+
+ // Initialise
+ for( type = 0; type < 3; type ++ )
+ {
+ tVFS_SelectList **list;
+ int *flag, wanted, maxAllowed;
+ if( !(TypeFlags & (1 << type)) ) continue;
+ if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Alloc if needed
+ if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList));
+
+ VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
+ if( *flag == wanted )
+ {
+ VFS_int_Select_RemThread(*list, thisthread);
+ LEAVE('i', 1);
+ return 1;
+ }
+ }
+
+ // - Fast return for polling
+ if( Timeout && *Timeout == 0 ) return 0;
+
+ // Wait for things
+ if( !Timeout || *Timeout > 0 )
+ {
+ LOG("Semaphore_Wait()");
+ // TODO: Actual timeout
+ Threads_WaitEvents( THREAD_EVENT_VFS );
+ }
+
+ // Get return value
+ ret = 0;
+ for( type = 0; type < 3; type ++ )
+ {
+ tVFS_SelectList **list;
+ int *flag, wanted, maxAllowed;
+ if( !(TypeFlags & (1 << type)) ) continue;
+ VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
+ LOG("VFS_int_Select_RemThread()");
+ VFS_int_Select_RemThread(*list, thisthread);
+ ret = ret || *flag == wanted;
+ }
+
+ LEAVE('i', ret);
+ return ret;
+}
+
+int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel)
+{
+ tThread *thisthread = Proc_GetCurThread();
+ int ret;
+
+ ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel",
+ MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel);
+
+ // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents)
+ // if we are going to be woken up (either by an event at a later time,
+ // or by an event that happened while or before we were registering).
+ // Hence, register must happen _before_ we check the state flag
+ // (See VFS_int_Select_Register), that way either we pick up the flag,
+ // or the semaphore is incremeneted (or both, but never none)
+
+ // Register with nodes
+ ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
+ ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
+ ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
+
+ LOG("Register ret = %i", ret);
+
+ // If there were events waiting, de-register and return
+ if( ret > 0 )
+ {
+ ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
+ ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
+ ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
+ LEAVE('i', ret);
+ return ret;
+ }
+
+ // TODO: Implement timeout
+ LOG("Timeout = %p", Timeout);
+
+ // Wait (only if there is no timeout, or it is greater than zero
+ if( !Timeout || *Timeout > 0 )
+ {
+ // TODO: Timeout
+ // TODO: Allow extra events to be waited upon
+ Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents );
+ }
+
+ // Fill output (modify *Handles)
+ // - Also, de-register
+ ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel);
+ ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel);
+ ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel);
+ LEAVE('i', ret);
+ return ret;
+}
+
+// Mark a node as having data ready for reading
+int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable)
+{
+ ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable);
+ Node->DataAvaliable = !!IsDataAvaliable;
+ if( Node->DataAvaliable )
+ VFS_int_Select_SignalAll(Node->ReadThreads);
+ LEAVE('i', 0);
+ return 0;
+}
+
+// Mark a node as having a full buffer
+int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull)
+{
+ ENTER("pNode bIsBufferFull", Node, IsBufferFull);
+ Node->BufferFull = !!IsBufferFull;
+ if( !Node->BufferFull )
+ VFS_int_Select_SignalAll(Node->WriteThreads);
+ LEAVE('i', 0);
+ return 0;
+}
+
+// Mark a node as errored
+int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState)
+{
+ ENTER("pNode bIsErrorState", Node, IsErrorState);
+ Node->ErrorOccurred = !!IsErrorState;
+ if( Node->ErrorOccurred )
+ VFS_int_Select_SignalAll(Node->ErrorThreads);
+ LEAVE('i', 0);
+ return 0;
+}
+
+// --- Internal ---
+int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed)
+{
+ // Get the type of the listen
+ switch(Type)
+ {
+ case 0: // Read
+ if(List) *List = &Node->ReadThreads;
+ if(Flag) *Flag = &Node->DataAvaliable;
+ if(WantedFlag) *WantedFlag = 1;
+ if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read
+ break;
+ case 1: // Write
+ if(List) *List = &Node->WriteThreads;
+ if(Flag) *Flag = &Node->BufferFull;
+ if(WantedFlag) *WantedFlag = 0;
+ if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write
+ break;
+ case 2: // Error
+ if(List) *List = &Node->ErrorThreads;
+ if(Flag) *Flag = &Node->ErrorOccurred;
+ if(WantedFlag) *WantedFlag = 1;
+ if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners
+ break;
+ default:
+ Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \return Number of files with an action
+ */
+int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
+{
+ int i, numFlagged = 0;
+ tVFS_SelectList **list;
+ int *flag, wantedFlagValue;
+ int maxAllowed;
+
+ if( !Handles ) return 0;
+
+ ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
+
+ for( i = 0; i < MaxHandle; i ++ )
+ {
+ tVFS_Handle *handle;
+
+ // Is the descriptor set
+ if( !FD_ISSET(i, Handles) ) continue;
+ LOG("FD #%i", i);
+
+ handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
+ // Is the handle valid?
+ if( !handle || !handle->Node )
+ {
+ if( Type == 2 ) { // Bad FD counts as an error
+ numFlagged ++;
+ }
+ else {
+ FD_CLR(i, Handles);
+ }
+ continue;
+ }
+
+ // Get the type of the listen
+ if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Alloc if needed
+ if( !*list ) {
+ *list = calloc(1, sizeof(tVFS_SelectList));
+ }
+
+ // Register
+ if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) )
+ {
+ // Oops, error (or just no space)
+ FD_CLR(i, Handles);
+ }
+
+ // Check for the flag
+ if( !!*flag == !!wantedFlagValue )
+ numFlagged ++;
+ }
+
+ LEAVE('i', numFlagged);
+
+ return numFlagged;
+}
+/**
+ * \return Number of files with an action
+ */
+int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel)
+{
+ int i, numFlagged = 0;
+ tVFS_SelectList **list;
+ int *flag, wantedFlagValue;
+
+ if( !Handles ) return 0;
+
+ ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel);
+
+ for( i = 0; i < MaxHandle; i ++ )
+ {
+ tVFS_Handle *handle;
+
+ // Is the descriptor set
+ if( !FD_ISSET(i, Handles) ) continue;
+ LOG("FD #%i", i);
+
+ handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) );
+ // Is the handle valid?
+ if( !handle || !handle->Node )
+ {
+ if( Type == 2 ) { // Bad FD counts as an error
+ numFlagged ++;
+ }
+ else {
+ FD_CLR(i, Handles);
+ }
+ continue;
+ }
+
+ // Get the type of the listen
+
+ // Get the type of the listen
+ if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Remove
+ VFS_int_Select_RemThread(*list, Thread );
+
+ // Check for the flag
+ if( !!*flag == !!wantedFlagValue ) {
+ numFlagged ++;
+ }
+ else {
+ FD_CLR(i, Handles);
+ }
+ }
+
+ LEAVE('i', numFlagged);
+
+ return numFlagged;
+}
+
+/**
+ * \return Boolean failure
+ */
+int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed)
+{
+ int i, count = 0;
+ tVFS_SelectListEnt *block, *prev;
+
+ ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed);
+
+ // Lock to avoid concurrency issues
+ Mutex_Acquire(&List->Lock);
+
+ block = &List->FirstEnt;
+
+ // Look for free space
+ do
+ {
+ for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
+ {
+ if( block->Threads[i] == NULL )
+ {
+ block->Threads[i] = Thread;
+ Mutex_Release(&List->Lock);
+ LEAVE('i', 0);
+ return 0;
+ }
+ count ++;
+ if( MaxAllowed && count >= MaxAllowed ) {
+ LEAVE('i', 1);
+ return 1;
+ }
+ }
+
+ prev = block;
+ block = block->Next;
+ } while(block);
+
+ LOG("New block");
+
+ // Create new block
+ block = malloc( sizeof(tVFS_SelectListEnt) );
+ if( !block ) {
+ Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed");
+ Mutex_Release(&List->Lock);
+ return -1;
+ }
+ block->Next = NULL;
+ block->Threads[0] = Thread;
+ for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ )
+ {
+ block->Threads[i] = NULL;
+ }
+
+ // Add to list
+ prev->Next = block;
+
+ // Release
+ Mutex_Release(&List->Lock);
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread)
+{
+ int i;
+ tVFS_SelectListEnt *block, *prev = NULL;
+
+ ENTER("pList pThread", List, Thread);
+
+ // Lock to avoid concurrency issues
+ Mutex_Acquire(&List->Lock);
+
+ block = &List->FirstEnt;
+
+ // Look for the thread
+ do
+ {
+ for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
+ {
+ if( block->Threads[i] == Thread )
+ {
+ block->Threads[i] = NULL;
+
+ // Check if this block is empty
+ if( block != &List->FirstEnt )
+ {
+ for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
+ if( block->Threads[i] )
+ break;
+ // If empty, free it
+ if( i == NUM_THREADS_PER_ALLOC ) {
+ LOG("Deleting block");
+ prev->Next = block->Next;
+ free(block);
+ }
+ //TODO: If not empty, check if it can be merged downwards
+ }
+
+ Mutex_Release(&List->Lock);
+ LEAVE('-');
+ return ;
+ }
+ }
+
+ prev = block;
+ block = block->Next;
+ } while(block);
+
+ // Not on list, is this an error?
+
+ Mutex_Release(&List->Lock);
+
+ LOG("Not on list");
+ LEAVE('-');
+}
+
+/**
+ * \brief Signal all threads on a list
+ */
+void VFS_int_Select_SignalAll(tVFS_SelectList *List)
+{
+ int i;
+ tVFS_SelectListEnt *block;
+
+ if( !List ) return ;
+
+ ENTER("pList", List);
+
+ // Lock to avoid concurrency issues
+ Mutex_Acquire(&List->Lock);
+
+ block = &List->FirstEnt;
+
+ // Look for the thread
+ do
+ {
+ for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ )
+ {
+ if( block->Threads[i] )
+ {
+ LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]);
+ Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS );
+ }
+ }
+
+ block = block->Next;
+ } while(block);
+
+ Mutex_Release(&List->Lock);
+
+ LEAVE('-');
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * workqueue.c
+ * - Worker FIFO Queue (Single Consumer, Interrupt Producer)
+ */
+#include <acess.h>
+#include <workqueue.h>
+#include <threads_int.h>
+
+// === CODE ===
+void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset)
+{
+ Queue->Name = Name;
+ Queue->NextOffset = NextOfset;
+}
+
+void *Workqueue_GetWork(tWorkqueue *Queue)
+{
+ tThread *us;
+
+ for( ;; )
+ {
+ // Check for work
+ SHORTLOCK(&Queue->Protector);
+ if(Queue->Head)
+ {
+ void *ret = Queue->Head;
+ Queue->Head = *( (void**)ret + Queue->NextOffset/sizeof(void*) );
+ if(Queue->Tail == ret)
+ Queue->Tail = NULL;
+ SHORTREL(&Queue->Protector);
+ return ret;
+ }
+
+ // Go to sleep
+ SHORTLOCK(&glThreadListLock);
+ us = Threads_RemActive();
+ us->WaitPointer = Queue;
+ us->Status = THREAD_STAT_QUEUESLEEP;
+ Queue->Sleeper = us;
+ SHORTREL(&Queue->Protector);
+ SHORTREL(&glThreadListLock);
+
+ // Yield and sleep
+ Threads_Yield();
+ if(us->Status == THREAD_STAT_QUEUESLEEP) {
+ // Why are we awake?!
+ }
+
+ us->WaitPointer = NULL;
+ }
+}
+
+void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr)
+{
+ SHORTLOCK(&Queue->Protector);
+
+ if( Queue->Tail )
+ *( (void**)Queue->Tail + Queue->NextOffset/sizeof(void*) ) = Ptr;
+ else
+ Queue->Head = Ptr;
+ Queue->Tail = Ptr;
+
+ if( Queue->Sleeper )
+ {
+ if( Queue->Sleeper->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(Queue->Sleeper);
+ Queue->Sleeper = NULL;
+ }
+ SHORTREL(&Queue->Protector);
+}
--- /dev/null
+#
+#
+
+OBJ = bochsvbe.o
+NAME = BochsGA
+
+-include ../Makefile.tpl
--- /dev/null
+/**\r
+ * Acess2 Bochs graphics adapter Driver\r
+ * - By John Hodge (thePowersGang)\r
+ *\r
+ * bochsvbe.c\r
+ * - Driver core\r
+ */\r
+#define DEBUG 0\r
+#define VERSION VER2(0,10)\r
+\r
+#include <acess.h>\r
+#include <errno.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+#include <api_drv_video.h>\r
+\r
+// === TYPES ===\r
+typedef struct sBGA_Mode {\r
+ Uint16 width;\r
+ Uint16 height;\r
+ Uint16 bpp;\r
+ Uint32 fbSize;\r
+} tBGA_Mode;\r
+\r
+// === CONSTANTS ===\r
+#define BGA_LFB_MAXSIZE (1024*768*4)\r
+#define VBE_DISPI_BANK_ADDRESS 0xA0000\r
+#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000\r
+#define VBE_DISPI_IOPORT_INDEX 0x01CE\r
+#define VBE_DISPI_IOPORT_DATA 0x01CF\r
+#define VBE_DISPI_DISABLED 0x00\r
+#define VBE_DISPI_ENABLED 0x01\r
+#define VBE_DISPI_LFB_ENABLED 0x40\r
+#define VBE_DISPI_NOCLEARMEM 0x80\r
+enum {\r
+ VBE_DISPI_INDEX_ID,\r
+ VBE_DISPI_INDEX_XRES,\r
+ VBE_DISPI_INDEX_YRES,\r
+ VBE_DISPI_INDEX_BPP,\r
+ VBE_DISPI_INDEX_ENABLE,\r
+ VBE_DISPI_INDEX_BANK,\r
+ VBE_DISPI_INDEX_VIRT_WIDTH,\r
+ VBE_DISPI_INDEX_VIRT_HEIGHT,\r
+ VBE_DISPI_INDEX_X_OFFSET,\r
+ VBE_DISPI_INDEX_Y_OFFSET\r
+};\r
+\r
+extern void MM_DumpTables(tVAddr Start, tVAddr End);\r
+\r
+// === PROTOTYPES ===\r
+// Driver\r
+ int BGA_Install(char **Arguments);\r
+void BGA_Uninstall();\r
+// Internal\r
+void BGA_int_WriteRegister(Uint16 reg, Uint16 value);\r
+Uint16 BGA_int_ReadRegister(Uint16 reg);\r
+void BGA_int_SetBank(Uint16 bank);\r
+void BGA_int_SetMode(Uint16 width, Uint16 height);\r
+ int BGA_int_UpdateMode(int id);\r
+ int BGA_int_FindMode(tVideo_IOCtl_Mode *info);\r
+ int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);\r
+ int BGA_int_MapFB(void *Dest);\r
+// Filesystem\r
+Uint64 BGA_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer);\r
+Uint64 BGA_Write(tVFS_Node *Node, Uint64 off, Uint64 len, const void *buffer);\r
+ int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, VERSION, BochsGA, BGA_Install, NULL, "PCI", NULL);\r
+tVFS_NodeType gBGA_NodeType = {\r
+ .Read = BGA_Read,\r
+ .Write = BGA_Write,\r
+ .IOCtl = BGA_IOCtl\r
+ };\r
+tDevFS_Driver gBGA_DriverStruct = {\r
+ NULL, "BochsGA",\r
+ {.Type = &gBGA_NodeType}\r
+ };\r
+ int giBGA_CurrentMode = -1;\r
+tVideo_IOCtl_Pos gBGA_CursorPos = {-1,-1};\r
+Uint *gBGA_Framebuffer;\r
+const tBGA_Mode *gpBGA_CurrentMode;\r
+const tBGA_Mode gBGA_Modes[] = {\r
+ {640,480,32, 640*480*4},\r
+ {800,600,32, 800*600*4},\r
+ {1024,768,32, 1024*768*4}\r
+};\r
+#define BGA_MODE_COUNT (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))\r
+tDrvUtil_Video_BufInfo gBGA_DrvUtil_BufInfo;\r
+\r
+// === CODE ===\r
+/**\r
+ * \fn int BGA_Install(char **Arguments)\r
+ */\r
+int BGA_Install(char **Arguments)\r
+{\r
+ int version = 0;\r
+ tPAddr base;\r
+ tPCIDev dev;\r
+ \r
+ // Check BGA Version\r
+ version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);\r
+ LOG("version = 0x%x", version);\r
+ \r
+ // NOTE: This driver was written for BGA versions >= 0xBOC2\r
+ // NOTE: However, Qemu is braindead and doesn't return the actual version\r
+ if( version != 0xB0C0 && ((version & 0xFFF0) != 0xB0C0 || version < 0xB0C2) ) {\r
+ Log_Warning("BGA", "Bochs Adapter Version is not compatible (need >= 0xB0C2), instead 0x%x", version);\r
+ return MODULE_ERR_NOTNEEDED;\r
+ }\r
+\r
+ // Get framebuffer base \r
+ dev = PCI_GetDevice(0x1234, 0x1111, 0);\r
+ if(dev == -1)\r
+ base = VBE_DISPI_LFB_PHYSICAL_ADDRESS;\r
+ else\r
+ base = PCI_GetBAR(dev, 0);\r
+\r
+ // Map Framebuffer to hardware address\r
+ gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768); // 768 pages (3Mb)\r
+\r
+ // Install Device\r
+ if( DevFS_AddDevice( &gBGA_DriverStruct ) == -1 )\r
+ {\r
+ Log_Warning("BGA", "Unable to register with DevFS, maybe already loaded?");\r
+ return MODULE_ERR_MISC;\r
+ }\r
+ \r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ * \brief Clean up driver resources before destruction\r
+ */\r
+void BGA_Uninstall(void)\r
+{\r
+ DevFS_DelDevice( &gBGA_DriverStruct );\r
+ MM_UnmapHWPages( (tVAddr)gBGA_Framebuffer, 768 );\r
+}\r
+\r
+/**\r
+ * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+ * \brief Read from the framebuffer\r
+ */\r
+Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+{\r
+ // Check Mode\r
+ if(giBGA_CurrentMode == -1) return -1;\r
+ \r
+ // Check Offset and Length against Framebuffer Size\r
+ if(off+len > gpBGA_CurrentMode->fbSize)\r
+ return -1;\r
+ \r
+ // Copy from Framebuffer\r
+ memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);\r
+ return len;\r
+}\r
+\r
+/**\r
+ * \brief Write to the framebuffer\r
+ */\r
+Uint64 BGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
+{\r
+ if( giBGA_CurrentMode == -1 ) BGA_int_UpdateMode(0);\r
+ return DrvUtil_Video_WriteLFB(&gBGA_DrvUtil_BufInfo, Offset, Length, Buffer);\r
+}\r
+\r
+const char *csaBGA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
+/**\r
+ * \brief Handle messages to the device\r
+ */\r
+int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+{\r
+ int ret = -2;\r
+ ENTER("pNode iId pData", Node, ID, Data);\r
+ \r
+ switch(ID)\r
+ {\r
+ BASE_IOCTLS(DRV_TYPE_VIDEO, "BochsGA", VERSION, csaBGA_IOCtls);\r
+\r
+ case VIDEO_IOCTL_GETSETMODE:\r
+ if( Data ) BGA_int_UpdateMode(*(int*)(Data));\r
+ ret = giBGA_CurrentMode;\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_FINDMODE:\r
+ ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data);\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_MODEINFO:\r
+ ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_SETBUFFORMAT:\r
+ DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo );\r
+ ret = gBGA_DrvUtil_BufInfo.BufferFormat;\r
+ if(Data)\r
+ gBGA_DrvUtil_BufInfo.BufferFormat = *(int*)Data;\r
+ if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_SetCursor( &gBGA_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_SETCURSOR:\r
+ DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo );\r
+ gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x;\r
+ gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y;\r
+ if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_DrawCursor(\r
+ &gBGA_DrvUtil_BufInfo,\r
+ gBGA_CursorPos.x*giVT_CharWidth,\r
+ gBGA_CursorPos.y*giVT_CharHeight\r
+ );\r
+ else\r
+ DrvUtil_Video_DrawCursor(\r
+ &gBGA_DrvUtil_BufInfo,\r
+ gBGA_CursorPos.x, gBGA_CursorPos.y\r
+ );\r
+ break;\r
+ \r
+ default:\r
+ LEAVE('i', -2);\r
+ return -2;\r
+ }\r
+ \r
+ LEAVE('i', ret);\r
+ return ret;\r
+}\r
+\r
+//== Internal Functions ==\r
+/**\r
+ * \brief Writes to a BGA register\r
+ */\r
+void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
+{\r
+ outw(VBE_DISPI_IOPORT_INDEX, reg);\r
+ outw(VBE_DISPI_IOPORT_DATA, value);\r
+}\r
+\r
+Uint16 BGA_int_ReadRegister(Uint16 reg)\r
+{\r
+ outw(VBE_DISPI_IOPORT_INDEX, reg);\r
+ return inw(VBE_DISPI_IOPORT_DATA);\r
+}\r
+\r
+/**\r
+ * \brief Sets the video mode (32bpp only)\r
+ */\r
+void BGA_int_SetMode(Uint16 Width, Uint16 Height)\r
+{\r
+ ENTER("iWidth iHeight", Width, Height);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, Width);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, Height);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, 32);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);\r
+ LEAVE('-');\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_UpdateMode(int id)\r
+ * \brief Set current vide mode given a mode id\r
+ */\r
+int BGA_int_UpdateMode(int id)\r
+{\r
+ // Sanity Check\r
+ if(id < 0 || id >= BGA_MODE_COUNT) return -1;\r
+ \r
+ BGA_int_SetMode(\r
+ gBGA_Modes[id].width,\r
+ gBGA_Modes[id].height);\r
+ \r
+ gBGA_DrvUtil_BufInfo.Framebuffer = gBGA_Framebuffer;\r
+ gBGA_DrvUtil_BufInfo.Pitch = gBGA_Modes[id].width * 4;\r
+ gBGA_DrvUtil_BufInfo.Width = gBGA_Modes[id].width;\r
+ gBGA_DrvUtil_BufInfo.Height = gBGA_Modes[id].height;\r
+ gBGA_DrvUtil_BufInfo.Depth = gBGA_Modes[id].bpp;\r
+\r
+ giBGA_CurrentMode = id;\r
+ gpBGA_CurrentMode = &gBGA_Modes[id];\r
+ return id;\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
+ * \brief Find a mode matching the given options\r
+ */\r
+int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
+{\r
+ int i;\r
+ int best = 0, bestFactor = 1000;\r
+ int tmp;\r
+ int rqdProduct = info->width * info->height;\r
+ \r
+ ENTER("pinfo", info);\r
+ LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);\r
+ \r
+ for(i = 0; i < BGA_MODE_COUNT; i++)\r
+ {\r
+ #if DEBUG >= 2\r
+ LOG("Mode %i (%ix%i,%ibpp), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);\r
+ #endif\r
+\r
+ if( gBGA_Modes[i].bpp != info->bpp )\r
+ continue ; \r
+ \r
+ // Ooh! A perfect match\r
+ if(gBGA_Modes[i].width == info->width && gBGA_Modes[i].height == info->height)\r
+ {\r
+ #if DEBUG >= 2\r
+ LOG("Perfect");\r
+ #endif\r
+ best = i;\r
+ break;\r
+ }\r
+\r
+\r
+ // If not, how close are we?\r
+ tmp = gBGA_Modes[i].width * gBGA_Modes[i].height - rqdProduct;\r
+ tmp = tmp < 0 ? -tmp : tmp; // tmp = ABS(tmp)\r
+ \r
+ #if DEBUG >= 2\r
+ LOG("tmp = %i", tmp);\r
+ #endif\r
+ \r
+ if(tmp < bestFactor)\r
+ {\r
+ bestFactor = tmp;\r
+ best = i;\r
+ }\r
+ }\r
+ \r
+ info->id = best;\r
+ info->width = gBGA_Modes[best].width;\r
+ info->height = gBGA_Modes[best].height;\r
+ info->bpp = gBGA_Modes[best].bpp;\r
+\r
+ LEAVE('i', best); \r
+ return best;\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
+ * \brief Get mode information\r
+ */\r
+int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
+{\r
+ // Sanity Check\r
+ //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {\r
+ // return -EINVAL;\r
+ //}\r
+ \r
+ if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1;\r
+ \r
+ info->width = gBGA_Modes[info->id].width;\r
+ info->height = gBGA_Modes[info->id].height;\r
+ info->bpp = gBGA_Modes[info->id].bpp;\r
+ \r
+ return 1;\r
+}\r
+\r
--- /dev/null
+CATEGORY = Video
+
+-include ../../Makefile.tpl
--- /dev/null
+#
+#
+
+OBJ = main.o
+NAME = PL110
+
+-include ../Makefile.tpl
--- /dev/null
+/**\r
+ * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver\r
+ * - By John Hodge (thePowersGang)\r
+ *\r
+ * main.c\r
+ * - Driver core\r
+ *\r
+ *\r
+ * NOTE: The PL110 is set to 24bpp, but these are stored as 32-bit words.\r
+ * This corresponds to the Acess 32bpp mode, as the Acess 24bpp is packed\r
+ */\r
+#define DEBUG 0\r
+#define VERSION ((0<<8)|10)\r
+#include <acess.h>\r
+#include <errno.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+#include <api_drv_video.h>\r
+#include <lib/keyvalue.h>\r
+#include <options.h> // ARM Arch\r
+\r
+#define ABS(a) ((a)>0?(a):-(a))\r
+\r
+// === TYPEDEFS ===\r
+typedef struct sPL110 tPL110;\r
+\r
+struct sPL110\r
+{\r
+ Uint32 LCDTiming0;\r
+ Uint32 LCDTiming1;\r
+ Uint32 LCDTiming2;\r
+ Uint32 LCDTiming3;\r
+ \r
+ Uint32 LCDUPBase;\r
+ Uint32 LCDLPBase;\r
+ Uint32 LCDIMSC;\r
+ Uint32 LCDControl;\r
+ Uint32 LCDRIS;\r
+ Uint32 LCDMIS;\r
+ Uint32 LCDICR;\r
+ Uint32 LCDUPCurr;\r
+ Uint32 LCDLPCurr;\r
+};\r
+\r
+#ifndef PL110_BASE\r
+#define PL110_BASE 0x10020000 // Integrator\r
+#endif\r
+\r
+// === CONSTANTS ===\r
+const struct {\r
+ short W, H;\r
+} caPL110_Modes[] = {\r
+ {640,480},\r
+ {800,600},\r
+ {1024,768} // MAX\r
+};\r
+const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]);\r
+\r
+// === PROTOTYPES ===\r
+// Driver\r
+ int PL110_Install(char **Arguments);\r
+void PL110_Uninstall();\r
+// Internal\r
+// Filesystem\r
+Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, const void *buffer);\r
+ int PL110_IOCtl(tVFS_Node *node, int id, void *data);\r
+// -- Internals\r
+ int PL110_int_SetResolution(int W, int H);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL);\r
+tVFS_NodeType gPL110_DevNodeType = {\r
+ .Read = PL110_Read,\r
+ .Write = PL110_Write,\r
+ .IOCtl = PL110_IOCtl\r
+ };\r
+tDevFS_Driver gPL110_DriverStruct = {\r
+ NULL, "PL110",\r
+ {.Type = &gPL110_DevNodeType}\r
+};\r
+// -- Options\r
+tPAddr gPL110_PhysBase = PL110_BASE;\r
+ int gbPL110_IsVersatile = 1;\r
+// -- KeyVal parse rules\r
+const tKeyVal_ParseRules gPL110_KeyValueParser = {\r
+ NULL,\r
+ {\r
+ {"Base", "P", &gPL110_PhysBase},\r
+ {"IsVersatile", "i", &gbPL110_IsVersatile},\r
+ {NULL, NULL, NULL}\r
+ }\r
+};\r
+// -- Driver state\r
+ int giPL110_CurrentMode = 0;\r
+ int giPL110_BufferMode;\r
+ int giPL110_Width = 640;\r
+ int giPL110_Height = 480;\r
+size_t giPL110_FramebufferSize;\r
+tPL110 *gpPL110_IOMem;\r
+tPAddr gPL110_FramebufferPhys;\r
+void *gpPL110_Framebuffer;\r
+// -- Misc\r
+tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo;\r
+tVideo_IOCtl_Pos gPL110_CursorPos;\r
+\r
+// === CODE ===\r
+/**\r
+ */\r
+int PL110_Install(char **Arguments)\r
+{\r
+// KeyVal_Parse(&gPL110_KeyValueParser, Arguments);\r
+ \r
+ gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1);\r
+\r
+ PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H);\r
+\r
+ DevFS_AddDevice( &gPL110_DriverStruct );\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \brief Clean up resources for driver unloading\r
+ */\r
+void PL110_Uninstall()\r
+{\r
+}\r
+\r
+/**\r
+ * \brief Read from the framebuffer\r
+ */\r
+Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \brief Write to the framebuffer\r
+ */\r
+Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
+{\r
+ gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode;\r
+ return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer);\r
+}\r
+\r
+const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
+\r
+/**\r
+ * \brief Handle messages to the device\r
+ */\r
+int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+{\r
+ int ret = -2;\r
+ ENTER("pNode iID pData", Node, ID, Data);\r
+ \r
+ switch(ID)\r
+ {\r
+ BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls);\r
+\r
+ case VIDEO_IOCTL_SETBUFFORMAT:\r
+ DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );\r
+ ret = giPL110_BufferMode;\r
+ if(Data) giPL110_BufferMode = *(int*)Data;\r
+ if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_GETSETMODE:\r
+ if(Data)\r
+ {\r
+ int newMode;\r
+ \r
+ if( !CheckMem(Data, sizeof(int)) )\r
+ LEAVE_RET('i', -1);\r
+ \r
+ newMode = *(int*)Data;\r
+ \r
+ if(newMode < 0 || newMode >= ciPL110_ModeCount)\r
+ LEAVE_RET('i', -1);\r
+\r
+ if(newMode != giPL110_CurrentMode)\r
+ {\r
+ giPL110_CurrentMode = newMode;\r
+ PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H );\r
+ }\r
+ }\r
+ ret = giPL110_CurrentMode;\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_FINDMODE:\r
+ {\r
+ tVideo_IOCtl_Mode *mode = Data;\r
+ int closest, closestArea, reqArea = 0;\r
+ if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
+ LEAVE_RET('i', -1);\r
+ if( mode->bpp != 32 )\r
+ LEAVE_RET('i', 0);\r
+ if( mode->flags != 0 )\r
+ LEAVE_RET('i', 0);\r
+\r
+ ret = 0;\r
+\r
+ for( int i = 0; i < ciPL110_ModeCount; i ++ )\r
+ {\r
+ int area;\r
+ if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) {\r
+ mode->id = i;\r
+ ret = 1;\r
+ break;\r
+ }\r
+ \r
+ area = caPL110_Modes[i].W * caPL110_Modes[i].H;\r
+ if(!reqArea) {\r
+ reqArea = mode->width * mode->height;\r
+ closest = i;\r
+ closestArea = area;\r
+ }\r
+ else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {\r
+ closest = i;\r
+ closestArea = area;\r
+ }\r
+ }\r
+ \r
+ if( ret == 0 )\r
+ {\r
+ mode->id = closest;\r
+ ret = 1;\r
+ }\r
+ mode->width = caPL110_Modes[mode->id].W;\r
+ mode->height = caPL110_Modes[mode->id].H;\r
+ break;\r
+ }\r
+ \r
+ case VIDEO_IOCTL_MODEINFO:\r
+ {\r
+ tVideo_IOCtl_Mode *mode = Data;\r
+ if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
+ LEAVE_RET('i', -1);\r
+ if(mode->id < 0 || mode->id >= ciPL110_ModeCount)\r
+ LEAVE_RET('i', 0);\r
+ \r
+\r
+ mode->bpp = 32;\r
+ mode->flags = 0;\r
+ mode->width = caPL110_Modes[mode->id].W;\r
+ mode->height = caPL110_Modes[mode->id].H;\r
+\r
+ ret = 1;\r
+ break;\r
+ }\r
+ \r
+ case VIDEO_IOCTL_SETCURSOR:\r
+ if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
+ LEAVE_RET('i', -1);\r
+\r
+ DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );\r
+ \r
+ gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data;\r
+ if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_DrawCursor(\r
+ &gPL110_DrvUtil_BufInfo,\r
+ gPL110_CursorPos.x*giVT_CharWidth,\r
+ gPL110_CursorPos.y*giVT_CharHeight\r
+ );\r
+ else\r
+ DrvUtil_Video_DrawCursor(\r
+ &gPL110_DrvUtil_BufInfo,\r
+ gPL110_CursorPos.x,\r
+ gPL110_CursorPos.y\r
+ );\r
+ break;\r
+ \r
+ default:\r
+ LEAVE('i', -2);\r
+ return -2;\r
+ }\r
+ \r
+ LEAVE('i', ret);\r
+ return ret;\r
+}\r
+\r
+//\r
+//\r
+//\r
+\r
+/**\r
+ * \brief Set the LCD controller resolution\r
+ * \param W Width (aligned to 16 pixels, cipped to 1024)\r
+ * \param H Height (clipped to 768)\r
+ * \return Boolean failure\r
+ */\r
+int PL110_int_SetResolution(int W, int H)\r
+{\r
+ W = (W + 15) & ~0xF;\r
+ if(W <= 0 || H <= 0) {\r
+ Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H);\r
+ return 1;\r
+ }\r
+ if(W > 1024) W = 1024;\r
+ if(H > 768) H = 768;\r
+\r
+ gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2;\r
+ gpPL110_IOMem->LCDTiming1 = H-1;\r
+ gpPL110_IOMem->LCDTiming2 = (14 << 27);\r
+ gpPL110_IOMem->LCDTiming3 = 0;\r
+\r
+ if( gpPL110_Framebuffer ) {\r
+ MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12);\r
+ }\r
+ giPL110_FramebufferSize = W*H*4;\r
+\r
+ gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys );\r
+ gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys;\r
+ gpPL110_IOMem->LCDLPBase = 0;\r
+\r
+ // Power on, BGR mode, ???, ???, enabled\r
+ Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1;\r
+ // According to qemu, the Versatile version has these two the wrong\r
+ // way around\r
+ if( gbPL110_IsVersatile )\r
+ {\r
+ gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl\r
+ gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC\r
+ }\r
+ else\r
+ {\r
+ gpPL110_IOMem->LCDIMSC = 0;\r
+ gpPL110_IOMem->LCDControl = controlWord;\r
+ }\r
+\r
+ giPL110_Width = W;\r
+ giPL110_Height = H;\r
+\r
+ // Update the DrvUtil buffer info\r
+ gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer;\r
+ gPL110_DrvUtil_BufInfo.Pitch = W * 4;\r
+ gPL110_DrvUtil_BufInfo.Width = W;\r
+ gPL110_DrvUtil_BufInfo.Height = H;\r
+ gPL110_DrvUtil_BufInfo.Depth = 32;\r
+ \r
+ return 0;\r
+}\r
--- /dev/null
+#
+#
+
+OBJ = main.o
+NAME = Tegra2Vid
+
+-include ../Makefile.tpl
--- /dev/null
+/**\r
+ * main.c\r
+ * - Driver core\r
+ */\r
+#define DEBUG 0\r
+#define VERSION ((0<<8)|10)\r
+#include <acess.h>\r
+#include <errno.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+#include <api_drv_video.h>\r
+#include <lib/keyvalue.h>\r
+#include <options.h> // ARM Arch\r
+#include "tegra2.h"\r
+\r
+#define ABS(a) ((a)>0?(a):-(a))\r
+\r
+// === PROTOTYPES ===\r
+// Driver\r
+ int Tegra2Vid_Install(char **Arguments);\r
+void Tegra2Vid_Uninstall();\r
+// Internal\r
+// Filesystem\r
+Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+Uint64 Tegra2Vid_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+ int Tegra2Vid_IOCtl(tVFS_Node *node, int id, void *data);\r
+// -- Internals\r
+ int Tegra2Vid_int_SetMode(int Mode);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, VERSION, Tegra2Vid, Tegra2Vid_Install, NULL, NULL);\r
+tDevFS_Driver gTegra2Vid_DriverStruct = {\r
+ NULL, "Tegra2Vid",\r
+ {\r
+ .Read = Tegra2Vid_Read,\r
+ .Write = Tegra2Vid_Write,\r
+ .IOCtl = Tegra2Vid_IOCtl\r
+ }\r
+};\r
+// -- Options\r
+tPAddr gTegra2Vid_PhysBase = TEGRA2VID_BASE;\r
+ int gbTegra2Vid_IsVersatile = 1;\r
+// -- KeyVal parse rules\r
+const tKeyVal_ParseRules gTegra2Vid_KeyValueParser = {\r
+ NULL,\r
+ {\r
+ {"Base", "P", &gTegra2Vid_PhysBase},\r
+ {NULL, NULL, NULL}\r
+ }\r
+};\r
+// -- Driver state\r
+ int giTegra2Vid_CurrentMode = 0;\r
+ int giTegra2Vid_BufferMode;\r
+size_t giTegra2Vid_FramebufferSize;\r
+Uint32 *gpTegra2Vid_IOMem;\r
+tPAddr gTegra2Vid_FramebufferPhys;\r
+void *gpTegra2Vid_Framebuffer;\r
+// -- Misc\r
+tDrvUtil_Video_BufInfo gTegra2Vid_DrvUtil_BufInfo;\r
+tVideo_IOCtl_Pos gTegra2Vid_CursorPos;\r
+\r
+// === CODE ===\r
+/**\r
+ */\r
+int Tegra2Vid_Install(char **Arguments)\r
+{\r
+// KeyVal_Parse(&gTegra2Vid_KeyValueParser, Arguments);\r
+\r
+ gpTegra2Vid_IOMem = (void*)MM_MapHWPages(gTegra2Vid_PhysBase, 256/4);\r
+ {\r
+ Log_Debug("Tegra2Vid", "Display CMD Registers");\r
+ for( int i = 0x000; i <= 0x01A; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ for( int i = 0x028; i <= 0x043; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ Log_Debug("Tegra2Vid", "Display COM Registers");\r
+ for( int i = 0x300; i <= 0x329; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ Log_Debug("Tegra2Vid", "Display DISP Registers");\r
+ for( int i = 0x400; i <= 0x446; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ for( int i = 0x480; i <= 0x484; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ for( int i = 0x4C0; i <= 0x4C1; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+\r
+ Log_Debug("Tegra2Vid", "WINC_A Registers");\r
+ for( int i = 0x700; i <= 0x714; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ Log_Debug("Tegra2Vid", "WINBUF_A");\r
+ for( int i = 0x800; i <= 0x80A; i ++ )\r
+ Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+ }\r
+// return 1;\r
+ \r
+ giTegra2Vid_FramebufferSize =\r
+ (gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]&0xFFFF)\r
+ *(gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]>>16)*4;\r
+\r
+ Log_Debug("Tegra2Vid", "giTegra2Vid_FramebufferSize = 0x%x", giTegra2Vid_FramebufferSize);\r
+ gpTegra2Vid_Framebuffer = MM_MapHWPages(\r
+ gpTegra2Vid_IOMem[DC_WINBUF_A_START_ADDR_0],\r
+ (giTegra2Vid_FramebufferSize+PAGE_SIZE-1)/PAGE_SIZE\r
+ );\r
+ memset(gpTegra2Vid_Framebuffer, 0x1F, 0x1000);\r
+\r
+\r
+// Tegra2Vid_int_SetMode(4);\r
+\r
+ DevFS_AddDevice( &gTegra2Vid_DriverStruct );\r
+\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \brief Clean up resources for driver unloading\r
+ */\r
+void Tegra2Vid_Uninstall()\r
+{\r
+}\r
+\r
+/**\r
+ * \brief Read from the framebuffer\r
+ */\r
+Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \brief Write to the framebuffer\r
+ */\r
+Uint64 Tegra2Vid_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{\r
+ gTegra2Vid_DrvUtil_BufInfo.BufferFormat = giTegra2Vid_BufferMode;\r
+ return DrvUtil_Video_WriteLFB(&gTegra2Vid_DrvUtil_BufInfo, Offset, Length, Buffer);\r
+}\r
+\r
+const char *csaTegra2Vid_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
+\r
+/**\r
+ * \brief Handle messages to the device\r
+ */\r
+int Tegra2Vid_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+{\r
+ int ret = -2;\r
+ ENTER("pNode iID pData", Node, ID, Data);\r
+ \r
+ switch(ID)\r
+ {\r
+ BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls);\r
+\r
+ case VIDEO_IOCTL_SETBUFFORMAT:\r
+ DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );\r
+ ret = giTegra2Vid_BufferMode;\r
+ if(Data) giTegra2Vid_BufferMode = *(int*)Data;\r
+ if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_SetCursor( &gTegra2Vid_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_GETSETMODE:\r
+ if(Data)\r
+ {\r
+ int newMode;\r
+ \r
+ if( !CheckMem(Data, sizeof(int)) )\r
+ LEAVE_RET('i', -1);\r
+ \r
+ newMode = *(int*)Data;\r
+ \r
+ if(newMode < 0 || newMode >= ciTegra2Vid_ModeCount)\r
+ LEAVE_RET('i', -1);\r
+\r
+ if(newMode != giTegra2Vid_CurrentMode)\r
+ {\r
+ giTegra2Vid_CurrentMode = newMode;\r
+ Tegra2Vid_int_SetMode( newMode );\r
+ }\r
+ }\r
+ ret = giTegra2Vid_CurrentMode;\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_FINDMODE:\r
+ {\r
+ tVideo_IOCtl_Mode *mode = Data;\r
+ int closest, closestArea, reqArea = 0;\r
+ if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
+ LEAVE_RET('i', -1);\r
+ if( mode->bpp != 32 )\r
+ LEAVE_RET('i', 0);\r
+ if( mode->flags != 0 )\r
+ LEAVE_RET('i', 0);\r
+\r
+ ret = 0;\r
+\r
+ for( int i = 0; i < ciTegra2Vid_ModeCount; i ++ )\r
+ {\r
+ int area;\r
+ if(mode->width == caTegra2Vid_Modes[i].W && mode->height == caTegra2Vid_Modes[i].H) {\r
+ mode->id = i;\r
+ ret = 1;\r
+ break;\r
+ }\r
+ \r
+ area = caTegra2Vid_Modes[i].W * caTegra2Vid_Modes[i].H;\r
+ if(!reqArea) {\r
+ reqArea = mode->width * mode->height;\r
+ closest = i;\r
+ closestArea = area;\r
+ }\r
+ else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {\r
+ closest = i;\r
+ closestArea = area;\r
+ }\r
+ }\r
+ \r
+ if( ret == 0 )\r
+ {\r
+ mode->id = closest;\r
+ ret = 1;\r
+ }\r
+ mode->width = caTegra2Vid_Modes[mode->id].W;\r
+ mode->height = caTegra2Vid_Modes[mode->id].H;\r
+ break;\r
+ }\r
+ \r
+ case VIDEO_IOCTL_MODEINFO:\r
+ {\r
+ tVideo_IOCtl_Mode *mode = Data;\r
+ if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
+ LEAVE_RET('i', -1);\r
+ if(mode->id < 0 || mode->id >= ciTegra2Vid_ModeCount)\r
+ LEAVE_RET('i', 0);\r
+ \r
+\r
+ mode->bpp = 32;\r
+ mode->flags = 0;\r
+ mode->width = caTegra2Vid_Modes[mode->id].W;\r
+ mode->height = caTegra2Vid_Modes[mode->id].H;\r
+\r
+ ret = 1;\r
+ break;\r
+ }\r
+ \r
+ case VIDEO_IOCTL_SETCURSOR:\r
+ if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
+ LEAVE_RET('i', -1);\r
+\r
+ DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );\r
+ \r
+ gTegra2Vid_CursorPos = *(tVideo_IOCtl_Pos*)Data;\r
+ if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_DrawCursor(\r
+ &gTegra2Vid_DrvUtil_BufInfo,\r
+ gTegra2Vid_CursorPos.x*giVT_CharWidth,\r
+ gTegra2Vid_CursorPos.y*giVT_CharHeight\r
+ );\r
+ else\r
+ DrvUtil_Video_DrawCursor(\r
+ &gTegra2Vid_DrvUtil_BufInfo,\r
+ gTegra2Vid_CursorPos.x,\r
+ gTegra2Vid_CursorPos.y\r
+ );\r
+ break;\r
+ \r
+ default:\r
+ LEAVE('i', -2);\r
+ return -2;\r
+ }\r
+ \r
+ LEAVE('i', ret);\r
+ return ret;\r
+}\r
+\r
+//\r
+//\r
+//\r
+\r
+int Tegra2Vid_int_SetMode(int Mode)\r
+{\r
+ const struct sTegra2_Disp_Mode *mode = &caTegra2Vid_Modes[Mode];\r
+ int w = mode->W, h = mode->H; // Horizontal/Vertical Active\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_FRONT_PORCH_0) = (mode->VFP << 16) | mode->HFP; \r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_SYNC_WIDTH_0) = (mode->HS << 16) | mode->HS;\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_BACK_PORCH_0) = (mode->VBP << 16) | mode->HBP;\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_ACTIVE_0) = (mode->H << 16) | mode->W;\r
+\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_POSITION_0) = 0;\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_SIZE_0) = (mode->H << 16) | mode->W;\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_COLOR_CONTROL_0) = 0x8; // BASE888\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_COLOR_DEPTH_0) = 12; // Could be 13 (BGR/RGB)\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_PRESCALED_SIZE_0) = (mode->H << 16) | mode->W;\r
+\r
+ Log_Debug("Tegra2Vid", "Mode %i (%ix%i) selected", Mode, w, h);\r
+\r
+ if( !gpTegra2Vid_Framebuffer || w*h*4 != giTegra2Vid_FramebufferSize )\r
+ {\r
+ if( gpTegra2Vid_Framebuffer )\r
+ {\r
+ // TODO: Free framebuffer for reallocation\r
+ }\r
+\r
+ giTegra2Vid_FramebufferSize = w*h*4; \r
+\r
+ gpTegra2Vid_Framebuffer = (void*)MM_AllocDMA(\r
+ (giTegra2Vid_FramebufferSize + PAGE_SIZE-1) / PAGE_SIZE,\r
+ 32,\r
+ &gTegra2Vid_FramebufferPhys\r
+ );\r
+ // TODO: Catch allocation failures\r
+ Log_Debug("Tegra2Vid", "0x%x byte framebuffer at %p (%P phys)",\r
+ giTegra2Vid_FramebufferSize,\r
+ gpTegra2Vid_Framebuffer,\r
+ gTegra2Vid_FramebufferPhys\r
+ );\r
+ \r
+ // Tell hardware\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_START_ADDR_0) = gTegra2Vid_FramebufferPhys;\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_V_OFFSET_0) = 0; // Y offset\r
+ *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_H_OFFSET_0) = 0; // X offset\r
+ }\r
+\r
+ return 0;\r
+}\r
--- /dev/null
+/*
+ * Acess2 NVidia Tegra2 Display Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * tegra2.h
+ * - Driver definitions
+ */
+#ifndef _TEGRA2_DISP_H_
+#define _TEGRA2_DISP_H_
+
+#define TEGRA2VID_BASE 0x54200000 // 0x40000 Large (256 KB)
+
+const struct sTegra2_Disp_Mode
+{
+ Uint16 W, H;
+ Uint16 HFP, VFP;
+ Uint16 HS, VS;
+ Uint16 HBP, VBP;
+} caTegra2Vid_Modes[] = {
+ // TODO: VESA timings
+ {720, 487, 16,33, 63, 33, 59, 133}, // NTSC 2
+ {720, 576, 12,33, 63, 33, 69, 193}, // PAL 2 (VFP shown as 2/33, used 33)
+ {720, 483, 16, 6, 63, 6, 59, 30}, // 480p
+ {1280, 720, 70, 5, 804, 6, 220, 20}, // 720p
+ {1920,1080, 44, 4, 884, 5, 148, 36}, // 1080p
+ // TODO: Can all but HA/VA be constant and those select the resolution?
+};
+const int ciTegra2Vid_ModeCount = sizeof(caTegra2Vid_Modes)/sizeof(caTegra2Vid_Modes[0]);
+
+enum eTegra2_Disp_Regs
+{
+ DC_DISP_DISP_SIGNAL_OPTIONS0_0 = 0x400,
+ DC_DISP_DISP_SIGNAL_OPTIONS1_0, // 401
+ DC_DISP_DISP_WIN_OPTIONS_0, // 402
+ DC_DISP_MEM_HIGH_PRIORITY_0, // 403
+ DC_DISP_MEM_HIGH_PRIORITY_TIMER_0, // 404
+ DC_DISP_DISP_TIMING_OPTIONS_0, // 405
+ DC_DISP_REF_TO_SYNC_0, // 406 (TrimSlice 0x0001 000B)
+ DC_DISP_SYNC_WIDTH_0, // 407 (TrimSlice 0x0004 003A)
+ DC_DISP_BACK_PORCH_0, // 408 (TrimSlice 0x0004 003A)
+ DC_DISP_DISP_ACTIVE_0, // 409 (TrimSlice 0x0300 0400)
+ DC_DISP_FRONT_PORCH_0, // 40A (TrimSlice 0x0004 003A)
+
+ DC_DISP_H_PULSE0_CONTROL_0, // 40B
+
+ DC_DISP_DISP_COLOR_CONTROL_0 = 0x430,
+
+ DC_WINC_A_COLOR_PALETTE_0 = 0x500,
+ DC_WINC_A_PALETTE_COLOR_EXT_0 = 0x600,
+ DC_WIN_A_WIN_OPTIONS_0 = 0x700,
+ DC_WIN_A_BYTE_SWAP_0, // 701
+ DC_WIN_A_BUFFER_CONTROL_0, // 702
+ DC_WIN_A_COLOR_DEPTH_0, // 703
+ DC_WIN_A_POSITION_0, // 704
+ DC_WIN_A_SIZE_0, // 705 (TrimSlice 0x0300 0400)
+ DC_WIN_A_PRESCALED_SIZE_0,
+ DC_WIN_A_H_INITIAL_DDA_0,
+ DC_WIN_A_V_INITIAL_DDA_0,
+ DC_WIN_A_DDA_INCREMENT_0,
+ DC_WIN_A_LINE_STRIDE_0,
+ DC_WIN_A_BUF_STRIDE_0,
+ DC_WIN_A_BUFFER_ADDR_MODE_0,
+ DC_WIN_A_DV_CONTROL_0,
+ DC_WIN_A_BLEND_NOKEY_0,
+
+ DC_WINBUF_A_START_ADDR_0 = 0x800,
+ DC_WINBUF_A_START_ADDR_NS_0,
+ DC_WINBUF_A_ADDR_H_OFFSET_0,
+ DC_WINBUF_A_ADDR_H_OFFSET_NS_0,
+ DC_WINBUF_A_ADDR_V_OFFSET_0,
+ DC_WINBUF_A_ADDR_V_OFFSET_NS_0,
+};
+
+#endif
+
--- /dev/null
+#
+#
+
+OBJ = main.o
+NAME = VESA
+
+-include ../Makefile.tpl
--- /dev/null
+/**
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+// === TYPES ===
+typedef struct sFarPtr
+{
+ Uint16 ofs;
+ Uint16 seg;
+} tFarPtr;
+
+typedef struct sVesa_Mode
+{
+ Uint16 code;
+ Uint16 width, height;
+ Uint16 pitch, bpp;
+ Uint16 flags;
+ Uint32 fbSize;
+ Uint32 framebuffer;
+} tVesa_Mode;
+
+typedef struct sVesa_CallModeInfo
+{
+ Uint16 attributes;
+ Uint8 winA,winB;
+ Uint16 granularity;
+ Uint16 winsize;
+ Uint16 segmentA, segmentB;
+ tFarPtr realFctPtr;
+ Uint16 pitch; // Bytes per scanline
+
+ Uint16 Xres, Yres;
+ Uint8 Wchar, Ychar, planes, bpp, banks;
+ Uint8 memory_model, bank_size, image_pages;
+ Uint8 reserved0;
+
+ Uint8 red_mask, red_position;
+ Uint8 green_mask, green_position;
+ Uint8 blue_mask, blue_position;
+ Uint8 rsv_mask, rsv_position;
+ Uint8 directcolor_attributes;
+
+ Uint32 physbase; // Your LFB address ;)
+ Uint32 reserved1;
+ Sint16 reserved2;
+} tVesa_CallModeInfo;
+
+typedef struct sVesa_CallInfo
+{
+ char signature[4]; // == "VESA"
+ Uint16 Version; // == 0x0300 for Vesa 3.0
+ tFarPtr OEMString; // isa vbeFarPtr
+ Uint8 Capabilities[4];
+ tFarPtr VideoModes; // isa vbeParPtr
+ Uint16 TotalMemory; // as # of 64KB blocks
+} tVesa_CallInfo;
+
+#endif
--- /dev/null
+/*\r
+ * AcessOS 1\r
+ * Video BIOS Extensions (Vesa) Driver\r
+ */\r
+#define DEBUG 0\r
+#define VERSION 0x100\r
+\r
+#include <acess.h>\r
+#include <vfs.h>\r
+#include <api_drv_video.h>\r
+#include <fs_devfs.h>\r
+#include <modules.h>\r
+#include <vm8086.h>\r
+#include "common.h"\r
+\r
+// === CONSTANTS ===\r
+#define FLAG_LFB 0x1\r
+#define VESA_DEFAULT_FRAMEBUFFER (KERNEL_BASE|0xA0000)\r
+#define BLINKING_CURSOR 1\r
+#if BLINKING_CURSOR\r
+# define VESA_CURSOR_PERIOD 1000\r
+#endif\r
+\r
+// === PROTOTYPES ===\r
+ int Vesa_Install(char **Arguments);\r
+Uint64 Vesa_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
+Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);\r
+ int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
+ int Vesa_Int_SetMode(int Mode);\r
+ int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data);\r
+ int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data);\r
+void Vesa_int_HideCursor(void);\r
+void Vesa_int_ShowCursor(void);\r
+void Vesa_FlipCursor(void *Arg);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL);\r
+tVFS_NodeType gVesa_NodeType = {\r
+ .Read = Vesa_Read,\r
+ .Write = Vesa_Write,\r
+ .IOCtl = Vesa_IOCtl\r
+ };\r
+tDevFS_Driver gVesa_DriverStruct = {\r
+ NULL, "Vesa",\r
+ {.Type = &gVesa_NodeType}\r
+ };\r
+tMutex glVesa_Lock;\r
+tVM8086 *gpVesa_BiosState;\r
+ int giVesaDriverId = -1;\r
+// --- Video Modes ---\r
+ int giVesaCurrentMode = 0;\r
+tVesa_Mode *gVesa_Modes;\r
+tVesa_Mode *gpVesaCurMode;\r
+ int giVesaModeCount = 0;\r
+ int gbVesaModesChecked;\r
+// --- Framebuffer ---\r
+char *gpVesa_Framebuffer = (void*)VESA_DEFAULT_FRAMEBUFFER;\r
+ int giVesaPageCount = 0; //!< Framebuffer size in pages\r
+// --- Cursor Control ---\r
+ int giVesaCursorX = -1;\r
+ int giVesaCursorY = -1;\r
+ int giVesaCursorTimer = -1; // Invalid timer\r
+ int gbVesa_CursorVisible = 0;\r
+// --- 2D Video Stream Handlers ---\r
+tDrvUtil_Video_BufInfo gVesa_BufInfo;\r
+\r
+// === CODE ===\r
+int Vesa_Install(char **Arguments)\r
+{\r
+ tVesa_CallInfo *info;\r
+ tFarPtr infoPtr;\r
+ Uint16 *modes;\r
+ int i;\r
+ \r
+ // Allocate Info Block\r
+ gpVesa_BiosState = VM8086_Init();\r
+ info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs);\r
+ // Set Requested Version\r
+ memcpy(info->signature, "VBE2", 4);\r
+ // Set Registers\r
+ gpVesa_BiosState->AX = 0x4F00;\r
+ gpVesa_BiosState->ES = infoPtr.seg; gpVesa_BiosState->DI = infoPtr.ofs;\r
+ // Call Interrupt\r
+ VM8086_Int(gpVesa_BiosState, 0x10);\r
+ if(gpVesa_BiosState->AX != 0x004F) {\r
+ Log_Warning("VESA", "Vesa_Install - VESA/VBE Unsupported (AX = 0x%x)", gpVesa_BiosState->AX);\r
+ return MODULE_ERR_NOTNEEDED;\r
+ }\r
+ \r
+ //Log_Debug("VESA", "info->VideoModes = %04x:%04x", info->VideoModes.seg, info->VideoModes.ofs);\r
+ modes = (Uint16 *) VM8086_GetPointer(gpVesa_BiosState, info->VideoModes.seg, info->VideoModes.ofs);\r
+ \r
+ // Read Modes\r
+ for( giVesaModeCount = 0; modes[giVesaModeCount] != 0xFFFF; giVesaModeCount++ );\r
+ gVesa_Modes = (tVesa_Mode *)malloc( giVesaModeCount * sizeof(tVesa_Mode) );\r
+ \r
+ Log_Debug("VESA", "%i Modes", giVesaModeCount);\r
+ \r
+ // Insert Text Mode\r
+ gVesa_Modes[0].width = 80;\r
+ gVesa_Modes[0].height = 25;\r
+ gVesa_Modes[0].bpp = 12;\r
+ gVesa_Modes[0].code = 0x3;\r
+ gVesa_Modes[0].flags = 1;\r
+ gVesa_Modes[0].fbSize = 80*25*2;\r
+ gVesa_Modes[0].framebuffer = 0xB8000;\r
+ \r
+ for( i = 1; i < giVesaModeCount; i++ )\r
+ {\r
+ gVesa_Modes[i].code = modes[i];\r
+ }\r
+\r
+// VM8086_Deallocate( info );\r
+ \r
+ // Install Device\r
+ giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct );\r
+ if(giVesaDriverId == -1) return MODULE_ERR_MISC;\r
+ \r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+void Vesa_int_FillModeList(void)\r
+{\r
+ if( !gbVesaModesChecked )\r
+ {\r
+ int i;\r
+ tVesa_CallModeInfo *modeinfo;\r
+ tFarPtr modeinfoPtr;\r
+ \r
+ modeinfo = VM8086_Allocate(gpVesa_BiosState, 512, &modeinfoPtr.seg, &modeinfoPtr.ofs);\r
+ for( i = 1; i < giVesaModeCount; i ++ )\r
+ {\r
+ // Get Mode info\r
+ gpVesa_BiosState->AX = 0x4F01;\r
+ gpVesa_BiosState->CX = gVesa_Modes[i].code;\r
+ gpVesa_BiosState->ES = modeinfoPtr.seg;\r
+ gpVesa_BiosState->DI = modeinfoPtr.ofs;\r
+ VM8086_Int(gpVesa_BiosState, 0x10);\r
+ \r
+ // Parse Info\r
+ gVesa_Modes[i].flags = 0;\r
+ if ( (modeinfo->attributes & 0x90) == 0x90 )\r
+ {\r
+ gVesa_Modes[i].flags |= FLAG_LFB;\r
+ gVesa_Modes[i].framebuffer = modeinfo->physbase;\r
+ gVesa_Modes[i].fbSize = modeinfo->Yres*modeinfo->pitch;\r
+ } else {\r
+ gVesa_Modes[i].framebuffer = 0;\r
+ gVesa_Modes[i].fbSize = 0;\r
+ }\r
+ \r
+ gVesa_Modes[i].pitch = modeinfo->pitch;\r
+ gVesa_Modes[i].width = modeinfo->Xres;\r
+ gVesa_Modes[i].height = modeinfo->Yres;\r
+ gVesa_Modes[i].bpp = modeinfo->bpp;\r
+ \r
+ #if DEBUG\r
+ Log_Log("VESA", "0x%x - %ix%ix%i",\r
+ gVesa_Modes[i].code, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp);\r
+ #endif\r
+ }\r
+ \r
+// VM8086_Deallocate( modeinfo );\r
+ \r
+ gbVesaModesChecked = 1;\r
+ }\r
+}\r
+\r
+/* Read from the framebuffer\r
+ */\r
+Uint64 Vesa_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer)\r
+{\r
+ #if DEBUG >= 2\r
+ Log("Vesa_Read: () - NULL\n");\r
+ #endif\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \brief Write to the framebuffer\r
+ */\r
+Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
+{\r
+ if( gVesa_Modes[giVesaCurrentMode].framebuffer == 0 ) {\r
+ Log_Warning("VESA", "Vesa_Write - Non-LFB Modes not yet supported.");\r
+ return 0;\r
+ }\r
+\r
+ return DrvUtil_Video_WriteLFB(&gVesa_BufInfo, Offset, Length, Buffer);\r
+}\r
+\r
+const char *csaVESA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
+/**\r
+ * \brief Handle messages to the device\r
+ */\r
+int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+{\r
+ int ret;\r
+ //Log_Debug("VESA", "Vesa_Ioctl: (Node=%p, ID=%i, Data=%p)", Node, ID, Data);\r
+ switch(ID)\r
+ {\r
+ BASE_IOCTLS(DRV_TYPE_VIDEO, "VESA", VERSION, csaVESA_IOCtls);\r
+\r
+ case VIDEO_IOCTL_GETSETMODE:\r
+ if( !Data ) return giVesaCurrentMode;\r
+ return Vesa_Int_SetMode( *(int*)Data );\r
+ \r
+ case VIDEO_IOCTL_FINDMODE:\r
+ return Vesa_Int_FindMode((tVideo_IOCtl_Mode*)Data);\r
+ case VIDEO_IOCTL_MODEINFO:\r
+ return Vesa_Int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
+ \r
+ case VIDEO_IOCTL_SETBUFFORMAT:\r
+ Vesa_int_HideCursor();\r
+ ret = gVesa_BufInfo.BufferFormat;\r
+ if(Data) gVesa_BufInfo.BufferFormat = *(int*)Data;\r
+ if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor );\r
+ Vesa_int_ShowCursor();\r
+ return ret;\r
+ \r
+ case VIDEO_IOCTL_SETCURSOR: // Set cursor position\r
+ Vesa_int_HideCursor();\r
+ giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x;\r
+ giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y;\r
+ Vesa_int_ShowCursor();\r
+ return 0;\r
+ \r
+ case VIDEO_IOCTL_SETCURSORBITMAP:\r
+ DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data );\r
+ return 0;\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \brief Updates the video mode\r
+ */\r
+int Vesa_Int_SetMode(int mode)\r
+{ \r
+ // Sanity Check values\r
+ if(mode < 0 || mode > giVesaModeCount) return -1;\r
+\r
+ // Check for fast return\r
+ if(mode == giVesaCurrentMode) return 1;\r
+ \r
+ Vesa_int_FillModeList();\r
+\r
+ Time_RemoveTimer(giVesaCursorTimer);\r
+ giVesaCursorTimer = -1;\r
+ \r
+ Mutex_Acquire( &glVesa_Lock );\r
+ \r
+ gpVesa_BiosState->AX = 0x4F02;\r
+ gpVesa_BiosState->BX = gVesa_Modes[mode].code;\r
+ if(gVesa_Modes[mode].flags & FLAG_LFB) {\r
+ gpVesa_BiosState->BX |= 0x4000; // Bit 14 - Use LFB\r
+ }\r
+ \r
+ // Set Mode\r
+ VM8086_Int(gpVesa_BiosState, 0x10);\r
+ \r
+ // Map Framebuffer\r
+ if( (tVAddr)gpVesa_Framebuffer != VESA_DEFAULT_FRAMEBUFFER )\r
+ MM_UnmapHWPages((tVAddr)gpVesa_Framebuffer, giVesaPageCount);\r
+ giVesaPageCount = (gVesa_Modes[mode].fbSize + 0xFFF) >> 12;\r
+ gpVesa_Framebuffer = (void*)MM_MapHWPages(gVesa_Modes[mode].framebuffer, giVesaPageCount);\r
+ \r
+ Log_Log("VESA", "Setting mode to %i (%ix%i %ibpp) %p[0x%x] maps %P",\r
+ mode,\r
+ gVesa_Modes[mode].width, gVesa_Modes[mode].height,\r
+ gVesa_Modes[mode].bpp,\r
+ gpVesa_Framebuffer, giVesaPageCount << 12, gVesa_Modes[mode].framebuffer\r
+ );\r
+ \r
+ // Record Mode Set\r
+ giVesaCurrentMode = mode;\r
+ gpVesaCurMode = &gVesa_Modes[giVesaCurrentMode];\r
+ \r
+ Mutex_Release( &glVesa_Lock );\r
+\r
+ gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer;\r
+ gVesa_BufInfo.Pitch = gVesa_Modes[mode].pitch;\r
+ gVesa_BufInfo.Width = gVesa_Modes[mode].width;\r
+ gVesa_BufInfo.Height = gVesa_Modes[mode].height;\r
+ gVesa_BufInfo.Depth = gVesa_Modes[mode].bpp; \r
+\r
+ return 1;\r
+}\r
+\r
+int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data)\r
+{\r
+ int i;\r
+ int best = -1, bestFactor = 1000;\r
+ int factor, tmp;\r
+ \r
+ ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp);\r
+\r
+ Vesa_int_FillModeList();\r
+ \r
+ for(i=0;i<giVesaModeCount;i++)\r
+ {\r
+ LOG("Mode %i (%ix%ix%i)", i, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp);\r
+ \r
+ if(gVesa_Modes[i].width == data->width && gVesa_Modes[i].height == data->height)\r
+ {\r
+ //if( (data->bpp == 32 || data->bpp == 24)\r
+ // && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) )\r
+ if( data->bpp == gVesa_Modes[i].bpp )\r
+ {\r
+ LOG("Perfect!");\r
+ best = i;\r
+ break;\r
+ }\r
+ }\r
+ \r
+ tmp = gVesa_Modes[i].width * gVesa_Modes[i].height;\r
+ tmp -= data->width * data->height;\r
+ tmp = tmp < 0 ? -tmp : tmp;\r
+ factor = tmp * 1000 / (data->width * data->height);\r
+ \r
+ if( data->bpp == 8 && gVesa_Modes[i].bpp != 8 ) continue;\r
+ if( data->bpp == 16 && gVesa_Modes[i].bpp != 16 ) continue;\r
+ \r
+ if( (data->bpp == 32 || data->bpp == 24)\r
+ && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) )\r
+ {\r
+ if( data->bpp == gVesa_Modes[i].bpp )\r
+ factor /= 2;\r
+ }\r
+ else {\r
+ if( data->bpp != gVesa_Modes[i].bpp )\r
+ continue ;\r
+ }\r
+ \r
+ LOG("factor = %i", factor);\r
+ \r
+ if(factor < bestFactor)\r
+ {\r
+ bestFactor = factor;\r
+ best = i;\r
+ }\r
+ }\r
+ data->id = best;\r
+ data->width = gVesa_Modes[best].width;\r
+ data->height = gVesa_Modes[best].height;\r
+ data->bpp = gVesa_Modes[best].bpp;\r
+ LEAVE('i', best);\r
+ return best;\r
+}\r
+\r
+int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data)\r
+{\r
+ if(data->id < 0 || data->id > giVesaModeCount) return -1;\r
+\r
+ Vesa_int_FillModeList();\r
+\r
+ data->width = gVesa_Modes[data->id].width;\r
+ data->height = gVesa_Modes[data->id].height;\r
+ data->bpp = gVesa_Modes[data->id].bpp;\r
+ return 1;\r
+}\r
+\r
+void Vesa_int_HideCursor(void)\r
+{\r
+ DrvUtil_Video_RemoveCursor( &gVesa_BufInfo );\r
+ #if BLINKING_CURSOR\r
+ if(giVesaCursorTimer != -1) {\r
+ Time_RemoveTimer(giVesaCursorTimer);\r
+ giVesaCursorTimer = -1;\r
+ }\r
+ #endif\r
+}\r
+\r
+void Vesa_int_ShowCursor(void)\r
+{\r
+ gbVesa_CursorVisible = (giVesaCursorX >= 0);\r
+ if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+ {\r
+ DrvUtil_Video_DrawCursor(\r
+ &gVesa_BufInfo,\r
+ giVesaCursorX*giVT_CharWidth,\r
+ giVesaCursorY*giVT_CharHeight\r
+ );\r
+ #if BLINKING_CURSOR\r
+ giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, NULL);\r
+ #endif\r
+ }\r
+ else\r
+ DrvUtil_Video_DrawCursor(\r
+ &gVesa_BufInfo,\r
+ giVesaCursorX,\r
+ giVesaCursorY\r
+ );\r
+}\r
+\r
+/**\r
+ * \brief Swaps the text cursor on/off\r
+ */\r
+void Vesa_FlipCursor(void *Arg)\r
+{\r
+ if( gVesa_BufInfo.BufferFormat != VIDEO_BUFFMT_TEXT )\r
+ return ;\r
+\r
+ if( gbVesa_CursorVisible )\r
+ DrvUtil_Video_RemoveCursor(&gVesa_BufInfo);\r
+ else\r
+ DrvUtil_Video_DrawCursor(&gVesa_BufInfo,\r
+ giVesaCursorX*giVT_CharWidth,\r
+ giVesaCursorY*giVT_CharHeight\r
+ );\r
+ gbVesa_CursorVisible = !gbVesa_CursorVisible;\r
+ \r
+ #if BLINKING_CURSOR\r
+ giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, Arg);\r
+ #endif\r
+}\r
+\r
--- /dev/null
+#
+#
+
+OBJ = ext2.o read.o dir.o write.o
+NAME = Ext2
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file dir.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG 1
+#define VERBOSE 0
+#include "ext2_common.h"
+
+// === MACROS ===
+#define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)])
+
+// === PROTOTYPES ===
+char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
+ int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
+ int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name);
+// --- Helpers ---
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId);
+
+// === GLOBALS ===
+tVFS_NodeType gExt2_DirType = {
+ .TypeName = "ext2-dir",
+ .ReadDir = Ext2_ReadDir,
+ .FindDir = Ext2_FindDir,
+ .MkNod = Ext2_MkNod,
+ .Relink = Ext2_Relink,
+ .Link = Ext2_Link,
+ .Close = Ext2_CloseFile
+ };
+tVFS_NodeType gExt2_FileType = {
+ .TypeName = "ext2-file",
+ .Read = Ext2_Read,
+ .Write = Ext2_Write,
+ .Close = Ext2_CloseFile
+ };
+
+// === CODE ===
+/**
+ * \brief Reads a directory entry
+ * \param Node Directory node
+ * \param Pos Position of desired element
+ */
+char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tExt2_Inode inode;
+ tExt2_DirEnt dirent;
+ Uint64 Base; // Block's Base Address
+ int block = 0;
+ Uint ofs = 0;
+ int entNum = 0;
+ tExt2_Disk *disk = Node->ImplPtr;
+ Uint size;
+
+ ENTER("pNode iPos", Node, Pos);
+
+ // Read directory's inode
+ Ext2_int_ReadInode(disk, Node->Inode, &inode);
+ size = inode.i_size;
+
+ LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
+
+ // Find Entry
+ // Get First Block
+ // - Do this ourselves as it is a simple operation
+ Base = inode.i_block[0] * disk->BlockSize;
+ // Scan directory
+ while(Pos -- && size > 0)
+ {
+ VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
+ ofs += dirent.rec_len;
+ size -= dirent.rec_len;
+ entNum ++;
+
+ if(ofs >= disk->BlockSize) {
+ block ++;
+ if( ofs > disk->BlockSize ) {
+ Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
+ entNum-1, Node->Inode);
+ }
+ ofs = 0;
+ Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
+ }
+ }
+
+ // Check for the end of the list
+ if(size <= 0) {
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Read Entry
+ VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
+ //LOG("dirent.inode = %i", dirent.inode);
+ //LOG("dirent.rec_len = %i", dirent.rec_len);
+ //LOG("dirent.name_len = %i", dirent.name_len);
+ dirent.name[ dirent.name_len ] = '\0'; // Cap off string
+
+
+ // Ignore . and .. (these are done in the VFS)
+ if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
+ || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
+ LEAVE('p', VFS_SKIP);
+ return VFS_SKIP; // Skip
+ }
+
+ LEAVE('s', dirent.name);
+ // Create new node
+ return strdup(dirent.name);
+}
+
+/**
+ * \brief Gets information about a file
+ * \param Node Parent Node
+ * \param Filename Name of wanted file
+ * \return VFS Node of file
+ */
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
+{
+ tExt2_Disk *disk = Node->ImplPtr;
+ tExt2_Inode inode;
+ tExt2_DirEnt dirent;
+ Uint64 Base; // Block's Base Address
+ int block = 0;
+ Uint ofs = 0;
+ int entNum = 0;
+ Uint size;
+ int filenameLen = strlen(Filename);
+
+ // Read directory's inode
+ Ext2_int_ReadInode(disk, Node->Inode, &inode);
+ size = inode.i_size;
+
+ // Get First Block
+ // - Do this ourselves as it is a simple operation
+ Base = inode.i_block[0] * disk->BlockSize;
+ // Find File
+ while(size > 0)
+ {
+ VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
+ dirent.name[ dirent.name_len ] = '\0'; // Cap off string
+ // If it matches, create a node and return it
+ if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
+ return Ext2_int_CreateNode( disk, dirent.inode );
+ // Increment pointers
+ ofs += dirent.rec_len;
+ size -= dirent.rec_len;
+ entNum ++;
+
+ // Check for end of block
+ if(ofs >= disk->BlockSize) {
+ block ++;
+ if( ofs > disk->BlockSize ) {
+ Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
+ entNum-1, Node->Inode);
+ }
+ ofs = 0;
+ Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
+ * \brief Create a new node
+ */
+int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
+{
+ #if 0
+ tVFS_Node *child;
+ Uint64 inodeNum;
+ tExt2_Inode inode;
+ inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
+
+ memset(&inode, 0, sizeof(tExt2_Inode));
+
+ // File type
+ inode.i_mode = 0664;
+ if( Flags & VFS_FFLAG_READONLY )
+ inode.i_mode &= ~0222;
+ if( Flags & VFS_FFLAG_SYMLINK )
+ inode.i_mode |= EXT2_S_IFLNK;
+ else if( Flags & VFS_FFLAG_DIRECTORY )
+ inode.i_mode |= EXT2_S_IFDIR | 0111;
+
+ inode.i_uid = Threads_GetUID();
+ inode.i_gid = Threads_GetGID();
+ inode.i_ctime =
+ inode.i_mtime =
+ inode.i_atime = now() / 1000;
+
+ child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
+ return Ext2_Link(Parent, child, Name);
+ #else
+ return 1;
+ #endif
+}
+
+/**
+ * \brief Rename a file
+ * \param Node This (directory) node
+ * \param OldName Old name of file
+ * \param NewName New name for file
+ * \return Boolean Failure - See ::tVFS_Node.Relink for info
+ */
+int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
+{
+ return 1;
+}
+
+/**
+ * \brief Links an existing node to a new name
+ * \param Parent Parent (directory) node
+ * \param Node Node to link
+ * \param Name New name for the node
+ * \return Boolean Failure - See ::tVFS_Node.Link for info
+ */
+int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
+{
+ #if 0
+ tExt2_Disk *disk = Node->ImplPtr;
+ tExt2_Inode inode;
+ tExt2_DirEnt dirent;
+ tExt2_DirEnt newEntry;
+ Uint64 Base; // Block's Base Address
+ int block = 0, ofs = 0;
+ Uint size;
+ void *blockData;
+ int bestMatch = -1, bestSize, bestBlock, bestOfs;
+ int nEntries;
+
+ blockData = malloc(disk->BlockSize);
+
+ // Read child inode (get's the file type)
+ Ext2_int_ReadInode(disk, Child->Inode, &inode);
+
+ // Create a stub entry
+ newEntry.inode = Child->Inode;
+ newEntry.name_len = strlen(Name);
+ newEntry.rec_len = (newEntry.name_len+3+8)&~3;
+ newEntry.type = inode.i_mode >> 12;
+ memcpy(newEntry.name, Name, newEntry.name_len);
+
+ // Read directory's inode
+ Ext2_int_ReadInode(disk, Node->Inode, &inode);
+ size = inode.i_size;
+
+ // Get a lock on the inode
+ Ext2_int_LockInode(disk, Node->Inode);
+
+ // Get First Block
+ // - Do this ourselves as it is a simple operation
+ base = inode.i_block[0] * disk->BlockSize;
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
+ block = 0;
+ // Find File
+ while(size > 0)
+ {
+ dirent = blockData + ofs;
+ // Sanity Check the entry
+ if(ofs + dirent->rec_len > disk->BlockSize) {
+ Log_Warning("EXT2",
+ "Directory entry %i of inode 0x%x extends over a block boundary",
+ nEntries, (Uint)Node->Inode);
+ }
+ else {
+
+ // Free entry
+ if(dirent->type == 0) {
+ if( dirent->rec_len >= newEntry.rec_len
+ && (bestMatch == -1 || bestSize > dirent->rec_len) )
+ {
+ bestMatch = nEntries;
+ bestSize = dirent->rec_len;
+ bestBlock = block;
+ bestOfs = ofs;
+ }
+ }
+ // Non free - check name to avoid duplicates
+ else {
+ if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
+ Ext2_int_UnlockInode(disk, Node->Inode);
+ return 1; // ERR_???
+ }
+ }
+ }
+
+ // Increment the pointer
+ nEntries ++;
+ ofs += dirent->rec_len;
+ if( ofs >= disk->BlockSize ) {
+ // Read the next block if needed
+ BLOCK_DIR_OFS(Node->Data, block) = nEntries;
+ block ++;
+ ofs = 0;
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
+ }
+ }
+
+ // Check if a free slot was found
+ if( bestMatch >= 0 ) {
+ // Read-Modify-Write
+ bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
+ if( block > 0 )
+ bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
+ dirent = blockData + bestOfs;
+ memcpy(dirent, newEntry, newEntry.rec_len);
+ VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
+ }
+ else {
+ // Allocate block, Write
+ block = Ext2_int_AllocateBlock(Disk, block);
+ Log_Warning("EXT2", "");
+ }
+
+ Ext2_int_UnlockInode(disk, Node->Inode);
+ return 0;
+ #else
+ return 1;
+ #endif
+}
+
+// ---- INTERNAL FUNCTIONS ----
+/**
+ * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
+ * \brief Create a new VFS Node
+ */
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
+{
+ tExt2_Inode inode;
+ tVFS_Node retNode;
+ tVFS_Node *tmpNode;
+
+ if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
+ return NULL;
+
+ if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
+ return tmpNode;
+
+
+ // Set identifiers
+ retNode.Inode = InodeID;
+ retNode.ImplPtr = Disk;
+
+ // Set file length
+ retNode.Size = inode.i_size;
+ retNode.Data = NULL;
+
+ // Set Access Permissions
+ retNode.UID = inode.i_uid;
+ retNode.GID = inode.i_gid;
+ retNode.NumACLs = 3;
+ retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
+
+ // Set Function Pointers
+ retNode.Type = &gExt2_FileType;
+
+ switch(inode.i_mode & EXT2_S_IFMT)
+ {
+ // Symbolic Link
+ case EXT2_S_IFLNK:
+ retNode.Flags = VFS_FFLAG_SYMLINK;
+ break;
+ // Regular File
+ case EXT2_S_IFREG:
+ retNode.Flags = 0;
+ retNode.Size |= (Uint64)inode.i_dir_acl << 32;
+ break;
+ // Directory
+ case EXT2_S_IFDIR:
+ retNode.Type = &gExt2_DirType;
+ retNode.Flags = VFS_FFLAG_DIRECTORY;
+ retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
+ break;
+ // Unknown, Write protect it to be safe
+ default:
+ retNode.Flags = VFS_FFLAG_READONLY;
+ break;
+ }
+
+ // Set Timestamps
+ retNode.ATime = inode.i_atime * 1000;
+ retNode.MTime = inode.i_mtime * 1000;
+ retNode.CTime = inode.i_ctime * 1000;
+
+ // Save in node cache and return saved node
+ return Inode_CacheNode(Disk->CacheID, &retNode);
+}
--- /dev/null
+/*\r
+ * Acess OS\r
+ * Ext2 Driver Version 1\r
+ */\r
+/**\r
+ * \file fs/ext2.c\r
+ * \brief Second Extended Filesystem Driver\r
+ * \todo Implement file full write support\r
+ */\r
+#define DEBUG 1\r
+#define VERBOSE 0\r
+#include "ext2_common.h"\r
+#include <modules.h>\r
+\r
+// === IMPORTS ===\r
+extern tVFS_NodeType gExt2_DirType;\r
+\r
+// === PROTOTYPES ===\r
+ int Ext2_Install(char **Arguments);\r
+// Interface Functions\r
+tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options);\r
+void Ext2_Unmount(tVFS_Node *Node);\r
+void Ext2_CloseFile(tVFS_Node *Node);\r
+// Internal Helpers\r
+ int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);\r
+Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
+Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);\r
+void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);\r
+\r
+// === SEMI-GLOBALS ===\r
+MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL);\r
+tExt2_Disk gExt2_disks[6];\r
+ int giExt2_count = 0;\r
+tVFS_Driver gExt2_FSInfo = {\r
+ "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL\r
+ };\r
+\r
+// === CODE ===\r
+/**\r
+ * \fn int Ext2_Install(char **Arguments)\r
+ * \brief Install the Ext2 Filesystem Driver\r
+ */\r
+int Ext2_Install(char **Arguments)\r
+{\r
+ VFS_AddDriver( &gExt2_FSInfo );\r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ \brief Initializes a device to be read by by the driver\r
+ \param Device String - Device to read from\r
+ \param Options NULL Terminated array of option strings\r
+ \return Root Node\r
+*/\r
+tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
+{\r
+ tExt2_Disk *disk;\r
+ int fd;\r
+ int groupCount;\r
+ tExt2_SuperBlock sb;\r
+ tExt2_Inode inode;\r
+ \r
+ ENTER("sDevice pOptions", Device, Options);\r
+ \r
+ // Open Disk\r
+ fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); //Open Device\r
+ if(fd == -1) {\r
+ Log_Warning("EXT2", "Unable to open '%s'", Device);\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Read Superblock at offset 1024\r
+ VFS_ReadAt(fd, 1024, 1024, &sb); // Read Superblock\r
+ \r
+ // Sanity Check Magic value\r
+ if(sb.s_magic != 0xEF53) {\r
+ Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",\r
+ Device, sb.s_magic);\r
+ VFS_Close(fd);\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Get Group count\r
+ groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
+ LOG("groupCount = %i", groupCount);\r
+ \r
+ // Allocate Disk Information\r
+ disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
+ if(!disk) {\r
+ Log_Warning("EXT2", "Unable to allocate disk structure");\r
+ VFS_Close(fd);\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ disk->FD = fd;\r
+ memcpy(&disk->SuperBlock, &sb, 1024);\r
+ disk->GroupCount = groupCount;\r
+ \r
+ // Get an inode cache handle\r
+ disk->CacheID = Inode_GetHandle();\r
+ \r
+ // Get Block Size\r
+ LOG("s_log_block_size = 0x%x", sb.s_log_block_size);\r
+ disk->BlockSize = 1024 << sb.s_log_block_size;\r
+ \r
+ // Read Group Information\r
+ VFS_ReadAt(\r
+ disk->FD,\r
+ sb.s_first_data_block * disk->BlockSize + 1024,\r
+ sizeof(tExt2_Group)*groupCount,\r
+ disk->Groups\r
+ );\r
+ \r
+ #if VERBOSE\r
+ LOG("Block Group 0");\r
+ LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
+ LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
+ LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
+ LOG("Block Group 1");\r
+ LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
+ LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
+ LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
+ #endif\r
+ \r
+ // Get root Inode\r
+ Ext2_int_ReadInode(disk, 2, &inode);\r
+ \r
+ // Create Root Node\r
+ memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
+ disk->RootNode.Inode = 2; // Root inode ID\r
+ disk->RootNode.ImplPtr = disk; // Save disk pointer\r
+ disk->RootNode.Size = -1; // Fill in later (on readdir)\r
+ disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
+\r
+ disk->RootNode.Type = &gExt2_DirType;\r
+ \r
+ // Complete root node\r
+ disk->RootNode.UID = inode.i_uid;\r
+ disk->RootNode.GID = inode.i_gid;\r
+ disk->RootNode.NumACLs = 1;\r
+ disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;\r
+ \r
+ #if DEBUG\r
+ LOG("inode.i_size = 0x%x", inode.i_size);\r
+ LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);\r
+ #endif\r
+ \r
+ LEAVE('p', &disk->RootNode);\r
+ return &disk->RootNode;\r
+}\r
+\r
+/**\r
+ * \fn void Ext2_Unmount(tVFS_Node *Node)\r
+ * \brief Close a mounted device\r
+ */\r
+void Ext2_Unmount(tVFS_Node *Node)\r
+{\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ \r
+ VFS_Close( disk->FD );\r
+ Inode_ClearCache( disk->CacheID );\r
+ memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));\r
+ free(disk);\r
+}\r
+\r
+/**\r
+ * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
+ * \brief Close a file (Remove it from the cache)\r
+ */\r
+void Ext2_CloseFile(tVFS_Node *Node)\r
+{\r
+ tExt2_Disk *disk = Node->ImplPtr;\r
+ Inode_UncacheNode(disk->CacheID, Node->Inode);\r
+ return ;\r
+}\r
+\r
+//==================================\r
+//= INTERNAL FUNCTIONS =\r
+//==================================\r
+/**\r
+ * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
+ * \brief Read an inode into memory\r
+ */\r
+int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
+{\r
+ int group, subId;\r
+ \r
+ ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
+ \r
+ if(InodeId == 0) return 0;\r
+ \r
+ InodeId --; // Inodes are numbered starting at 1\r
+ \r
+ group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
+ subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
+ \r
+ LOG("group=%i, subId = %i", group, subId);\r
+ \r
+ // Read Inode\r
+ VFS_ReadAt(Disk->FD,\r
+ Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
+ sizeof(tExt2_Inode),\r
+ Inode);\r
+ \r
+ LEAVE('i', 1);\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \brief Write a modified inode out to disk\r
+ */\r
+int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
+{\r
+ int group, subId;\r
+ ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
+ \r
+ if(InodeId == 0) {\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ \r
+ InodeId --; // Inodes are numbered starting at 1\r
+ \r
+ group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
+ subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
+ \r
+ LOG("group=%i, subId = %i", group, subId);\r
+ \r
+ // Write Inode\r
+ VFS_WriteAt(Disk->FD,\r
+ Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
+ sizeof(tExt2_Inode),\r
+ Inode\r
+ );\r
+ \r
+ LEAVE('i', 1);\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
+ * \brief Get the address of a block from an inode's list\r
+ * \param Disk Disk information structure\r
+ * \param Blocks Pointer to an inode's block list\r
+ * \param BlockNum Block index in list\r
+ */\r
+Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
+{\r
+ Uint32 *iBlocks;\r
+ int dwPerBlock = Disk->BlockSize / 4;\r
+ \r
+ // Direct Blocks\r
+ if(BlockNum < 12)\r
+ return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
+ \r
+ // Single Indirect Blocks\r
+ iBlocks = malloc( Disk->BlockSize );\r
+ VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ \r
+ BlockNum -= 12;\r
+ if(BlockNum < dwPerBlock)\r
+ {\r
+ BlockNum = iBlocks[BlockNum];\r
+ free(iBlocks);\r
+ return (Uint64)BlockNum * Disk->BlockSize;\r
+ }\r
+ \r
+ BlockNum -= dwPerBlock;\r
+ // Double Indirect Blocks\r
+ if(BlockNum < dwPerBlock*dwPerBlock)\r
+ {\r
+ VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ BlockNum = iBlocks[BlockNum%dwPerBlock];\r
+ free(iBlocks);\r
+ return (Uint64)BlockNum * Disk->BlockSize;\r
+ }\r
+ \r
+ BlockNum -= dwPerBlock*dwPerBlock;\r
+ // Triple Indirect Blocks\r
+ VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
+ BlockNum = iBlocks[BlockNum%dwPerBlock];\r
+ free(iBlocks);\r
+ return (Uint64)BlockNum * Disk->BlockSize;\r
+}\r
+\r
+/**\r
+ * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
+ * \brief Allocate an inode (from the current group preferably)\r
+ * \param Disk EXT2 Disk Information Structure\r
+ * \param Parent Inode ID of the parent (used to locate the child nearby)\r
+ */\r
+Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
+{\r
+// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
+ Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
+ * \brief Updates the superblock\r
+ */\r
+void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
+{\r
+ int bpg = Disk->SuperBlock.s_blocks_per_group;\r
+ int ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
+ int i;\r
+ \r
+ // Update Primary\r
+ VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
+ \r
+ // Secondaries\r
+ // at Block Group 1, 3^n, 5^n, 7^n\r
+ \r
+ // 1\r
+ if(ngrp <= 1) return;\r
+ VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+ \r
+ #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)\r
+ \r
+ // Powers of 3\r
+ for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )\r
+ VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+ \r
+ // Powers of 5\r
+ for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )\r
+ VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+ \r
+ // Powers of 7\r
+ for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )\r
+ VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
+}\r
--- /dev/null
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file ext2_common.h
+ * \brief Second Extended Filesystem Driver
+ */
+#ifndef _EXT2_COMMON_H
+#define _EXT2_COMMON_H
+#include <acess.h>
+#include <vfs.h>
+#include "ext2fs.h"
+
+#define EXT2_UPDATE_WRITEBACK 1
+
+// === STRUCTURES ===
+typedef struct {
+ int FD;
+ int CacheID;
+ tVFS_Node RootNode;
+
+ tExt2_SuperBlock SuperBlock;
+ Uint BlockSize;
+
+ int GroupCount;
+ tExt2_Group Groups[];
+} tExt2_Disk;
+
+// === FUNCTIONS ===
+// --- Common ---
+extern void Ext2_CloseFile(tVFS_Node *Node);
+extern Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
+extern void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);
+extern int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode);
+extern int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode);
+// --- Dir ---
+extern char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
+extern tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
+extern int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+extern int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name);
+// --- Read ---
+extern Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
+// --- Write ---
+extern Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, const void *buffer);
+
+#endif
--- /dev/null
+/**\r
+ * Acess2\r
+ * \file ext2fs.h\r
+ * \brief EXT2 Filesystem Driver\r
+ */\r
+#ifndef _EXT2FS_H_\r
+#define _EXT2FS_H_\r
+\r
+/**\r
+ \name Inode Flag Values\r
+ \{\r
+*/\r
+#define EXT2_S_IFMT 0xF000 //!< Format Mask\r
+#define EXT2_S_IFSOCK 0xC000 //!< Socket\r
+#define EXT2_S_IFLNK 0xA000 //!< Symbolic Link\r
+#define EXT2_S_IFREG 0x8000 //!< Regular File\r
+#define EXT2_S_IFBLK 0x6000 //!< Block Device\r
+#define EXT2_S_IFDIR 0x4000 //!< Directory\r
+#define EXT2_S_IFCHR 0x2000 //!< Character Device\r
+#define EXT2_S_IFIFO 0x1000 //!< FIFO\r
+#define EXT2_S_ISUID 0x0800 //!< SUID\r
+#define EXT2_S_ISGID 0x0400 //!< SGID\r
+#define EXT2_S_ISVTX 0x0200 //!< sticky bit\r
+#define EXT2_S_IRWXU 0700 //!< user access rights mask\r
+#define EXT2_S_IRUSR 0400 //!< Owner Read\r
+#define EXT2_S_IWUSR 0200 //!< Owner Write\r
+#define EXT2_S_IXUSR 0100 //!< Owner Execute\r
+#define EXT2_S_IRWXG 0070 //!< Group Access rights mask\r
+#define EXT2_S_IRGRP 0040 //!< Group Read\r
+#define EXT2_S_IWGRP 0020 //!< Group Write\r
+#define EXT2_S_IXGRP 0010 //!< Group Execute\r
+#define EXT2_S_IRWXO 0007 //!< Global Access rights mask\r
+#define EXT2_S_IROTH 0004 //!< Global Read\r
+#define EXT2_S_IWOTH 0002 //!< Global Write\r
+#define EXT2_S_IXOTH 0001 //!< Global Execute\r
+//! \}\r
+\r
+#define EXT2_NAME_LEN 255 //!< Maximum Name Length\r
+\r
+// === TYPEDEFS ===\r
+typedef struct ext2_inode_s tExt2_Inode; //!< Inode Type\r
+typedef struct ext2_super_block_s tExt2_SuperBlock; //!< Superblock Type\r
+typedef struct ext2_group_desc_s tExt2_Group; //!< Group Descriptor Type\r
+typedef struct ext2_dir_entry_s tExt2_DirEnt; //!< Directory Entry Type\r
+\r
+// === STRUCTURES ===\r
+/**\r
+ * \brief EXT2 Superblock Structure\r
+ */\r
+struct ext2_super_block_s {\r
+ Uint32 s_inodes_count; //!< Inodes count\r
+ Uint32 s_blocks_count; //!< Blocks count\r
+ Uint32 s_r_blocks_count; //!< Reserved blocks count\r
+ Uint32 s_free_blocks_count; //!< Free blocks count\r
+ \r
+ Uint32 s_free_inodes_count; //!< Free inodes count\r
+ Uint32 s_first_data_block; //!< First Data Block\r
+ Uint32 s_log_block_size; //!< Block size\r
+ Sint32 s_log_frag_size; //!< Fragment size\r
+ \r
+ Uint32 s_blocks_per_group; //!< Number Blocks per group\r
+ Uint32 s_frags_per_group; //!< Number Fragments per group\r
+ Uint32 s_inodes_per_group; //!< Number Inodes per group\r
+ Uint32 s_mtime; //!< Mount time\r
+ \r
+ Uint32 s_wtime; //!< Write time\r
+ Uint16 s_mnt_count; //!< Mount count\r
+ Sint16 s_max_mnt_count; //!< Maximal mount count\r
+ Uint16 s_magic; //!< Magic signature\r
+ Uint16 s_state; //!< File system state\r
+ Uint16 s_errors; //!< Behaviour when detecting errors\r
+ Uint16 s_pad; //!< Padding\r
+ \r
+ Uint32 s_lastcheck; //!< time of last check\r
+ Uint32 s_checkinterval; //!< max. time between checks\r
+ Uint32 s_creator_os; //!< Formatting OS\r
+ Uint32 s_rev_level; //!< Revision level\r
+ \r
+ Uint16 s_def_resuid; //!< Default uid for reserved blocks\r
+ Uint16 s_def_resgid; //!< Default gid for reserved blocks\r
+ Uint32 s_reserved[235]; //!< Padding to the end of the block\r
+};\r
+\r
+/**\r
+ * \struct ext2_inode_s\r
+ * \brief EXT2 Inode Definition\r
+ */\r
+struct ext2_inode_s {\r
+ Uint16 i_mode; //!< File mode\r
+ Uint16 i_uid; //!< Owner Uid\r
+ Uint32 i_size; //!< Size in bytes\r
+ Uint32 i_atime; //!< Access time\r
+ Uint32 i_ctime; //!< Creation time\r
+ Uint32 i_mtime; //!< Modification time\r
+ Uint32 i_dtime; //!< Deletion Time\r
+ Uint16 i_gid; //!< Group Id\r
+ Uint16 i_links_count; //!< Links count\r
+ Uint32 i_blocks; //!< Number of blocks allocated for the file\r
+ Uint32 i_flags; //!< File flags\r
+ union {\r
+ Uint32 linux_reserved1; //!< Linux: Reserved\r
+ Uint32 hurd_translator; //!< HURD: Translator\r
+ Uint32 masix_reserved1; //!< Masix: Reserved\r
+ } osd1; //!< OS dependent 1\r
+ Uint32 i_block[15]; //!< Pointers to blocks\r
+ Uint32 i_version; //!< File version (for NFS)\r
+ Uint32 i_file_acl; //!< File ACL\r
+ Uint32 i_dir_acl; //!< Directory ACL / Extended File Size\r
+ Uint32 i_faddr; //!< Fragment address\r
+ union {\r
+ struct {\r
+ Uint8 l_i_frag; //!< Fragment number\r
+ Uint8 l_i_fsize; //!< Fragment size\r
+ Uint16 i_pad1; //!< Padding\r
+ Uint32 l_i_reserved2[2]; //!< Reserved\r
+ } linux2;\r
+ struct {\r
+ Uint8 h_i_frag; //!< Fragment number\r
+ Uint8 h_i_fsize; //!< Fragment size\r
+ Uint16 h_i_mode_high; //!< Mode High Bits\r
+ Uint16 h_i_uid_high; //!< UID High Bits\r
+ Uint16 h_i_gid_high; //!< GID High Bits\r
+ Uint32 h_i_author; //!< Creator ID\r
+ } hurd2;\r
+ struct {\r
+ Uint8 m_i_frag; //!< Fragment number\r
+ Uint8 m_i_fsize; //!< Fragment size\r
+ Uint16 m_pad1; //!< Padding\r
+ Uint32 m_i_reserved2[2]; //!< reserved\r
+ } masix2;\r
+ } osd2; //!< OS dependent 2\r
+};\r
+\r
+/**\r
+ * \struct ext2_group_desc_s\r
+ * \brief EXT2 Group Descriptor\r
+ */\r
+struct ext2_group_desc_s {\r
+ Uint32 bg_block_bitmap; //!< Blocks bitmap block\r
+ Uint32 bg_inode_bitmap; //!< Inodes bitmap block\r
+ Uint32 bg_inode_table; //!< Inodes table block\r
+ Uint16 bg_free_blocks_count; //!< Free blocks count\r
+ Uint16 bg_free_inodes_count; //!< Free inodes count\r
+ Uint16 bg_used_dirs_count; //!< Directories count\r
+ Uint16 bg_pad; //!< Padding\r
+ Uint32 bg_reserved[3]; //!< Reserved\r
+};\r
+\r
+/**\r
+ * \brief EXT2 Directory Entry\r
+ * \note The name may take up less than 255 characters\r
+ */\r
+struct ext2_dir_entry_s {\r
+ Uint32 inode; //!< Inode number\r
+ Uint16 rec_len; //!< Directory entry length\r
+ Uint8 name_len; //!< Short Name Length\r
+ Uint8 type; //!< File Type (Duplicate of ext2_inode_s.i_mode)\r
+ char name[EXT2_NAME_LEN+1]; //!< File name\r
+};\r
+#define EXT2_DIRENT_SIZE (sizeof(struct ext2_dir_entry_s)-EXT2_NAME_LEN+1)\r
+\r
+#endif\r
--- /dev/null
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file read.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG 1
+#define VERBOSE 0
+#include "ext2_common.h"
+
+// === PROTOTYPES ===
+Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
+
+// === CODE ===
+/**
+ * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from a file
+ */
+Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tExt2_Disk *disk = Node->ImplPtr;
+ tExt2_Inode inode;
+ Uint64 base;
+ Uint block;
+ Uint64 remLen;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ // Get Inode
+ Ext2_int_ReadInode(disk, Node->Inode, &inode);
+
+ // Sanity Checks
+ if(Offset >= inode.i_size) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ if(Offset + Length > inode.i_size)
+ Length = inode.i_size - Offset;
+
+ block = Offset / disk->BlockSize;
+ Offset = Offset / disk->BlockSize;
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ if(base == 0) {
+ Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Read only block
+ if(Length <= disk->BlockSize - Offset)
+ {
+ VFS_ReadAt( disk->FD, base+Offset, Length, Buffer);
+ LEAVE('X', Length);
+ return Length;
+ }
+
+ // Read first block
+ remLen = Length;
+ VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer);
+ remLen -= disk->BlockSize - Offset;
+ Buffer += disk->BlockSize - Offset;
+ block ++;
+
+ // Read middle blocks
+ while(remLen > disk->BlockSize)
+ {
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ if(base == 0) {
+ Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
+ LEAVE('i', 0);
+ return 0;
+ }
+ VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer);
+ Buffer += disk->BlockSize;
+ remLen -= disk->BlockSize;
+ block ++;
+ }
+
+ // Read last block
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ VFS_ReadAt( disk->FD, base, remLen, Buffer);
+
+ LEAVE('X', Length);
+ return Length;
+}
--- /dev/null
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file write.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG 1
+#define VERBOSE 0
+#include "ext2_common.h"
+
+// === PROTOYPES ===
+Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);
+void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block);
+ int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block);
+
+// === CODE ===
+/**
+ * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Write to a file
+ */
+Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tExt2_Disk *disk = Node->ImplPtr;
+ tExt2_Inode inode;
+ Uint64 base;
+ Uint64 retLen;
+ Uint block;
+ Uint64 allocSize;
+ int bNewBlocks = 0;
+
+ Debug_HexDump("Ext2_Write", Buffer, Length);
+
+ Ext2_int_ReadInode(disk, Node->Inode, &inode);
+
+ // Get the ammount of space already allocated
+ // - Round size up to block size
+ // - block size is a power of two, so this will work
+ allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
+
+ // Are we writing to inside the allocated space?
+ if( Offset > allocSize ) return 0;
+
+ if( Offset < allocSize )
+ {
+ // Will we go out of it?
+ if(Offset + Length > allocSize) {
+ bNewBlocks = 1;
+ retLen = allocSize - Offset;
+ } else
+ retLen = Length;
+
+ // Within the allocated space
+ block = Offset / disk->BlockSize;
+ Offset %= disk->BlockSize;
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+
+ // Write only block (if only one)
+ if(Offset + retLen <= disk->BlockSize) {
+ VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
+ if(!bNewBlocks) return Length;
+ goto addBlocks; // Ugh! A goto, but it seems unavoidable
+ }
+
+ // Write First Block
+ VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
+ Buffer += disk->BlockSize-Offset;
+ retLen -= disk->BlockSize-Offset;
+ block ++;
+
+ // Write middle blocks
+ while(retLen > disk->BlockSize)
+ {
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
+ Buffer += disk->BlockSize;
+ retLen -= disk->BlockSize;
+ block ++;
+ }
+
+ // Write last block
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+ VFS_WriteAt(disk->FD, base, retLen, Buffer);
+ if(!bNewBlocks) return Length; // Writing in only allocated space
+ }
+ else
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
+
+addBlocks:
+ Log_Notice("EXT2", "File extending is untested");
+
+ // Allocate blocks and copy data to them
+ retLen = Length - (allocSize-Offset);
+ while( retLen > disk->BlockSize )
+ {
+ // Allocate a block
+ block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
+ if(!block) return Length - retLen;
+ // Add it to this inode
+ if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
+ Ext2_int_DeallocateBlock(disk, block);
+ goto ret;
+ }
+ // Copy data to the node
+ base = block * disk->BlockSize;
+ VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
+ // Update pointer and size remaining
+ inode.i_size += disk->BlockSize;
+ Buffer += disk->BlockSize;
+ retLen -= disk->BlockSize;
+ }
+ // Last block :D
+ block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
+ if(!block) goto ret;
+ if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
+ Ext2_int_DeallocateBlock(disk, block);
+ goto ret;
+ }
+ base = block * disk->BlockSize;
+ VFS_WriteAt(disk->FD, base, retLen, Buffer);
+ inode.i_size += retLen;
+ retLen = 0;
+
+ret: // Makes sure the changes to the inode are committed
+ Ext2_int_WriteInode(disk, Node->Inode, &inode);
+ return Length - retLen;
+}
+
+/**
+ * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
+ * \brief Allocate a block from the best possible location
+ * \param Disk EXT2 Disk Information Structure
+ * \param PrevBlock Previous block ID in the file
+ */
+Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
+{
+ int bpg = Disk->SuperBlock.s_blocks_per_group;
+ Uint blockgroup = PrevBlock / bpg;
+ Uint bitmap[Disk->BlockSize/sizeof(Uint)];
+ Uint bitsperblock = 8*Disk->BlockSize;
+ int i, j = 0;
+ Uint block;
+
+ // Are there any free blocks?
+ if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
+
+ if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
+ {
+ // Search block group's bitmap
+ for(i = 0; i < bpg; i++)
+ {
+ // Get the block in the bitmap block
+ j = i & (bitsperblock-1);
+
+ // Read in if needed
+ if(j == 0) {
+ VFS_ReadAt(
+ Disk->FD,
+ (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
+ Disk->BlockSize,
+ bitmap
+ );
+ }
+
+ // Fast Check
+ if( bitmap[j/32] == 0xFFFFFFFF ) {
+ j = (j + 31) & ~31;
+ continue;
+ }
+
+ // Is the bit set?
+ if( bitmap[j/32] & (1 << (j%32)) )
+ continue;
+
+ // Ooh! We found one
+ break;
+ }
+ if( i < bpg ) {
+ Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
+ goto checkAll; // Search the entire filesystem for a free block
+ // Goto needed for neatness
+ }
+
+ // Mark as used
+ bitmap[j/32] |= (1 << (j%32));
+ VFS_WriteAt(
+ Disk->FD,
+ (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
+ Disk->BlockSize,
+ bitmap
+ );
+ block = i;
+ Disk->Groups[blockgroup].bg_free_blocks_count --;
+ #if EXT2_UPDATE_WRITEBACK
+ //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
+ #endif
+ }
+ else
+ {
+ checkAll:
+ Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
+ return 0;
+ }
+
+ // Reduce global count
+ Disk->SuperBlock.s_free_blocks_count --;
+ #if EXT2_UPDATE_WRITEBACK
+ Ext2_int_UpdateSuperblock(Disk);
+ #endif
+
+ return block;
+}
+
+/**
+ * \brief Deallocates a block
+ */
+void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
+{
+}
+
+/**
+ * \brief Append a block to an inode
+ */
+int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
+{
+ int nBlocks;
+ int dwPerBlock = Disk->BlockSize / 4;
+ Uint32 *blocks;
+ Uint32 id1, id2;
+
+ nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
+
+ // Direct Blocks
+ if( nBlocks < 12 ) {
+ Inode->i_block[nBlocks] = Block;
+ return 0;
+ }
+
+ blocks = malloc( Disk->BlockSize );
+ if(!blocks) return 1;
+
+ nBlocks -= 12;
+ // Single Indirect
+ if( nBlocks < dwPerBlock)
+ {
+ // Allocate/Get Indirect block
+ if( nBlocks == 0 ) {
+ Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
+ if( !Inode->i_block[12] ) {
+ free(blocks);
+ return 1;
+ }
+ memset(blocks, 0, Disk->BlockSize);
+ }
+ else
+ VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
+
+ blocks[nBlocks] = Block;
+
+ VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
+ free(blocks);
+ return 0;
+ }
+
+ nBlocks += dwPerBlock;
+ // Double Indirect
+ if( nBlocks < dwPerBlock*dwPerBlock )
+ {
+ // Allocate/Get Indirect block
+ if( nBlocks == 0 ) {
+ Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
+ if( !Inode->i_block[13] ) {
+ free(blocks);
+ return 1;
+ }
+ memset(blocks, 0, Disk->BlockSize);
+ }
+ else
+ VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
+
+ // Allocate / Get Indirect lvl2 Block
+ if( nBlocks % dwPerBlock == 0 ) {
+ id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
+ if( !id1 ) {
+ free(blocks);
+ return 1;
+ }
+ blocks[nBlocks/dwPerBlock] = id1;
+ // Write back indirect 1 block
+ VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
+ memset(blocks, 0, Disk->BlockSize);
+ }
+ else {
+ id1 = blocks[nBlocks / dwPerBlock];
+ VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
+ }
+
+ blocks[nBlocks % dwPerBlock] = Block;
+
+ VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
+ free(blocks);
+ return 0;
+ }
+
+ nBlocks -= dwPerBlock*dwPerBlock;
+ // Triple Indirect
+ if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
+ {
+ // Allocate/Get Indirect block
+ if( nBlocks == 0 ) {
+ Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
+ if( !Inode->i_block[14] ) {
+ free(blocks);
+ return 1;
+ }
+ memset(blocks, 0, Disk->BlockSize);
+ }
+ else
+ VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
+
+ // Allocate / Get Indirect lvl2 Block
+ if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
+ {
+ id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
+ if( !id1 ) {
+ free(blocks);
+ return 1;
+ }
+ blocks[nBlocks/dwPerBlock] = id1;
+ // Write back indirect 1 block
+ VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
+ memset(blocks, 0, Disk->BlockSize);
+ }
+ else {
+ id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
+ VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
+ }
+
+ // Allocate / Get Indirect Level 3 Block
+ if( nBlocks % dwPerBlock == 0 ) {
+ id2 = Ext2_int_AllocateBlock(Disk, id1);
+ if( !id2 ) {
+ free(blocks);
+ return 1;
+ }
+ blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
+ // Write back indirect 1 block
+ VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
+ memset(blocks, 0, Disk->BlockSize);
+ }
+ else {
+ id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
+ VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
+ }
+
+ blocks[nBlocks % dwPerBlock] = Block;
+
+ VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
+ free(blocks);
+ return 0;
+ }
+
+ Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");
+ free(blocks);
+ return 1;
+}
--- /dev/null
+#
+#
+
+OBJ = fat.o
+NAME = FAT
+
+-include ../Makefile.tpl
--- /dev/null
+/*\r
+ * Acess 2\r
+ * FAT12/16/32 Driver Version (Incl LFN)\r
+ * \r
+ * NOTE: This driver will only support _reading_ long file names, not\r
+ * writing. I don't even know why I'm adding write-support. FAT sucks.\r
+ * \r
+ * Known Bugs:\r
+ * - LFN Is buggy in FAT_ReadDir\r
+ * \r
+ * Notes:\r
+ * - There's hard-coded 512 byte sectors everywhere, that needs to be\r
+ * cleaned.\r
+ * - Thread safety is out the window with the write and LFN code\r
+ */\r
+/**\r
+ * \todo Implement changing of the parent directory when a file is written to\r
+ * \todo Implement file creation / deletion\r
+ */\r
+#define DEBUG 0\r
+#define VERBOSE 1\r
+\r
+#define CACHE_FAT 0 //!< Caches the FAT in memory\r
+#define USE_LFN 1 //!< Enables the use of Long File Names\r
+#define SUPPORT_WRITE 0 //!< Enables write support\r
+\r
+#include <acess.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include "fs_fat.h"\r
+\r
+#define FAT_FLAG_DIRTY 0x10000\r
+#define FAT_FLAG_DELETE 0x20000\r
+\r
+// === TYPES ===\r
+#if USE_LFN\r
+/**\r
+ * \brief Long-Filename cache entry\r
+ */\r
+typedef struct sFAT_LFNCacheEnt\r
+{\r
+ int ID;\r
+ // TODO: Handle UTF16 names correctly\r
+ char Data[256];\r
+} tFAT_LFNCacheEnt;\r
+/**\r
+ * \brief Long-Filename cache\r
+ */\r
+typedef struct sFAT_LFNCache\r
+{\r
+ int NumEntries;\r
+ tFAT_LFNCacheEnt Entries[];\r
+} tFAT_LFNCache;\r
+#endif\r
+\r
+// === PROTOTYPES ===\r
+// --- Driver Core\r
+ int FAT_Install(char **Arguments);\r
+tVFS_Node *FAT_InitDevice(const char *device, const char **options);\r
+void FAT_Unmount(tVFS_Node *Node);\r
+// --- Helpers\r
+ int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);\r
+Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);\r
+#if SUPPORT_WRITE\r
+Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);\r
+Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);\r
+#endif\r
+void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);\r
+// --- File IO\r
+Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
+#if SUPPORT_WRITE\r
+void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer);\r
+Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
+#endif\r
+// --- Directory IO\r
+char *FAT_ReadDir(tVFS_Node *Node, int ID);\r
+tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);\r
+tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);\r
+#if SUPPORT_WRITE\r
+ int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);\r
+ int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);\r
+#endif\r
+void FAT_CloseFile(tVFS_Node *node);\r
+\r
+// === Options ===\r
+ int giFAT_MaxCachedClusters = 1024*512/4;\r
+\r
+// === SEMI-GLOBALS ===\r
+MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL);\r
+tFAT_VolInfo gFAT_Disks[8];\r
+ int giFAT_PartCount = 0;\r
+tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL};\r
+tVFS_NodeType gFAT_DirType = {\r
+ .TypeName = "FAT-Dir",\r
+ .ReadDir = FAT_ReadDir,\r
+ .FindDir = FAT_FindDir,\r
+ #if SUPPORT_WRITE\r
+ .MkNod = FAT_Mknod,\r
+ .Relink = FAT_Relink,\r
+ #endif\r
+ .Close = FAT_CloseFile\r
+ };\r
+tVFS_NodeType gFAT_FileType = {\r
+ .TypeName = "FAT-File",\r
+ .Read = FAT_Read,\r
+ #if SUPPORT_WRITE\r
+ .Write = FAT_Write,\r
+ #endif\r
+ .Close = FAT_CloseFile\r
+ };\r
+\r
+// === CODE ===\r
+/**\r
+ * \fn int FAT_Install(char **Arguments)\r
+ * \brief Install the FAT Driver\r
+ */\r
+int FAT_Install(char **Arguments)\r
+{\r
+ VFS_AddDriver( &gFAT_FSInfo );\r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ * \brief Reads the boot sector of a disk and prepares the structures for it\r
+ */\r
+tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)\r
+{\r
+ fat_bootsect *bs;\r
+ int i;\r
+ Uint32 FATSz, RootDirSectors, TotSec;\r
+ tVFS_Node *node = NULL;\r
+ tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];\r
+ \r
+ // Temporary Pointer\r
+ bs = &diskInfo->bootsect;\r
+ \r
+ // Open device and read boot sector\r
+ diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);\r
+ if(diskInfo->fileHandle == -1) {\r
+ Log_Notice("FAT", "Unable to open device '%s'", Device);\r
+ return NULL;\r
+ }\r
+ \r
+ VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);\r
+ \r
+ if(bs->bps == 0 || bs->spc == 0) {\r
+ Log_Notice("FAT", "Error in FAT Boot Sector");\r
+ return NULL;\r
+ }\r
+ \r
+ // FAT Type Determining\r
+ // - From Microsoft FAT Specifcation\r
+ RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;\r
+ \r
+ if(bs->fatSz16 != 0)\r
+ FATSz = bs->fatSz16;\r
+ else\r
+ FATSz = bs->spec.fat32.fatSz32;\r
+ \r
+ if(bs->totalSect16 != 0)\r
+ TotSec = bs->totalSect16;\r
+ else\r
+ TotSec = bs->totalSect32;\r
+ \r
+ diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;\r
+ \r
+ if(diskInfo->ClusterCount < 4085)\r
+ diskInfo->type = FAT12;\r
+ else if(diskInfo->ClusterCount < 65525)\r
+ diskInfo->type = FAT16;\r
+ else\r
+ diskInfo->type = FAT32;\r
+ \r
+ #if VERBOSE\r
+ {\r
+ char *sFatType, *sSize;\r
+ Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;\r
+ \r
+ switch(diskInfo->type)\r
+ {\r
+ case FAT12: sFatType = "FAT12"; break;\r
+ case FAT16: sFatType = "FAT16"; break;\r
+ case FAT32: sFatType = "FAT32"; break;\r
+ default: sFatType = "UNKNOWN"; break;\r
+ }\r
+ if(iSize <= 2*1024) {\r
+ sSize = "KiB";\r
+ }\r
+ else if(iSize <= 2*1024*1024) {\r
+ sSize = "MiB";\r
+ iSize >>= 10;\r
+ }\r
+ else {\r
+ sSize = "GiB";\r
+ iSize >>= 20;\r
+ }\r
+ Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);\r
+ }\r
+ #endif\r
+ \r
+ // Get Name\r
+ if(diskInfo->type == FAT32) {\r
+ for(i=0;i<11;i++)\r
+ diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);\r
+ }\r
+ else {\r
+ for(i=0;i<11;i++)\r
+ diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);\r
+ }\r
+ diskInfo->name[11] = '\0';\r
+ \r
+ // Compute Root directory offset\r
+ if(diskInfo->type == FAT32)\r
+ diskInfo->rootOffset = bs->spec.fat32.rootClust;\r
+ else\r
+ diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;\r
+ \r
+ diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;\r
+ \r
+ //Allow for Caching the FAT\r
+ #if CACHE_FAT\r
+ if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )\r
+ {\r
+ Uint32 Ofs;\r
+ diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);\r
+ if(diskInfo->FATCache == NULL) {\r
+ Log_Warning("FAT", "Heap Exhausted");\r
+ return NULL;\r
+ }\r
+ Ofs = bs->resvSectCount*512;\r
+ if(diskInfo->type == FAT12)\r
+ {\r
+ Uint32 val;\r
+ int j;\r
+ char buf[1536];\r
+ for(i = 0; i < diskInfo->ClusterCount/2; i++) {\r
+ j = i & 511; //%512\r
+ if( j == 0 ) {\r
+ VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);\r
+ Ofs += 3*512;\r
+ }\r
+ val = *((int*)(buf+j*3));\r
+ diskInfo->FATCache[i*2] = val & 0xFFF;\r
+ diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;\r
+ }\r
+ }\r
+ else if(diskInfo->type == FAT16)\r
+ {\r
+ Uint16 buf[256];\r
+ for(i=0;i<diskInfo->ClusterCount;i++) {\r
+ if( (i & 255) == 0 ) {\r
+ VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);\r
+ Ofs += 512;\r
+ }\r
+ diskInfo->FATCache[i] = buf[i&255];\r
+ }\r
+ }\r
+ else if(diskInfo->type == FAT32)\r
+ {\r
+ Uint32 buf[128];\r
+ for(i=0;i<diskInfo->ClusterCount;i++) {\r
+ if( (i & 127) == 0 ) {\r
+ VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);\r
+ Ofs += 512;\r
+ }\r
+ diskInfo->FATCache[i] = buf[i&127];\r
+ }\r
+ }\r
+ LOG("FAT Fully Cached");\r
+ }\r
+ #endif /*CACHE_FAT*/\r
+ \r
+ diskInfo->BytesPerCluster = bs->spc * bs->bps;\r
+ \r
+ // Initalise inode cache for filesystem\r
+ diskInfo->inodeHandle = Inode_GetHandle();\r
+ LOG("Inode Cache handle is %i", diskInfo->inodeHandle);\r
+ \r
+ // == VFS Interface\r
+ node = &diskInfo->rootNode;\r
+ //node->Size = bs->files_in_root;\r
+ node->Size = -1;\r
+ node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster\r
+ node->ImplPtr = diskInfo; // Disk info pointer\r
+ node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag\r
+ \r
+ node->ReferenceCount = 1;\r
+ \r
+ node->UID = 0; node->GID = 0;\r
+ node->NumACLs = 1;\r
+ node->ACLs = &gVFS_ACL_EveryoneRWX;\r
+ node->Flags = VFS_FFLAG_DIRECTORY;\r
+ node->CTime = node->MTime = node->ATime = now();\r
+\r
+ node->Type = &gFAT_DirType; \r
+ \r
+ giFAT_PartCount ++;\r
+ return node;\r
+}\r
+\r
+/**\r
+ * \brief Closes a mount and marks it as free\r
+ * \param Node Mount Root\r
+ * \r
+ * \todo Remove FAT Cache\r
+ * \todo Clear LFN Cache\r
+ * \todo Check that all files are closed and flushed\r
+ */\r
+void FAT_Unmount(tVFS_Node *Node)\r
+{\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ \r
+ // Close Disk Handle\r
+ VFS_Close( disk->fileHandle );\r
+ // Clear Node Cache\r
+ Inode_ClearCache(disk->inodeHandle);\r
+ // Mark as unused\r
+ disk->fileHandle = -2;\r
+ return;\r
+}\r
+\r
+/**\r
+ * \brief Converts an offset in a file into a disk address\r
+ * \param Node File (or directory) node\r
+ * \param Offset Offset in the file\r
+ * \param Addr Return Address\r
+ * \param Cluster Set to the current cluster (or the last one if \a Offset\r
+ * is past EOC) - Not touched if the node is the root\r
+ * directory.\r
+ * \return Zero on success, non-zero on error\r
+ */\r
+int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)\r
+{\r
+ Uint32 cluster;\r
+ Uint64 addr;\r
+ int skip;\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ \r
+ ENTER("pNode XOffset", Node, Offset);\r
+ \r
+ cluster = Node->Inode & 0xFFFFFFF; // Cluster ID\r
+ LOG("cluster = 0x%07x", cluster);\r
+ \r
+ // Do Cluster Skip\r
+ // - Pre FAT32 had a reserved area for the root.\r
+ if( disk->type == FAT32 || cluster != disk->rootOffset )\r
+ {\r
+ skip = Offset / disk->BytesPerCluster;\r
+ LOG("skip = %i", skip);\r
+ // Skip previous clusters\r
+ for(; skip-- ; )\r
+ {\r
+ if(Cluster) *Cluster = cluster;\r
+ cluster = FAT_int_GetFatValue(disk, cluster);\r
+ // Check for end of cluster chain\r
+ if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;}\r
+ }\r
+ if(Cluster) *Cluster = cluster;\r
+ }\r
+ else {\r
+ // Increment by clusters in offset\r
+ cluster += Offset / disk->BytesPerCluster;\r
+ }\r
+ \r
+ LOG("cluster = %08x", cluster);\r
+ \r
+ // Bounds Checking (Used to spot corruption)\r
+ if(cluster > disk->ClusterCount + 2)\r
+ {\r
+ Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",\r
+ cluster, disk->ClusterCount+2);\r
+ LEAVE('i', 1);\r
+ return 1;\r
+ }\r
+ \r
+ // Compute Offsets\r
+ // - Pre FAT32 cluster base (in sectors)\r
+ if( cluster == disk->rootOffset && disk->type != FAT32 ) {\r
+ addr = disk->bootsect.resvSectCount * disk->bootsect.bps;\r
+ addr += cluster * disk->BytesPerCluster;\r
+ }\r
+ else {\r
+ addr = disk->firstDataSect * disk->bootsect.bps;\r
+ addr += (cluster - 2) * disk->BytesPerCluster;\r
+ }\r
+ // In-cluster offset\r
+ addr += Offset % disk->BytesPerCluster;\r
+ \r
+ LOG("addr = 0x%08x", addr);\r
+ *Addr = addr;\r
+ LEAVE('i', 0);\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * ====================\r
+ * FAT Manipulation\r
+ * ====================\r
+ */\r
+/**\r
+ * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
+ * \brief Fetches a value from the FAT\r
+ */\r
+Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
+{\r
+ Uint32 val = 0;\r
+ Uint32 ofs;\r
+ ENTER("pDisk xCluster", Disk, cluster);\r
+ Mutex_Acquire( &Disk->lFAT );\r
+ #if CACHE_FAT\r
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
+ {\r
+ val = Disk->FATCache[cluster];\r
+ if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;\r
+ if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;\r
+ if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;\r
+ }\r
+ else\r
+ {\r
+ #endif\r
+ ofs = Disk->bootsect.resvSectCount*512;\r
+ if(Disk->type == FAT12) {\r
+ VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);\r
+ val = (cluster & 1 ? val>>12 : val & 0xFFF);\r
+ if(val == EOC_FAT12) val = -1;\r
+ } else if(Disk->type == FAT16) {\r
+ VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);\r
+ if(val == EOC_FAT16) val = -1;\r
+ } else {\r
+ VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);\r
+ if(val == EOC_FAT32) val = -1;\r
+ }\r
+ #if CACHE_FAT\r
+ }\r
+ #endif /*CACHE_FAT*/\r
+ Mutex_Release( &Disk->lFAT );\r
+ LEAVE('x', val);\r
+ return val;\r
+}\r
+\r
+#if SUPPORT_WRITE\r
+/**\r
+ * \brief Allocate a new cluster\r
+ */\r
+Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)\r
+{\r
+ Uint32 ret = Previous;\r
+ #if CACHE_FAT\r
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
+ {\r
+ Uint32 eoc;\r
+ \r
+ LOCK(Disk->lFAT);\r
+ for(ret = Previous; ret < Disk->ClusterCount; ret++)\r
+ {\r
+ if(Disk->FATCache[ret] == 0)\r
+ goto append;\r
+ }\r
+ for(ret = 0; ret < Previous; ret++)\r
+ {\r
+ if(Disk->FATCache[ret] == 0)\r
+ goto append;\r
+ }\r
+ \r
+ RELEASE(Disk->lFAT);\r
+ return 0;\r
+ \r
+ append:\r
+ switch(Disk->type)\r
+ {\r
+ case FAT12: eoc = EOC_FAT12; break;\r
+ case FAT16: eoc = EOC_FAT16; break;\r
+ case FAT32: eoc = EOC_FAT32; break;\r
+ default: return 0;\r
+ }\r
+ \r
+ Disk->FATCache[ret] = eoc;\r
+ Disk->FATCache[Previous] = ret;\r
+ \r
+ RELEASE(Disk->lFAT);\r
+ return ret;\r
+ }\r
+ else\r
+ {\r
+ #endif\r
+ Uint32 val;\r
+ Uint32 ofs = Disk->bootsect.resvSectCount*512;\r
+ Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT");\r
+ return 0;\r
+ \r
+ switch(Disk->type)\r
+ {\r
+ case FAT12:\r
+ VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
+ if( Previous & 1 ) {\r
+ val &= 0xFFF000;\r
+ val |= ret;\r
+ }\r
+ else {\r
+ val &= 0xFFF;\r
+ val |= ret<<12;\r
+ }\r
+ VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
+ \r
+ VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
+ if( Cluster & 1 ) {\r
+ val &= 0xFFF000;\r
+ val |= eoc;\r
+ }\r
+ else {\r
+ val &= 0x000FFF;\r
+ val |= eoc<<12;\r
+ }\r
+ VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
+ break;\r
+ case FAT16:\r
+ VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
+ VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);\r
+ break;\r
+ case FAT32:\r
+ VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
+ VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);\r
+ break;\r
+ }\r
+ return ret;\r
+ #if CACHE_FAT\r
+ }\r
+ #endif\r
+}\r
+\r
+/**\r
+ * \brief Free's a cluster\r
+ * \return The original contents of the cluster\r
+ */\r
+Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)\r
+{\r
+ Uint32 ret;\r
+ #if CACHE_FAT\r
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
+ {\r
+ LOCK(Disk->lFAT);\r
+ \r
+ ret = Disk->FATCache[Cluster];\r
+ Disk->FATCache[Cluster] = 0;\r
+ \r
+ RELEASE(Disk->lFAT);\r
+ }\r
+ else\r
+ {\r
+ #endif\r
+ Uint32 val;\r
+ Uint32 ofs = Disk->bootsect.resvSectCount*512;\r
+ LOCK(Disk->lFAT);\r
+ switch(Disk->type)\r
+ {\r
+ case FAT12:\r
+ VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);\r
+ if( Cluster & 1 ) {\r
+ ret = val & 0xFFF0000;\r
+ val &= 0xFFF;\r
+ }\r
+ else {\r
+ ret = val & 0xFFF;\r
+ val &= 0xFFF000;\r
+ }\r
+ VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
+ break;\r
+ case FAT16:\r
+ VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
+ val = 0;\r
+ VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
+ break;\r
+ case FAT32:\r
+ VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
+ val = 0;\r
+ VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
+ break;\r
+ }\r
+ RELEASE(Disk->lFAT);\r
+ #if CACHE_FAT\r
+ }\r
+ #endif\r
+ if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;\r
+ if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;\r
+ if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;\r
+ return ret;\r
+}\r
+#endif\r
+\r
+/*\r
+ * ====================\r
+ * Cluster IO\r
+ * ====================\r
+ */\r
+/**\r
+ * \brief Read a cluster\r
+ * \param Disk Disk (Volume) to read from\r
+ * \param Length Length to read\r
+ * \param Buffer Destination for read data\r
+ */\r
+void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)\r
+{\r
+ ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);\r
+ VFS_ReadAt(\r
+ Disk->fileHandle,\r
+ (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
+ * Disk->bootsect.bps,\r
+ Length,\r
+ Buffer\r
+ );\r
+ LEAVE('-');\r
+}\r
+\r
+/* ====================\r
+ * File IO\r
+ * ====================\r
+ */\r
+/**\r
+ * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)\r
+ * \brief Reads data from a specified file\r
+ */\r
+Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{\r
+ int preSkip, count;\r
+ Uint64 final_bytes;\r
+ int i, cluster, pos;\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ char tmpBuf[disk->BytesPerCluster];\r
+ int bpc = disk->BytesPerCluster;\r
+ \r
+ ENTER("pNode Xoffset Xlength pbuffer", Node, Offset, Length, Buffer);\r
+ \r
+ // Sanity Check offset\r
+ if(Offset > Node->Size) {\r
+ LOG("Seek past EOF (%i > %i)", Offset, Node->Size);\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ \r
+ // Cluster is stored in the low 32-bits of the Inode field\r
+ cluster = Node->Inode & 0xFFFFFFFF;\r
+ \r
+ // Clamp Size\r
+ if(Offset + Length > Node->Size) {\r
+ LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",\r
+ Offset, Length, Node->Size, Node->Size - Offset);\r
+ Length = Node->Size - Offset;\r
+ }\r
+ \r
+ // Skip previous clusters\r
+ preSkip = Offset / bpc;\r
+ Offset %= bpc;\r
+ LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);\r
+ for(i = preSkip; i--; )\r
+ {\r
+ cluster = FAT_int_GetFatValue(disk, cluster);\r
+ if(cluster == -1) {\r
+ Log_Warning("FAT", "Offset is past end of cluster chain mark");\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ // Reading from within one cluster\r
+ if((int)Offset + (int)Length <= bpc)\r
+ {\r
+ LOG("single cluster only");\r
+ FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);\r
+ memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );\r
+ LEAVE('X', Length);\r
+ return Length;\r
+ }\r
+ \r
+ // Align read to a cluster\r
+ if( Offset > 0 )\r
+ {\r
+ pos = bpc - Offset;\r
+ FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);\r
+ memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );\r
+ LOG("pos = %i, Reading the rest of the clusters");\r
+ // Get next cluster in the chain\r
+ cluster = FAT_int_GetFatValue(disk, cluster);\r
+ if(cluster == -1) {\r
+ Log_Warning("FAT", "Read past End of Cluster Chain (Align)");\r
+ LEAVE('X', pos);\r
+ return pos;\r
+ }\r
+ }\r
+ else\r
+ pos = 0;\r
+\r
+ // Get Count of Clusters to read\r
+// count = DivMod64U(Length - pos, bpc, &final_bytes);\r
+ count = (Length - pos) / bpc;\r
+ final_bytes = (Length - pos) % bpc;\r
+ LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);\r
+ \r
+ // Read the rest of the cluster data\r
+ for( ; count; count -- )\r
+ {\r
+ if(cluster == -1) {\r
+ Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");\r
+ LEAVE('X', pos);\r
+ return pos;\r
+ }\r
+ // Read cluster\r
+ FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));\r
+ pos += bpc;\r
+ // Get next cluster in the chain\r
+ cluster = FAT_int_GetFatValue(disk, cluster);\r
+ }\r
+\r
+ if( final_bytes > 0 )\r
+ {\r
+ if(cluster == -1) {\r
+ Log_Warning("FAT", "Read past End of Cluster Chain (Final)");\r
+ LEAVE('X', pos);\r
+ return pos;\r
+ }\r
+ // Read final cluster\r
+ FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );\r
+ memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );\r
+ }\r
+ \r
+ #if DEBUG\r
+ //Debug_HexDump("FAT_Read", Buffer, Length);\r
+ #endif\r
+ \r
+ LEAVE('X', Length);\r
+ return Length;\r
+}\r
+\r
+#if SUPPORT_WRITE\r
+/**\r
+ * \brief Write a cluster to disk\r
+ */\r
+void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)\r
+{\r
+ ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);\r
+ VFS_ReadAt(\r
+ Disk->fileHandle,\r
+ (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
+ * Disk->bootsect.bps,\r
+ Disk->BytesPerCluster,\r
+ Buffer\r
+ );\r
+ LEAVE('-');\r
+}\r
+\r
+/**\r
+ * \brief Write to a file\r
+ * \param Node File Node\r
+ * \param Offset Offset within file\r
+ * \param Length Size of data to write\r
+ * \param Buffer Data source\r
+ */\r
+Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ char tmpBuf[disk->BytesPerCluster];\r
+ int remLength = Length;\r
+ Uint32 cluster, tmpCluster;\r
+ int bNewCluster = 0;\r
+ \r
+ if(Offset > Node->Size) return 0;\r
+ \r
+ // Seek Clusters\r
+ cluster = Node->Inode & 0xFFFFFFFF;\r
+ while( Offset > disk->BytesPerCluster )\r
+ {\r
+ cluster = FAT_int_GetFatValue( disk, cluster );\r
+ if(cluster == -1) {\r
+ Log_Warning("FAT", "EOC Unexpectedly Reached");\r
+ return 0;\r
+ }\r
+ Offset -= disk->BytesPerCluster;\r
+ }\r
+ if( Offset == disk->BytesPerCluster )\r
+ {\r
+ Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);\r
+ if(!tmp) return 0;\r
+ cluster = tmp;\r
+ Offset -= disk->BytesPerCluster;\r
+ }\r
+ \r
+ if( Offset + Length < disk->BytesPerCluster )\r
+ {\r
+ char tmpBuf[disk->BytesPerCluster];\r
+ \r
+ // Read-Modify-Write\r
+ FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
+ memcpy( tmpBuf + Offset, Buffer, Length );\r
+ FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
+ \r
+ return Length;\r
+ }\r
+ \r
+ // Clean up changes within a cluster\r
+ if( Offset )\r
+ { \r
+ // Read-Modify-Write\r
+ FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
+ memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );\r
+ FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
+ \r
+ remLength -= disk->BytesPerCluster - Offset;\r
+ Buffer += disk->BytesPerCluster - Offset;\r
+ \r
+ // Get next cluster (allocating if needed)\r
+ tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
+ if(tmpCluster == -1) {\r
+ tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
+ if( tmpCluster == 0 ) {\r
+ return Length - remLength;\r
+ }\r
+ }\r
+ cluster = tmpCluster;\r
+ }\r
+ \r
+ while( remLength > disk->BytesPerCluster )\r
+ {\r
+ FAT_int_WriteCluster( disk, cluster, Buffer );\r
+ Buffer += disk->BytesPerCluster;\r
+ \r
+ // Get next cluster (allocating if needed)\r
+ tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
+ if(tmpCluster == -1) {\r
+ bNewCluster = 1;\r
+ tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
+ if( tmpCluster == 0 ) {\r
+ return Length - remLength;\r
+ }\r
+ }\r
+ cluster = tmpCluster;\r
+ }\r
+ \r
+ // Finish off\r
+ tmpBuf = malloc( disk->BytesPerCluster );\r
+ if( bNewCluster )\r
+ memset(tmpBuf, 0, disk->BytesPerCluster);\r
+ else\r
+ FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
+ memcpy( tmpBuf, Buffer, remLength );\r
+ FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
+ free( tmpBuf );\r
+ \r
+ return Length;\r
+}\r
+#endif\r
+\r
+/* ====================\r
+ * File Names & Nodes\r
+ * ====================\r
+ */\r
+/**\r
+ * \brief Converts a FAT directory entry name into a proper filename\r
+ * \param dest Destination array (must be at least 13 bytes in size)\r
+ * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT')\r
+ */\r
+void FAT_int_ProperFilename(char *dest, const char *src)\r
+{\r
+ int inpos, outpos;\r
+ \r
+ // Name\r
+ outpos = 0;\r
+ for( inpos = 0; inpos < 8; inpos++ ) {\r
+ if(src[inpos] == ' ') break;\r
+ dest[outpos++] = src[inpos];\r
+ }\r
+ inpos = 8;\r
+ // Check for empty extensions\r
+ if(src[8] != ' ')\r
+ {\r
+ dest[outpos++] = '.';\r
+ for( ; inpos < 11; inpos++) {\r
+ if(src[inpos] == ' ') break;\r
+ dest[outpos++] = src[inpos];\r
+ }\r
+ }\r
+ dest[outpos++] = '\0';\r
+ \r
+ //LOG("dest='%s'", dest);\r
+}\r
+\r
+/**\r
+ * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
+ * \brief Converts either a LFN or a 8.3 Name into a proper name\r
+ * \param ft Pointer to the file's entry in the parent directory\r
+ * \param LongFileName Long file name pointer\r
+ * \return Filename as a heap string\r
+ */\r
+char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
+{\r
+ char *ret;\r
+ ENTER("pft sLongFileName", ft, LongFileName);\r
+ //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);\r
+ #if USE_LFN\r
+ if(LongFileName && LongFileName[0] != '\0')\r
+ { \r
+ ret = strdup(LongFileName);\r
+ }\r
+ else\r
+ {\r
+ #endif\r
+ ret = (char*) malloc(13);\r
+ if( !ret ) {\r
+ Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");\r
+ return NULL;\r
+ }\r
+ FAT_int_ProperFilename(ret, ft->name);\r
+ #if USE_LFN\r
+ }\r
+ #endif\r
+ LEAVE('s', ret);\r
+ return ret;\r
+}\r
+\r
+/**\r
+ * \brief Creates a tVFS_Node structure for a given file entry\r
+ * \param Parent Parent directory VFS node\r
+ * \param Entry File table entry for the new node\r
+ * \param Pos Position in the parent of the new node\r
+ */\r
+tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos)\r
+{\r
+ tVFS_Node node;\r
+ tVFS_Node *ret;\r
+ tFAT_VolInfo *disk = Parent->ImplPtr;\r
+ \r
+ ENTER("pParent pFT", Parent, Entry);\r
+ LOG("disk = %p", disk);\r
+ \r
+ memset(&node, 0, sizeof(tVFS_Node));\r
+ \r
+ // Set Other Data\r
+ // 0-27: Cluster, 32-59: Parent Cluster\r
+ node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);\r
+ LOG("node.Inode = %llx", node.Inode);\r
+ // Position in parent directory\r
+ node.ImplInt = Pos & 0xFFFF;\r
+ // Disk Pointer\r
+ node.ImplPtr = disk;\r
+ node.Size = Entry->size;\r
+ LOG("Entry->size = %i", Entry->size);\r
+ // root:root\r
+ node.UID = 0; node.GID = 0;\r
+ node.NumACLs = 1;\r
+ \r
+ node.Flags = 0;\r
+ if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;\r
+ if(Entry->attrib & ATTR_READONLY) {\r
+ node.Flags |= VFS_FFLAG_READONLY;\r
+ node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X\r
+ }\r
+ else {\r
+ node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX\r
+ }\r
+ \r
+ // Create timestamps\r
+ node.ATime = timestamp(0,0,0,\r
+ ((Entry->adate&0x1F) - 1), // Days\r
+ ((Entry->adate&0x1E0) - 1), // Months\r
+ 1980+((Entry->adate&0xFF00)>>8) // Years\r
+ );\r
+ \r
+ node.CTime = Entry->ctimems * 10; // Miliseconds\r
+ node.CTime += timestamp(\r
+ ((Entry->ctime&0x1F)<<1), // Seconds\r
+ ((Entry->ctime&0x3F0)>>5), // Minutes\r
+ ((Entry->ctime&0xF800)>>11), // Hours\r
+ ((Entry->cdate&0x1F)-1), // Days\r
+ ((Entry->cdate&0x1E0)-1), // Months\r
+ 1980+((Entry->cdate&0xFF00)>>8) // Years\r
+ );\r
+ \r
+ node.MTime = timestamp(\r
+ ((Entry->mtime&0x1F)<<1), // Seconds\r
+ ((Entry->mtime&0x3F0)>>5), // Minutes\r
+ ((Entry->mtime&0xF800)>>11), // Hours\r
+ ((Entry->mdate&0x1F)-1), // Days\r
+ ((Entry->mdate&0x1E0)-1), // Months\r
+ 1980+((Entry->mdate&0xFF00)>>8) // Years\r
+ );\r
+ \r
+ // Set pointers\r
+ if(node.Flags & VFS_FFLAG_DIRECTORY) {\r
+ //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);\r
+ node.Type = &gFAT_DirType; \r
+ node.Size = -1;\r
+ }\r
+ else {\r
+ node.Type = &gFAT_FileType;\r
+ }\r
+ \r
+ ret = Inode_CacheNode(disk->inodeHandle, &node);\r
+ LEAVE('p', ret);\r
+ return ret;\r
+}\r
+\r
+/* \r
+ * ====================\r
+ * Directory IO\r
+ * ====================\r
+ */\r
+\r
+/**\r
+ * \brief Reads a sector from the disk\r
+ * \param Node Directory node to read\r
+ * \param Sector Sector number in the directory to read\r
+ * \param Buffer Destination buffer for the read data\r
+ */\r
+int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)\r
+{\r
+ Uint64 addr;\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ \r
+ ENTER("pNode iSector pEntry", Node, Sector, Buffer);\r
+ \r
+ // Parse address\r
+ if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))\r
+ {\r
+ LEAVE('i', 1);\r
+ return 1;\r
+ }\r
+ \r
+ LOG("addr = 0x%llx", addr);\r
+ // Read Sector\r
+ if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)\r
+ {\r
+ LEAVE('i', 1);\r
+ return 1;\r
+ }\r
+ \r
+ LEAVE('i', 0);\r
+ return 0;\r
+}\r
+\r
+#if SUPPORT_WRITE\r
+/**\r
+ * \brief Writes an entry to the disk\r
+ * \todo Support expanding a directory\r
+ * \param Node Directory node\r
+ * \param ID ID of entry to update\r
+ * \param Entry Entry data\r
+ * \return Zero on success, non-zero on error\r
+ */\r
+int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)\r
+{\r
+ Uint64 addr = 0;\r
+ int tmp;\r
+ Uint32 cluster = 0;\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ \r
+ ENTER("pNode iID pEntry", Node, ID, Entry);\r
+ \r
+ tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
+ if( tmp )\r
+ {\r
+ //TODO: Allocate a cluster\r
+ cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);\r
+ if(cluster == -1) {\r
+ Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);\r
+ LEAVE('i', 1);\r
+ return 1;\r
+ }\r
+ FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
+ }\r
+ \r
+\r
+ LOG("addr = 0x%llx", addr);\r
+ \r
+ // Read Sector\r
+ VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data\r
+ \r
+ LEAVE('i', 0);\r
+ return 0;\r
+}\r
+#endif\r
+\r
+#if USE_LFN \r
+/**\r
+ * \fn char *FAT_int_GetLFN(tVFS_Node *node)\r
+ * \brief Return pointer to LFN cache entry\r
+ * \param Node Directory node\r
+ * \param ID ID of the short name\r
+ * \return Pointer to the LFN cache entry\r
+ */\r
+char *FAT_int_GetLFN(tVFS_Node *Node, int ID)\r
+{\r
+ tFAT_LFNCache *cache;\r
+ int i, firstFree;\r
+ \r
+ Mutex_Acquire( &Node->Lock );\r
+ \r
+ // TODO: Thread Safety (Lock things)\r
+ cache = Node->Data;\r
+ \r
+ // Create a cache if it isn't there\r
+ if(!cache) {\r
+ cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );\r
+ cache->NumEntries = 1;\r
+ cache->Entries[0].ID = ID;\r
+ cache->Entries[0].Data[0] = '\0';\r
+ Mutex_Release( &Node->Lock );\r
+ //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);\r
+ return cache->Entries[0].Data;\r
+ }\r
+ \r
+ // Scan for this entry\r
+ firstFree = -1;\r
+ for( i = 0; i < cache->NumEntries; i++ )\r
+ {\r
+ if( cache->Entries[i].ID == ID ) {\r
+ Mutex_Release( &Node->Lock );\r
+ //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);\r
+ return cache->Entries[i].Data;\r
+ }\r
+ if( cache->Entries[i].ID == -1 && firstFree == -1 )\r
+ firstFree = i;\r
+ }\r
+ \r
+ if(firstFree == -1) {\r
+ // Use `i` for temp length\r
+ i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);\r
+ Node->Data = realloc( Node->Data, i );\r
+ if( !Node->Data ) {\r
+ Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);\r
+ Mutex_Release( &Node->Lock );\r
+ return NULL;\r
+ }\r
+ //Log_Debug("FAT", "Realloc (%i)\n", i);\r
+ cache = Node->Data;\r
+ i = cache->NumEntries;\r
+ cache->NumEntries ++;\r
+ }\r
+ else {\r
+ i = firstFree;\r
+ }\r
+ \r
+ // Create new entry\r
+ cache->Entries[ i ].ID = ID;\r
+ cache->Entries[ i ].Data[0] = '\0';\r
+ \r
+ Mutex_Release( &Node->Lock );\r
+ //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);\r
+ return cache->Entries[ i ].Data;\r
+}\r
+\r
+/**\r
+ * \fn void FAT_int_DelLFN(tVFS_Node *node)\r
+ * \brief Delete a LFN cache entry\r
+ * \param Node Directory node\r
+ * \param ID File Entry ID\r
+ */\r
+void FAT_int_DelLFN(tVFS_Node *Node, int ID)\r
+{\r
+ tFAT_LFNCache *cache = Node->Data;\r
+ int i;\r
+ \r
+ // Fast return\r
+ if(!cache) return;\r
+ \r
+ // Scan for a current entry\r
+ for( i = 0; i < cache->NumEntries; i++ )\r
+ {\r
+ if( cache->Entries[i].ID == ID )\r
+ cache->Entries[i].ID = -1;\r
+ }\r
+ return ;\r
+}\r
+#endif\r
+\r
+/**\r
+ * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
+ * \param Node Node structure of directory\r
+ * \param ID Directory position\r
+ * \return Filename as a heap string, NULL or VFS_SKIP\r
+ */\r
+char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
+{\r
+ fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector\r
+ int a = 0;\r
+ char *ret;\r
+ #if USE_LFN\r
+ char *lfn = NULL;\r
+ #endif\r
+ \r
+ ENTER("pNode iID", Node, ID);\r
+ \r
+ if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))\r
+ {\r
+ LOG("End of chain, end of dir");\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Offset in sector\r
+ a = ID % 16;\r
+\r
+ LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);\r
+ \r
+ // Check if this is the last entry\r
+ if( fileinfo[a].name[0] == '\0' ) {\r
+ Node->Size = ID;\r
+ LOG("End of list");\r
+ LEAVE('n');\r
+ return NULL; // break\r
+ }\r
+ \r
+ // Check for empty entry\r
+ if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {\r
+ LOG("Empty Entry");\r
+ #if 0 // Stop on empty entry?\r
+ LEAVE('n');\r
+ return NULL; // Stop\r
+ #else\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP; // Skip\r
+ #endif\r
+ }\r
+ \r
+ #if USE_LFN\r
+ // Get Long File Name Cache\r
+ if(fileinfo[a].attrib == ATTR_LFN)\r
+ {\r
+ fat_longfilename *lfnInfo;\r
+ \r
+ lfnInfo = (fat_longfilename *) &fileinfo[a];\r
+ \r
+ // Get cache for corresponding file\r
+ // > ID + Index gets the corresponding short node\r
+ lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );\r
+ \r
+ // Bit 6 indicates the start of an entry\r
+ if(lfnInfo->id & 0x40) memset(lfn, 0, 256);\r
+ \r
+ a = ((lfnInfo->id & 0x3F) - 1) * 13;\r
+ //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a);\r
+ \r
+ // Sanity Check (FAT implementations should not allow >255 character names)\r
+ if(a > 255) return VFS_SKIP;\r
+ \r
+ // Append new bytes\r
+ lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1];\r
+ lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3];\r
+ lfn[a+ 4] = lfnInfo->name1[4]; \r
+ lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1];\r
+ lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3];\r
+ lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5];\r
+ lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1];\r
+ LOG("lfn = '%s'", lfn);\r
+ //Log_Debug("FAT", "lfn = '%s'", lfn);\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ }\r
+ #endif\r
+ \r
+ // Check if it is a volume entry\r
+ if(fileinfo[a].attrib & 0x08) {\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ }\r
+ // Ignore .\r
+ if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ }\r
+ // and ..\r
+ if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ }\r
+ \r
+ LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",\r
+ fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],\r
+ fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],\r
+ fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );\r
+ \r
+ #if USE_LFN\r
+ lfn = FAT_int_GetLFN(Node, ID);\r
+ //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);\r
+ ret = FAT_int_CreateName(&fileinfo[a], lfn);\r
+ #else\r
+ ret = FAT_int_CreateName(&fileinfo[a], NULL);\r
+ #endif\r
+ \r
+ LEAVE('s', ret);\r
+ return ret;\r
+}\r
+\r
+/**\r
+ * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)\r
+ * \brief Finds an entry in the current directory\r
+ */\r
+tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)\r
+{\r
+ fat_filetable fileinfo[16];\r
+ char tmpName[13];\r
+ #if USE_LFN\r
+ fat_longfilename *lfnInfo;\r
+ char lfn[256];\r
+ int lfnPos=255, lfnId = -1;\r
+ #endif\r
+ int i;\r
+ tVFS_Node *tmpNode;\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ Uint32 cluster;\r
+ \r
+ ENTER("pNode sname", Node, Name); \r
+\r
+ // Fast Returns\r
+ if(!Name || Name[0] == '\0') {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ for( i = 0; ; i++ )\r
+ {\r
+ if((i & 0xF) == 0) {\r
+ if(FAT_int_ReadDirSector(Node, i/16, fileinfo))\r
+ {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ }\r
+ \r
+ //Check if the files are free\r
+ if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker\r
+ if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry\r
+ \r
+ \r
+ #if USE_LFN\r
+ // Long File Name Entry\r
+ if(fileinfo[i & 0xF].attrib == ATTR_LFN)\r
+ {\r
+ lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];\r
+ if(lfnInfo->id & 0x40) {\r
+ memset(lfn, 0, 256);\r
+ lfnPos = (lfnInfo->id & 0x3F) * 13 - 1;\r
+ }\r
+ // Sanity check the position so we don't overflow\r
+ if( lfnPos < 12 )\r
+ continue ;\r
+ lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0];\r
+ lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4];\r
+ lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2];\r
+ lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0];\r
+ lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3];\r
+ lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1];\r
+ lfn[lfnPos--] = lfnInfo->name1[0];\r
+ if((lfnInfo->id&0x3F) == 1)\r
+ {\r
+ lfnId = i+1;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Remove LFN if it does not apply\r
+ if(lfnId != i) lfn[0] = '\0';\r
+ #else\r
+ if(fileinfo[i&0xF].attrib == ATTR_LFN) continue;\r
+ #endif\r
+ // Get Real Filename\r
+ FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);\r
+ LOG("tmpName = '%s'", tmpName);\r
+ \r
+ // Only the long name is case sensitive, 8.3 is not\r
+ #if USE_LFN\r
+ if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0)\r
+ #else\r
+ if(strucmp(tmpName, Name) == 0)\r
+ #endif\r
+ {\r
+ cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);\r
+ tmpNode = Inode_GetCache(disk->inodeHandle, cluster);\r
+ if(tmpNode == NULL) // Node is not cached\r
+ {\r
+ tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i);\r
+ }\r
+ LEAVE('p', tmpNode);\r
+ return tmpNode;\r
+ }\r
+ #if USE_LFN\r
+ }\r
+ #endif\r
+ }\r
+ \r
+ LEAVE('n');\r
+ return NULL;\r
+}\r
+\r
+tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)\r
+{\r
+ tFAT_VolInfo *disk = Root->ImplPtr;\r
+ int ents_per_sector = 512 / sizeof(fat_filetable); \r
+ fat_filetable fileinfo[ents_per_sector];\r
+ int sector = 0, i;\r
+ tVFS_Node stub_node;\r
+\r
+ ENTER("pRoot XInode", Root, Inode);\r
+\r
+ stub_node.ImplPtr = disk;\r
+ stub_node.Size = -1;\r
+ stub_node.Inode = Inode >> 32;\r
+\r
+ for( i = 0; ; i ++ )\r
+ {\r
+ if( i == 0 || i == ents_per_sector )\r
+ {\r
+ if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo))\r
+ {\r
+ LOG("ReadDirSector failed");\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ i = 0;\r
+ sector ++;\r
+ }\r
+ \r
+ // Check for free/end of list\r
+ if(fileinfo[i].name[0] == '\0') break; // End of List marker\r
+ if(fileinfo[i].name[0] == '\xE5') continue; // Free entry\r
+ \r
+ if(fileinfo[i].attrib == ATTR_LFN) continue;\r
+\r
+ LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster);\r
+ #if DEBUG\r
+ {\r
+ char tmpName[13];\r
+ FAT_int_ProperFilename(tmpName, fileinfo[i].name);\r
+ LOG("tmpName = '%s'", tmpName);\r
+ }\r
+ #endif\r
+ \r
+ \r
+ if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue;\r
+ if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue;\r
+\r
+ LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i));\r
+ }\r
+ LOG("sector = %i, i = %i", sector, i);\r
+ LEAVE('n');\r
+ return NULL;\r
+}\r
+\r
+#if SUPPORT_WRITE\r
+/**\r
+ * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
+ * \brief Create a new node\r
+ */\r
+int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
+ * \brief Rename / Delete a file\r
+ */\r
+int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
+{\r
+ tVFS_Node *child;\r
+ fat_filetable ft = {0};\r
+ int ret;\r
+ \r
+ child = FAT_FindDir(Node, OldName);\r
+ if(!child) return ENOTFOUND;\r
+ \r
+ // Delete?\r
+ if( NewName == NULL )\r
+ {\r
+ child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close\r
+ \r
+ // Delete from the directory\r
+ ft.name[0] = '\xE9';\r
+ FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft);\r
+ \r
+ // Return success\r
+ ret = EOK;\r
+ }\r
+ // Rename\r
+ else\r
+ {\r
+ Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')",\r
+ Node, OldName, NewName);\r
+ ret = ENOTIMPL;\r
+ }\r
+ \r
+ // Close child\r
+ child->Close( child );\r
+ return ret;\r
+}\r
+#endif\r
+\r
+/**\r
+ * \fn void FAT_CloseFile(tVFS_Node *Node)\r
+ * \brief Close an open file\r
+ */\r
+void FAT_CloseFile(tVFS_Node *Node)\r
+{\r
+ tFAT_VolInfo *disk = Node->ImplPtr;\r
+ if(Node == NULL) return ;\r
+ \r
+ #if SUPPORT_WRITE\r
+ // Update the node if it's dirty (don't bother if it's marked for\r
+ // deletion)\r
+ if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )\r
+ {\r
+ tFAT_VolInfo buf[16];\r
+ tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ];\r
+ \r
+ FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf);\r
+ ft->size = Node->Size;\r
+ // TODO: update adate, mtime, mdate\r
+ FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft);\r
+ \r
+ Node->ImplInt &= ~FAT_FLAG_DIRTY;\r
+ }\r
+ #endif\r
+ \r
+ // TODO: Make this more thread safe somehow, probably by moving the\r
+ // Inode_UncacheNode higher up and saving the cluster value somewhere\r
+ if( Node->ReferenceCount == 1 )\r
+ { \r
+ #if SUPPORT_WRITE\r
+ // Delete File\r
+ if( Node->ImplInt & FAT_FLAG_DELETE ) {\r
+ // Since the node is marked, we only need to remove it's data\r
+ Uint32 cluster = Node->Inode & 0xFFFFFFFF;\r
+ while( cluster != -1 )\r
+ cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster);\r
+ }\r
+ #endif\r
+ }\r
+ \r
+ Inode_UncacheNode(disk->inodeHandle, Node->Inode);\r
+ return ;\r
+}\r
--- /dev/null
+/*\r
+ * Acess2\r
+ * FAT12/16/32 Driver\r
+ * vfs/fs/fs_fat.h\r
+ */\r
+#ifndef _FS_FAT_H_\r
+#define _FS_FAT_H_\r
+\r
+// === On Disk Structures ===\r
+/**\r
+ * \struct fat_bootsect_s\r
+ * \brief Bootsector format\r
+ */\r
+struct fat_bootsect_s\r
+{\r
+ Uint8 jmp[3]; //!< Jump Instruction\r
+ char oemname[8]; //!< OEM Name. Typically MSDOS1.1\r
+ Uint16 bps; //!< Bytes per Sector. Assumed to be 512\r
+ Uint8 spc; //!< Sectors per Cluster\r
+ Uint16 resvSectCount; //!< Number of reserved sectors at beginning of volume\r
+ // +0x10\r
+ Uint8 fatCount; //!< Number of copies of the FAT\r
+ Uint16 files_in_root; //!< Count of files in the root directory\r
+ Uint16 totalSect16; //!< Total sector count (FAT12/16)\r
+ Uint8 mediaDesc; //!< Media Desctiptor\r
+ Uint16 fatSz16; //!< FAT Size (FAT12/16)\r
+ // +0x18\r
+ Uint16 spt; //!< Sectors per track. Ignored (Acess uses LBA)\r
+ Uint16 heads; //!< Heads. Ignored (Acess uses LBA)\r
+ Uint32 hiddenCount; //!< ???\r
+ Uint32 totalSect32; //!< Total sector count (FAT32)\r
+ union {\r
+ struct {\r
+ Uint8 drvNum; //!< Drive Number. BIOS Drive ID (E.g. 0x80)\r
+ Uint8 resv; //!< Reserved byte\r
+ Uint8 bootSig; //!< Boot Signature. ???\r
+ Uint32 volId; //!< Volume ID\r
+ char label[11]; //!< Disk Label\r
+ char fsType[8]; //!< FS Type. ???\r
+ } __attribute__((packed)) fat16; //!< FAT16 Specific information\r
+ struct {\r
+ Uint32 fatSz32; //!< 32-Bit FAT Size\r
+ Uint16 extFlags; //!< Extended flags\r
+ Uint16 fsVer; //!< Filesystem Version\r
+ Uint32 rootClust; //!< Root Cluster ID\r
+ Uint16 fsInfo; //!< FS Info. ???\r
+ Uint16 backupBS; //!< Backup Bootsector Sector Offset\r
+ char resv[12]; //!< Reserved Data\r
+ Uint8 drvNum; //!< Drive Number\r
+ char resv2; //!< Reserved Data\r
+ Uint8 bootSig; //!< Boot Signature. ???\r
+ Uint32 volId; //!< Volume ID\r
+ char label[11]; //!< Disk Label\r
+ char fsType[8]; //!< Filesystem Type. ???\r
+ } __attribute__((packed)) fat32; //!< FAT32 Specific Information\r
+ }__attribute__((packed)) spec; //!< Non Shared Data\r
+ char pad[512-90]; //!< Bootsector Data (Code/Boot Signature 0xAA55)\r
+} __attribute__((packed));\r
+\r
+/**\r
+ \struct fat_filetable_s\r
+ \brief Format of a 8.3 file entry on disk\r
+*/\r
+struct fat_filetable_s {\r
+ char name[11]; //!< 8.3 Name\r
+ Uint8 attrib; //!< File Attributes.\r
+ Uint8 ntres; //!< Reserved for NT - Set to 0\r
+ Uint8 ctimems; //!< 10ths of a second ranging from 0-199 (2 seconds)\r
+ Uint16 ctime; //!< Creation Time\r
+ Uint16 cdate; //!< Creation Date\r
+ Uint16 adate; //!< Accessed Date. No Time feild though\r
+ Uint16 clusterHi; //!< High Cluster. 0 for FAT12 and FAT16\r
+ Uint16 mtime; //!< Last Modified Time\r
+ Uint16 mdate; //!< Last Modified Date\r
+ Uint16 cluster; //!< Low Word of First cluster\r
+ Uint32 size; //!< Size of file\r
+} __attribute__((packed));\r
+\r
+/**\r
+ * \struct fat_longfilename_s\r
+ * \brief Format of a long file name entry on disk\r
+ */\r
+struct fat_longfilename_s {\r
+ Uint8 id; //!< ID of entry. Bit 6 is set for last entry\r
+ Uint16 name1[5]; //!< 5 characters of name\r
+ Uint8 attrib; //!< Attributes. Must be ATTR_LFN\r
+ Uint8 type; //!< Type. ???\r
+ Uint8 checksum; //!< Checksum\r
+ Uint16 name2[6]; //!< 6 characters of name\r
+ Uint16 firstCluster; //!< Used for non LFN compatability. Set to 0\r
+ Uint16 name3[2]; //!< Last 2 characters of name\r
+} __attribute__((packed));\r
+\r
+/**\r
+ * \name File Attributes\r
+ * \brief Flag values for ::fat_filetable_s.attrib\r
+ * \{\r
+ */\r
+#define ATTR_READONLY 0x01 //!< Read-only file\r
+#define ATTR_HIDDEN 0x02 //!< Hidden File\r
+#define ATTR_SYSTEM 0x04 //!< System File\r
+#define ATTR_VOLUMEID 0x08 //!< Volume ID (Deprecated)\r
+#define ATTR_DIRECTORY 0x10 //!< Directory\r
+/**\r
+ * \brief File needs archiving\r
+ * \note User set flag, no significance to the FS driver\r
+ */\r
+#define ATTR_ARCHIVE 0x20\r
+/**\r
+ * \brief Meta Attribute \r
+ * \r
+ * If ::fat_filetable_s.attrib equals ATTR_LFN the file is a LFN entry\r
+ */\r
+#define ATTR_LFN (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUMEID)\r
+/**\r
+ * \}\r
+ */\r
+\r
+/**\r
+ * \brief Internal IDs for FAT types\r
+ */\r
+enum eFatType\r
+{\r
+ FAT12, //!< FAT12 Volume\r
+ FAT16, //!< FAT16 Volume\r
+ FAT32, //!< FAT32 Volume\r
+};\r
+\r
+/**\r
+ * \name End of Cluster marks\r
+ * \brief FAT values that indicate the end of a cluster chain in\r
+ * different versions.\r
+ * \{\r
+ */\r
+#define EOC_FAT12 0x0FFF //!< FAT-12 Mark\r
+#define EOC_FAT16 0xFFFF //!< FAT-16 Mark\r
+#define EOC_FAT32 0x00FFFFFF //!< FAT-32 Mark\r
+/**\r
+ * \}\r
+ */\r
+\r
+typedef struct fat_bootsect_s fat_bootsect;\r
+typedef struct fat_filetable_s fat_filetable;\r
+typedef struct fat_longfilename_s fat_longfilename;\r
+\r
+// === Memory Structures ===\r
+/**\r
+ * \struct drv_fat_volinfo_s\r
+ * \brief Representation of a volume in memory\r
+ */\r
+struct drv_fat_volinfo_s\r
+{\r
+ int fileHandle; //!< File Handle\r
+ int type; //!< FAT Type. See eFatType\r
+ char name[12]; //!< Volume Name (With NULL Terminator)\r
+ tMutex lFAT; //!< Lock to prevent double-writing to the FAT\r
+ Uint32 firstDataSect; //!< First data sector\r
+ Uint32 rootOffset; //!< Root Offset (clusters)\r
+ Uint32 ClusterCount; //!< Total Cluster Count\r
+ fat_bootsect bootsect; //!< Boot Sector\r
+ tVFS_Node rootNode; //!< Root Node\r
+ int BytesPerCluster;\r
+ int inodeHandle; //!< Inode Cache Handle\r
+ #if CACHE_FAT\r
+ Uint32 *FATCache; //!< FAT Cache\r
+ #endif\r
+};\r
+\r
+typedef struct drv_fat_volinfo_s tFAT_VolInfo;\r
+\r
+#endif\r
--- /dev/null
+<?php
+$lGenDate = date("Y-m-d H:i");
+$gOutput = <<<EOF
+/*
+ * Acess2 InitRD
+ * InitRD Data
+ * Generated $lGenDate
+ */
+#include "initrd.h"
+
+EOF;
+
+$ACESSDIR = getenv("ACESSDIR");
+$ARCH = getenv("ARCH");
+
+$gInputFile = $argv[1];
+$gOutputFile = $argv[2];
+$gOutputLDOptsFile = $argv[3];
+$gDepFile = ($argc > 4 ? $argv[4] : false);
+
+$gDependencies = array();
+
+$lines = file($argv[1]);
+
+$lDepth = 0;
+$lTree = array();
+$lStack = array( array("",array()) );
+foreach($lines as $line)
+{
+ $line = trim($line);
+ // Directory
+ if(preg_match('/^Dir\s+"([^"]+)"\s+{$/', $line, $matches))
+ {
+ $new = array($matches[1], array());
+ array_push($lStack, $new);
+ $lDepth ++;
+ continue;
+ }
+ // End of a block
+ if($line == "}")
+ {
+ $lDepth --;
+ $lStack[$lDepth][1][] = array_pop($lStack);
+ continue;
+ }
+ // File
+ if(preg_match('/^File\s+"([^"]+)"\s+"([^"]+)"$/', $line, $matches))
+ {
+ $lStack[$lDepth][1][] = array($matches[1], $matches[2]);
+ continue;
+ }
+ echo "ERROR: $line\n";
+ exit(0);
+}
+
+function hd($fp)
+{
+ //return "0x".str_pad( dechex(ord(fgetc($fp))), 8, "0", STR_PAD_LEFT );
+ $val = unpack("I", fread($fp, 4));
+ //print_r($val); exit -1;
+ return "0x".dechex($val[1]);
+}
+
+function hd8($fp)
+{
+ return "0x".str_pad( dechex(ord(fgetc($fp))), 2, "0", STR_PAD_LEFT );
+}
+
+$inode = 0;
+$gSymFiles = array();
+function ProcessFolder($prefix, $items)
+{
+ global $gOutput, $gDependencies;
+ global $ACESSDIR, $ARCH;
+ global $inode;
+ global $gSymFiles;
+ foreach($items as $i=>$item)
+ {
+ $inode ++;
+ if(is_array($item[1]))
+ {
+ ProcessFolder("{$prefix}_{$i}", $item[1]);
+
+ $gOutput .= "tInitRD_File {$prefix}_{$i}_entries[] = {\n";
+ foreach($item[1] as $j=>$child)
+ {
+ if($j) $gOutput .= ",\n";
+ $gOutput .= "\t{\"".addslashes($child[0])."\",&{$prefix}_{$i}_{$j}}";
+ }
+ $gOutput .= "\n};\n";
+
+ $size = count($item[1]);
+ $gOutput .= <<<EOF
+tVFS_Node {$prefix}_{$i} = {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Size = $size,
+ .Inode = {$inode},
+ .ImplPtr = {$prefix}_{$i}_entries,
+ .Type = &gInitRD_DirType
+};
+
+EOF;
+ }
+ else
+ {
+ $path = $item[1];
+
+ // Parse path components
+ $path = str_replace("__BIN__", "$ACESSDIR/Usermode/Output/$ARCH", $path);
+ $path = str_replace("__FS__", "$ACESSDIR/Usermode/Filesystem", $path);
+ $path = str_replace("__SRC__", "$ACESSDIR", $path);
+ echo $path,"\n";
+ // ---
+
+ $gDependencies[] = $path;
+
+ if(!file_exists($path)) {
+ echo "ERROR: '{$path}' does not exist\n",
+ exit(1);
+ }
+ $size = filesize($path);
+
+/*
+ $_sym = $prefix."_".$i."_data";
+ $fp = fopen($path, "rb");
+
+ $gOutput .= "Uint8 $_sym[] = {\n";
+ for( $j = 0; $j + 16 < $size; $j += 16 ) {
+ $gOutput .= "\t";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",";
+ $gOutput .= hd8($fp).",".hd8($fp).",\n";
+ }
+ $gOutput .= "\t";
+ for( ; $j < $size; $j ++ ) {
+ if( $j & 15 ) $gOutput .= ",";
+ $gOutput .= hd8($fp);
+ }
+ fclose($fp);
+ $gOutput .= "\n};\n";
+*/
+
+//*
+ $_sym = "_binary_".str_replace(array("/","-","."), "_", $path)."_start";
+ $gOutput .= "extern Uint8 {$_sym}[];";
+ $gSymFiles[] = $path;
+//*/
+ $gOutput .= <<<EOF
+tVFS_Node {$prefix}_{$i} = {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = 0,
+ .Size = $size,
+ .Inode = {$inode},
+ .ImplPtr = $_sym,
+ .Type = &gInitRD_FileType
+};
+
+EOF;
+ }
+ }
+}
+
+//print_r($lStack);
+//exit(1);
+
+ProcessFolder("gInitRD_Files", $lStack[0][1]);
+
+$gOutput .= "tInitRD_File gInitRD_Root_Files[] = {\n";
+foreach($lStack[0][1] as $j=>$child)
+{
+ if($j) $gOutput .= ",\n";
+ $gOutput .= "\t{\"".addslashes($child[0])."\",&gInitRD_Files_{$j}}";
+}
+$gOutput .= "\n};\n";
+$nRootFiles = count($lStack[0][1]);
+$gOutput .= <<<EOF
+tVFS_Node gInitRD_RootNode = {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Size = $nRootFiles,
+ .ImplPtr = gInitRD_Root_Files,
+ .Type = &gInitRD_DirType
+};
+EOF;
+
+$gOutput .= <<<EOF
+
+tVFS_Node * const gInitRD_FileList[] = {
+&gInitRD_RootNode
+EOF;
+
+function PutNodePointers($prefix, $items)
+{
+ global $gOutput;
+ foreach($items as $i=>$item)
+ {
+ $gOutput .= ",&{$prefix}_{$i}";
+ if(is_array($item[1]))
+ {
+ PutNodePointers("{$prefix}_{$i}", $item[1]);
+ }
+ }
+}
+
+PutNodePointers("gInitRD_Files", $lStack[0][1]);
+
+$gOutput .= <<<EOF
+};
+const int giInitRD_NumFiles = sizeof(gInitRD_FileList)/sizeof(gInitRD_FileList[0]);
+
+EOF;
+
+
+$fp = fopen($gOutputFile, "w");
+fputs($fp, $gOutput);
+fclose($fp);
+
+// - Create options call
+$fp = fopen($gOutputLDOptsFile, "w");
+fputs($fp, "--format binary\n");
+foreach($gSymFiles as $sym=>$file)
+{
+ fputs($fp, "$file\n");
+// fputs($fp, "--defsym $sym=_binary_".$sym_filename."_start\n");
+}
+fclose($fp);
+
+if($gDepFile !== false)
+{
+ $fp = fopen($gDepFile, "w");
+ $line = $gOutputFile.":\t".implode(" ", $gDependencies);
+ fputs($fp, $line);
+ fclose($fp);
+}
+
+?>
--- /dev/null
+# InitRD Filesystem Driver
+#
+
+OBJ = main.o files.$(ARCH).o
+EXTRA = files.c
+NAME = InitRD
+EXTRA = files.$(ARCH).c files.$(ARCH).c.dep files.$(ARCH).c.ldopts
+LDFLAGS += @files.$(ARCH).c.ldopts
+
+-include ../Makefile.tpl
+
+
+files.$(ARCH).c: GenerateInitRD.php files.lst
+
+-include files.$(ARCH).c.dep
--- /dev/null
+Dir "SBin" {
+ File "init" "__BIN__/SBin/init"
+ File "login" "__BIN__/SBin/login"
+}
+Dir "Bin" {
+ File "CLIShell" "__BIN__/Bin/CLIShell"
+ File "ls" "__BIN__/Bin/ls"
+ File "cat" "__BIN__/Bin/cat"
+ File "mount" "__BIN__/Bin/mount"
+ File "ifconfig" "__BIN__/Bin/ifconfig"
+ File "telnet" "__BIN__/Bin/telnet"
+ File "irc" "__BIN__/Bin/irc"
+}
+Dir "Libs" {
+ File "ld-acess.so" "__BIN__/Libs/ld-acess.so"
+ File "libld-acess.so" "__BIN__/Libs/libld-acess.so"
+ File "libc.so" "__BIN__/Libs/libc.so"
+ File "libgcc.so" "__BIN__/Libs/libgcc.so"
+ File "libreadline.so" "__BIN__/Libs/libreadline.so"
+ File "libnet.so" "__BIN__/Libs/libnet.so"
+ File "liburi.so" "__BIN__/Libs/liburi.so"
+ File "libimage_sif.so" "__BIN__/Libs/libimage_sif.so"
+ File "libaxwin3.so" "__BIN__/Libs/libaxwin3.so"
+}
+Dir "Conf" {
+ File "BootConf.cfg" "__FS__/Conf/BootConf.cfg"
+}
+Dir "Apps" {
+ Dir "AxWin" {
+ Dir "3.0" {
+ File "AxWinWM" "__BIN__/Apps/AxWin/3.0/AxWinWM"
+ File "AxWinUI" "__BIN__/Apps/AxWin/3.0/AxWinUI"
+ File "AcessLogoSmall.sif" "__SRC__/Usermode/Applications/axwin3_src/AcessLogoSmall.sif"
+ }
+ }
+}
--- /dev/null
+/*
+ */
+#ifndef _INITRD_H_
+#define _INITRD_H_
+
+#include <acess.h>
+#include <vfs.h>
+
+typedef struct sInitRD_File
+{
+ char *Name;
+ tVFS_Node *Node;
+} tInitRD_File;
+
+
+// === Functions ===
+extern Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer);
+extern char *InitRD_ReadDir(tVFS_Node *Node, int ID);
+extern tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name);
+
+// === Globals ===
+tVFS_NodeType gInitRD_DirType;
+tVFS_NodeType gInitRD_FileType;
+
+#endif
--- /dev/null
+/*
+ * Acess OS
+ * InitRD Driver Version 1
+ */
+#include "initrd.h"
+#include <modules.h>
+
+#define DUMP_ON_MOUNT 1
+
+// === IMPORTS ==
+extern tVFS_Node gInitRD_RootNode;
+extern const int giInitRD_NumFiles;
+extern tVFS_Node * const gInitRD_FileList[];
+
+// === PROTOTYPES ===
+ int InitRD_Install(char **Arguments);
+tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments);
+void InitRD_Unmount(tVFS_Node *Node);
+tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
+Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer);
+char *InitRD_ReadDir(tVFS_Node *Node, int ID);
+tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name);
+void InitRD_DumpDir(tVFS_Node *Node, int Indent);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x0A, FS_InitRD, InitRD_Install, NULL);
+tVFS_Driver gInitRD_FSInfo = {
+ "initrd", 0, InitRD_InitDevice, InitRD_Unmount, InitRD_GetNodeFromINode
+ };
+tVFS_NodeType gInitRD_DirType = {
+ .ReadDir = InitRD_ReadFile,
+ .FindDir = InitRD_FindDir
+ };
+tVFS_NodeType gInitRD_FileType = {
+ .Read = InitRD_ReadFile
+ };
+
+/**
+ * \brief Register initrd with the kernel
+ */
+int InitRD_Install(char **Arguments)
+{
+ Log_Notice("InitRD", "Installed");
+ VFS_AddDriver( &gInitRD_FSInfo );
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Mount the InitRD
+ */
+tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments)
+{
+ #if DUMP_ON_MOUNT
+ InitRD_DumpDir( &gInitRD_RootNode, 0 );
+ #endif
+ Log_Notice("InitRD", "Mounted (%i files)", giInitRD_NumFiles);
+ return &gInitRD_RootNode;
+}
+
+/**
+ * \brief Unmount the InitRD
+ */
+void InitRD_Unmount(tVFS_Node *Node)
+{
+}
+
+/**
+ */
+tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
+{
+ if( Inode >= giInitRD_NumFiles ) return NULL;
+ return gInitRD_FileList[Inode];
+}
+
+/**
+ * \brief Read from a file
+ */
+Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ if(Offset > Node->Size)
+ return 0;
+ if(Offset + Length > Node->Size)
+ Length = Node->Size - Offset;
+
+ memcpy(Buffer, Node->ImplPtr+Offset, Length);
+
+ return Length;
+}
+
+/**
+ * \brief Read from a directory
+ */
+char *InitRD_ReadDir(tVFS_Node *Node, int ID)
+{
+ tInitRD_File *dir = Node->ImplPtr;
+
+ if(ID >= Node->Size)
+ return NULL;
+
+ return strdup(dir[ID].Name);
+}
+
+/**
+ * \brief Find an element in a directory
+ */
+tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name)
+{
+ int i;
+ tInitRD_File *dir = Node->ImplPtr;
+
+ LOG("Name = '%s'", Name);
+
+ for( i = 0; i < Node->Size; i++ )
+ {
+ if(strcmp(Name, dir[i].Name) == 0)
+ return dir[i].Node;
+ }
+
+ return NULL;
+}
+
+void InitRD_DumpDir(tVFS_Node *Node, int Indent)
+{
+ int i;
+ char indent[Indent+1];
+ tInitRD_File *dir = Node->ImplPtr;
+
+ for( i = 0; i < Indent; i++ ) indent[i] = ' ';
+ indent[i] = '\0';
+
+ for( i = 0; i < Node->Size; i++ )
+ {
+ Log_Debug("InitRD", "%s- %p %s", indent, dir[i].Node, dir[i].Name);
+ if(dir[i].Node->Flags & VFS_FFLAG_DIRECTORY)
+ InitRD_DumpDir(dir[i].Node, Indent+1);
+ }
+}
--- /dev/null
+/*
+ * Acess 2 LEAN Filesystem Driver
+ * By John Hodge (thePowersGang)
+ *
+ * lean.h - Filesystem Structure Definitions
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#define EXTENTS_PER_INODE 6
+#define EXTENTS_PER_INDIRECT 38
+
+struct sLEAN_Superblock
+{
+ // TODO
+};
+
+struct sLEAN_IndirectBlock
+{
+ Uint32 Checksum; // Block checksum
+ Uint32 Magic; // 'INDX'
+ Uint64 SectorCount; // Number of blocks referenced by this block
+ Uint64 Inode; // Inode this block belongs to (in for robustness)
+ Uint64 ThisSector; // Sector number of this block
+ Uint64 PrevIndirect; // Backlink to the previous indirect block (or zero)
+ Uint64 NextIndirect; // Link to the next indirect block (or zero)
+
+ /**
+ * Number of extents stored in this block (only the last block can
+ * have this as < \a EXTENTS_PER_INDIRECT
+ */
+ Uint8 NumExtents;
+ Uint8 reserved1[3]; //!< Reserved/Padding
+ Uint32 reserved2; //!< Reserved/Padding
+
+ Uint64 ExtentsStarts[EXTENTS_PER_INDIRECT];
+ Uint32 ExtentsSizes[EXTENTS_PER_INDIRECT];
+}
+
+struct sLEAN_Inode
+{
+ Uint32 Checksum; //!< Checksum of the inode
+ Uint32 Magic; //!< Magic Value 'NODE'
+
+ Uint8 ExtentCount; //!< Number of extents defined in the inode structure
+ Uint8 reserved1[3]; //!< Reserved/Padding
+
+ Uint32 IndirectCount; //!< Number of indirect extent blocks used by the file
+ Uint32 LinkCount; //!< Number of directory entires that refer to this inode
+
+ Uint32 UID; //!< Owning User
+ Uint32 GID; //!< Owning Group
+ Uint32 Attributes; //!<
+ Uint64 FileSize; //!< Size of the file data (not including inode and other bookkeeping)
+ Uint64 SectorCount; //!< Number of sectors allocated to the file
+ Sint64 LastAccessTime; //!< Last access time
+ Sint64 StatusChangeTime; //!< Last change of the status bits time
+ Sint64 ModifcationTime; //!< Last modifcation time
+ Sint64 CreationTime; //!< File creation time
+
+ Uint64 FirstIndirect; //!< Sector number of the first indirect block
+ Uint64 LastIndirect; //!< Sector number of the last indirect block
+
+ Uint64 Fork; //!< ????
+
+ Uint64 ExtentsStarts[EXTENTS_PER_INODE];
+ Uint32 ExtentsSizes[EXTENTS_PER_INODE];
+};
+
+enum eLEAN_InodeAttributes
+{
+ LEAN_iaXOth = 1 << 0, LEAN_iaWOth = 1 << 1, LEAN_iaROth = 1 << 2,
+ LEAN_iaXGrp = 1 << 3, LEAN_iaWGrp = 1 << 4, LEAN_iaRGrp = 1 << 5,
+ LEAN_iaXUsr = 1 << 6, LEAN_iaWUsr = 1 << 7, LEAN_iaRUsr = 1 << 8,
+
+ LEAN_iaSVTX = 1 << 9,
+ LEAN_iaSGID = 1 << 10,
+ LEAN_iaSUID = 1 << 11,
+
+ LEAN_iaHidden = 1 << 12,
+ LEAN_iaSystem = 1 << 13,
+ LEAN_iaArchive = 1 << 14, // Set on any write
+ LEAN_iaSync = 1 << 15,
+ LEAN_iaNoAccessTime = 1 << 16, //!< Don't update the last accessed time
+ LEAN_iaImmutable = 1 << 17, //!< Don't move sectors (defragger flag)
+ LEAN_iaPrealloc = 1 << 18, //!< Keep preallocated sectors after close
+ LEAN_iaInlineExtAttr = 1 << 19, //!< Reserve the first sector
+
+ LEAN_iaFmtMask = 7 << 29, //!< Format mask
+ LEAN_iaFmtRegular = 1 << 29, //!< Regular File
+ LEAN_iaFmtDirectory = 2 << 29, //!< Directory
+ LEAN_iaFmtSymlink = 3 << 29, //!< Symlink
+ LEAN_iaFmtFork = 4 << 29, //!< Fork
+};
+
+struct tLEAN_ExtendedAttribute
+{
+ Uint32 Header; // 8.24 Name.Value Sizes
+};
+
+#endif
--- /dev/null
+/*
+ * Acess 2 LEAN Filesystem Driver
+ * By John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <vfs.h>
+
+// === CONSTANTS ===
+
+// === PROTOTYPES ===
+
+// === GLOBALS ===
+
+// === CODE ===
+int LEAN_Install(char **Arguments)
+{
+ return 1;
+}
--- /dev/null
+CATEGORY = FS
+-include ../../Makefile.tpl
--- /dev/null
+#
+#
+
+OBJ = main.o
+NAME = NFS
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 - NFS Driver
+ * By John Hodge (thePowersGang)
+ * This file is published under the terms of the Acess licence. See the
+ * file COPYING for details.
+ *
+ * common.h - Common definitions
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+typedef struct sNFS_Connection
+{
+ int FD;
+ tIPAddr Host;
+ char *Base;
+ tVFS_Node Node;
+} tNFS_Connection;
+
+#endif
--- /dev/null
+/*
+ * Acess2 - NFS Driver
+ * By John Hodge (thePowersGang)
+ * This file is published under the terms of the Acess licence. See the
+ * file COPYING for details.
+ *
+ * main.c - Driver core
+ */
+#define DEBUG 1
+#define VERBOSE 0
+#include "common.h"
+#include <modules.h>
+
+// === PROTOTYPES ===
+ int NFS_Install(char **Arguments);
+tVFS_Node *NFS_InitDevice(char *Devices, char **Options);
+void NFS_Unmount(tVFS_Node *Node);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x32 /*v0.5*/, FS_NFS, NFS_Install, NULL);
+tVFS_Driver gNFS_FSInfo = {"nfs", 0, NFS_InitDevice, NFS_Unmount, NULL};
+
+tNFS_Connection *gpNFS_Connections;
+
+// === CODE ===
+/**
+ * \brief Installs the NFS driver
+ */
+int NFS_Install(char **Arguments)
+{
+ VFS_AddDriver( &gNFS_FSInfo );
+ return 1;
+}
+
+/**
+ * \brief Mount a NFS share
+ */
+tVFS_Node *NFS_InitDevice(char *Device, char **Options)
+{
+ char *path, *host;
+ tNFS_Connection *conn;
+
+ path = strchr( Device, ':' ) + 1;
+ host = strndup( Device, (int)(path-Device)-1 );
+
+ conn = malloc( sizeof(tNFS_Connection) );
+
+ if( !IPTools_GetAddress(host, &conn->IP) ) {
+ free(conn);
+ return NULL;
+ }
+ free(host);
+
+ conn->FD = IPTools_OpenUdpClient( &conn->Host );
+ if(conn->FD == -1) {
+ free(conn);
+ return NULL;
+ }
+
+ conn->Base = strdup( path );
+ conn->RootNode.ImplPtr = conn;
+ conn->RootNode.Flags = VFS_FFLAG_DIRECTORY;
+
+ conn->RootNode.ReadDir = NFS_ReadDir;
+ conn->RootNode.FindDir = NFS_FindDir;
+ conn->RootNode.Close = NULL;
+
+ return &conn->RootNode;
+}
+
+void NFS_Unmount(tVFS_Node *Node)
+{
+
+}
--- /dev/null
+#
+#
+
+OBJ = main.o dir.o
+NAME = NTFS
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 - NTFS Driver
+ * By John Hodge (thePowersGang)
+ * This file is published under the terms of the Acess licence. See the
+ * file COPYING for details.
+ *
+ * attributes.h - MFT Attribute Types
+ */
+#ifndef _ATTRIBUTES_H_
+#define _ATTRIBUTES_H_
+
+typedef struct
+{
+ Uint64 ParentDirectory; //!< Parent directory MFT entry
+ Sint64 CreationTime; //!< Time the file was created
+ Sint64 LastDataModTime; //!< Last change time for the data
+ Sint64 LastMtfModTime; //!< Last change time for the MFT entry
+ Sint64 LastAccessTime; //!< Last Access Time (unreliable on most systems)
+
+ Uint64 AllocatedSize; //!< Allocated data size for $DATA unnamed stream
+ Uint64 DataSize; //!< Actual size of $DATA unnamed stream
+ Uint32 Flags; //!< File attribute files
+
+ union {
+ struct {
+ Uint16 PackedSize; //!< Size of buffer needed for extended attributes
+ Uint16 _reserved;
+ } PACKED ExtAttrib;
+ struct {
+ Uint32 Tag; //!< Type of reparse point
+ } PACKED ReparsePoint;
+ } PACKED Type;
+
+ Uint8 FilenameType; //!< Filename namespace (DOS, Windows, Unix)
+ WCHAR Filename[0];
+} PACKED tNTFS_Attrib_Filename;
+
+#endif
--- /dev/null
+/*
+ * Acess2 - NTFS Driver
+ * By John Hodge (thePowersGang)
+ * This file is published under the terms of the Acess licence. See the
+ * file COPYING for details.
+ *
+ * common.h - Common Types and Definitions
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <acess.h>
+#include <vfs.h>
+
+typedef Uint16 WCHAR;
+
+// === STRUCTURES ===
+/**
+ * In-memory representation of an NTFS Disk
+ */
+typedef struct sNTFS_Disk
+{
+ int FD;
+ int CacheHandle;
+
+ int ClusterSize;
+ Uint64 MFTBase;
+ Uint32 MFTRecSize;
+
+ tVFS_Node RootNode;
+} tNTFS_Disk;
+
+typedef struct sNTFS_BootSector
+{
+ // 0
+ Uint8 Jump[3];
+ Uint8 SystemID[8]; // = "NTFS "
+ Uint16 BytesPerSector;
+ Uint8 SectorsPerCluster;
+
+ // 0xE
+ Uint8 Unused[7];
+ Uint8 MediaDescriptor;
+ Uint16 Unused2;
+ Uint16 SectorsPerTrack;
+ Uint16 Heads;
+
+ // 0x1C
+ Uint64 Unused3;
+ Uint32 Unkown; // Usually 0x00800080 (according to Linux docs)
+
+ // 0x28
+ Uint64 TotalSectorCount; // Size of volume in sectors
+ Uint64 MFTStart; // Logical Cluster Number of Cluster 0 of MFT
+ Uint64 MFTMirrorStart; // Logical Cluster Number of Cluster 0 of MFT Backup
+
+ // 0x40
+ // If either of these are -ve, the size can be obtained via
+ // SizeInBytes = 2^(-1 * Value)
+ Sint8 ClustersPerMFTRecord;
+ Uint8 Unused4[3];
+ Sint8 ClustersPerIndexRecord;
+ Uint8 Unused5[3];
+
+ Uint64 SerialNumber;
+
+ Uint8 Padding[512-0x50];
+
+} PACKED tNTFS_BootSector;
+
+/**
+ * FILE header, an entry in the MFT
+ */
+typedef struct sNTFS_FILE_Header
+{
+ Uint32 Magic; // 'FILE'
+ Uint16 UpdateSequenceOfs;
+ Uint16 UpdateSequenceSize; // Size in words of the UpdateSequenceArray
+
+ Uint64 LSN; // $LogFile Sequence Number
+
+ Uint16 SequenceNumber;
+ Uint16 HardLinkCount;
+ Uint16 FirstAttribOfs; // Size of header?
+ Uint16 Flags; // 0: In Use, 1: Directory
+
+ Uint32 RecordSize; // Real Size of FILE Record
+ Uint32 RecordSpace; // Allocated Size for FILE Record
+
+ /**
+ * Base address of the MFT containing this record
+ */
+ Uint64 Reference; // "File reference to the base FILE record" ???
+
+ Uint16 NextAttribID;
+ union
+ {
+ // Only in XP
+ struct {
+ Uint16 AlignTo4Byte;
+ Uint16 RecordNumber; // Number of this MFT Record
+ Uint16 UpdateSequenceNumber;
+ Uint16 UpdateSequenceArray[];
+ } XP;
+ struct {
+ Uint16 UpdateSequenceNumber;
+ Uint16 UpdateSequenceArray[];
+ } All;
+ } OSDep;
+
+} PACKED tNTFS_FILE_Header;
+
+/**
+ * File Attribute, follows the FILE header
+ */
+typedef struct sNTFS_FILE_Attrib
+{
+ Uint32 Type; // See eNTFS_FILE_Attribs
+ Uint32 Size; // Includes header
+
+ Uint8 ResidentFlag; // (What does this mean?)
+ Uint8 NameLength;
+ Uint16 NameOffset;
+ Uint16 Flags; // 0: Compressed, 14: Encrypted, 15: Sparse
+ Uint16 AttributeID;
+
+ union
+ {
+ struct {
+ Uint32 AttribLen; // In words
+ Uint16 AttribOfs;
+ Uint8 IndexedFlag;
+ Uint8 Padding;
+
+ Uint16 Name[]; // UTF-16
+ // Attribute Data
+ } Resident;
+ struct {
+ Uint64 StartingVCN;
+ Uint64 LastVCN;
+ Uint16 DataRunOfs;
+ Uint16 CompressionUnitSize;
+ Uint32 Padding;
+ Uint64 AllocatedSize;
+ Uint64 RealSize;
+ Uint64 InitiatedSize; // One assumes, ammount of actual data stored
+ Uint16 Name[]; // UTF-16
+ // Data Runs
+ } NonResident;
+ };
+} PACKED tNTFS_FILE_Attrib;
+
+#endif
--- /dev/null
+/*
+ * Acess2 - NTFS Driver
+ * By John Hodge (thePowersGang)
+ * This file is published under the terms of the Acess licence. See the
+ * file COPYING for details.
+ *
+ * dir.c - Directory Handling
+ */
+#include "common.h"
+#include "index.h"
+
+// === PROTOTYPES ===
+char *NTFS_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name);
+Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str);
+
+// === CODE ===
+/**
+ * \brief Get the name of an indexed directory entry
+ */
+char *NTFS_ReadDir(tVFS_Node *Node, int Pos)
+{
+ return NULL;
+}
+
+/**
+ * \brief Get an entry from a directory by name
+ */
+tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name)
+{
+ tNTFS_Disk *disk = Node->ImplPtr;
+ Uint64 inode = NTFS_int_IndexLookup(Node->Inode, "$I30", Name);
+ tVFS_Node node;
+
+ if(!inode) return NULL;
+
+ node.Inode = inode;
+
+ return Inode_CacheNode(disk->CacheHandle, &node);
+}
+
+/**
+ * \brief Scans an index for the requested value and returns the associated ID
+ */
+Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str)
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Acess2 - NTFS Driver
+ * By John Hodge (thePowersGang)
+ * This file is published under the terms of the Acess licence. See the
+ * file COPYING for details.
+ *
+ * index.h - Index Types
+ */
+#ifndef _INDEX_H_
+#define _INDEX_H_
+
+#include "attributes.h"
+
+typedef struct
+{
+ Uint32 EntryOffset;
+ Uint32 IndexLength;
+ Uint32 AllocateSize;
+ Uint8 Flags;
+ Uint8 _reserved[3];
+} PACKED tNTFS_IndexHeader;
+
+typedef struct
+{
+ Uint32 Type;
+ Uint32 CollationRule;
+ Uint32 IndexBlockSize;
+ Uint8 ClustersPerIndexBlock;
+ Uint8 _reserved[3];
+ tNTFS_IndexHeader Header;
+} PACKED tNTFS_IndexRoot;
+
+typedef struct
+{
+ union {
+ struct {
+ Uint64 File; // MFT Index of file
+ } PACKED Dir;
+ /**
+ * Views/Indexes
+ */
+ struct {
+ Uint16 DataOffset;
+ Uint16 DataLength;
+ Uint32 _reserved;
+ } PACKED ViewIndex;
+ } PACKED Header;
+
+ Uint16 Length; //!< Size of the index entry (multiple of 8 bytes)
+ Uint16 KeyLength; //!< Size of key value
+ Uint16 Flags; //!< Flags Bitfield
+ Uint16 _reserved;
+
+ /**
+ * \brief Key Data
+ * \note Only valid if \a Flags does not have \a INDEX_ENTRY_END set
+ * \note In NTFS3 only \a Filename is used
+ */
+ union {
+ tNTFS_Attrib_Filename Filename;
+ //TODO: more key types
+ } PACKED Key;
+} PACKED tNTFS_IndexEntry;
+
+#endif
--- /dev/null
+/*
+ * Acess2 - NTFS Driver
+ * By John Hodge (thePowersGang)
+ *
+ * main.c - Driver core
+ */
+#define DEBUG 1
+#define VERBOSE 0
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+#include <modules.h>
+
+// === IMPORTS ===
+extern char *NTFS_ReadDir(tVFS_Node *Node, int Pos);
+extern tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name);
+
+// === PROTOTYPES ===
+ int NTFS_Install(char **Arguments);
+tVFS_Node *NTFS_InitDevice(const char *Devices, const char **Options);
+void NTFS_Unmount(tVFS_Node *Node);
+void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x0A /*v0.1*/, FS_NTFS, NTFS_Install, NULL);
+tVFS_Driver gNTFS_FSInfo = {"ntfs", 0, NTFS_InitDevice, NTFS_Unmount, NULL};
+tVFS_NodeType gNTFS_DirType = {
+ .TypeName = "NTFS-File",
+ .ReadDir = NTFS_ReadDir,
+ .FindDir = NTFS_FindDir,
+ .Close = NULL
+ };
+
+tNTFS_Disk gNTFS_Disks;
+
+// === CODE ===
+/**
+ * \brief Installs the NTFS driver
+ */
+int NTFS_Install(char **Arguments)
+{
+ VFS_AddDriver( &gNTFS_FSInfo );
+ return 0;
+}
+
+/**
+ * \brief Mount a NTFS volume
+ */
+tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options)
+{
+ tNTFS_Disk *disk;
+ tNTFS_BootSector bs;
+
+ disk = malloc( sizeof(tNTFS_Disk) );
+
+ disk->FD = VFS_Open(Device, VFS_OPENFLAG_READ);
+ if(!disk->FD) {
+ free(disk);
+ return NULL;
+ }
+
+ Log_Debug("FS_NTFS", "&bs = %p", &bs);
+ VFS_ReadAt(disk->FD, 0, 512, &bs);
+
+ Log_Debug("FS_NTFS", "Jump = %02x%02x%02x",
+ bs.Jump[0],
+ bs.Jump[1],
+ bs.Jump[2]);
+ Log_Debug("FS_NTFS", "SystemID = %02x%02x%02x%02x%02x%02x%02x%02x (%8C)",
+ bs.SystemID[0], bs.SystemID[1], bs.SystemID[2], bs.SystemID[3],
+ bs.SystemID[4], bs.SystemID[5], bs.SystemID[6], bs.SystemID[7],
+ bs.SystemID
+ );
+ Log_Debug("FS_NTFS", "BytesPerSector = %i", bs.BytesPerSector);
+ Log_Debug("FS_NTFS", "SectorsPerCluster = %i", bs.SectorsPerCluster);
+ Log_Debug("FS_NTFS", "MediaDescriptor = 0x%x", bs.MediaDescriptor);
+ Log_Debug("FS_NTFS", "SectorsPerTrack = %i", bs.SectorsPerTrack);
+ Log_Debug("FS_NTFS", "Heads = %i", bs.Heads);
+ Log_Debug("FS_NTFS", "TotalSectorCount = 0x%llx", bs.TotalSectorCount);
+ Log_Debug("FS_NTFS", "MFTStart = 0x%llx", bs.MFTStart);
+ Log_Debug("FS_NTFS", "MFTMirrorStart = 0x%llx", bs.MFTMirrorStart);
+ Log_Debug("FS_NTFS", "ClustersPerMFTRecord = %i", bs.ClustersPerMFTRecord);
+ Log_Debug("FS_NTFS", "ClustersPerIndexRecord = %i", bs.ClustersPerIndexRecord);
+ Log_Debug("FS_NTFS", "SerialNumber = 0x%llx", bs.SerialNumber);
+
+ disk->ClusterSize = bs.BytesPerSector * bs.SectorsPerCluster;
+ Log_Debug("NTFS", "Cluster Size = %i KiB", disk->ClusterSize/1024);
+ disk->MFTBase = bs.MFTStart;
+ Log_Debug("NTFS", "MFT Base = %i", disk->MFTBase);
+ Log_Debug("NTFS", "TotalSectorCount = 0x%x", bs.TotalSectorCount);
+
+ if( bs.ClustersPerMFTRecord < 0 ) {
+ disk->MFTRecSize = 1 << (-bs.ClustersPerMFTRecord);
+ }
+ else {
+ disk->MFTRecSize = bs.ClustersPerMFTRecord * disk->ClusterSize;
+ }
+
+ disk->RootNode.Inode = 5; // MFT Ent #5 is filesystem root
+ disk->RootNode.ImplPtr = disk;
+
+ disk->RootNode.UID = 0;
+ disk->RootNode.GID = 0;
+
+ disk->RootNode.NumACLs = 1;
+ disk->RootNode.ACLs = &gVFS_ACL_EveryoneRX;
+
+ disk->RootNode.Type = &gNTFS_DirType;
+
+
+ NTFS_DumpEntry(disk, 5);
+
+ return &disk->RootNode;
+}
+
+/**
+ * \brief Unmount an NTFS Disk
+ */
+void NTFS_Unmount(tVFS_Node *Node)
+{
+
+}
+
+/**
+ * \brief Dumps a MFT Entry
+ */
+void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry)
+{
+ void *buf = malloc( Disk->MFTRecSize );
+ tNTFS_FILE_Header *hdr = buf;
+ tNTFS_FILE_Attrib *attr;
+ int i;
+
+ if(!buf) {
+ Log_Warning("FS_NTFS", "malloc() fail!");
+ return ;
+ }
+
+ VFS_ReadAt( Disk->FD,
+ Disk->MFTBase * Disk->ClusterSize + Entry * Disk->MFTRecSize,
+ Disk->MFTRecSize,
+ buf);
+
+ Log_Debug("FS_NTFS", "MFT Entry #%i", Entry);
+ Log_Debug("FS_NTFS", "- Magic = 0x%08x (%4C)", hdr->Magic, &hdr->Magic);
+ Log_Debug("FS_NTFS", "- UpdateSequenceOfs = 0x%x", hdr->UpdateSequenceOfs);
+ Log_Debug("FS_NTFS", "- UpdateSequenceSize = 0x%x", hdr->UpdateSequenceSize);
+ Log_Debug("FS_NTFS", "- LSN = 0x%x", hdr->LSN);
+ Log_Debug("FS_NTFS", "- SequenceNumber = %i", hdr->SequenceNumber);
+ Log_Debug("FS_NTFS", "- HardLinkCount = %i", hdr->HardLinkCount);
+ Log_Debug("FS_NTFS", "- FirstAttribOfs = 0x%x", hdr->FirstAttribOfs);
+ Log_Debug("FS_NTFS", "- Flags = 0x%x", hdr->Flags);
+ Log_Debug("FS_NTFS", "- RecordSize = 0x%x", hdr->RecordSize);
+ Log_Debug("FS_NTFS", "- RecordSpace = 0x%x", hdr->RecordSpace);
+ Log_Debug("FS_NTFS", "- Reference = 0x%llx", hdr->Reference);
+ Log_Debug("FS_NTFS", "- NextAttribID = 0x%04x", hdr->NextAttribID);
+
+ attr = (void*)( (char*)hdr + hdr->FirstAttribOfs );
+ i = 0;
+ while( (tVAddr)attr < (tVAddr)hdr + hdr->RecordSize )
+ {
+ if(attr->Type == 0xFFFFFFFF) break;
+ Log_Debug("FS_NTFS", "- Attribute %i", i ++);
+ Log_Debug("FS_NTFS", " > Type = 0x%x", attr->Type);
+ Log_Debug("FS_NTFS", " > Size = 0x%x", attr->Size);
+ Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->ResidentFlag);
+ Log_Debug("FS_NTFS", " > NameLength = %i", attr->NameLength);
+ Log_Debug("FS_NTFS", " > NameOffset = 0x%x", attr->NameOffset);
+ Log_Debug("FS_NTFS", " > Flags = 0x%x", attr->Flags);
+ Log_Debug("FS_NTFS", " > AttributeID = 0x%x", attr->AttributeID);
+ if( !attr->ResidentFlag ) {
+ Log_Debug("FS_NTFS", " > AttribLen = 0x%x", attr->Resident.AttribLen);
+ Log_Debug("FS_NTFS", " > AttribOfs = 0x%x", attr->Resident.AttribOfs);
+ Log_Debug("FS_NTFS", " > IndexedFlag = 0x%x", attr->Resident.IndexedFlag);
+ Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->Resident.Name);
+ Debug_HexDump("FS_NTFS",
+ (void*)( (tVAddr)attr + attr->Resident.AttribOfs ),
+ attr->Resident.AttribLen
+ );
+ }
+ else {
+ Log_Debug("FS_NTFS", " > StartingVCN = 0x%llx", attr->NonResident.StartingVCN);
+ Log_Debug("FS_NTFS", " > LastVCN = 0x%llx", attr->NonResident.LastVCN);
+ Log_Debug("FS_NTFS", " > DataRunOfs = 0x%x", attr->NonResident.DataRunOfs);
+ Log_Debug("FS_NTFS", " > CompressionUnitSize = 0x%x", attr->NonResident.CompressionUnitSize);
+ Log_Debug("FS_NTFS", " > AllocatedSize = 0x%llx", attr->NonResident.AllocatedSize);
+ Log_Debug("FS_NTFS", " > RealSize = 0x%llx", attr->NonResident.RealSize);
+ Log_Debug("FS_NTFS", " > InitiatedSize = 0x%llx", attr->NonResident.InitiatedSize);
+ Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->NonResident.Name);
+ }
+
+ attr = (void*)( (tVAddr)attr + attr->Size );
+ }
+
+ free(buf);
+}
--- /dev/null
+#
+#
+
+OBJ := main.o interface.o
+OBJ += link.o arp.o
+OBJ += ipv4.o icmp.o
+OBJ += ipv6.o
+OBJ += firewall.o routing.o
+OBJ += udp.o tcp.o
+NAME := IPStack
+CATEGORY :=
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Address Resolution Protocol
+ * - Part of the IPv4 protocol
+ */
+#define DEBUG 0
+#include "ipstack.h"
+#include "arp.h"
+#include "link.h"
+
+#define ARPv6 0
+#define ARP_CACHE_SIZE 64
+#define ARP_MAX_AGE (60*60*1000) // 1Hr
+
+// === IMPORTS ===
+extern tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast);
+#if ARPv6
+extern tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
+#endif
+
+// === PROTOTYPES ===
+ int ARP_Initialise();
+tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address);
+void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer);
+
+// === GLOBALS ===
+struct sARP_Cache4 {
+ tIPv4 IP;
+ tMacAddr MAC;
+ Sint64 LastUpdate;
+ Sint64 LastUsed;
+} *gaARP_Cache4;
+ int giARP_Cache4Space;
+tMutex glARP_Cache4;
+#if ARPv6
+struct sARP_Cache6 {
+ tIPv6 IP;
+ tMacAddr MAC;
+ Sint64 LastUpdate;
+ Sint64 LastUsed;
+} *gaARP_Cache6;
+ int giARP_Cache6Space;
+tMutex glARP_Cache6;
+#endif
+volatile int giARP_LastUpdateID = 0;
+
+// === CODE ===
+/**
+ * \fn int ARP_Initialise()
+ * \brief Initalise the ARP section
+ */
+int ARP_Initialise()
+{
+ gaARP_Cache4 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) );
+ memset( gaARP_Cache4, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) );
+ giARP_Cache4Space = ARP_CACHE_SIZE;
+
+ #if ARPv6
+ gaARP_Cache6 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) );
+ memset( gaARP_Cache6, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) );
+ giARP_Cache6Space = ARP_CACHE_SIZE;
+ #endif
+
+ Link_RegisterType(0x0806, ARP_int_GetPacket);
+ return 1;
+}
+
+/**
+ * \brief Resolves a MAC address from an IPv4 address
+ */
+tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address)
+{
+ int lastID;
+ int i;
+ struct sArpRequest4 req;
+ Sint64 timeout;
+
+ ENTER("pInterface xAddress", Interface, Address);
+
+ // Check for broadcast
+ if( Address.L == -1 )
+ {
+ LOG("Broadcast");
+ LEAVE('-');
+ return cMAC_BROADCAST;
+ }
+
+ // Check routing tables if not on this subnet
+ if( IPStack_CompareAddress(4, &Address, Interface->Address, Interface->SubnetBits) == 0 )
+ {
+ tRoute *route = IPStack_FindRoute(4, Interface, &Address);
+ // If the next hop is defined, use it
+ // - 0.0.0.0 as the next hop means "no next hop / direct"
+ if( route && ((tIPv4*)route->NextHop)->L != 0 )
+ {
+ // Recursion: see /Recursion/
+ LOG("Recursing with %s", IPStack_PrintAddress(4, route->NextHop));
+ LEAVE('-');
+ return ARP_Resolve4(Interface, *(tIPv4*)route->NextHop);
+ }
+ // No route, fall though
+ }
+ else
+ {
+ Uint32 netmask;
+ // Check for broadcast
+ netmask = IPv4_Netmask(Interface->SubnetBits);
+ if( (Address.L & ~netmask) == (0xFFFFFFFF & ~netmask) )
+ {
+ LOG("Local Broadcast");
+ LEAVE('-');
+ return cMAC_BROADCAST;
+ }
+ }
+
+ // Check ARP Cache
+ Mutex_Acquire( &glARP_Cache4 );
+ for( i = 0; i < giARP_Cache4Space; i++ )
+ {
+ if(gaARP_Cache4[i].IP.L != Address.L) continue;
+
+ // Check if the entry needs to be refreshed
+ if( now() - gaARP_Cache4[i].LastUpdate > ARP_MAX_AGE ) break;
+
+ Mutex_Release( &glARP_Cache4 );
+ LOG("Return %x:%x:%x:%x:%x:%x",
+ gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1],
+ gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3],
+ gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5]
+ );
+ LEAVE('-');
+ return gaARP_Cache4[i].MAC;
+ }
+ Mutex_Release( &glARP_Cache4 );
+
+ lastID = giARP_LastUpdateID;
+
+ // Create request
+ Log_Log("ARP4", "Asking for address %i.%i.%i.%i",
+ Address.B[0], Address.B[1], Address.B[2], Address.B[3]
+ );
+ req.HWType = htons(0x0001); // Ethernet
+ req.Type = htons(0x0800);
+ req.HWSize = 6;
+ req.SWSize = 4;
+ req.Request = htons(1);
+ req.SourceMac = Interface->Adapter->MacAddr;
+ req.SourceIP = *(tIPv4*)Interface->Address;
+ req.DestMac = cMAC_BROADCAST;
+ req.DestIP = Address;
+
+ // Send Request
+ Link_SendPacket(Interface->Adapter, 0x0806, req.DestMac, sizeof(struct sArpRequest4), &req);
+
+ timeout = now() + Interface->TimeoutDelay;
+
+ // Wait for a reply
+ for(;;)
+ {
+ while(lastID == giARP_LastUpdateID && now() < timeout) {
+ Threads_Yield();
+ }
+
+ if( now() >= timeout ) {
+ Log_Log("ARP4", "Timeout");
+ break; // Timeout
+ }
+
+ lastID = giARP_LastUpdateID;
+
+ Mutex_Acquire( &glARP_Cache4 );
+ for( i = 0; i < giARP_Cache4Space; i++ )
+ {
+ if(gaARP_Cache4[i].IP.L != Address.L) continue;
+
+ Mutex_Release( &glARP_Cache4 );
+ Log_Debug("ARP4", "Return %02x:%02x:%02x:%02x:%02x:%02x",
+ gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1],
+ gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3],
+ gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5]);
+ return gaARP_Cache4[i].MAC;
+ }
+ Mutex_Release( &glARP_Cache4 );
+ }
+ {
+ tMacAddr ret = {{0,0,0,0,0,0}};
+ return ret;
+ }
+}
+
+/**
+ * \brief Updates the ARP Cache entry for an IPv4 Address
+ */
+void ARP_UpdateCache4(tIPv4 SWAddr, tMacAddr HWAddr)
+{
+ int i;
+ int free = -1;
+ int oldest = 0;
+
+ // Find an entry for the IP address in the cache
+ Mutex_Acquire(&glARP_Cache4);
+ for( i = giARP_Cache4Space; i--; )
+ {
+ if(gaARP_Cache4[oldest].LastUpdate > gaARP_Cache4[i].LastUpdate) {
+ oldest = i;
+ }
+ if( gaARP_Cache4[i].IP.L == SWAddr.L ) break;
+ if( gaARP_Cache4[i].LastUpdate == 0 && free == -1 ) free = i;
+ }
+ // If there was no match, we need to make one
+ if(i == -1) {
+ if(free != -1)
+ i = free;
+ else
+ i = oldest;
+ }
+
+ Log_Log("ARP4", "Caching %i.%i.%i.%i (%02x:%02x:%02x:%02x:%02x:%02x) in %i",
+ SWAddr.B[0], SWAddr.B[1], SWAddr.B[2], SWAddr.B[3],
+ HWAddr.B[0], HWAddr.B[1], HWAddr.B[2], HWAddr.B[3], HWAddr.B[4], HWAddr.B[5],
+ i
+ );
+
+ gaARP_Cache4[i].IP = SWAddr;
+ gaARP_Cache4[i].MAC = HWAddr;
+ gaARP_Cache4[i].LastUpdate = now();
+ giARP_LastUpdateID ++;
+ Mutex_Release(&glARP_Cache4);
+}
+
+#if ARPv6
+/**
+ * \brief Updates the ARP Cache entry for an IPv6 Address
+ */
+void ARP_UpdateCache6(tIPv6 SWAddr, tMacAddr HWAddr)
+{
+ int i;
+ int free = -1;
+ int oldest = 0;
+
+ // Find an entry for the MAC address in the cache
+ Mutex_Acquire(&glARP_Cache6);
+ for( i = giARP_Cache6Space; i--; )
+ {
+ if(gaARP_Cache6[oldest].LastUpdate > gaARP_Cache6[i].LastUpdate) {
+ oldest = i;
+ }
+ if( MAC_EQU(gaARP_Cache6[i].MAC, HWAddr) ) break;
+ if( gaARP_Cache6[i].LastUpdate == 0 && free == -1 ) free = i;
+ }
+ // If there was no match, we need to make one
+ if(i == -1) {
+ if(free != -1)
+ i = free;
+ else
+ i = oldest;
+ gaARP_Cache6[i].MAC = HWAddr;
+ }
+
+ gaARP_Cache6[i].IP = SWAddr;
+ gaARP_Cache6[i].LastUpdate = now();
+ giARP_LastUpdateID ++;
+ Mutex_Release(&glARP_Cache6);
+}
+#endif
+
+/**
+ * \fn void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
+ * \brief Called when an ARP packet is recieved
+ */
+void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
+{
+ tArpRequest4 *req4 = Buffer;
+ #if ARPv6
+ tArpRequest6 *req6 = Buffer;
+ #endif
+ tInterface *iface;
+
+ // Sanity Check Packet
+ if( Length < (int)sizeof(tArpRequest4) ) {
+ Log_Log("ARP", "Recieved undersized packet");
+ return ;
+ }
+ if( ntohs(req4->Type) != 0x0800 ) {
+ Log_Log("ARP", "Recieved a packet with a bad type (0x%x)", ntohs(req4->Type));
+ return ;
+ }
+ if( req4->HWSize != 6 ) {
+ Log_Log("ARP", "Recieved a packet with HWSize != 6 (%i)", req4->HWSize);
+ return;
+ }
+ #if ARP_DETECT_SPOOFS
+ if( !MAC_EQU(req4->SourceMac, From) ) {
+ Log_Log("ARP", "ARP spoofing detected "
+ "(%02x%02x:%02x%02x:%02x%02x != %02x%02x:%02x%02x:%02x%02x)",
+ req4->SourceMac.B[0], req4->SourceMac.B[1], req4->SourceMac.B[2],
+ req4->SourceMac.B[3], req4->SourceMac.B[4], req4->SourceMac.B[5],
+ From.B[0], From.B[1], From.B[2],
+ From.B[3], From.B[4], From.B[5]
+ );
+ return;
+ }
+ #endif
+
+ switch( ntohs(req4->Request) )
+ {
+ case 1: // You want my IP?
+ // Check what type of IP it is
+ switch( req4->SWSize )
+ {
+ case 4:
+ Log_Debug("ARP", "ARP Request IPv4 Address %i.%i.%i.%i from %i.%i.%i.%i",
+ req4->DestIP.B[0], req4->DestIP.B[1], req4->DestIP.B[2],
+ req4->DestIP.B[3],
+ req4->SourceIP.B[0], req4->SourceIP.B[1],
+ req4->SourceIP.B[2], req4->SourceIP.B[3]);
+ Log_Debug("ARP", " from MAC %02x:%02x:%02x:%02x:%02x:%02x",
+ req4->SourceMac.B[0], req4->SourceMac.B[1],
+ req4->SourceMac.B[2], req4->SourceMac.B[3],
+ req4->SourceMac.B[4], req4->SourceMac.B[5]);
+ iface = IPv4_GetInterface(Adapter, req4->DestIP, 0);
+ if( iface )
+ {
+ ARP_UpdateCache4(req4->SourceIP, req4->SourceMac);
+
+ req4->DestIP = req4->SourceIP;
+ req4->DestMac = req4->SourceMac;
+ req4->SourceIP = *(tIPv4*)iface->Address;;
+ req4->SourceMac = Adapter->MacAddr;
+ req4->Request = htons(2);
+ Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)",
+ req4->SourceMac.B[0], req4->SourceMac.B[1],
+ req4->SourceMac.B[2], req4->SourceMac.B[3],
+ req4->SourceMac.B[4], req4->SourceMac.B[5]);
+ Link_SendPacket(Adapter, 0x0806, req4->DestMac, sizeof(tArpRequest4), req4);
+ }
+ break;
+ #if ARPv6
+ case 6:
+ if( Length < (int)sizeof(tArpRequest6) ) {
+ Log_Log("ARP", "Recieved undersized packet (IPv6)");
+ return ;
+ }
+ Log_Debug("ARP", "ARP Request IPv6 Address %08x:%08x:%08x:%08x",
+ ntohl(req6->DestIP.L[0]), ntohl(req6->DestIP.L[1]),
+ ntohl(req6->DestIP.L[2]), ntohl(req6->DestIP.L[3])
+ );
+ iface = IPv6_GetInterface(Adapter, req6->DestIP, 0);
+ if( iface )
+ {
+ req6->DestIP = req6->SourceIP;
+ req6->DestMac = req6->SourceMac;
+ req6->SourceIP = *(tIPv6*)iface->Address;
+ req6->SourceMac = Adapter->MacAddr;
+ req6->Request = htons(2);
+ Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)",
+ req4->SourceMac.B[0], req4->SourceMac.B[1],
+ req4->SourceMac.B[2], req4->SourceMac.B[3],
+ req4->SourceMac.B[4], req4->SourceMac.B[5]);
+ Link_SendPacket(Adapter, 0x0806, req6->DestMac, sizeof(tArpRequest6), req6);
+ }
+ break;
+ #endif
+ default:
+ Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize);
+ return ;
+ }
+
+ break;
+
+ case 2: // Ooh! A response!
+ // Check what type of IP it is
+ switch( req4->SWSize )
+ {
+ case 4:
+ ARP_UpdateCache4( req4->SourceIP, From );
+ break;
+ #if ARPv6
+ case 6:
+ if( Length < (int)sizeof(tArpRequest6) ) {
+ Log_Debug("ARP", "Recieved undersized packet (IPv6)");
+ return ;
+ }
+ ARP_UpdateCache6( req6->SourceIP, From );
+ break;
+ #endif
+ default:
+ Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize);
+ return ;
+ }
+
+ break;
+
+ default:
+ Log_Warning("ARP", "Unknown Request ID %i", ntohs(req4->Request));
+ break;
+ }
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Common Header
+ */
+#ifndef _ARP_H_
+#define _ARP_H_
+
+#include "ipstack.h"
+
+typedef struct sArpRequest4 tArpRequest4;
+typedef struct sArpRequest6 tArpRequest6;
+
+struct sArpRequest4 {
+ Uint16 HWType;
+ Uint16 Type;
+ Uint8 HWSize, SWSize;
+ Uint16 Request;
+ tMacAddr SourceMac;
+ tIPv4 SourceIP;
+ tMacAddr DestMac;
+ tIPv4 DestIP;
+} __attribute__((packed));
+
+struct sArpRequest6 {
+ Uint16 HWType;
+ Uint16 Type;
+ Uint8 HWSize, SWSize;
+ Uint16 Request;
+ tMacAddr SourceMac;
+ tIPv6 SourceIP;
+ tMacAddr DestMac;
+ tIPv6 DestIP;
+} __attribute__((packed));
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Firewall Rules
+ */
+#include "ipstack.h"
+#include "firewall.h"
+
+#define MAX_ADDRTYPE 9
+
+// === IMPORTS ===
+
+// === TYPES ===
+typedef struct sKeyValue tKeyValue;
+typedef struct sFirewallMod tFirewallMod;
+typedef struct sModuleRule tModuleRule;
+typedef struct sRule tRule;
+typedef struct sChain tChain;
+
+// === STRUCTURES ===
+struct sKeyValue
+{
+ const char *Key;
+ const char *Value;
+};
+
+struct sFirewallMod
+{
+ const char *Name;
+
+ int (*Match)(tModuleRule *Rule, int AddrType,
+ const void *Src, const void *Dest,
+ Uint8 Type, Uint32 Flags,
+ size_t Length, const void *Data);
+
+ tModuleRule *(*Create)(tKeyValue *Params);
+};
+
+struct sModuleRule
+{
+ tModuleRule *Next;
+
+ tFirewallMod *Mod;
+
+ char Data[];
+};
+
+struct sRule
+{
+ tRule *Next;
+
+ int PacketCount; // Number of packets seen
+ int ByteCount; // Number of bytes seen (IP Payload bytes)
+
+ int bInvertSource; // Boolean NOT flag on source
+ void *Source; // Source address bytes
+ int SourceMask; // Source address mask bits
+
+ int bInvertDest; // Boolean NOT flag on destination
+ void *Dest; // Destination address bytes
+ int DestMask; // Destination address mask bits
+
+ tModuleRule *Modules; // Modules loaded for this rule
+
+ char Target[]; // Target rule name
+};
+
+struct sChain
+{
+ tChain *Next;
+
+ tRule *FirstRule;
+ tRule *LastRule;
+
+ char Name[];
+};
+
+// === PROTOTYPES ===
+ int IPTables_TestChain(
+ const char *RuleName,
+ const int AddressType,
+ const void *Src, const void *Dest,
+ Uint8 Type, Uint32 Flags,
+ size_t Length, const void *Data
+ );
+
+// === GLOBALS ===
+tChain *gapFirewall_Chains[MAX_ADDRTYPE+1];
+tChain gFirewall_DROP = {.Name="DROP"};
+tChain gFirewall_ACCEPT = {.Name="ACCEPT"};
+tChain gFirewall_RETURN = {.Name="RETURN"};
+
+// === CODE ===
+/**
+ * \brief Apply a rule to a packet
+ * \return -1 for no match, -2 for RETURN, eFirewallAction otherwise
+ */
+int IPTables_DoRule(
+ tRule *Rule, int AddrType,
+ const void *Src, const void *Dest,
+ Uint8 Type, Uint32 Flags,
+ size_t Length, const void *Data)
+{
+ int rv;
+ // Check if source doesn't match
+ if( !IPStack_CompareAddress(AddrType, Src, Rule->Source, Rule->SourceMask) == !Rule->bInvertSource )
+ return -1;
+ // Check if destination doesn't match
+ if( !IPStack_CompareAddress(AddrType, Dest, Rule->Dest, Rule->DestMask) == !Rule->bInvertDest )
+ return -1;
+
+ // TODO: Handle modules (UDP/TCP/etc)
+ tModuleRule *modrule;
+ for( modrule = Rule->Modules; modrule; modrule = modrule->Next )
+ {
+ if( !modrule->Mod->Match ) continue;
+ rv = modrule->Mod->Match(modrule, AddrType, Src, Dest, Type, Flags, Length, Data);
+ if(rv != 0) return rv; // No match / action
+ }
+
+ // Update statistics
+ Rule->PacketCount ++;
+ Rule->ByteCount += Length;
+
+ return IPTables_TestChain(Rule->Target, AddrType, Src, Dest, Type, Flags, Length, Data);
+}
+
+/**
+ * \brief Tests an IPv4 chain on a packet
+ * \return Boolean Disallow (0: Packet Allowed, 1: Drop, 2: Reject, 3: Continue, -1 no match)
+ */
+int IPTables_TestChain(
+ const char *RuleName,
+ const int AddressType,
+ const void *Src, const void *Dest,
+ Uint8 Type, Uint32 Flags,
+ size_t Length, const void *Data
+ )
+{
+ int rv;
+ tChain *chain;
+ tRule *rule;
+
+ if( AddressType >= MAX_ADDRTYPE ) return -1; // Bad address type
+
+ // Catch builtin targets
+ if(strcmp(RuleName, "") == 0) return -1; // No action
+ if(strcmp(RuleName, "ACCEPT") == 0) return 0; // Accept packet
+ if(strcmp(RuleName, "DROP") == 0) return 1; // Drop packet
+ if(strcmp(RuleName, "RETURN") == 0) return -2; // Return from rule
+
+ // Find the rule
+ for( chain = gapFirewall_Chains[AddressType]; chain; chain = chain->Next )
+ {
+ if( strcmp(chain->Name, RuleName) == 0 )
+ break;
+ }
+ if( !chain ) return -1; // Bad rule name
+
+ // Check the rules
+ for( rule = chain->FirstRule; rule; rule = rule->Next )
+ {
+ rv = IPTables_DoRule(rule, AddressType, Src, Dest, Type, Flags, Length, Data);
+ if( rv == -1 )
+ continue ;
+ if( rv == -2 ) // -2 = Return from a chain/table, pretend no match
+ return -1;
+
+ return rv;
+ }
+
+
+ return 0; // Accept all for now
+}
--- /dev/null
+/*
+ */
+#ifndef _FIREWALL_H_
+#define _FIREWALL_H_
+
+enum eFirewallActions
+{
+ FIREWALL_ACCEPT,
+ FIREWALL_DROP
+};
+
+/**
+ * \brief Tests a packet on a chain
+ */
+extern int IPTables_TestChain(
+ const char *RuleName,
+ const int AddressType,
+ const void *Src, const void *Dest,
+ Uint8 Type, Uint32 Flags,
+ size_t Length, const void *Data
+ );
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - ICMP Handling
+ */
+#include "ipstack.h"
+#include "ipv4.h"
+#include "icmp.h"
+
+// === CONSTANTS ===
+#define PING_SLOTS 64
+
+// === PROTOTYPES ===
+void ICMP_Initialise();
+void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
+
+// === GLOBALS ===
+struct {
+ tInterface *Interface;
+ int bArrived;
+} gICMP_PingSlots[PING_SLOTS];
+
+// === CODE ===
+/**
+ * \fn void ICMP_Initialise()
+ * \brief Initialise the ICMP Layer
+ */
+void ICMP_Initialise()
+{
+ IPv4_RegisterCallback(IP4PROT_ICMP, ICMP_GetPacket);
+}
+
+/**
+ * \fn void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+ * \brief Handles a packet from the IP Layer
+ */
+void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+ tICMPHeader *hdr = Buffer;
+
+ //Log_Debug("ICMPv4", "Length = %i", Length);
+ Log_Debug("ICMPv4", "hdr->Type, hdr->Code = %i, %i", hdr->Type, hdr->Code);
+ //Log_Debug("ICMPv4", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
+ Log_Debug("ICMPv4", "hdr->ID = 0x%x", ntohs(hdr->ID));
+ Log_Debug("ICMPv4", "hdr->Sequence = 0x%x", ntohs(hdr->Sequence));
+
+ switch(hdr->Type)
+ {
+ // -- 0: Echo Reply
+ case ICMP_ECHOREPLY:
+ if(hdr->Code != 0) {
+ Log_Warning("ICMPv4", "Code == %i for ICMP Echo Reply, should be 0", hdr->Code);
+ return ;
+ }
+ if(hdr->ID != (Uint16)~hdr->Sequence) {
+ Log_Warning("ICMPv4", "ID and Sequence values do not match");
+ //return ;
+ }
+ gICMP_PingSlots[hdr->ID].bArrived = 1;
+ break;
+
+ // -- 3: Destination Unreachable
+ case ICMP_UNREACHABLE:
+ switch(hdr->Code)
+ {
+ case 3: // Port Unreachable
+ Log_Debug("ICMPv4", "Destination Unreachable (Port Unreachable)");
+ break;
+ default:
+ Log_Debug("ICMPv4", "Destination Unreachable (Code %i)", hdr->Code);
+ break;
+ }
+// IPv4_Unreachable( Interface, hdr->Code, htons(hdr->Length)-sizeof(tICMPHeader), hdr->Data );
+ break;
+
+ // -- 8: Echo Request
+ case ICMP_ECHOREQ:
+ if(hdr->Code != 0) {
+ Log_Warning("ICMPv4", "Code == %i for ICMP Echo Request, should be 0", hdr->Code);
+ return ;
+ }
+ //Log_Debug("ICMPv4", "Replying");
+ hdr->Type = ICMP_ECHOREPLY;
+ hdr->Checksum = 0;
+ hdr->Checksum = htons( IPv4_Checksum( (Uint16*)hdr, Length/2 ) );
+ //Log_Debug("ICMPv4", "Checksum = 0x%04x", hdr->Checksum);
+ IPv4_SendPacket(Interface, *(tIPv4*)Address, 1, ntohs(hdr->Sequence), Length, hdr);
+ break;
+ default:
+ break;
+ }
+
+}
+
+/**
+ * \brief Sends ICMP Echo and waits for the reply
+ * \note Times out after \a Interface->TimeoutDelay has elapsed
+ */
+int ICMP_Ping(tInterface *Interface, tIPv4 Addr)
+{
+ Sint64 ts;
+ Sint64 end;
+ char buf[32] = "\x8\0\0\0\0\0\0\0Acess2 I"
+ "P/TCP Stack 1.0\0";
+ tICMPHeader *hdr = (void*)buf;
+ int i;
+
+ for(;;)
+ {
+ for(i=0;i<PING_SLOTS;i++)
+ {
+ if(gICMP_PingSlots[i].Interface == NULL) break;
+ }
+ if( i < PING_SLOTS ) break;
+ Threads_Yield();
+ }
+ gICMP_PingSlots[i].Interface = Interface;
+ gICMP_PingSlots[i].bArrived = 0;
+ hdr->ID = i;
+ hdr->Sequence = ~i;
+ hdr->Checksum = htons( IPv4_Checksum((Uint16*)hdr, sizeof(buf)/2) );
+
+ ts = now();
+
+ IPv4_SendPacket(Interface, Addr, 1, i, sizeof(buf), buf);
+
+ end = ts + Interface->TimeoutDelay;
+ while( !gICMP_PingSlots[i].bArrived && now() < end) Threads_Yield();
+
+ if(now() > end)
+ return -1;
+
+ return (int)( now() - ts );
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - ICMP Handling
+ */
+#ifndef _ICMP_H_
+#define _ICMP_H_
+
+// === TYPEDEFS ===
+typedef struct sICMPHeader tICMPHeader;
+
+// === STRUCTURES ===
+struct sICMPHeader
+{
+ Uint8 Type;
+ Uint8 Code;
+ Uint16 Checksum;
+ Uint16 ID;
+ Uint16 Sequence;
+ Uint8 Data[];
+};
+
+// === CONSTANTS ===
+enum eICMPTypes
+{
+ ICMP_ECHOREPLY = 0,
+ ICMP_UNREACHABLE = 3,
+ ICMP_QUENCH = 4,
+ ICMP_REDIRECT = 5,
+ ICMP_ALTADDR = 6,
+ ICMP_ECHOREQ = 8,
+ ICMP_TRACE = 30 // Information Request
+};
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Interface Control
+ */
+#define DEBUG 0
+#define VERSION VER2(0,10)
+#include "ipstack.h"
+#include "link.h"
+#include <api_drv_common.h>
+#include <api_drv_network.h>
+
+// === CONSTANTS ===
+//! Default timeout value, 30 seconds
+#define DEFAULT_TIMEOUT (30*1000)
+
+// === IMPORTS ===
+extern int IPv4_Ping(tInterface *Iface, tIPv4 Addr);
+//extern int IPv6_Ping(tInterface *Iface, tIPv6 Addr);
+extern tVFS_Node gIP_RouteNode;
+
+// === PROTOTYPES ===
+char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name);
+ int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data);
+
+ int IPStack_AddFile(tSocketFile *File);
+tInterface *IPStack_AddInterface(const char *Device, const char *Name);
+tAdapter *IPStack_GetAdapter(const char *Path);
+
+char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name);
+ int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data);
+
+// === GLOBALS ===
+tVFS_NodeType gIP_InterfaceNodeType = {
+ .ReadDir = IPStack_Iface_ReadDir,
+ .FindDir = IPStack_Iface_FindDir,
+ .IOCtl = IPStack_Iface_IOCtl
+};
+//! Loopback (127.0.0.0/8, ::1) Pseudo-Interface
+tInterface gIP_LoopInterface = {
+ .Node = {
+ .ImplPtr = &gIP_LoopInterface,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Size = -1,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Type = &gIP_InterfaceNodeType
+ },
+ .Adapter = NULL,
+ .Type = 0
+};
+tShortSpinlock glIP_Interfaces;
+tInterface *gIP_Interfaces = NULL;
+tInterface *gIP_Interfaces_Last = NULL;
+
+tSocketFile *gIP_FileTemplates;
+
+tAdapter gIP_LoopAdapter = {
+ .DeviceLen = 8,
+ .Device = "LOOPBACK"
+ };
+tMutex glIP_Adapters;
+tAdapter *gIP_Adapters = NULL;
+ int giIP_NextIfaceId = 1;
+
+// === CODE ===
+
+/**
+ * \brief Read from the IP Stack's Device Directory
+ */
+char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tInterface *iface;
+ char *name;
+ ENTER("pNode iPos", Node, Pos);
+
+
+ // Routing Subdir
+ if( Pos == 0 ) {
+ LEAVE('s', "routes");
+ return strdup("routes");
+ }
+ // Pseudo Interfaces
+ if( Pos == 1 ) {
+ LEAVE('s', "lo");
+ return strdup("lo");
+ }
+ Pos -= 2;
+
+ // Traverse the list
+ for( iface = gIP_Interfaces; iface && Pos--; iface = iface->Next ) ;
+
+ // Did we run off the end?
+ if(!iface) {
+ LEAVE('n');
+ return NULL;
+ }
+
+ name = malloc(4);
+ if(!name) {
+ Log_Warning("IPStack", "IPStack_Root_ReadDir - malloc error");
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Create the name
+ Pos = iface->Node.ImplInt;
+ if(Pos < 10) {
+ name[0] = '0' + Pos;
+ name[1] = '\0';
+ }
+ else if(Pos < 100) {
+ name[0] = '0' + Pos/10;
+ name[1] = '0' + Pos%10;
+ name[2] = '\0';
+ }
+ else {
+ name[0] = '0' + Pos/100;
+ name[1] = '0' + (Pos/10)%10;
+ name[2] = '0' + Pos%10;
+ name[3] = '\0';
+ }
+
+ LEAVE('s', name);
+ // Return the pre-generated name
+ return name;
+}
+
+/**
+ * \brief Get the node of an interface
+ */
+tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name)
+{
+ #if 0
+ int i, num;
+ #endif
+ tInterface *iface;
+
+ ENTER("pNode sName", Node, Name);
+
+ // Routing subdir
+ if( strcmp(Name, "routes") == 0 ) {
+ LEAVE('p', &gIP_RouteNode);
+ return &gIP_RouteNode;
+ }
+
+ // Loopback
+ if( strcmp(Name, "lo") == 0 ) {
+ LEAVE('p', &gIP_LoopInterface.Node);
+ return &gIP_LoopInterface.Node;
+ }
+
+ for( iface = gIP_Interfaces; iface; iface = iface->Next )
+ {
+ if( strcmp(iface->Name, Name) == 0 )
+ {
+ LEAVE('p', &iface->Node);
+ return &iface->Node;
+ }
+ }
+
+ LEAVE('p', NULL);
+ return NULL;
+}
+
+static const char *casIOCtls_Root[] = { DRV_IOCTLNAMES, "add_interface", NULL };
+/**
+ * \brief Handles IOCtls for the IPStack root
+ */
+int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ int tmp;
+ ENTER("pNode iID pData", Node, ID, Data);
+
+ switch(ID)
+ {
+ // --- Standard IOCtls (0-3) ---
+ BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Root)
+
+ /*
+ * add_interface
+ * - Adds a new IP interface and binds it to a device
+ */
+ case 4:
+ if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1);
+ if( !CheckString( Data ) ) LEAVE_RET('i', -1);
+ LOG("New interface for '%s'", Data);
+ {
+ char name[4] = "";
+ tInterface *iface = IPStack_AddInterface(Data, name);
+ if(iface == NULL) LEAVE_RET('i', -1);
+ tmp = iface->Node.ImplInt;
+ }
+ LEAVE_RET('i', tmp);
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \fn tInterface *IPStack_AddInterface(char *Device)
+ * \brief Adds an interface to the list
+ */
+tInterface *IPStack_AddInterface(const char *Device, const char *Name)
+{
+ tInterface *iface;
+ tAdapter *card;
+ int nameLen;
+
+ ENTER("sDevice", Device);
+
+ card = IPStack_GetAdapter(Device);
+ if( !card ) {
+ Log_Debug("IPStack", "Unable to open card '%s'", Device);
+ LEAVE('n');
+ return NULL; // ERR_YOURBAD
+ }
+
+ nameLen = sprintf(NULL, "%i", giIP_NextIfaceId);
+
+ iface = malloc(
+ sizeof(tInterface)
+ + nameLen + 1
+ + IPStack_GetAddressSize(-1)*3 // Address, Route->Network, Route->NextHop
+ );
+ if(!iface) {
+ Log_Warning("IPStack", "AddInterface - malloc() failed");
+ LEAVE('n');
+ return NULL; // Return ERR_MYBAD
+ }
+
+ iface->Next = NULL;
+ iface->Type = 0; // Unset type
+ iface->Address = iface->Name + nameLen + 1; // Address
+ iface->Route.Network = iface->Address + IPStack_GetAddressSize(-1);
+ iface->Route.NextHop = iface->Route.Network + IPStack_GetAddressSize(-1);
+
+ // Create Node
+ iface->Node.ImplPtr = iface;
+ iface->Node.Flags = VFS_FFLAG_DIRECTORY;
+ iface->Node.Size = -1;
+ iface->Node.NumACLs = 1;
+ iface->Node.ACLs = &gVFS_ACL_EveryoneRX;
+ iface->Node.Type = &gIP_InterfaceNodeType;
+
+ // Set Defaults
+ iface->TimeoutDelay = DEFAULT_TIMEOUT;
+
+ // Get adapter handle
+ iface->Adapter = card;
+
+ // Delay setting ImplInt until after the adapter is opened
+ // Keeps things simple
+ iface->Node.ImplInt = giIP_NextIfaceId++;
+ sprintf(iface->Name, "%i", (int)iface->Node.ImplInt);
+
+ // Append to list
+ SHORTLOCK( &glIP_Interfaces );
+ if( gIP_Interfaces ) {
+ gIP_Interfaces_Last->Next = iface;
+ gIP_Interfaces_Last = iface;
+ }
+ else {
+ gIP_Interfaces = iface;
+ gIP_Interfaces_Last = iface;
+ }
+ SHORTREL( &glIP_Interfaces );
+
+// gIP_DriverInfo.RootNode.Size ++;
+
+ // Success!
+ LEAVE('p', iface);
+ return iface;
+}
+
+/**
+ * \brief Adds a file to the socket list
+ */
+int IPStack_AddFile(tSocketFile *File)
+{
+ Log_Log("IPStack", "Added file '%s'", File->Name);
+ File->Next = gIP_FileTemplates;
+ gIP_FileTemplates = File;
+ return 0;
+}
+
+// ---
+// VFS Functions
+// ---
+/**
+ * \brief Read from an interface's directory
+ */
+char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tSocketFile *file = gIP_FileTemplates;
+ while(Pos-- && file) {
+ file = file->Next;
+ }
+
+ if(!file) return NULL;
+
+ return strdup(file->Name);
+}
+
+/**
+ * \brief Gets a named node from an interface directory
+ */
+tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name)
+{
+ tSocketFile *file = gIP_FileTemplates;
+
+ // Get file definition
+ for(;file;file = file->Next)
+ {
+ if( strcmp(file->Name, Name) == 0 ) break;
+ }
+ if(!file) return NULL;
+
+ // Pass the buck!
+ return file->Init(Node->ImplPtr);
+}
+
+/**
+ * \brief Names for interface IOCtl Calls
+ */
+static const char *casIOCtls_Iface[] = {
+ DRV_IOCTLNAMES,
+ "getset_type",
+ "get_address", "set_address",
+ "getset_subnet",
+ "get_device",
+ "ping",
+ NULL
+ };
+/**
+ * \brief Handles IOCtls for the IPStack interfaces
+ */
+int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ int tmp, size;
+ tInterface *iface = (tInterface*)Node->ImplPtr;
+ ENTER("pNode iID pData", Node, ID, Data);
+
+ switch(ID)
+ {
+ // --- Standard IOCtls (0-3) ---
+ BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Iface)
+
+ /*
+ * getset_type
+ * - Get/Set the interface type
+ */
+ case 4:
+ // Set Type?
+ if( Data )
+ {
+ // Ok, it's set type
+ if( Threads_GetUID() != 0 ) {
+ LOG("Attempt by non-root to alter an interface (%i)", Threads_GetUID());
+ LEAVE('i', -1);
+ return -1;
+ }
+ if( !CheckMem( Data, sizeof(int) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Set type
+ iface->Type = *(int*)Data;
+ LOG("Interface type set to %i", iface->Type);
+ size = IPStack_GetAddressSize(iface->Type);
+ // Check it's actually valid
+ if( iface->Type != 0 && size == 0 ) {
+ iface->Type = 0;
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Clear address
+ memset(iface->Address, 0, size);
+ }
+ LEAVE('i', iface->Type);
+ return iface->Type;
+
+ /*
+ * get_address
+ * - Get the interface's address
+ */
+ case 5:
+ size = IPStack_GetAddressSize(iface->Type);
+ if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1);
+ memcpy( Data, iface->Address, size );
+ LEAVE('i', 1);
+ return 1;
+
+ /*
+ * set_address
+ * - Set the interface's address
+ */
+ case 6:
+ if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1);
+
+ size = IPStack_GetAddressSize(iface->Type);
+ if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1);
+ // TODO: Protect against trashing
+ LOG("Interface address set to '%s'", IPStack_PrintAddress(iface->Type, Data));
+ memcpy( iface->Address, Data, size );
+ LEAVE_RET('i', 1);
+
+ /*
+ * getset_subnet
+ * - Get/Set the bits in the address subnet
+ */
+ case 7:
+ // Do we want to set the value?
+ if( Data )
+ {
+ // Are we root? (TODO: Check Owner/Group)
+ if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1);
+ // Is the memory valid
+ if( !CheckMem(Data, sizeof(int)) ) LEAVE_RET('i', -1);
+
+ // Is the mask sane?
+ if( *(int*)Data < 0 || *(int*)Data > IPStack_GetAddressSize(iface->Type)*8-1 )
+ LEAVE_RET('i', -1);
+ LOG("Set subnet bits to %i", *(int*)Data);
+ // Ok, set it
+ iface->SubnetBits = *(int*)Data;
+ }
+ LEAVE_RET('i', iface->SubnetBits);
+
+ /*
+ * get_device
+ * - Gets the name of the attached device
+ */
+ case 8:
+ if( iface->Adapter == NULL )
+ LEAVE_RET('i', 0);
+ if( Data == NULL )
+ LEAVE_RET('i', iface->Adapter->DeviceLen);
+ if( !CheckMem( Data, iface->Adapter->DeviceLen+1 ) )
+ LEAVE_RET('i', -1);
+ strcpy( Data, iface->Adapter->Device );
+ LEAVE_RET('i', iface->Adapter->DeviceLen);
+
+ /*
+ * ping
+ * - Send an ICMP Echo
+ */
+ case 9:
+ switch(iface->Type)
+ {
+ case 0:
+ LEAVE_RET('i', 1);
+
+ case 4:
+ if( !CheckMem( Data, sizeof(tIPv4) ) ) LEAVE_RET('i', -1);
+ tmp = IPv4_Ping(iface, *(tIPv4*)Data);
+ LEAVE_RET('i', tmp);
+
+ case 6:
+ LEAVE_RET('i', 1);
+ }
+ break;
+
+ }
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+// --- Internal ---
+/**
+ * \fn tAdapter *IPStack_GetAdapter(const char *Path)
+ * \brief Gets/opens an adapter given the path
+ */
+tAdapter *IPStack_GetAdapter(const char *Path)
+{
+ tAdapter *dev;
+ int tmp;
+
+ ENTER("sPath", Path);
+
+ // Check for loopback
+ if( strcmp(Path, "LOOPBACK") == 0 )
+ {
+ // Initialise if required
+ if( gIP_LoopAdapter.DeviceFD == 0 )
+ {
+ dev = &gIP_LoopAdapter;
+
+ dev->NRef = 1;
+ dev->DeviceLen = 8;
+
+ dev->DeviceFD = VFS_Open( "/Devices/fifo/anon", VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE );
+ if( dev->DeviceFD == -1 ) {
+ Log_Warning("IPStack", "Unable to open FIFO '/Devices/fifo/anon' for loopback");
+ return NULL;
+ }
+
+ dev->MacAddr.B[0] = 'A';
+ dev->MacAddr.B[1] = 'c';
+ dev->MacAddr.B[2] = 'e';
+ dev->MacAddr.B[3] = 's';
+ dev->MacAddr.B[4] = 's';
+ dev->MacAddr.B[5] = '2';
+
+ // Start watcher
+ Link_WatchDevice( dev );
+ }
+ LEAVE('p', &gIP_LoopAdapter);
+ return &gIP_LoopAdapter;
+ }
+
+ Mutex_Acquire( &glIP_Adapters );
+
+ // Check if this adapter is already open
+ for( dev = gIP_Adapters; dev; dev = dev->Next )
+ {
+ if( strcmp(dev->Device, Path) == 0 ) {
+ dev->NRef ++;
+ Mutex_Release( &glIP_Adapters );
+ LEAVE('p', dev);
+ return dev;
+ }
+ }
+
+ // Ok, so let's open it
+ dev = malloc( sizeof(tAdapter) + strlen(Path) + 1 );
+ if(!dev) {
+ Log_Warning("IPStack", "GetAdapter - malloc() failed");
+ Mutex_Release( &glIP_Adapters );
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Fill Structure
+ strcpy( dev->Device, Path );
+ dev->NRef = 1;
+ dev->DeviceLen = strlen(Path);
+
+ // Open Device
+ dev->DeviceFD = VFS_Open( dev->Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE );
+ if( dev->DeviceFD == -1 ) {
+ free( dev );
+ Mutex_Release( &glIP_Adapters );
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Check that it is a network interface
+ tmp = VFS_IOCtl(dev->DeviceFD, 0, NULL);
+ LOG("Device type = %i", tmp);
+ if( tmp != DRV_TYPE_NETWORK ) {
+ Log_Warning("IPStack", "IPStack_GetAdapter: '%s' is not a network interface", dev->Device);
+ VFS_Close( dev->DeviceFD );
+ free( dev );
+ Mutex_Release( &glIP_Adapters );
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Get MAC Address
+ VFS_IOCtl(dev->DeviceFD, NET_IOCTL_GETMAC, &dev->MacAddr);
+
+ // Add to list
+ dev->Next = gIP_Adapters;
+ gIP_Adapters = dev;
+
+ Mutex_Release( &glIP_Adapters );
+
+ // Start watcher
+ Link_WatchDevice( dev );
+
+ LEAVE('p', dev);
+ return dev;
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Common Header
+ */
+#ifndef _IPSTACK_H_
+#define _IPSTACK_H_
+
+#include <acess.h>
+#include <vfs.h>
+
+typedef union uIPv4 tIPv4;
+typedef union uIPv6 tIPv6;
+typedef struct sMacAddr tMacAddr;
+typedef struct sAdapter tAdapter;
+typedef struct sInterface tInterface;
+typedef struct sSocketFile tSocketFile;
+
+typedef void (*tIPCallback)(tInterface *Interface, void *Address, int Length, void *Buffer);
+
+enum eInterfaceTypes {
+ AF_NULL,
+ AF_INET4 = 4, // tIPv4
+ AF_INET6 = 6 // tIPv6
+};
+
+union uIPv4 {
+ Uint32 L;
+ Uint8 B[4];
+} __attribute__((packed));
+
+union uIPv6 {
+ Uint16 W[8];
+ Uint32 L[4];
+ Uint8 B[16];
+} __attribute__((packed));
+
+struct sMacAddr {
+ Uint8 B[6];
+} __attribute__((packed));
+
+/**
+ * \brief Route definition structure
+ */
+typedef struct sRoute {
+ struct sRoute *Next;
+
+ tVFS_Node Node; //!< Node for route manipulation
+
+ tInterface *Interface; //!< Interface for this route
+ int AddressType; //!< 0: Invalid, 4: IPv4, 6: IPv4
+ void *Network; //!< Network - Pointer to tIPv4/tIPv6/... at end of structure
+ int SubnetBits; //!< Number of bits in \a Network that are valid
+ void *NextHop; //!< Next Hop address - Pointer to tIPv4/tIPv6/... at end of structure
+ int Metric; //!< Route priority
+} tRoute;
+
+struct sInterface {
+ struct sInterface *Next; //!< Next interface in list
+
+ tVFS_Node Node; //!< Node to use the interface
+
+ tAdapter *Adapter; //!< Adapter the interface is associated with
+ int TimeoutDelay; //!< Time in miliseconds before a packet times out
+ int Type; //!< Interface type, see ::eInterfaceTypes
+
+ void *Address; //!< IP address (stored after the Name)
+ int SubnetBits; //!< Number of bits that denote the address network
+
+ tRoute Route; //!< Interface route
+
+ char Name[];
+};
+
+/**
+ * \brief Represents a network adapter
+ */
+struct sAdapter {
+ struct sAdapter *Next;
+
+ int DeviceFD; //!< File descriptor of the device
+ int NRef; //!< Number of times it's been referenced
+
+ tMacAddr MacAddr; //!< Physical address of the adapter
+ int DeviceLen; //!< Device name length
+ char Device[]; //!< Device name
+};
+
+/**
+ * \brief Describes a socket file definition
+ */
+struct sSocketFile
+{
+ struct sSocketFile *Next;
+ const char *Name;
+
+ tVFS_Node *(*Init)(tInterface *Interface);
+};
+
+static const tMacAddr cMAC_BROADCAST = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
+static const tMacAddr cMAC_ZERO = {{0x00,0x00,0x00,0x00,0x00,0x00}};
+
+#define MAC_SET(t,v) memcpy(&(t),&(v),sizeof(tMacAddr))
+#define IP4_SET(t,v) (t).L = (v).L;
+#define IP6_SET(t,v) memcpy(&(t),&(v),sizeof(tIPv6))
+
+#define MAC_EQU(a,b) (memcmp(&(a),&(b),sizeof(tMacAddr))==0)
+#define IP4_EQU(a,b) ((a).L==(b).L)
+#define IP6_EQU(a,b) (memcmp(&(a),&(b),sizeof(tIPv6))==0)
+
+// === FUNCTIONS ===
+#define htonb(v) (v)
+#define htons(v) BigEndian16(v)
+#define htonl(v) BigEndian32(v)
+#define ntonb(v) (v)
+#define ntohs(v) BigEndian16(v)
+#define ntohl(v) BigEndian32(v)
+
+extern int IPStack_AddFile(tSocketFile *File);
+extern int IPStack_GetAddressSize(int AddressType);
+extern int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits);
+extern const char *IPStack_PrintAddress(int AddressType, const void *Address);
+
+extern tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address);
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - IPv4 Protcol Handling
+ */
+#define DEBUG 1
+#include "ipstack.h"
+#include "link.h"
+#include "ipv4.h"
+#include "firewall.h"
+
+#define DEFAULT_TTL 32
+
+// === IMPORTS ===
+extern tInterface *gIP_Interfaces;
+extern void ICMP_Initialise();
+extern int ICMP_Ping(tInterface *Interface, tIPv4 Addr);
+extern tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address);
+
+// === PROTOTYPES ===
+ int IPv4_Initialise();
+ int IPv4_RegisterCallback(int ID, tIPCallback Callback);
+void IPv4_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
+tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast);
+Uint32 IPv4_Netmask(int FixedBits);
+Uint16 IPv4_Checksum(const void *Buf, size_t Length);
+ int IPv4_Ping(tInterface *Iface, tIPv4 Addr);
+
+// === GLOBALS ===
+tIPCallback gaIPv4_Callbacks[256];
+
+// === CODE ===
+/**
+ * \brief Initialise the IPv4 Code
+ */
+int IPv4_Initialise()
+{
+ ICMP_Initialise();
+ Link_RegisterType(IPV4_ETHERNET_ID, IPv4_int_GetPacket);
+ return 1;
+}
+
+/**
+ * \brief Registers a callback
+ * \param ID 8-bit packet type ID
+ * \param Callback Callback function
+ */
+int IPv4_RegisterCallback(int ID, tIPCallback Callback)
+{
+ if( ID < 0 || ID > 255 ) return 0;
+ if( gaIPv4_Callbacks[ID] ) return 0;
+ gaIPv4_Callbacks[ID] = Callback;
+ return 1;
+}
+
+/**
+ * \brief Creates and sends an IPv4 Packet
+ * \param Iface Interface
+ * \param Address Destination IP
+ * \param Protocol Protocol ID
+ * \param ID Some random ID number
+ * \param Length Data Length
+ * \param Data Packet Data
+ * \return Boolean Success
+ */
+int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data)
+{
+ tMacAddr to;
+ int bufSize = sizeof(tIPv4Header) + Length;
+ char buf[bufSize];
+ tIPv4Header *hdr = (void*)buf;
+ int ret;
+
+ to = ARP_Resolve4(Iface, Address);
+ if( MAC_EQU(to, cMAC_ZERO) ) {
+ // No route to host
+ Log_Notice("IPv4", "No route to host %i.%i.%i.%i",
+ Address.B[0], Address.B[1], Address.B[2], Address.B[3]);
+ return 0;
+ }
+
+ // OUTPUT Firewall rule go here
+ ret = IPTables_TestChain("OUTPUT",
+ 4, (tIPv4*)Iface->Address, &Address,
+ Protocol, 0,
+ Length, Data);
+ if(ret > 0) {
+ // Just drop it (with an error)
+ Log_Notice("IPv4", "Firewall dropped packet");
+ return 0;
+ }
+
+ memcpy(&hdr->Options[0], Data, Length);
+ hdr->Version = 4;
+ hdr->HeaderLength = sizeof(tIPv4Header)/4;
+ hdr->DiffServices = 0; // TODO: Check
+
+ hdr->Reserved = 0;
+ hdr->DontFragment = 0;
+ hdr->MoreFragments = 0;
+ hdr->FragOffLow = 0;
+ hdr->FragOffHi = 0;
+
+ hdr->TotalLength = htons( bufSize );
+ hdr->Identifcation = htons( ID ); // TODO: Check
+ hdr->TTL = DEFAULT_TTL;
+ hdr->Protocol = Protocol;
+ hdr->HeaderChecksum = 0; // Will be set later
+ hdr->Source = *(tIPv4*)Iface->Address;
+ hdr->Destination = Address;
+ hdr->HeaderChecksum = htons( IPv4_Checksum(hdr, sizeof(tIPv4Header)) );
+
+ Log_Log("IPv4", "Sending packet to %i.%i.%i.%i",
+ Address.B[0], Address.B[1], Address.B[2], Address.B[3]);
+ Link_SendPacket(Iface->Adapter, IPV4_ETHERNET_ID, to, bufSize, buf);
+ return 1;
+}
+
+/**
+ * \fn void IPv4_int_GetPacket(tInterface *Adapter, tMacAddr From, int Length, void *Buffer)
+ * \brief Process an IPv4 Packet
+ */
+void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
+{
+ tIPv4Header *hdr = Buffer;
+ tInterface *iface;
+ Uint8 *data;
+ int dataLength;
+ int ret;
+
+ if(Length < sizeof(tIPv4Header)) return;
+
+ #if 0
+ //Log_Log("IPv4", "Version = %i", hdr->Version);
+ //Log_Log("IPv4", "HeaderLength = %i", hdr->HeaderLength);
+ //Log_Log("IPv4", "DiffServices = %i", hdr->DiffServices);
+ Log_Debug("IPv4", "TotalLength = %i", ntohs(hdr->TotalLength) );
+ //Log_Log("IPv4", "Identifcation = %i", ntohs(hdr->Identifcation) );
+ //Log_Log("IPv4", "TTL = %i", hdr->TTL );
+ Log_Debug("IPv4", "Protocol = %i", hdr->Protocol );
+ //Log_Log("IPv4", "HeaderChecksum = 0x%x", ntohs(hdr->HeaderChecksum) );
+ Log_Debug("IPv4", "Source = %i.%i.%i.%i",
+ hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3] );
+ Log_Debug("IPv4", "Destination = %i.%i.%i.%i",
+ hdr->Destination.B[0], hdr->Destination.B[1],
+ hdr->Destination.B[2], hdr->Destination.B[3] );
+ #endif
+
+ // Check that the version IS IPv4
+ if(hdr->Version != 4) {
+ Log_Log("IPv4", "hdr->Version(%i) != 4", hdr->Version);
+ return;
+ }
+
+ // Check Header checksum
+ {
+ Uint16 hdrVal, compVal;
+ hdrVal = ntohs(hdr->HeaderChecksum);
+ hdr->HeaderChecksum = 0;
+ compVal = IPv4_Checksum(hdr, hdr->HeaderLength * 4);
+ if(hdrVal != compVal) {
+ Log_Log("IPv4", "Header checksum fails (%04x != %04x)", hdrVal, compVal);
+ return ;
+ }
+ hdr->HeaderChecksum = hdrVal;
+ }
+
+ // Check Packet length
+ if( ntohs(hdr->TotalLength) > Length) {
+ Log_Log("IPv4", "hdr->TotalLength(%i) > Length(%i)", ntohs(hdr->TotalLength), Length);
+ return;
+ }
+
+ // TODO: Handle packet fragmentation
+
+
+ Log_Debug("IPv4", " From %i.%i.%i.%i to %i.%i.%i.%i",
+ hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3],
+ hdr->Destination.B[0], hdr->Destination.B[1], hdr->Destination.B[2], hdr->Destination.B[3]
+ );
+
+ // Get Data and Data Length
+ dataLength = ntohs(hdr->TotalLength) - sizeof(tIPv4Header);
+ data = &hdr->Options[0];
+
+ // Get Interface (allowing broadcasts)
+ iface = IPv4_GetInterface(Adapter, hdr->Destination, 1);
+
+ // Firewall rules
+ if( iface ) {
+ // Incoming Packets
+ ret = IPTables_TestChain("INPUT",
+ 4, &hdr->Source, &hdr->Destination,
+ hdr->Protocol, 0,
+ dataLength, data
+ );
+ }
+ else {
+ // Routed packets
+ ret = IPTables_TestChain("FORWARD",
+ 4, &hdr->Source, &hdr->Destination,
+ hdr->Protocol, 0,
+ dataLength, data
+ );
+ }
+ switch(ret)
+ {
+ // 0 - Allow
+ case 0: break;
+ // 1 - Silent Drop
+ case 1:
+ Log_Debug("IPv4", "Silently dropping packet");
+ return ;
+ case -1:
+ // Bad rule
+ break ;
+ // Unknown, silent drop
+ default:
+ Log_Warning("IPv4", "Unknown firewall rule");
+ return ;
+ }
+
+ // Routing
+ if(!iface)
+ {
+ tMacAddr to;
+ tRoute *rt;
+
+ Log_Debug("IPv4", "Route the packet");
+ // Drop the packet if the TTL is zero
+ if( hdr->TTL == 0 ) {
+ Log_Warning("IPv4", "TODO: Send ICMP-Timeout when TTL exceeded");
+ return ;
+ }
+
+ hdr->TTL --;
+
+ rt = IPStack_FindRoute(4, NULL, &hdr->Destination); // Get the route (gets the interface)
+ if( !rt || !rt->Interface )
+ return ;
+ to = ARP_Resolve4(rt->Interface, hdr->Destination); // Resolve address
+ if( MAC_EQU(to, cMAC_ZERO) )
+ return ;
+
+ // Send packet
+ Log_Log("IPv4", "Forwarding packet to %i.%i.%i.%i (via %i.%i.%i.%i)",
+ hdr->Destination.B[0], hdr->Destination.B[1],
+ hdr->Destination.B[2], hdr->Destination.B[3],
+ ((tIPv4*)rt->NextHop)->B[0], ((tIPv4*)rt->NextHop)->B[1],
+ ((tIPv4*)rt->NextHop)->B[2], ((tIPv4*)rt->NextHop)->B[3]);
+ Link_SendPacket(rt->Interface->Adapter, IPV4_ETHERNET_ID, to, Length, Buffer);
+
+
+ return ;
+ }
+
+ // Send it on
+ if( !gaIPv4_Callbacks[hdr->Protocol] ) {
+ Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol);
+ return ;
+ }
+
+ gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data );
+}
+
+/**
+ * \fn tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address)
+ * \brief Searches an adapter for a matching address
+ * \param Adapter Incoming Adapter
+ * \param Address Destination Address
+ * \param Broadcast Allow broadcast packets
+ */
+tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast)
+{
+ tInterface *iface = NULL;
+ Uint32 netmask;
+ Uint32 addr, this;
+
+ ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast);
+
+ addr = ntohl( Address.L );
+ LOG("addr = 0x%x", addr);
+
+ for( iface = gIP_Interfaces; iface; iface = iface->Next)
+ {
+ if( iface->Adapter != Adapter ) continue;
+ if( iface->Type != 4 ) continue;
+ if( IP4_EQU(Address, *(tIPv4*)iface->Address) ) {
+ LOG("Exact match");
+ LEAVE('p', iface);
+ return iface;
+ }
+
+ if( !Broadcast ) continue;
+
+ // Check for broadcast
+ this = ntohl( ((tIPv4*)iface->Address)->L );
+ netmask = IPv4_Netmask(iface->SubnetBits);
+ LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits);
+
+ if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) )
+ {
+ LOG("Broadcast match");
+ LEAVE('p', iface);
+ return iface;
+ }
+ }
+ LEAVE('n');
+ return NULL;
+}
+
+/**
+ * \brief Convert a network prefix to a netmask
+ * \param FixedBits Netmask size (/n)
+ *
+ * For example /24 will become 255.255.255.0 (0xFFFFFF00)
+ */
+Uint32 IPv4_Netmask(int FixedBits)
+{
+ Uint32 ret = 0xFFFFFFFF;
+ if( FixedBits == 0 )
+ return 0;
+ if( FixedBits < 32 )
+ {
+ ret >>= (32-FixedBits);
+ ret <<= (32-FixedBits);
+ }
+ // Returns a native endian netmask
+ return ret;
+}
+
+/**
+ * \brief Calculate the IPv4 Checksum
+ * \param Buf Input buffer
+ * \param Size Size of input
+ *
+ * One's complement sum of all 16-bit words (bitwise inverted)
+ */
+Uint16 IPv4_Checksum(const void *Buf, size_t Length)
+{
+ const Uint16 *words = Buf;
+ Uint32 sum = 0;
+ int i;
+
+ // Sum all whole words
+ for(i = 0; i < Length/2; i++ )
+ {
+ sum += ntohs(words[i]);
+ }
+ if( Length & 1 )
+ sum += ntohs( words[i] & 0xFF );
+
+ // Apply one's complement
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ return ~sum;
+}
+
+/**
+ * \brief Sends an ICMP Echo and waits for a reply
+ * \param IFace Interface
+ * \param Addr Destination address
+ */
+int IPv4_Ping(tInterface *IFace, tIPv4 Addr)
+{
+ return ICMP_Ping(IFace, Addr);
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - IPv4 Definitions
+ */
+#ifndef _IPV4_H_
+#define _IPV4_H_
+
+#include "ipstack.h"
+
+typedef struct sIPv4Header tIPv4Header;
+
+struct sIPv4Header
+{
+ struct {
+ // Spec says Version is first, but stupid bit ordering
+ unsigned HeaderLength: 4; // in 4-byte chunks
+ unsigned Version: 4; // = 4
+ } __attribute__((packed));
+ Uint8 DiffServices; // Differentiated Services
+ Uint16 TotalLength;
+ Uint16 Identifcation;
+
+ struct {
+ unsigned Reserved: 1;
+ unsigned DontFragment: 1;
+ unsigned MoreFragments: 1;
+ unsigned FragOffLow: 5;
+ } __attribute__((packed));
+ Uint8 FragOffHi; // Number of 8-byte blocks from the original start
+
+ Uint8 TTL; // Max number of hops, effectively
+ Uint8 Protocol;
+ Uint16 HeaderChecksum; // One's Complement Sum of the entire header must equal zero
+
+ tIPv4 Source;
+ tIPv4 Destination;
+
+ Uint8 Options[];
+} __attribute__((packed));
+
+#define IP4PROT_ICMP 1
+#define IP4PROT_TCP 6
+#define IP4PROT_UDP 17
+
+#define IPV4_ETHERNET_ID 0x0800
+
+// === FUNCTIONS ===
+extern int IPv4_RegisterCallback(int ID, tIPCallback Callback);
+extern Uint16 IPv4_Checksum(const void *Buf, size_t Length);
+extern int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data);
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - IPv6 Protcol Handling
+ */
+#include "ipstack.h"
+#include "link.h"
+#include "ipv6.h"
+#include "firewall.h"
+
+// === IMPORTS ===
+extern tInterface *gIP_Interfaces;
+extern Uint32 IPv4_Netmask(int FixedBits);
+
+// === PROTOTYPES ===
+ int IPv6_Initialise();
+ int IPv6_RegisterCallback(int ID, tIPCallback Callback);
+void IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
+tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
+
+// === GLOBALS ===
+tIPCallback gaIPv6_Callbacks[256];
+
+// === CODE ===
+/**
+ * \brief Initialise the IPv6 handling code
+ */
+int IPv6_Initialise()
+{
+ Link_RegisterType(IPV6_ETHERNET_ID, IPv6_int_GetPacket);
+ return 1;
+}
+
+/**
+ * \brief Registers a callback
+ * \param ID 8-bit packet type ID
+ * \param Callback Callback function
+ */
+int IPv6_RegisterCallback(int ID, tIPCallback Callback)
+{
+ if( ID < 0 || ID > 255 ) return 0;
+ if( gaIPv6_Callbacks[ID] ) return 0;
+ gaIPv6_Callbacks[ID] = Callback;
+ return 1;
+}
+
+/**
+ * \brief Creates and sends an IPv6 Packet
+ * \param Iface Interface
+ * \param Destination Destination IP
+ * \param Protocol Protocol ID
+ * \param Length Data Length
+ * \param Data Packet Data
+ * \return Boolean Success
+ */
+int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data)
+{
+ return 0;
+}
+
+/**
+ * \fn void IPv6_int_GetPacket(tInterface *Interface, tMacAddr From, int Length, void *Buffer)
+ * \brief Process an IPv6 Packet
+ * \param Interface Input interface
+ * \param From Source MAC address
+ * \param Length Packet length
+ * \param Buffer Packet data
+ */
+void IPv6_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
+{
+ tInterface *iface;
+ tIPv6Header *hdr = Buffer;
+ int ret, dataLength;
+ char *dataPtr;
+ Uint8 nextHeader;
+
+ if(Length < sizeof(tIPv6Header)) return;
+
+ hdr->Head = ntohl(hdr->Head);
+
+ //if( ((hdr->Head >> (20+8)) & 0xF) != 6 )
+ if( hdr->Version != 6 )
+ return;
+
+ #if 1
+ Log_Debug("IPv6", "hdr = {");
+ Log_Debug("IPv6", " .Version = %i", hdr->Version );
+ Log_Debug("IPv6", " .TrafficClass = %i", hdr->TrafficClass );
+ Log_Debug("IPv6", " .FlowLabel = %i", hdr->FlowLabel );
+ Log_Debug("IPv6", " .PayloadLength = 0x%04x", ntohs(hdr->PayloadLength) );
+ Log_Debug("IPv6", " .NextHeader = 0x%02x", hdr->NextHeader );
+ Log_Debug("IPv6", " .HopLimit = 0x%02x", hdr->HopLimit );
+ Log_Debug("IPv6", " .Source = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Source );
+ Log_Debug("IPv6", " .Destination = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Destination );
+ Log_Debug("IPv6", "}");
+ #endif
+
+ // No checksum in IPv6
+
+ // Check Packet length
+ if( ntohs(hdr->PayloadLength)+sizeof(tIPv6Header) > Length) {
+ Log_Log("IPv6", "hdr->PayloadLength(%i) > Length(%i)", ntohs(hdr->PayloadLength), Length);
+ return;
+ }
+
+ // Process Options
+ nextHeader = hdr->NextHeader;
+ dataPtr = hdr->Data;
+ dataLength = hdr->PayloadLength;
+ for( ;; )
+ {
+ struct {
+ Uint8 NextHeader;
+ Uint8 Length; // In 8-byte chunks, with 0 being 8 bytes long
+ Uint8 Data[];
+ } *optionHdr;
+ optionHdr = (void*)dataPtr;
+ // Hop-by-hop options
+ if(nextHeader == 0)
+ {
+ // TODO: Parse the options (actually, RFC2460 doesn't specify any)
+ }
+ // Routing Options
+ else if(nextHeader == 43)
+ {
+ // TODO: Routing Header options
+ }
+ else
+ {
+ break; // Unknown, pass on
+ }
+ nextHeader = optionHdr->NextHeader;
+ dataPtr += (optionHdr->Length + 1) * 8; // 8-octet length (0 = 8 bytes long)
+ }
+
+ // Get Interface (allowing broadcasts)
+ iface = IPv6_GetInterface(Adapter, hdr->Destination, 1);
+
+ // Firewall rules
+ if( iface ) {
+ // Incoming Packets
+ ret = IPTables_TestChain("INPUT",
+ 6, &hdr->Source, &hdr->Destination,
+ hdr->NextHeader, 0,
+ hdr->PayloadLength, hdr->Data
+ );
+ }
+ else {
+ // Routed packets
+ ret = IPTables_TestChain("FORWARD",
+ 6, &hdr->Source, &hdr->Destination,
+ hdr->NextHeader, 0,
+ hdr->PayloadLength, hdr->Data
+ );
+ }
+
+ switch(ret)
+ {
+ // 0 - Allow
+ case 0: break;
+ // 1 - Silent Drop
+ case 1:
+ Log_Debug("IPv6", "Silently dropping packet");
+ return ;
+ // Unknown, silent drop
+ default:
+ return ;
+ }
+
+ // Routing
+ if(!iface)
+ {
+ #if 0
+ tMacAddr to;
+ tRoute *rt;
+
+ Log_Debug("IPv6", "Route the packet");
+ // Drop the packet if the TTL is zero
+ if( hdr->HopLimit == 0 ) {
+ Log_Warning("IPv6", "TODO: Sent ICMP-Timeout when TTL exceeded");
+ return ;
+ }
+
+ hdr->HopLimit --;
+
+ rt = IPStack_FindRoute(6, NULL, &hdr->Destination); // Get the route (gets the interface)
+ to = ICMP6_ResolveHWAddr(rt->Interface, hdr->Destination); // Resolve address
+
+ // Send packet
+ Log_Log("IPv6", "Forwarding packet");
+ Link_SendPacket(rt->Interface->Adapter, IPV6_ETHERNET_ID, to, Length, Buffer);
+ #endif
+
+ return ;
+ }
+
+ // Send it on
+ if( !gaIPv6_Callbacks[hdr->NextHeader] ) {
+ Log_Log("IPv6", "Unknown Protocol %i", hdr->NextHeader);
+ return ;
+ }
+
+ gaIPv6_Callbacks[hdr->NextHeader]( iface, &hdr->Source, hdr->PayloadLength, hdr->Data );
+}
+
+/**
+ * \fn tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address)
+ * \brief Searches an adapter for a matching address
+ * \param Adapter Source adapter
+ * \param Address Destination Address
+ * \param Broadcast Allow broadcast?
+ */
+tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast)
+{
+ int i, j;
+ tInterface *iface = NULL;
+ Uint32 netmask;
+
+ for( iface = gIP_Interfaces; iface; iface = iface->Next)
+ {
+ tIPv6 *thisAddr;
+
+ // Check for this adapter
+ if( iface->Adapter != Adapter ) continue;
+
+ // Skip non-IPv6 Interfaces
+ if( iface->Type != 6 ) continue;
+
+ thisAddr = (tIPv6*)iface->Address;
+ // If the address is a perfect match, return this interface
+ if( IP6_EQU(Address, *thisAddr) ) return iface;
+
+ // Check if we want to match broadcast addresses
+ if( !Broadcast ) continue;
+
+ // Check for broadcast
+ // - Check first DWORDs
+ if( iface->SubnetBits > 32 && Address.L[0] != thisAddr->L[0] )
+ continue;
+ if( iface->SubnetBits > 64 && Address.L[1] != thisAddr->L[1] )
+ continue;
+ if( iface->SubnetBits > 96 && Address.L[2] != thisAddr->L[2] )
+ continue;
+
+ // Check final DWORD
+ j = iface->SubnetBits / 32;
+ i = iface->SubnetBits % 32;
+ netmask = IPv4_Netmask( iface->SubnetBits % 32 );
+
+ // Check the last bit of the netmask
+ if( (Address.L[j] >> i) != (thisAddr->L[j] >> i) ) continue;
+
+ // Check that the host portion is one
+ if( (Address.L[j] & ~netmask) != (0xFFFFFFFF & ~netmask) ) continue;
+ if( j >= 2 && Address.L[3] != 0xFFFFFFFF) continue;
+ if( j >= 1 && Address.L[2] != 0xFFFFFFFF) continue;
+ if( j >= 0 && Address.L[1] != 0xFFFFFFFF) continue;
+
+ return iface;
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - IPv6 Definitions
+ */
+#ifndef _IPV6_H_
+#define _IPV6_H_
+
+#include "ipstack.h"
+
+typedef struct sIPv6Header tIPv6Header;
+
+struct sIPv6Header
+{
+ #if 0
+ // High 4: Version
+ // Next 8: Traffic Class
+ // Low 20: Flow Label
+ Uint32 Head;
+ #else
+ union {
+ Uint32 Head; // Allow a ntohl to happen
+ struct {
+ unsigned Version: 4;
+ unsigned TrafficClass: 8;
+ unsigned FlowLabel: 20;
+ } PACKED;
+ } PACKED;
+ #endif
+ Uint16 PayloadLength;
+ Uint8 NextHeader; // Type of payload data
+ Uint8 HopLimit;
+ tIPv6 Source;
+ tIPv6 Destination;
+ char Data[];
+};
+
+#define IPV6_ETHERNET_ID 0x86DD
+
+extern int IPv6_RegisterCallback(int ID, tIPCallback Callback);
+extern int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data);
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Link/Media Layer Interface
+ */
+#include "ipstack.h"
+#include "link.h"
+
+// === CONSTANTS ===
+#define MAX_PACKET_SIZE 2048
+
+// === PROTOTYPES ===
+void Link_RegisterType(Uint16 Type, tPacketCallback Callback);
+void Link_InitCRC();
+Uint32 Link_CalculateCRC(void *Data, int Length);
+void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer);
+void Link_WatchDevice(tAdapter *Adapter);
+
+// === GLOBALS ===
+ int giRegisteredTypes = 0;
+ int giRegisteredTypeSpace = 0;
+struct {
+ Uint16 Type;
+ tPacketCallback Callback;
+} *gaRegisteredTypes;
+ int gbLink_CRCTableGenerated = 0;
+Uint32 gaiLink_CRCTable[256];
+
+// === CODE ===
+/**
+ * \fn void Link_RegisterType(Uint16 Type, tPacketCallback Callback)
+ * \brief Registers a callback for a specific packet type
+ *
+ * \todo Make thread safe (place a mutex on the list)
+ */
+void Link_RegisterType(Uint16 Type, tPacketCallback Callback)
+{
+ int i;
+ void *tmp;
+
+ for( i = giRegisteredTypes; i -- ; )
+ {
+ if(gaRegisteredTypes[i].Type == Type) {
+ Log_Warning("Net Link", "Attempt to register 0x%x twice", Type);
+ return ;
+ }
+ // Ooh! Free slot!
+ if(gaRegisteredTypes[i].Callback == NULL) break;
+ }
+
+ if(i == -1)
+ {
+ giRegisteredTypeSpace += 5;
+ tmp = realloc(gaRegisteredTypes, giRegisteredTypeSpace*sizeof(*gaRegisteredTypes));
+ if(!tmp) {
+ Log_Warning("Net Link",
+ "Out of heap space! (Attempted to allocate %i)",
+ giRegisteredTypeSpace*sizeof(*gaRegisteredTypes)
+ );
+ return ;
+ }
+ gaRegisteredTypes = tmp;
+ i = giRegisteredTypes;
+ giRegisteredTypes ++;
+ }
+
+ gaRegisteredTypes[i].Callback = Callback;
+ gaRegisteredTypes[i].Type = Type;
+}
+
+/**
+ * \fn void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer)
+ * \brief Formats and sends a packet on the specified interface
+ */
+void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer)
+{
+ int bufSize = sizeof(tEthernetHeader) + ((Length+3)&~3) + 4;
+ Uint8 buf[bufSize]; // dynamic stack arrays ftw!
+ tEthernetHeader *hdr = (void*)buf;
+
+ Log_Log("Net Link", "Sending %i bytes to %02x:%02x:%02x:%02x:%02x:%02x (Type 0x%x)",
+ Length, To.B[0], To.B[1], To.B[2], To.B[3], To.B[4], To.B[5], Type);
+
+ hdr->Dest = To;
+ hdr->Src = Adapter->MacAddr;
+ hdr->Type = htons(Type);
+
+ memcpy(hdr->Data, Buffer, Length);
+
+ *(Uint32*) &hdr->Data[bufSize-4] = 0;
+ *(Uint32*) &hdr->Data[bufSize-4] = htonl( Link_CalculateCRC(buf, bufSize) );
+
+ VFS_Write(Adapter->DeviceFD, bufSize, buf);
+}
+
+void Link_WorkerThread(void *Ptr)
+{
+ tAdapter *Adapter = Ptr;
+
+ Threads_SetName(Adapter->Device);
+ Log_Log("Net Link", "Thread %i watching '%s'", Threads_GetTID(), Adapter->Device);
+
+ // Child Thread
+ while(Adapter->DeviceFD != -1)
+ {
+ Uint8 buf[MAX_PACKET_SIZE];
+ tEthernetHeader *hdr = (void*)buf;
+ int ret, i;
+ Uint32 checksum;
+
+ // Wait for a packet (Read on a network device is blocking)
+ //Log_Debug("NET", "Waiting on adapter FD#0x%x", Adapter->DeviceFD);
+ ret = VFS_Read(Adapter->DeviceFD, MAX_PACKET_SIZE, buf);
+ if(ret == -1) break;
+
+ if(ret < sizeof(tEthernetHeader)) {
+ Log_Log("Net Link", "Recieved an undersized packet (%i < %i)",
+ ret, sizeof(tEthernetHeader));
+ continue;
+ }
+
+ Log_Log("Net Link",
+ "Packet from %02x:%02x:%02x:%02x:%02x:%02x"
+ " to %02x:%02x:%02x:%02x:%02x:%02x (Type=%04x)",
+ hdr->Src.B[0], hdr->Src.B[1], hdr->Src.B[2],
+ hdr->Src.B[3], hdr->Src.B[4], hdr->Src.B[5],
+ hdr->Dest.B[0], hdr->Dest.B[1], hdr->Dest.B[2],
+ hdr->Dest.B[3], hdr->Dest.B[4], hdr->Dest.B[5],
+ ntohs(hdr->Type)
+ );
+ checksum = *(Uint32*)&hdr->Data[ret-sizeof(tEthernetHeader)-4];
+ //Log_Log("NET", "Checksum 0x%08x", checksum);
+ // TODO: Check checksum
+
+ // Check if there is a registered callback for this packet type
+ for( i = giRegisteredTypes; i--; )
+ {
+ if(gaRegisteredTypes[i].Type == ntohs(hdr->Type)) break;
+ }
+ // No? Ignore it
+ if( i == -1 ) {
+ Log_Log("Net Link", "Unregistered type 0x%x", ntohs(hdr->Type));
+ continue;
+ }
+
+ // Call the callback
+ gaRegisteredTypes[i].Callback(
+ Adapter,
+ hdr->Src,
+ ret - sizeof(tEthernetHeader),
+ hdr->Data
+ );
+ }
+
+ Log_Log("Net Link", "Watcher terminated (file closed)");
+
+ Threads_Exit(0, 0);
+}
+
+/**
+ * \fn void Link_WatchDevice(tAdapter *Adapter)
+ * \brief Spawns a worker thread to watch the specified adapter
+ */
+void Link_WatchDevice(tAdapter *Adapter)
+{
+ int tid;
+
+ if( !gbLink_CRCTableGenerated )
+ Link_InitCRC();
+
+ tid = Proc_SpawnWorker(Link_WorkerThread, Adapter); // Create a new worker thread
+
+ if(tid < 0) {
+ Log_Warning("Net Link", "Unable to create watcher thread for '%s'", Adapter->Device);
+ return ;
+ }
+
+ Log_Log("Net Link", "Watching '%s' using tid %i", Adapter->Device, tid);
+}
+
+// From http://www.cl.cam.ac.uk/research/srg/bluebook/21/crc/node6.html
+#define QUOTIENT 0x04c11db7
+void Link_InitCRC(void)
+{
+ int i, j;
+ Uint32 crc;
+
+ for (i = 0; i < 256; i++)
+ {
+ crc = i << 24;
+ for (j = 0; j < 8; j++)
+ {
+ if (crc & 0x80000000)
+ crc = (crc << 1) ^ QUOTIENT;
+ else
+ crc = crc << 1;
+ }
+ gaiLink_CRCTable[i] = crc;
+ }
+
+ gbLink_CRCTableGenerated = 1;
+}
+
+Uint32 Link_CalculateCRC(void *Data, int Length)
+{
+ // x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
+ Uint32 result;
+ int i;
+ Uint32 *data = Data;
+
+ if(Length < 4) return 0;
+
+ result = *data++ << 24;
+ result |= *data++ << 16;
+ result |= *data++ << 8;
+ result |= *data++;
+ result = ~ result;
+ Length -= 4;
+
+ for( i = 0; i < Length; i++ )
+ {
+ result = (result << 8 | *data++) ^ gaiLink_CRCTable[result >> 24];
+ }
+
+ return ~result;
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Link/Media Layer Header
+ */
+#ifndef _LINK_H_
+#define _LINK_H_
+
+// === EXTERNAL ===
+typedef void (*tPacketCallback)(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
+
+extern void Link_RegisterType(Uint16 Type, tPacketCallback Callback);
+extern void Link_SendPacket(tAdapter *Interface, Uint16 Type, tMacAddr To, int Length, void *Buffer);
+extern void Link_WatchDevice(tAdapter *Adapter);
+
+// === INTERNAL ===
+typedef struct sEthernetHeader tEthernetHeader;
+typedef struct sEthernetFooter tEthernetFooter;
+struct sEthernetHeader {
+ tMacAddr Dest;
+ tMacAddr Src;
+ Uint16 Type;
+ Uint8 Data[];
+};
+
+struct sEthernetFooter {
+ //Uint32 CRC;
+};
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Stack Initialisation
+ */
+#define DEBUG 0
+#define VERSION VER2(0,10)
+#include "ipstack.h"
+#include "link.h"
+#include <modules.h>
+#include <fs_devfs.h>
+
+// === IMPORTS ===
+extern int ARP_Initialise();
+extern void UDP_Initialise();
+extern void TCP_Initialise();
+extern int IPv4_Initialise();
+extern int IPv6_Initialise();
+
+extern tAdapter *IPStack_GetAdapter(const char *Path);
+extern char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos);
+extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name);
+extern int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data);
+extern tInterface gIP_LoopInterface;
+extern tInterface *IPStack_AddInterface(const char *Device, const char *Name);
+extern tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric);
+
+// === PROTOTYPES ===
+ int IPStack_Install(char **Arguments);
+ int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, IPStack, IPStack_Install, NULL, NULL);
+tVFS_NodeType gIP_RootNodeType = {
+ .ReadDir = IPStack_Root_ReadDir,
+ .FindDir = IPStack_Root_FindDir,
+ .IOCtl = IPStack_Root_IOCtl
+};
+tDevFS_Driver gIP_DriverInfo = {
+ NULL, "ip",
+ {
+ .Size = -1, // Number of interfaces
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gIP_RootNodeType
+ }
+};
+
+// === CODE ===
+/**
+ * \fn int IPStack_Install(char **Arguments)
+ * \brief Intialise the relevant parts of the stack and register with DevFS
+ */
+int IPStack_Install(char **Arguments)
+{
+ int i = 0;
+
+ // Layer 3 - Network Layer Protocols
+ ARP_Initialise();
+ IPv4_Initialise();
+ IPv6_Initialise();
+ // Layer 4 - Transport Layer Protocols
+ TCP_Initialise();
+ UDP_Initialise();
+
+ if(Arguments)
+ {
+ // Parse module arguments
+ for( i = 0; Arguments[i]; i++ )
+ {
+ // TODO:
+ // Define interfaces by <Device>:<Type>:<HexStreamAddress>:<Bits>
+ // Where:
+ // - <Device> is the device path (E.g. /Devices/ne2k/0)
+ // - <Type> is a number (e.g. 4) or symbol (e.g. AF_INET4)
+ // - <HexStreamAddress> is a condensed hexadecimal stream (in big endian)
+ // (E.g. 0A000201 for 10.0.2.1 IPv4)
+ // - <Bits> is the number of subnet bits (E.g. 24 for an IPv4 Class C)
+ // Example: /Devices/ne2k/0:4:0A00020A:24
+ // would define an interface with the address 10.0.2.10/24
+ if( Arguments[i][0] == '/' ) {
+ // Define Interface
+ char *dev, *type, *addr, *bits;
+
+ // Read definition
+ dev = Arguments[i];
+ type = strchr(dev, ':');
+ if( !type ) {
+ Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
+ continue;
+ }
+ *type = '\0'; type ++;
+
+ addr = strchr(type, ':');
+ if( !addr ) {
+ Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
+ continue;
+ }
+ *addr = '\0'; addr ++;
+
+ bits = strchr(addr, ':');
+ if( !bits ) {
+ Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
+ continue;
+ }
+ *bits = '\0'; bits ++;
+
+ // Define interface
+ {
+ int iType = atoi(type);
+ int size = IPStack_GetAddressSize(iType);
+ Uint8 addrData[size];
+ int iBits = atoi(bits);
+
+ UnHex(addrData, size, addr);
+
+ tInterface *iface = IPStack_AddInterface(dev, "");
+ if( !iface ) {
+ Log_Warning("IPStack", "Unable to add interface on '%s'", dev);
+ continue ;
+ }
+ iface->Type = iType;
+ memcpy(iface->Address, addrData, size);
+ iface->SubnetBits = iBits;
+
+ // Route for addrData/iBits, no next hop, default metric
+ IPStack_AddRoute(iface->Name, iface->Address, iBits, NULL, 0);
+
+ Log_Notice("IPStack", "Boot interface %s/%i on %s",
+ IPStack_PrintAddress(iType, addrData), iBits,
+ dev);
+ }
+
+ continue;
+ }
+
+ // I could also define routes using <Interface>:<HexStreamNetwork>:<Bits>[:<HexStreamGateway>]
+ // Example: 1:00000000:0:0A000201
+ if( '0' <= Arguments[i][0] && Arguments[i][0] <= '9' )
+ {
+ // Define Interface
+ char *ifaceName, *network, *bits, *gateway;
+
+ // Read definition
+ ifaceName = Arguments[i];
+
+ network = strchr(ifaceName, ':');
+ if( !network ) {
+ Log_Warning("IPStack", "<iface>:<HexStreamNetwork>:<Bits>:<HexStreamGateway>");
+ continue;
+ }
+ *network = '\0'; network ++;
+
+ bits = strchr(network, ':');
+ if( !bits ) {
+ Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
+ continue;
+ }
+ *bits = '\0'; bits ++;
+
+ gateway = strchr(bits, ':');
+ if( gateway ) {
+ *gateway = '\0'; gateway ++;
+ }
+
+ // Define route
+ {
+ tVFS_Node *node = IPStack_Root_FindDir(NULL, ifaceName);
+ if( !node ) {
+ Log_Warning("IPStack", "Unknown interface '%s' in arg %i", ifaceName, i);
+ continue ;
+ }
+ tInterface *iface = node->ImplPtr;
+
+ int size = IPStack_GetAddressSize(iface->Type);
+ Uint8 netData[size];
+ Uint8 gwData[size];
+ int iBits = atoi(bits);
+
+ UnHex(netData, size, network);
+ if( gateway )
+ UnHex(gwData, size, gateway);
+ else
+ memset(gwData, 0, size);
+
+ IPStack_AddRoute(ifaceName, netData, iBits, gwData, 30);
+ }
+
+ continue;
+ }
+ }
+ }
+
+ // Initialise loopback interface
+ gIP_LoopInterface.Adapter = IPStack_GetAdapter("LOOPBACK");
+
+ DevFS_AddDevice( &gIP_DriverInfo );
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Gets the size (in bytes) of a specified form of address
+ */
+int IPStack_GetAddressSize(int AddressType)
+{
+ switch(AddressType)
+ {
+ case -1: // -1 = maximum
+ return sizeof(tIPv6);
+
+ case AF_NULL:
+ return 0;
+
+ case AF_INET4:
+ return sizeof(tIPv4);
+ case AF_INET6:
+ return sizeof(tIPv6);
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * \brief Compare two IP Addresses masked by CheckBits
+ */
+int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits)
+{
+ int size = IPStack_GetAddressSize(AddressType);
+ Uint8 mask;
+ const Uint8 *addr1 = Address1, *addr2 = Address2;
+
+ // Sanity check size
+ if( CheckBits < 0 ) CheckBits = size*8;
+ if( CheckBits > size*8 ) CheckBits = size*8;
+
+ if( CheckBits == 0 ) return 1; // /0 matches anything
+
+ // Check first bits/8 bytes
+ if( memcmp(Address1, Address2, CheckBits/8) != 0 ) return 0;
+
+ // Check if the mask is a multiple of 8
+ if( CheckBits % 8 == 0 ) return 1;
+
+ // Check last bits
+ mask = 0xFF << (8 - (CheckBits % 8));
+ if( (addr1[CheckBits/8] & mask) == (addr2[CheckBits/8] & mask) )
+ return 1;
+
+ return 0;
+}
+
+const char *IPStack_PrintAddress(int AddressType, const void *Address)
+{
+ switch( AddressType )
+ {
+ case 4: {
+ static char ret[4*3+3+1];
+ const Uint8 *addr = Address;
+ sprintf(ret, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]);
+ return ret;
+ }
+
+ case 6: { // TODO: address compression
+ static char ret[8*4+7+1];
+ const Uint16 *addr = Address;
+ sprintf(ret, "%x:%x:%x:%x:%x:%x:%x:%x",
+ ntohs(addr[0]), ntohs(addr[1]), ntohs(addr[2]), ntohs(addr[3]),
+ ntohs(addr[4]), ntohs(addr[5]), ntohs(addr[6]), ntohs(addr[7])
+ );
+ return ret;
+ }
+
+ default:
+ return "";
+ }
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - Routing Tables
+ */
+#define DEBUG 0
+#define VERSION VER2(0,10)
+#include <acess.h>
+#include <api_drv_common.h>
+#include "ipstack.h"
+#include "link.h"
+
+#define DEFAUTL_METRIC 30
+
+// === IMPORTS ===
+extern tInterface *gIP_Interfaces;
+extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Filename);
+
+// === PROTOTYPES ===
+// - Routes directory
+char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name);
+ int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
+tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric);
+ int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric);
+ int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data);
+// - Route Management
+tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric);
+tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric);
+tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address);
+// - Individual Routes
+ int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data);
+
+// === GLOBALS ===
+ int giIP_NextRouteId = 1;
+tRoute *gIP_Routes;
+tRoute *gIP_RoutesEnd;
+tVFS_NodeType gIP_RouteNodeType = {
+ .IOCtl = IPStack_Route_IOCtl
+};
+tVFS_NodeType gIP_RouteDirNodeType = {
+ .ReadDir = IPStack_RouteDir_ReadDir,
+ .FindDir = IPStack_RouteDir_FindDir,
+ .MkNod = IPStack_RouteDir_MkNod,
+ .Relink = IPStack_RouteDir_Relink,
+ .IOCtl = IPStack_RouteDir_IOCtl
+};
+tVFS_Node gIP_RouteNode = {
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Size = -1,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Type = &gIP_RouteDirNodeType
+};
+
+// === CODE ===
+/**
+ * \brief ReadDir for the /Devices/ip/routes/ directory
+ */
+char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tRoute *rt;
+
+ for(rt = gIP_Routes; rt && Pos --; rt = rt->Next);
+ if( !rt ) return NULL;
+
+ {
+ int addrlen = IPStack_GetAddressSize(rt->AddressType);
+ int len = sprintf(NULL, "%i::%i:%i", rt->AddressType, rt->SubnetBits, rt->Metric) + addrlen*2;
+ char buf[len+1];
+ int ofs;
+ ofs = sprintf(buf, "%i:", rt->AddressType);
+ ofs += Hex(buf+ofs, addrlen, rt->Network);
+ sprintf(buf+ofs, ":%i:%i", rt->SubnetBits, rt->Metric);
+ return strdup(buf);
+ }
+}
+
+/**
+ * \brief FindDir for the /Devices/ip/routes/ directory
+ */
+tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name)
+{
+ // Interpret the name as <type>:<addr>, returning the interface for
+ // needed to access that address.
+ // E.g. '@4:0A02000A' - 10.2.0.10
+ // Hm... It could do with a way to have a better address type representation
+ if( Name[0] == '@' )
+ {
+ tRoute *rt;
+ int ofs = 1;
+ int type;
+
+ ofs += ParseInt(Name+ofs, &type);
+ int addrSize = IPStack_GetAddressSize(type);
+ Uint8 addrData[addrSize];
+
+ // Separator
+ if( Name[ofs] != ':' ) return NULL;
+ ofs ++;
+
+ // Error if the size is invalid
+ if( strlen(Name+ofs) != addrSize*2 )
+ return NULL;
+
+ // Parse the address
+ // - Error if the address data is not fully hex
+ if( UnHex(addrData, addrSize, Name + ofs) != addrSize )
+ return NULL;
+
+ // Find the route
+ rt = IPStack_FindRoute(type, NULL, addrData);
+ if(!rt) return NULL;
+
+ if( rt->Interface )
+ {
+ // Return the interface node
+ // - Sure it's hijacking it from inteface.c's area, but it's
+ // simpler this way
+ return &rt->Interface->Node;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else if( Name[0] == '#' )
+ {
+ int num, ofs = 1;
+
+ ofs = ParseInt(Name+ofs, &num);
+ if( ofs == 1 ) return NULL;
+ if( Name[ofs] != '\0' ) return NULL;
+ if( num < 0) return NULL;
+
+ for( tRoute *rt = gIP_Routes; rt; rt = rt->Next )
+ {
+ if( rt->Node.Inode > num ) return NULL;
+ if( rt->Node.Inode == num ) return &rt->Node;
+ }
+ return NULL;
+ }
+ else
+ {
+ int type = _Route_ParseRouteName(Name, NULL, NULL, NULL);
+ if( type <= 0 ) return NULL;
+
+ int addrSize = IPStack_GetAddressSize(type);
+ Uint8 addrData[addrSize];
+ int subnet_bits, metric;
+
+ _Route_ParseRouteName(Name, addrData, &subnet_bits, &metric);
+
+ tRoute *rt = _Route_FindExactRoute(type, addrData, subnet_bits, metric);
+ if(rt) return &rt->Node;
+ return NULL;
+ }
+}
+
+/**
+ * \brief Create a new route node
+ */
+int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
+{
+ if( Flags ) return -EINVAL;
+ if( Threads_GetUID() != 0 ) return -EACCES;
+
+ int type = _Route_ParseRouteName(Name, NULL, NULL, NULL);
+ if( type <= 0 ) return -EINVAL;
+
+ int size = IPStack_GetAddressSize(type);
+ Uint8 addrdata[size];
+ int subnet, metric;
+
+ _Route_ParseRouteName(Name, addrdata, &subnet, &metric);
+
+ // Check for duplicates
+ if( _Route_FindExactRoute(type, addrdata, subnet, metric) )
+ return -EEXIST;
+
+ IPStack_Route_Create(type, addrdata, subnet, metric);
+
+ return 0;
+}
+
+/**
+ * \brief Rename / Delete a route
+ */
+int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
+{
+ tRoute *rt;
+
+ if( Threads_GetUID() != 0 ) return -EACCES;
+
+ // Get the original route entry
+ {
+ int type = _Route_ParseRouteName(OldName, NULL, NULL, NULL);
+ if(type <= 0) return -EINVAL;
+ Uint8 addr[IPStack_GetAddressSize(type)];
+ int subnet, metric;
+ _Route_ParseRouteName(OldName, addr, &subnet, &metric);
+
+ rt = _Route_FindExactRoute(type, addr, subnet, metric);
+ }
+
+ if( NewName == NULL )
+ {
+ // Delete the route
+ tRoute *prev = NULL;
+ for(tRoute *r = gIP_Routes; r && r != rt; prev = r, r = r->Next);
+
+ if(prev)
+ prev->Next = rt->Next;
+ else
+ gIP_Routes = rt->Next;
+ free(rt);
+ }
+ else
+ {
+ // Change the route
+ int type = _Route_ParseRouteName(NewName, NULL, NULL, NULL);
+ if(type <= 0) return -EINVAL;
+ Uint8 addr[IPStack_GetAddressSize(type)];
+ int subnet, metric;
+ _Route_ParseRouteName(NewName, addr, &subnet, &metric);
+
+ return -ENOTIMPL;
+ }
+ return 0;
+}
+
+tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric)
+{
+ for(tRoute *rt = gIP_Routes; rt; rt = rt->Next)
+ {
+ if( rt->AddressType != Type ) continue;
+ if( rt->Metric != Metric ) continue;
+ if( rt->SubnetBits != Subnet ) continue;
+ if( IPStack_CompareAddress(Type, rt->Network, Network, -1) == 0 )
+ continue ;
+ return rt;
+ }
+ return NULL;
+}
+
+int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric)
+{
+ int type, addrlen;
+ int ofs = 0, ilen;
+
+ ENTER("sName pAddr pSubnetBits pMetric", Name, Addr, SubnetBits, Metric);
+
+ ilen = ParseInt(Name, &type);
+ if(ilen == 0) {
+ LOG("Type failed to parse");
+ LEAVE_RET('i', -1);
+ }
+ ofs += ilen;
+
+ if(Name[ofs] != ':') LEAVE_RET('i', -1);
+ ofs ++;
+
+ addrlen = IPStack_GetAddressSize(type);
+ if( Addr )
+ {
+ if( UnHex(Addr, addrlen, Name + ofs) != addrlen )
+ return -1;
+ }
+ ofs += addrlen*2;
+
+ if(Name[ofs] != ':') LEAVE_RET('i', -1);
+ ofs ++;
+
+ ilen = ParseInt(Name+ofs, SubnetBits);
+ if(ilen == 0) {
+ LOG("Subnet failed to parse");
+ LEAVE_RET('i', -1);
+ }
+ ofs += ilen;
+
+ if(Name[ofs] != ':') LEAVE_RET('i', -1);
+ ofs ++;
+
+ ilen = ParseInt(Name+ofs, Metric);
+ if(ilen == 0) {
+ LOG("Metric failed to parse");
+ LEAVE_RET('i', -1);
+ }
+ ofs += ilen;
+
+ if(Name[ofs] != '\0') LEAVE_RET('i', -1);
+
+ LEAVE('i', type);
+ return type;
+}
+
+/**
+ * \brief Names for the route list IOCtl Calls
+ */
+static const char *casIOCtls_RouteDir[] = {
+ DRV_IOCTLNAMES,
+ "locate_route", // Find the best route for an address - struct {int Type, char Address[]} *
+ NULL
+ };
+
+/**
+ * \brief IOCtl for /Devices/ip/routes/
+ */
+int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tRoute *rt;
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ // --- Standard IOCtls (0-3) ---
+ BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_RouteDir)
+
+ case 4: // Locate Route
+ {
+ struct {
+ int Type;
+ Uint8 Addr[];
+ } *data = Data;
+
+ if( !CheckMem(Data, sizeof(int)) )
+ LEAVE_RET('i', -1);
+ if( !CheckMem(Data, sizeof(int) + IPStack_GetAddressSize(data->Type)) )
+ LEAVE_RET('i', -1);
+
+ Log_Debug("IPStack", "Route_RouteDir_IOCtl - FindRoute %i, %s",
+ data->Type, IPStack_PrintAddress(data->Type, data->Addr) );
+ rt = IPStack_FindRoute(data->Type, NULL, data->Addr);
+
+ if( !rt )
+ LEAVE_RET('i', 0);
+
+ LEAVE('i', rt->Node.Inode);
+ return rt->Node.Inode;
+ }
+ break;
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \brief Create a new route entry
+ * \param InterfaceName Name of the interface using this route
+ */
+tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric)
+{
+ tRoute *rt;
+ int size;
+
+ // Get the size of the specified address type
+ size = IPStack_GetAddressSize(AddrType);
+ if( size == 0 ) {
+ return NULL;
+ }
+
+ // Allocate space
+ rt = calloc(1, sizeof(tRoute) + size*2 );
+
+ // Set up node
+ rt->Node.ImplPtr = rt;
+ rt->Node.Inode = giIP_NextRouteId ++;
+ rt->Node.Size = 0;
+ rt->Node.NumACLs = 1,
+ rt->Node.ACLs = &gVFS_ACL_EveryoneRO;
+ rt->Node.Type = &gIP_RouteNodeType;
+
+ // Set up state
+ rt->AddressType = AddrType;
+ rt->Network = (void *)( (tVAddr)rt + sizeof(tRoute) );
+ rt->SubnetBits = SubnetBits;
+ rt->NextHop = (void *)( (tVAddr)rt + sizeof(tRoute) + size );
+ rt->Interface = NULL;
+ rt->Metric = Metric;
+ memcpy(rt->Network, Network, size);
+ memset(rt->NextHop, 0, size);
+
+ // Clear non-fixed bits
+ {
+ Uint8 *data = rt->Network;
+ int i;
+ i = SubnetBits / 8;
+ if( SubnetBits % 8 ) {
+ data[i] &= ~((1 << (8 - SubnetBits % 8)) - 1);
+ i ++;
+ }
+ memset(data + i, 0, size - i);
+ }
+
+
+ // Add to list
+ if( gIP_RoutesEnd ) {
+ gIP_RoutesEnd->Next = rt;
+ gIP_RoutesEnd = rt;
+ }
+ else {
+ gIP_Routes = gIP_RoutesEnd = rt;
+ }
+
+// Log_Log("IPStack", "Route entry for '%s' created", InterfaceName);
+
+ return rt;
+}
+
+/**
+ * \brief Add and fill a route
+ */
+tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric)
+{
+ tInterface *iface;
+ tRoute *rt;
+ int addrSize;
+
+ {
+ tVFS_Node *tmp;
+ tmp = IPStack_Root_FindDir(NULL, Interface);
+ if(!tmp) return NULL;
+ iface = tmp->ImplPtr;
+ if(tmp->Type->Close) tmp->Type->Close(tmp);
+ }
+
+ rt = IPStack_Route_Create(iface->Type, Network, SubnetBits, Metric);
+ if( !rt ) return NULL;
+
+ addrSize = IPStack_GetAddressSize(iface->Type);
+ rt->Interface = iface;
+
+ if( NextHop )
+ memcpy(rt->NextHop, NextHop, addrSize);
+
+ return rt;
+}
+
+/**
+ */
+tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address)
+{
+ tRoute *rt;
+ tRoute *best = NULL;
+ tInterface *iface;
+ int addrSize;
+
+ ENTER("iAddressType pInterface sAddress",
+ AddressType, Interface, IPStack_PrintAddress(AddressType, Address));
+
+ if( Interface && AddressType != Interface->Type ) {
+ LOG("Interface->Type (%i) != AddressType", Interface->Type);
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Get address size
+ addrSize = IPStack_GetAddressSize(AddressType);
+
+ // Check against explicit routes
+ for( rt = gIP_Routes; rt; rt = rt->Next )
+ {
+ // Check interface
+ if( Interface && rt->Interface != Interface ) continue;
+ // Check address type
+ if( rt->AddressType != AddressType ) continue;
+
+ LOG("Checking network %s/%i", IPStack_PrintAddress(AddressType, rt->Network), rt->SubnetBits);
+
+ // Check if the address matches
+ if( !IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) )
+ continue;
+
+ if( best ) {
+ // More direct routes are preferred
+ if( best->SubnetBits > rt->SubnetBits ) {
+ LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits);
+ continue;
+ }
+ // If equally direct, choose the best metric
+ if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) {
+ LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric);
+ continue;
+ }
+ }
+
+ best = rt;
+ }
+
+ // Check against implicit routes
+ if( !best && !Interface )
+ {
+ for( iface = gIP_Interfaces; iface; iface = iface->Next )
+ {
+ if( Interface && iface != Interface ) continue;
+ if( iface->Type != AddressType ) continue;
+
+
+ // Check if the address matches
+ if( !IPStack_CompareAddress(AddressType, iface->Address, Address, iface->SubnetBits) )
+ continue;
+
+ if( best ) {
+ // More direct routes are preferred
+ if( best->SubnetBits > rt->SubnetBits ) {
+ LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits);
+ continue;
+ }
+ // If equally direct, choose the best metric
+ if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) {
+ LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric);
+ continue;
+ }
+ }
+
+ rt = &iface->Route;
+ memcpy(rt->Network, iface->Address, addrSize);
+ memset(rt->NextHop, 0, addrSize);
+ rt->Metric = DEFAUTL_METRIC;
+ rt->SubnetBits = iface->SubnetBits;
+
+ best = rt;
+ }
+ }
+ if( !best && Interface )
+ {
+ rt = &Interface->Route;
+ // Make sure route is up to date
+ memcpy(rt->Network, Interface->Address, addrSize);
+ memset(rt->NextHop, 0, addrSize);
+ rt->Metric = DEFAUTL_METRIC;
+ rt->SubnetBits = Interface->SubnetBits;
+
+ if( IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) )
+ {
+ best = rt;
+ }
+ }
+
+ LEAVE('p', best);
+ return best;
+}
+
+/**
+ * \brief Names for route IOCtl Calls
+ */
+static const char *casIOCtls_Route[] = {
+ DRV_IOCTLNAMES,
+ "get_nexthop", // Get next hop - (void *Data), returns boolean success
+ "set_nexthop", // Set next hop - (void *Data), returns boolean success
+ "get_interface", // Get interface name - (char *Name), returns name length, NULL OK
+ "set_interface", // Set interface - (const char *Name)
+ NULL
+ };
+
+/**
+ * \brief IOCtl for /Devices/ip/routes/#
+ */
+int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tRoute *rt = Node->ImplPtr;
+ int addrSize = IPStack_GetAddressSize(rt->AddressType);
+
+ switch(ID)
+ {
+ // --- Standard IOCtls (0-3) ---
+ BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_Route)
+
+ // Get Next Hop
+ case 4:
+ if( !CheckMem(Data, addrSize) ) return -1;
+ memcpy(Data, rt->NextHop, addrSize);
+ return 1;
+ // Set Next Hop
+ case 5:
+ if( Threads_GetUID() != 0 ) return -1;
+ if( !CheckMem(Data, addrSize) ) return -1;
+ memcpy(rt->NextHop, Data, addrSize);
+ return 1;
+
+ // Get interface name
+ case 6:
+ if( !rt->Interface ) {
+ if(Data && !CheckMem(Data, 1) )
+ return -1;
+ if(Data)
+ *(char*)Data = 0;
+ return 0;
+ }
+ if( Data ) {
+ if( !CheckMem(Data, strlen(rt->Interface->Name) + 1) )
+ return -1;
+ strcpy(Data, rt->Interface->Name);
+ }
+ return strlen(rt->Interface->Name);
+ // Set interface name
+ case 7:
+ if( Threads_GetUID() != 0 )
+ return -1;
+ if( !CheckString(Data) )
+ return -1;
+ else
+ {
+ tInterface *iface;
+ tVFS_Node *tmp;
+ tmp = IPStack_Root_FindDir(NULL, Data);
+ if(!tmp)
+ return -1;
+ iface = tmp->ImplPtr;
+ if(tmp->Type->Close) tmp->Type->Close(tmp);
+
+ if( iface->Type != rt->AddressType )
+ return -1;
+
+ // TODO: Other checks?
+
+ rt->Interface = iface;
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - SCTP (Stream Control Transmission Protocol) Handling
+ */
+#include "ipstack.h"
+#include <api_drv_common.h>
+#include "sctp.h"
+
+#define SCTP_ALLOC_BASE 0xC000
+
+// === PROTOTYPES ===
+void SCTP_Initialise();
+void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
+void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
+void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length);
+// --- Listening Server
+tVFS_Node *SCTP_Server_Init(tInterface *Interface);
+char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID);
+tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name);
+ int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data);
+void SCTP_Server_Close(tVFS_Node *Node);
+// --- Client Channels
+tVFS_Node *SCTP_Channel_Init(tInterface *Interface);
+Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+ int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data);
+void SCTP_Channel_Close(tVFS_Node *Node);
+// --- Helpers
+Uint16 SCTP_int_AllocatePort();
+ int SCTP_int_MarkPortAsUsed(Uint16 Port);
+void SCTP_int_FreePort(Uint16 Port);
+
+// === GLOBALS ===
+tMutex glSCTP_Servers;
+tSCTPServer *gpSCTP_Servers;
+
+tMutex glSCTP_Channels;
+tSCTPChannel *gpSCTP_Channels;
+
+tMutex glSCTP_Ports;
+Uint32 gSCTP_Ports[0x10000/32];
+
+tSocketFile gSCTP_ServerFile = {NULL, "sctps", SCTP_Server_Init};
+tSocketFile gSCTP_ClientFile = {NULL, "sctpc", SCTP_Channel_Init};
+
+// === CODE ===
+/**
+ * \fn void TCP_Initialise()
+ * \brief Initialise the TCP Layer
+ */
+void SCTP_Initialise()
+{
+ IPStack_AddFile(&gSCTP_ServerFile);
+ IPStack_AddFile(&gSCTP_ClientFile);
+ //IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket, SCTP_Unreachable);
+ IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket);
+}
+
+/**
+ * \brief Scan a list of tSCTPChannels and find process the first match
+ * \return 0 if no match was found, -1 on error and 1 if a match was found
+ */
+int SCTP_int_ScanList(tSCTPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+ tSCTPHeader *hdr = Buffer;
+ tSCTPChannel *chan;
+ tSCTPPacket *pack;
+ int len;
+
+ for(chan = List;
+ chan;
+ chan = chan->Next)
+ {
+ if(chan->Interface != Interface) continue;
+ if(chan->LocalPort != ntohs(hdr->DestPort)) continue;
+ if(chan->RemotePort != ntohs(hdr->SourcePort)) continue;
+
+ if(Interface->Type == 4) {
+ if(!IP4_EQU(chan->RemoteAddr.v4, *(tIPv4*)Address)) continue;
+ }
+ else if(Interface->Type == 6) {
+ if(!IP6_EQU(chan->RemoteAddr.v6, *(tIPv6*)Address)) continue;
+ }
+ else {
+ Log_Warning("SCTP", "Address type %i unknown", Interface->Type);
+ Mutex_Release(&glSCTP_Channels);
+ return -1;
+ }
+
+ Log_Log("SCTP", "Recieved packet for %p", chan);
+ // Create the cached packet
+ len = ntohs(hdr->Length);
+ pack = malloc(sizeof(tSCTPPacket) + len);
+ pack->Next = NULL;
+ pack->Length = len;
+ memcpy(pack->Data, hdr->Data, len);
+
+ // Add the packet to the channel's queue
+ SHORTLOCK(&chan->lQueue);
+ if(chan->Queue)
+ chan->QueueEnd->Next = pack;
+ else
+ chan->QueueEnd = chan->Queue = pack;
+ SHORTREL(&chan->lQueue);
+ Mutex_Release(&glSCTP_Channels);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \fn void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+ * \brief Handles a packet from the IP Layer
+ */
+void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+ tSCTPHeader *hdr = Buffer;
+ tSCTPServer *srv;
+ int ret;
+
+ Log_Log("SCTP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort));
+ Log_Log("SCTP", "hdr->DestPort = %i", ntohs(hdr->DestPort));
+ Log_Log("SCTP", "hdr->VerifcationTag = %i", ntohs(hdr->VerifcationTag));
+ Log_Log("SCTP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
+
+ // Check registered connections
+ Mutex_Acquire(&glSCTP_Channels);
+ ret = SCTP_int_ScanList(gpSCTP_Channels, Interface, Address, Length, Buffer);
+ Mutex_Release(&glSCTP_Channels);
+ if(ret != 0) return ;
+
+
+ // TODO: Server/Listener
+ Mutex_Acquire(&glSCTP_Servers);
+ for(srv = gpSCTP_Servers;
+ srv;
+ srv = srv->Next)
+ {
+ if(srv->Interface != Interface) continue;
+ if(srv->ListenPort != ntohs(hdr->DestPort)) continue;
+ ret = SCTP_int_ScanList(srv->Channels, Interface, Address, Length, Buffer);
+ if(ret != 0) break;
+
+ // Add connection
+ Log_Warning("SCTP", "TODO - Add channel on connection");
+ //TODO
+ }
+ Mutex_Release(&glSCTP_Servers);
+
+}
+
+/**
+ * \brief Handle an ICMP Unrechable Error
+ */
+void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
+{
+
+}
+
+/**
+ * \brief Send a packet
+ * \param Channel Channel to send the packet from
+ * \param Data Packet data
+ * \param Length Length in bytes of packet data
+ */
+void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length)
+{
+ tSCTPHeader *hdr;
+
+ switch(Channel->Interface->Type)
+ {
+ case 4:
+ // Create the packet
+ hdr = malloc(sizeof(tSCTPHeader)+Length);
+ hdr->SourcePort = htons( Channel->LocalPort );
+ hdr->DestPort = htons( Channel->RemotePort );
+ hdr->Length = htons( sizeof(tSCTPHeader) + Length );
+ hdr->Checksum = 0; // Checksum can be zero on IPv4
+ memcpy(hdr->Data, Data, Length);
+ // Pass on the the IPv4 Layer
+ IPv4_SendPacket(Channel->Interface, Channel->RemoteAddr.v4, IP4PROT_SCTP, 0, sizeof(tSCTPHeader)+Length, hdr);
+ // Free allocated packet
+ free(hdr);
+ break;
+ }
+}
+
+// --- Listening Server
+tVFS_Node *SCTP_Server_Init(tInterface *Interface)
+{
+ tSCTPServer *new;
+ new = calloc( sizeof(tSCTPServer), 1 );
+ if(!new) return NULL;
+
+ new->Node.ImplPtr = new;
+ new->Node.Flags = VFS_FFLAG_DIRECTORY;
+ new->Node.NumACLs = 1;
+ new->Node.ACLs = &gVFS_ACL_EveryoneRX;
+ new->Node.ReadDir = SCTP_Server_ReadDir;
+ new->Node.FindDir = SCTP_Server_FindDir;
+ new->Node.IOCtl = SCTP_Server_IOCtl;
+ new->Node.Close = SCTP_Server_Close;
+
+ Mutex_Acquire(&glSCTP_Servers);
+ new->Next = gpSCTP_Servers;
+ gpSCTP_Servers = new;
+ Mutex_Release(&glSCTP_Servers);
+
+ return &new->Node;
+}
+
+/**
+ * \brief Wait for a connection and return its ID in a string
+ */
+char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID)
+{
+ tSCTPServer *srv = Node->ImplPtr;
+ tSCTPChannel *chan;
+ char *ret;
+
+ if( srv->ListenPort == 0 ) return NULL;
+
+ // Lock (so another thread can't collide with us here) and wait for a connection
+ Mutex_Acquire( &srv->Lock );
+ while( srv->NewChannels == NULL ) Threads_Yield();
+ // Pop the connection off the new list
+ chan = srv->NewChannels;
+ srv->NewChannels = chan->Next;
+ // Release the lock
+ Mutex_Release( &srv->Lock );
+
+ // Create the ID string and return it
+ ret = malloc(11+1);
+ sprintf(ret, "%i", chan->Node.ImplInt);
+
+ return ret;
+}
+
+/**
+ * \brief Take a string and find the channel
+ */
+tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name)
+{
+ tSCTPServer *srv = Node->ImplPtr;
+ tSCTPChannel *chan;
+ int id = atoi(Name);
+
+ for(chan = srv->Channels;
+ chan;
+ chan = chan->Next)
+ {
+ if( chan->Node.ImplInt < id ) continue;
+ if( chan->Node.ImplInt > id ) break; // Go sorted lists!
+
+ return &chan->Node;
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Names for server IOCtl Calls
+ */
+static const char *casIOCtls_Server[] = {
+ DRV_IOCTLNAMES,
+ "getset_listenport",
+ NULL
+ };
+/**
+ * \brief Channel IOCtls
+ */
+int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tSCTPServer *srv = Node->ImplPtr;
+
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Server", 0x100, casIOCtls_Server);
+
+ case 4: // getset_localport (returns bool success)
+ if(!Data) LEAVE_RET('i', srv->ListenPort);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ // Set port
+ srv->ListenPort = *(Uint16*)Data;
+ // Permissions check (Ports lower than 1024 are root-only)
+ if(srv->ListenPort != 0 && srv->ListenPort < 1024) {
+ if( Threads_GetUID() != 0 ) {
+ LOG("Attempt by non-superuser to listen on port %i", srv->ListenPort);
+ srv->ListenPort = 0;
+ LEAVE_RET('i', -1);
+ }
+ }
+ // Allocate a random port if requested
+ if( srv->ListenPort == 0 )
+ srv->ListenPort = SCTP_int_AllocatePort();
+ else
+ {
+ // Else, mark the requested port as used
+ if( SCTP_int_MarkPortAsUsed(srv->ListenPort) == 0 ) {
+ LOG("Port %i us currently in use", srv->ListenPort);
+ srv->ListenPort = 0;
+ LEAVE_RET('i', -1);
+ }
+ LEAVE_RET('i', 1);
+ }
+ LEAVE_RET('i', 1);
+
+ default:
+ LEAVE_RET('i', -1);
+ }
+ LEAVE_RET('i', 0);
+}
+
+void SCTP_Server_Close(tVFS_Node *Node)
+{
+ tSCTPServer *srv = Node->ImplPtr;
+ tSCTPServer *prev;
+ tSCTPChannel *chan;
+ tSCTPPacket *tmp;
+
+
+ // Remove from the main list first
+ Mutex_Acquire(&glSCTP_Servers);
+ if(gpSCTP_Servers == srv)
+ gpSCTP_Servers = gpSCTP_Servers->Next;
+ else
+ {
+ for(prev = gpSCTP_Servers;
+ prev->Next && prev->Next != srv;
+ prev = prev->Next);
+ if(!prev->Next)
+ Log_Warning("SCTP", "Bookeeping Fail, server %p is not in main list", srv);
+ else
+ prev->Next = prev->Next->Next;
+ }
+ Mutex_Release(&glSCTP_Servers);
+
+
+ Mutex_Acquire(&srv->Lock);
+ for(chan = srv->Channels;
+ chan;
+ chan = chan->Next)
+ {
+ // Clear Queue
+ SHORTLOCK(&chan->lQueue);
+ while(chan->Queue)
+ {
+ tmp = chan->Queue;
+ chan->Queue = tmp->Next;
+ free(tmp);
+ }
+ SHORTREL(&chan->lQueue);
+
+ // Free channel structure
+ free(chan);
+ }
+ Mutex_Release(&srv->Lock);
+
+ free(srv);
+}
+
+// --- Client Channels
+tVFS_Node *SCTP_Channel_Init(tInterface *Interface)
+{
+ tSCTPChannel *new;
+ new = calloc( sizeof(tSCTPChannel), 1 );
+ new->Interface = Interface;
+ new->Node.ImplPtr = new;
+ new->Node.NumACLs = 1;
+ new->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ new->Node.Read = SCTP_Channel_Read;
+ new->Node.Write = SCTP_Channel_Write;
+ new->Node.IOCtl = SCTP_Channel_IOCtl;
+ new->Node.Close = SCTP_Channel_Close;
+
+ Mutex_Acquire(&glSCTP_Channels);
+ new->Next = gpSCTP_Channels;
+ gpSCTP_Channels = new;
+ Mutex_Release(&glSCTP_Channels);
+
+ return &new->Node;
+}
+
+/**
+ * \brief Read from the channel file (wait for a packet)
+ */
+Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tSCTPChannel *chan = Node->ImplPtr;
+ tSCTPPacket *pack;
+
+ if(chan->LocalPort == 0) return 0;
+ if(chan->RemotePort == 0) return 0;
+
+ while(chan->Queue == NULL) Threads_Yield();
+
+ for(;;)
+ {
+ SHORTLOCK(&chan->lQueue);
+ if(chan->Queue == NULL) {
+ SHORTREL(&chan->lQueue);
+ continue;
+ }
+ pack = chan->Queue;
+ chan->Queue = pack->Next;
+ if(!chan->Queue) chan->QueueEnd = NULL;
+ SHORTREL(&chan->lQueue);
+ break;
+ }
+
+ // Clip length to packet length
+ if(Length > pack->Length) Length = pack->Length;
+ // Copy packet data from cache
+ memcpy(Buffer, pack->Data, Length);
+ // Free cached packet
+ free(pack);
+
+ return Length;
+}
+
+/**
+ * \brief Write to the channel file (send a packet)
+ */
+Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tSCTPChannel *chan = Node->ImplPtr;
+ if(chan->RemotePort == 0) return 0;
+
+ SCTP_SendPacket(chan, Buffer, (size_t)Length);
+
+ return 0;
+}
+
+/**
+ * \brief Names for channel IOCtl Calls
+ */
+static const char *casIOCtls_Channel[] = {
+ DRV_IOCTLNAMES,
+ "getset_localport",
+ "getset_remoteport",
+ "set_remoteaddr",
+ NULL
+ };
+/**
+ * \brief Channel IOCtls
+ */
+int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tSCTPChannel *chan = Node->ImplPtr;
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Channel", 0x100, casIOCtls_Channel);
+
+ case 4: // getset_localport (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->LocalPort);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ // Set port
+ chan->LocalPort = *(Uint16*)Data;
+ // Permissions check (Ports lower than 1024 are root-only)
+ if(chan->LocalPort != 0 && chan->LocalPort < 1024) {
+ if( Threads_GetUID() != 0 ) {
+ LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort);
+ chan->LocalPort = 0;
+ LEAVE_RET('i', -1);
+ }
+ }
+ // Allocate a random port if requested
+ if( chan->LocalPort == 0 )
+ chan->LocalPort = SCTP_int_AllocatePort();
+ else
+ {
+ // Else, mark the requested port as used
+ if( SCTP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) {
+ LOG("Port %i us currently in use", chan->LocalPort);
+ chan->LocalPort = 0;
+ LEAVE_RET('i', 0);
+ }
+ LEAVE_RET('i', 1);
+ }
+ LEAVE_RET('i', 1);
+
+ case 5: // getset_remoteport (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->RemotePort);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ chan->RemotePort = *(Uint16*)Data;
+ return 1;
+
+ case 6: // set_remoteaddr (returns bool success)
+ switch(chan->Interface->Type)
+ {
+ case 4:
+ if(!CheckMem(Data, sizeof(tIPv4))) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ chan->RemoteAddr.v4 = *(tIPv4*)Data;
+ break;
+ }
+ break;
+ }
+ LEAVE_RET('i', 0);
+}
+
+/**
+ * \brief Close and destroy an open channel
+ */
+void SCTP_Channel_Close(tVFS_Node *Node)
+{
+ tSCTPChannel *chan = Node->ImplPtr;
+ tSCTPChannel *prev;
+
+ // Remove from the main list first
+ Mutex_Acquire(&glSCTP_Channels);
+ if(gpSCTP_Channels == chan)
+ gpSCTP_Channels = gpSCTP_Channels->Next;
+ else
+ {
+ for(prev = gpSCTP_Channels;
+ prev->Next && prev->Next != chan;
+ prev = prev->Next);
+ if(!prev->Next)
+ Log_Warning("SCTP", "Bookeeping Fail, channel %p is not in main list", chan);
+ else
+ prev->Next = prev->Next->Next;
+ }
+ Mutex_Release(&glSCTP_Channels);
+
+ // Clear Queue
+ SHORTLOCK(&chan->lQueue);
+ while(chan->Queue)
+ {
+ tSCTPPacket *tmp;
+ tmp = chan->Queue;
+ chan->Queue = tmp->Next;
+ free(tmp);
+ }
+ SHORTREL(&chan->lQueue);
+
+ // Free channel structure
+ free(chan);
+}
+
+/**
+ * \return Port Number on success, or zero on failure
+ */
+Uint16 SCTP_int_AllocatePort()
+{
+ int i;
+ Mutex_Acquire(&glSCTP_Ports);
+ // Fast Search
+ for( i = SCTP_ALLOC_BASE; i < 0x10000; i += 32 )
+ if( gSCTP_Ports[i/32] != 0xFFFFFFFF )
+ break;
+ if(i == 0x10000) return 0;
+ for( ;; i++ )
+ {
+ if( !(gSCTP_Ports[i/32] & (1 << (i%32))) )
+ return i;
+ }
+ Mutex_Release(&glSCTP_Ports);
+}
+
+/**
+ * \brief Allocate a specific port
+ * \return Boolean Success
+ */
+int SCTP_int_MarkPortAsUsed(Uint16 Port)
+{
+ Mutex_Acquire(&glSCTP_Ports);
+ if( gSCTP_Ports[Port/32] & (1 << (Port%32)) ) {
+ return 0;
+ Mutex_Release(&glSCTP_Ports);
+ }
+ gSCTP_Ports[Port/32] |= 1 << (Port%32);
+ Mutex_Release(&glSCTP_Ports);
+ return 1;
+}
+
+/**
+ * \brief Free an allocated port
+ */
+void SCTP_int_FreePort(Uint16 Port)
+{
+ Mutex_Acquire(&glSCTP_Ports);
+ gSCTP_Ports[Port/32] &= ~(1 << (Port%32));
+ Mutex_Release(&glSCTP_Ports);
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - TCP Handling
+ */
+#define DEBUG 1
+#include "ipstack.h"
+#include "ipv4.h"
+#include "ipv6.h"
+#include "tcp.h"
+
+#define USE_SELECT 1
+#define HEXDUMP_INCOMING 0
+#define HEXDUMP_OUTGOING 0
+#define CACHE_FUTURE_PACKETS_IN_BYTES 1 // Use a ring buffer to cache out of order packets
+
+#define TCP_MIN_DYNPORT 0xC000
+#define TCP_MAX_HALFOPEN 1024 // Should be enough
+
+#define TCP_MAX_PACKET_SIZE 1024
+#define TCP_WINDOW_SIZE 0x2000
+#define TCP_RECIEVE_BUFFER_SIZE 0x4000
+
+// === PROTOTYPES ===
+void TCP_Initialise(void);
+void TCP_StartConnection(tTCPConnection *Conn);
+void TCP_SendPacket(tTCPConnection *Conn, size_t Length, tTCPHeader *Data);
+void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
+void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
+int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length);
+void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection);
+Uint16 TCP_GetUnusedPort();
+ int TCP_AllocatePort(Uint16 Port);
+ int TCP_DeallocatePort(Uint16 Port);
+// --- Server
+tVFS_Node *TCP_Server_Init(tInterface *Interface);
+char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name);
+ int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data);
+void TCP_Server_Close(tVFS_Node *Node);
+// --- Client
+tVFS_Node *TCP_Client_Init(tInterface *Interface);
+Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data);
+void TCP_Client_Close(tVFS_Node *Node);
+// --- Helpers
+ int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue);
+
+// === TEMPLATES ===
+tSocketFile gTCP_ServerFile = {NULL, "tcps", TCP_Server_Init};
+tSocketFile gTCP_ClientFile = {NULL, "tcpc", TCP_Client_Init};
+tVFS_NodeType gTCP_ServerNodeType = {
+ .TypeName = "TCP Server",
+ .ReadDir = TCP_Server_ReadDir,
+ .FindDir = TCP_Server_FindDir,
+ .IOCtl = TCP_Server_IOCtl,
+ .Close = TCP_Server_Close
+ };
+tVFS_NodeType gTCP_ClientNodeType = {
+ .TypeName = "TCP Client/Connection",
+ .Read = TCP_Client_Read,
+ .Write = TCP_Client_Write,
+ .IOCtl = TCP_Client_IOCtl,
+ .Close = TCP_Client_Close
+ };
+
+// === GLOBALS ===
+ int giTCP_NumHalfopen = 0;
+tShortSpinlock glTCP_Listeners;
+tTCPListener *gTCP_Listeners;
+tShortSpinlock glTCP_OutbountCons;
+tTCPConnection *gTCP_OutbountCons;
+Uint32 gaTCP_PortBitmap[0x800];
+ int giTCP_NextOutPort = TCP_MIN_DYNPORT;
+
+// === CODE ===
+/**
+ * \brief Initialise the TCP Layer
+ *
+ * Registers the client and server files and the GetPacket callback
+ */
+void TCP_Initialise(void)
+{
+ giTCP_NextOutPort += rand()%32;
+ IPStack_AddFile(&gTCP_ServerFile);
+ IPStack_AddFile(&gTCP_ClientFile);
+ IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
+ IPv6_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
+}
+
+/**
+ * \brief Sends a packet from the specified connection, calculating the checksums
+ * \param Conn Connection
+ * \param Length Length of data
+ * \param Data Packet data (cast as a TCP Header)
+ */
+void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data )
+{
+ Uint16 checksum[2];
+
+ Data->Checksum = 0;
+ checksum[1] = htons( ~IPv4_Checksum( (void*)Data, Length ) ); // Partial checksum
+ if(Length & 1)
+ ((Uint8*)Data)[Length] = 0;
+
+ // TODO: Fragment packet
+
+ switch( Conn->Interface->Type )
+ {
+ case 4:
+ // Append IPv4 Pseudo Header
+ {
+ Uint32 buf[3];
+ buf[0] = ((tIPv4*)Conn->Interface->Address)->L;
+ buf[1] = Conn->RemoteIP.v4.L;
+ buf[2] = (htons(Length)<<16) | (6<<8) | 0;
+ checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum
+ }
+ Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two
+ IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, Length, Data);
+ break;
+
+ case 6:
+ // Append IPv6 Pseudo Header
+ {
+ Uint32 buf[4+4+1+1];
+ memcpy(buf, Conn->Interface->Address, 16);
+ memcpy(&buf[4], &Conn->RemoteIP, 16);
+ buf[8] = htonl(Length);
+ buf[9] = htonl(6);
+ checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum
+ }
+ Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two
+ IPv6_SendPacket(Conn->Interface, Conn->RemoteIP.v6, IP4PROT_TCP, Length, Data);
+ break;
+ }
+}
+
+/**
+ * \brief Handles a packet from the IP Layer
+ * \param Interface Interface the packet arrived from
+ * \param Address Pointer to the addres structure
+ * \param Length Size of packet in bytes
+ * \param Buffer Packet data
+ */
+void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+ tTCPHeader *hdr = Buffer;
+ tTCPListener *srv;
+ tTCPConnection *conn;
+
+ Log_Log("TCP", "TCP_GetPacket: <Local>:%i from [%s]:%i, Flags= %s%s%s%s%s%s%s%s",
+ ntohs(hdr->DestPort),
+ IPStack_PrintAddress(Interface->Type, Address),
+ ntohs(hdr->SourcePort),
+ (hdr->Flags & TCP_FLAG_CWR) ? "CWR " : "",
+ (hdr->Flags & TCP_FLAG_ECE) ? "ECE " : "",
+ (hdr->Flags & TCP_FLAG_URG) ? "URG " : "",
+ (hdr->Flags & TCP_FLAG_ACK) ? "ACK " : "",
+ (hdr->Flags & TCP_FLAG_PSH) ? "PSH " : "",
+ (hdr->Flags & TCP_FLAG_RST) ? "RST " : "",
+ (hdr->Flags & TCP_FLAG_SYN) ? "SYN " : "",
+ (hdr->Flags & TCP_FLAG_FIN) ? "FIN " : ""
+ );
+
+ if( Length > (hdr->DataOffset >> 4)*4 )
+ {
+ Log_Log("TCP", "TCP_GetPacket: SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber));
+#if HEXDUMP_INCOMING
+ Debug_HexDump(
+ "TCP_GetPacket: Packet Data = ",
+ (Uint8*)hdr + (hdr->DataOffset >> 4)*4,
+ Length - (hdr->DataOffset >> 4)*4
+ );
+#endif
+ }
+
+ // Check Servers
+ {
+ for( srv = gTCP_Listeners; srv; srv = srv->Next )
+ {
+ // Check if the server is active
+ if(srv->Port == 0) continue;
+ // Check the interface
+ if(srv->Interface && srv->Interface != Interface) continue;
+ // Check the destination port
+ if(srv->Port != htons(hdr->DestPort)) continue;
+
+ Log_Log("TCP", "TCP_GetPacket: Matches server %p", srv);
+ // Is this in an established connection?
+ for( conn = srv->Connections; conn; conn = conn->Next )
+ {
+ // Check that it is coming in on the same interface
+ if(conn->Interface != Interface) continue;
+
+ // Check Source Port
+ Log_Log("TCP", "TCP_GetPacket: conn->RemotePort(%i) == hdr->SourcePort(%i)",
+ conn->RemotePort, ntohs(hdr->SourcePort));
+ if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
+
+ // Check Source IP
+ Log_Debug("TCP", "TCP_GetPacket: conn->RemoteIP(%s)",
+ IPStack_PrintAddress(conn->Interface->Type, &conn->RemoteIP));
+ Log_Debug("TCP", " == Address(%s)",
+ IPStack_PrintAddress(conn->Interface->Type, Address));
+ if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
+ continue ;
+
+ Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn);
+ // We have a response!
+ TCP_INT_HandleConnectionPacket(conn, hdr, Length);
+
+ return;
+ }
+
+ Log_Log("TCP", "TCP_GetPacket: Opening Connection");
+ // Open a new connection (well, check that it's a SYN)
+ if(hdr->Flags != TCP_FLAG_SYN) {
+ Log_Log("TCP", "TCP_GetPacket: Packet is not a SYN");
+ return ;
+ }
+
+ // TODO: Check for halfopen max
+
+ conn = calloc(1, sizeof(tTCPConnection));
+ conn->State = TCP_ST_SYN_RCVD;
+ conn->LocalPort = srv->Port;
+ conn->RemotePort = ntohs(hdr->SourcePort);
+ conn->Interface = Interface;
+
+ switch(Interface->Type)
+ {
+ case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break;
+ case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break;
+ }
+
+ conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE );
+
+ conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1;
+ conn->NextSequenceSend = rand();
+
+ // Create node
+ conn->Node.NumACLs = 1;
+ conn->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ conn->Node.ImplPtr = conn;
+ conn->Node.ImplInt = srv->NextID ++;
+ conn->Node.Type = &gTCP_ClientNodeType; // TODO: Special type for the server end?
+
+ // Hmm... Theoretically, this lock will never have to wait,
+ // as the interface is locked to the watching thread, and this
+ // runs in the watching thread. But, it's a good idea to have
+ // it, just in case
+ // Oh, wait, there is a case where a wildcard can be used
+ // (srv->Interface == NULL) so having the lock is a good idea
+ SHORTLOCK(&srv->lConnections);
+ if( !srv->Connections )
+ srv->Connections = conn;
+ else
+ srv->ConnectionsTail->Next = conn;
+ srv->ConnectionsTail = conn;
+ if(!srv->NewConnections)
+ srv->NewConnections = conn;
+ VFS_MarkAvaliable( &srv->Node, 1 );
+ SHORTREL(&srv->lConnections);
+
+ // Send the SYN ACK
+ hdr->Flags |= TCP_FLAG_ACK;
+ hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
+ hdr->SequenceNumber = htonl(conn->NextSequenceSend);
+ hdr->DestPort = hdr->SourcePort;
+ hdr->SourcePort = htons(srv->Port);
+ hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4;
+ TCP_SendPacket( conn, sizeof(tTCPHeader), hdr );
+ conn->NextSequenceSend ++;
+ return ;
+ }
+ }
+
+
+ // Check Open Connections
+ {
+ for( conn = gTCP_OutbountCons; conn; conn = conn->Next )
+ {
+ // Check that it is coming in on the same interface
+ if(conn->Interface != Interface) continue;
+
+ // Check Source Port
+ if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
+
+ // Check Source IP
+ if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
+ continue;
+ if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
+ continue;
+
+ TCP_INT_HandleConnectionPacket(conn, hdr, Length);
+ return ;
+ }
+ }
+
+ Log_Log("TCP", "TCP_GetPacket: No Match");
+}
+
+/**
+ * \brief Handles a packet sent to a specific connection
+ * \param Connection TCP Connection pointer
+ * \param Header TCP Packet pointer
+ * \param Length Length of the packet
+ */
+void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
+{
+ int dataLen;
+ Uint32 sequence_num;
+
+ // Silently drop once finished
+ // TODO: Check if this needs to be here
+ if( Connection->State == TCP_ST_FINISHED ) {
+ Log_Log("TCP", "Packet ignored - connection finnished");
+ return ;
+ }
+
+ // Syncronise sequence values
+ if(Header->Flags & TCP_FLAG_SYN) {
+ // TODO: What if the packet also has data?
+ Connection->NextSequenceRcv = ntohl(Header->SequenceNumber);
+ }
+
+ // Ackowledge a sent packet
+ if(Header->Flags & TCP_FLAG_ACK) {
+ // TODO: Process an ACKed Packet
+ Log_Log("TCP", "Conn %p, Sent packet 0x%x ACKed", Connection, Header->AcknowlegementNumber);
+ }
+
+ // Get length of data
+ dataLen = Length - (Header->DataOffset>>4)*4;
+ Log_Log("TCP", "HandleConnectionPacket - dataLen = %i", dataLen);
+
+ //
+ // State Machine
+ //
+ switch( Connection->State )
+ {
+ // Pre-init connection?
+ case TCP_ST_CLOSED:
+ Log_Log("TCP", "Packets to a closed connection?!");
+ break;
+
+ // --- Init States ---
+ // SYN sent, expecting SYN-ACK Connection Opening
+ case TCP_ST_SYN_SENT:
+ if( Header->Flags & TCP_FLAG_SYN )
+ {
+ Connection->NextSequenceRcv ++;
+ Header->DestPort = Header->SourcePort;
+ Header->SourcePort = htons(Connection->LocalPort);
+ Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ Header->SequenceNumber = htonl(Connection->NextSequenceSend);
+ Header->WindowSize = htons(TCP_WINDOW_SIZE);
+ Header->Flags = TCP_FLAG_ACK;
+ Header->DataOffset = (sizeof(tTCPHeader)/4) << 4;
+ TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
+
+ if( Header->Flags & TCP_FLAG_ACK )
+ {
+ Log_Log("TCP", "ACKing SYN-ACK");
+ Connection->State = TCP_ST_OPEN;
+ }
+ else
+ {
+ Log_Log("TCP", "ACKing SYN");
+ Connection->State = TCP_ST_SYN_RCVD;
+ }
+ }
+ break;
+
+ // SYN-ACK sent, expecting ACK
+ case TCP_ST_SYN_RCVD:
+ if( Header->Flags & TCP_FLAG_ACK )
+ {
+ // TODO: Handle max half-open limit
+ Connection->State = TCP_ST_OPEN;
+ Log_Log("TCP", "Connection fully opened");
+ }
+ break;
+
+ // --- Established State ---
+ case TCP_ST_OPEN:
+ // - Handle State changes
+ //
+ if( Header->Flags & TCP_FLAG_FIN ) {
+ Log_Log("TCP", "Conn %p closed, recieved FIN", Connection);
+ VFS_MarkError(&Connection->Node, 1);
+ Connection->State = TCP_ST_CLOSE_WAIT;
+// Header->Flags &= ~TCP_FLAG_FIN;
+ // CLOSE WAIT requires the client to close (or does it?)
+ #if 0
+
+ #endif
+ }
+
+ // Check for an empty packet
+ if(dataLen == 0) {
+ if( Header->Flags == TCP_FLAG_ACK )
+ {
+ Log_Log("TCP", "ACK only packet");
+ return ;
+ }
+ Connection->NextSequenceRcv ++; // TODO: Is this right? (empty packet counts as one byte)
+ Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number");
+ Header->DestPort = Header->SourcePort;
+ Header->SourcePort = htons(Connection->LocalPort);
+ Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ Header->SequenceNumber = htonl(Connection->NextSequenceSend);
+ Header->Flags |= TCP_FLAG_ACK;
+ TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
+ return ;
+ }
+
+ // NOTES:
+ // Flags
+ // PSH - Has Data?
+ // /NOTES
+
+ sequence_num = ntohl(Header->SequenceNumber);
+
+ Log_Log("TCP", "0x%08x <= 0x%08x < 0x%08x",
+ Connection->NextSequenceRcv,
+ ntohl(Header->SequenceNumber),
+ Connection->NextSequenceRcv + TCP_WINDOW_SIZE
+ );
+
+ // Is this packet the next expected packet?
+ if( sequence_num == Connection->NextSequenceRcv )
+ {
+ int rv;
+ // Ooh, Goodie! Add it to the recieved list
+ rv = TCP_INT_AppendRecieved(Connection,
+ (Uint8*)Header + (Header->DataOffset>>4)*4,
+ dataLen
+ );
+ if(rv != 0) {
+ break;
+ }
+ Log_Log("TCP", "0x%08x += %i", Connection->NextSequenceRcv, dataLen);
+ Connection->NextSequenceRcv += dataLen;
+
+ // TODO: This should be moved out of the watcher thread,
+ // so that a single lost packet on one connection doesn't cause
+ // all connections on the interface to lag.
+ // - Meh, no real issue, as the cache shouldn't be that large
+ TCP_INT_UpdateRecievedFromFuture(Connection);
+
+ // ACK Packet
+ Header->DestPort = Header->SourcePort;
+ Header->SourcePort = htons(Connection->LocalPort);
+ Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ Header->SequenceNumber = htonl(Connection->NextSequenceSend);
+ Header->WindowSize = htons(TCP_WINDOW_SIZE);
+ Header->Flags &= TCP_FLAG_SYN; // Eliminate all flags save for SYN
+ Header->Flags |= TCP_FLAG_ACK; // Add ACK
+ Log_Log("TCP", "Sending ACK for 0x%08x", Connection->NextSequenceRcv);
+ TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
+ //Connection->NextSequenceSend ++;
+ }
+ // Check if the packet is in window
+ else if( WrapBetween(Connection->NextSequenceRcv, sequence_num,
+ Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) )
+ {
+ Uint8 *dataptr = (Uint8*)Header + (Header->DataOffset>>4)*4;
+ #if CACHE_FUTURE_PACKETS_IN_BYTES
+ Uint32 index;
+ int i;
+
+ index = sequence_num % TCP_WINDOW_SIZE;
+ for( i = 0; i < dataLen; i ++ )
+ {
+ Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8);
+ Connection->FuturePacketData[index] = dataptr[i];
+ // Do a wrap increment
+ index ++;
+ if(index == TCP_WINDOW_SIZE) index = 0;
+ }
+ #else
+ tTCPStoredPacket *pkt, *tmp, *prev = NULL;
+
+ // Allocate and fill cached packet
+ pkt = malloc( sizeof(tTCPStoredPacket) + dataLen );
+ pkt->Next = NULL;
+ pkt->Sequence = ntohl(Header->SequenceNumber);
+ pkt->Length = dataLen;
+ memcpy(pkt->Data, dataptr, dataLen);
+
+ Log_Log("TCP", "We missed a packet, caching",
+ pkt->Sequence, Connection->NextSequenceRcv);
+
+ // No? Well, let's cache it and look at it later
+ SHORTLOCK( &Connection->lFuturePackets );
+ for(tmp = Connection->FuturePackets;
+ tmp;
+ prev = tmp, tmp = tmp->Next)
+ {
+ if(tmp->Sequence >= pkt->Sequence) break;
+ }
+
+ // Add if before first, or sequences don't match
+ if( !tmp || tmp->Sequence != pkt->Sequence )
+ {
+ if(prev)
+ prev->Next = pkt;
+ else
+ Connection->FuturePackets = pkt;
+ pkt->Next = tmp;
+ }
+ // Replace if larger
+ else if(pkt->Length > tmp->Length)
+ {
+ if(prev)
+ prev->Next = pkt;
+ pkt->Next = tmp->Next;
+ free(tmp);
+ }
+ else
+ {
+ free(pkt); // TODO: Find some way to remove this
+ }
+ SHORTREL( &Connection->lFuturePackets );
+ #endif
+ }
+ // Badly out of sequence packet
+ else
+ {
+ Log_Log("TCP", "Fully out of sequence packet (0x%08x not between 0x%08x and 0x%08x), dropped",
+ sequence_num, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE);
+ // TODO: Spec says we should send an empty ACK with the current state
+ }
+ break;
+
+ // --- Remote close states
+ case TCP_ST_CLOSE_WAIT:
+
+ // Ignore everything, CLOSE_WAIT is terminated by the client
+ Log_Debug("TCP", "CLOSE WAIT - Ignoring packets");
+
+ break;
+
+ // LAST-ACK - Waiting for the ACK of FIN (from CLOSE WAIT)
+ case TCP_ST_LAST_ACK:
+ if( Header->Flags & TCP_FLAG_ACK )
+ {
+ Connection->State = TCP_ST_FINISHED; // Connection completed
+ Log_Log("TCP", "LAST-ACK to CLOSED - Connection remote closed");
+ // TODO: Destrory the TCB
+ }
+ break;
+
+ // --- Local close States
+ case TCP_ST_FIN_WAIT1:
+ if( Header->Flags & TCP_FLAG_FIN )
+ {
+ Connection->State = TCP_ST_CLOSING;
+ Log_Debug("TCP", "Conn %p closed, sent FIN and recieved FIN", Connection);
+ VFS_MarkError(&Connection->Node, 1);
+
+ // ACK Packet
+ Header->DestPort = Header->SourcePort;
+ Header->SourcePort = htons(Connection->LocalPort);
+ Header->AcknowlegementNumber = Header->SequenceNumber;
+ Header->SequenceNumber = htonl(Connection->NextSequenceSend);
+ Header->WindowSize = htons(TCP_WINDOW_SIZE);
+ Header->Flags = TCP_FLAG_ACK;
+ TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
+ break ;
+ }
+
+ // TODO: Make sure that the packet is actually ACKing the FIN
+ if( Header->Flags & TCP_FLAG_ACK )
+ {
+ Connection->State = TCP_ST_FIN_WAIT2;
+ Log_Debug("TCP", "Conn %p closed, sent FIN ACKed", Connection);
+ VFS_MarkError(&Connection->Node, 1);
+ return ;
+ }
+ break;
+
+ case TCP_ST_FIN_WAIT2:
+ if( Header->Flags & TCP_FLAG_FIN )
+ {
+ Connection->State = TCP_ST_TIME_WAIT;
+ Log_Debug("TCP", "FIN sent and recieved, ACKing and going into TIME WAIT %p FINWAIT-2 -> TIME WAIT", Connection);
+ // Send ACK
+ Header->DestPort = Header->SourcePort;
+ Header->SourcePort = htons(Connection->LocalPort);
+ Header->AcknowlegementNumber = Header->SequenceNumber;
+ Header->SequenceNumber = htonl(Connection->NextSequenceSend);
+ Header->WindowSize = htons(TCP_WINDOW_SIZE);
+ Header->Flags = TCP_FLAG_ACK;
+ TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
+ }
+ break;
+
+ case TCP_ST_CLOSING:
+ // TODO: Make sure that the packet is actually ACKing the FIN
+ if( Header->Flags & TCP_FLAG_ACK )
+ {
+ Connection->State = TCP_ST_TIME_WAIT;
+ Log_Debug("TCP", "Conn %p CLOSING -> TIME WAIT", Connection);
+ VFS_MarkError(&Connection->Node, 1);
+ return ;
+ }
+ break;
+
+ // --- Closed (or near closed) states) ---
+ case TCP_ST_TIME_WAIT:
+ Log_Log("TCP", "Packets on Time-Wait, ignored");
+ break;
+
+ case TCP_ST_FINISHED:
+ Log_Log("TCP", "Packets when CLOSED, ignoring");
+ break;
+
+ //default:
+ // Log_Warning("TCP", "Unhandled TCP state %i", Connection->State);
+ // break;
+ }
+
+}
+
+/**
+ * \brief Appends a packet to the recieved list
+ * \param Connection Connection structure
+ * \param Data Packet contents
+ * \param Length Length of \a Data
+ */
+int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length)
+{
+ Mutex_Acquire( &Connection->lRecievedPackets );
+
+ if(Connection->RecievedBuffer->Length + Length > Connection->RecievedBuffer->Space )
+ {
+ VFS_MarkAvaliable(&Connection->Node, 1);
+ Log_Error("TCP", "Buffer filled, packet dropped (:%i) - %i + %i > %i",
+ Connection->LocalPort, Connection->RecievedBuffer->Length, Length,
+ Connection->RecievedBuffer->Space
+ );
+ Mutex_Release( &Connection->lRecievedPackets );
+ return 1;
+ }
+
+ RingBuffer_Write( Connection->RecievedBuffer, Data, Length );
+
+ VFS_MarkAvaliable(&Connection->Node, 1);
+
+ Mutex_Release( &Connection->lRecievedPackets );
+ return 0;
+}
+
+/**
+ * \brief Updates the connections recieved list from the future list
+ * \param Connection Connection structure
+ *
+ * Updates the recieved packets list with packets from the future (out
+ * of order) packets list that are now able to be added in direct
+ * sequence.
+ */
+void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection)
+{
+ #if CACHE_FUTURE_PACKETS_IN_BYTES
+ int i, length = 0;
+ Uint32 index;
+
+ // Calculate length of contiguous bytes
+ length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv;
+ index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
+ for( i = 0; i < length; i ++ )
+ {
+ if( Connection->FuturePacketValidBytes[i / 8] == 0xFF ) {
+ i += 7; index += 7;
+ continue;
+ }
+ else if( !(Connection->FuturePacketValidBytes[i / 8] & (1 << (i%8))) )
+ break;
+
+ index ++;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ }
+ length = i;
+
+ index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
+
+ // Write data to to the ring buffer
+ if( TCP_WINDOW_SIZE - index > length )
+ {
+ // Simple case
+ RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length );
+ }
+ else
+ {
+ int endLen = TCP_WINDOW_SIZE - index;
+ // 2-part case
+ RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, endLen );
+ RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - length );
+ }
+
+ // Mark (now saved) bytes as invalid
+ // - Align index
+ while(index % 8 && length)
+ {
+ Connection->FuturePacketData[index] = 0;
+ Connection->FuturePacketData[index/8] &= ~(1 << (index%8));
+ index ++;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ length --;
+ }
+ while( length > 7 )
+ {
+ Connection->FuturePacketData[index] = 0;
+ Connection->FuturePacketValidBytes[index/8] = 0;
+ length -= 8;
+ index += 8;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ }
+ while(length)
+ {
+ Connection->FuturePacketData[index] = 0;
+ Connection->FuturePacketData[index/8] &= ~(1 << (index%8));
+ index ++;
+ if(index > TCP_WINDOW_SIZE)
+ index -= TCP_WINDOW_SIZE;
+ length --;
+ }
+
+ #else
+ tTCPStoredPacket *pkt;
+ for(;;)
+ {
+ SHORTLOCK( &Connection->lFuturePackets );
+
+ // Clear out duplicates from cache
+ // - If a packet has just been recieved, and it is expected, then
+ // (since NextSequenceRcv = rcvd->Sequence + rcvd->Length) all
+ // packets in cache that are smaller than the next expected
+ // are now defunct.
+ pkt = Connection->FuturePackets;
+ while(pkt && pkt->Sequence < Connection->NextSequenceRcv)
+ {
+ tTCPStoredPacket *next = pkt->Next;
+ free(pkt);
+ pkt = next;
+ }
+
+ // If there's no packets left in cache, stop looking
+ if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) {
+ SHORTREL( &Connection->lFuturePackets );
+ return;
+ }
+
+ // Delete packet from future list
+ Connection->FuturePackets = pkt->Next;
+
+ // Release list
+ SHORTREL( &Connection->lFuturePackets );
+
+ // Looks like we found one
+ TCP_INT_AppendRecieved(Connection, pkt);
+ Connection->NextSequenceRcv += pkt->Length;
+ free(pkt);
+ }
+ #endif
+}
+
+/**
+ * \fn Uint16 TCP_GetUnusedPort()
+ * \brief Gets an unused port and allocates it
+ */
+Uint16 TCP_GetUnusedPort()
+{
+ Uint16 ret;
+
+ // Get Next outbound port
+ ret = giTCP_NextOutPort++;
+ while( gaTCP_PortBitmap[ret/32] & (1UL << (ret%32)) )
+ {
+ ret ++;
+ giTCP_NextOutPort++;
+ if(giTCP_NextOutPort == 0x10000) {
+ ret = giTCP_NextOutPort = TCP_MIN_DYNPORT;
+ }
+ }
+
+ // Mark the new port as used
+ gaTCP_PortBitmap[ret/32] |= 1 << (ret%32);
+
+ return ret;
+}
+
+/**
+ * \fn int TCP_AllocatePort(Uint16 Port)
+ * \brief Marks a port as used
+ */
+int TCP_AllocatePort(Uint16 Port)
+{
+ // Check if the port has already been allocated
+ if( gaTCP_PortBitmap[Port/32] & (1 << (Port%32)) )
+ return 0;
+
+ // Allocate
+ gaTCP_PortBitmap[Port/32] |= 1 << (Port%32);
+
+ return 1;
+}
+
+/**
+ * \fn int TCP_DeallocatePort(Uint16 Port)
+ * \brief Marks a port as unused
+ */
+int TCP_DeallocatePort(Uint16 Port)
+{
+ // Check if the port has already been allocated
+ if( !(gaTCP_PortBitmap[Port/32] & (1 << (Port%32))) )
+ return 0;
+
+ // Allocate
+ gaTCP_PortBitmap[Port/32] &= ~(1 << (Port%32));
+
+ return 1;
+}
+
+// --- Server
+tVFS_Node *TCP_Server_Init(tInterface *Interface)
+{
+ tTCPListener *srv;
+
+ srv = calloc( 1, sizeof(tTCPListener) );
+
+ if( srv == NULL ) {
+ Log_Warning("TCP", "malloc failed for listener (%i) bytes", sizeof(tTCPListener));
+ return NULL;
+ }
+
+ srv->Interface = Interface;
+ srv->Port = 0;
+ srv->NextID = 0;
+ srv->Connections = NULL;
+ srv->ConnectionsTail = NULL;
+ srv->NewConnections = NULL;
+ srv->Next = NULL;
+ srv->Node.Flags = VFS_FFLAG_DIRECTORY;
+ srv->Node.Size = -1;
+ srv->Node.ImplPtr = srv;
+ srv->Node.NumACLs = 1;
+ srv->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ srv->Node.Type = &gTCP_ServerNodeType;
+
+ SHORTLOCK(&glTCP_Listeners);
+ srv->Next = gTCP_Listeners;
+ gTCP_Listeners = srv;
+ SHORTREL(&glTCP_Listeners);
+
+ return &srv->Node;
+}
+
+/**
+ * \brief Wait for a new connection and return the connection ID
+ * \note Blocks until a new connection is made
+ * \param Node Server node
+ * \param Pos Position (ignored)
+ */
+char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tTCPListener *srv = Node->ImplPtr;
+ tTCPConnection *conn;
+ char *ret;
+
+ ENTER("pNode iPos", Node, Pos);
+
+ Log_Log("TCP", "Thread %i waiting for a connection", Threads_GetTID());
+ for(;;)
+ {
+ SHORTLOCK( &srv->lConnections );
+ if( srv->NewConnections != NULL ) break;
+ SHORTREL( &srv->lConnections );
+ Threads_Yield(); // TODO: Sleep until poked
+ }
+
+
+ // Increment the new list (the current connection is still on the
+ // normal list)
+ conn = srv->NewConnections;
+ srv->NewConnections = conn->Next;
+
+ if( srv->NewConnections == NULL )
+ VFS_MarkAvaliable( Node, 0 );
+
+ SHORTREL( &srv->lConnections );
+
+ LOG("conn = %p", conn);
+ LOG("srv->Connections = %p", srv->Connections);
+ LOG("srv->NewConnections = %p", srv->NewConnections);
+ LOG("srv->ConnectionsTail = %p", srv->ConnectionsTail);
+
+ ret = malloc(9);
+ itoa(ret, conn->Node.ImplInt, 16, 8, '0');
+ Log_Log("TCP", "Thread %i got '%s'", Threads_GetTID(), ret);
+ LEAVE('s', ret);
+ return ret;
+}
+
+/**
+ * \brief Gets a client connection node
+ * \param Node Server node
+ * \param Name Hexadecimal ID of the node
+ */
+tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name)
+{
+ tTCPConnection *conn;
+ tTCPListener *srv = Node->ImplPtr;
+ char tmp[9];
+ int id = atoi(Name);
+
+ ENTER("pNode sName", Node, Name);
+
+ // Check for a non-empty name
+ if( Name[0] )
+ {
+ // Sanity Check
+ itoa(tmp, id, 16, 8, '0');
+ if(strcmp(tmp, Name) != 0) {
+ LOG("'%s' != '%s' (%08x)", Name, tmp, id);
+ LEAVE('n');
+ return NULL;
+ }
+
+ Log_Debug("TCP", "srv->Connections = %p", srv->Connections);
+ Log_Debug("TCP", "srv->NewConnections = %p", srv->NewConnections);
+ Log_Debug("TCP", "srv->ConnectionsTail = %p", srv->ConnectionsTail);
+
+ // Search
+ SHORTLOCK( &srv->lConnections );
+ for(conn = srv->Connections;
+ conn;
+ conn = conn->Next)
+ {
+ LOG("conn->Node.ImplInt = %i", conn->Node.ImplInt);
+ if(conn->Node.ImplInt == id) break;
+ }
+ SHORTREL( &srv->lConnections );
+
+ // If not found, ret NULL
+ if(!conn) {
+ LOG("Connection %i not found", id);
+ LEAVE('n');
+ return NULL;
+ }
+ }
+ // Empty Name - Check for a new connection and if it's there, open it
+ else
+ {
+ SHORTLOCK( &srv->lConnections );
+ conn = srv->NewConnections;
+ if( conn != NULL )
+ srv->NewConnections = conn->Next;
+ VFS_MarkAvaliable( Node, srv->NewConnections != NULL );
+ SHORTREL( &srv->lConnections );
+ if( !conn ) {
+ LOG("No new connections");
+ LEAVE('n');
+ return NULL;
+ }
+ }
+
+ // Return node
+ LEAVE('p', &conn->Node);
+ return &conn->Node;
+}
+
+/**
+ * \brief Handle IOCtl calls
+ */
+int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tTCPListener *srv = Node->ImplPtr;
+
+ switch(ID)
+ {
+ case 4: // Get/Set Port
+ if(!Data) // Get Port
+ return srv->Port;
+
+ if(srv->Port) // Wait, you can't CHANGE the port
+ return -1;
+
+ if(!CheckMem(Data, sizeof(Uint16))) // Sanity check
+ return -1;
+
+ // Permissions check
+ if(Threads_GetUID() != 0
+ && *(Uint16*)Data != 0
+ && *(Uint16*)Data < 1024)
+ return -1;
+
+ // TODO: Check if a port is in use
+
+ // Set Port
+ srv->Port = *(Uint16*)Data;
+ if(srv->Port == 0) // Allocate a random port
+ srv->Port = TCP_GetUnusedPort();
+ else // Else, mark this as used
+ TCP_AllocatePort(srv->Port);
+
+ Log_Log("TCP", "Server %p listening on port %i", srv, srv->Port);
+
+ return srv->Port;
+ }
+ return 0;
+}
+
+void TCP_Server_Close(tVFS_Node *Node)
+{
+ free(Node->ImplPtr);
+}
+
+// --- Client
+/**
+ * \brief Create a client node
+ */
+tVFS_Node *TCP_Client_Init(tInterface *Interface)
+{
+ tTCPConnection *conn = calloc( sizeof(tTCPConnection) + TCP_WINDOW_SIZE + TCP_WINDOW_SIZE/8, 1 );
+
+ conn->State = TCP_ST_CLOSED;
+ conn->Interface = Interface;
+ conn->LocalPort = -1;
+ conn->RemotePort = -1;
+
+ conn->Node.ImplPtr = conn;
+ conn->Node.NumACLs = 1;
+ conn->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ conn->Node.Type = &gTCP_ClientNodeType;
+
+ conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE );
+ #if 0
+ conn->SentBuffer = RingBuffer_Create( TCP_SEND_BUFFER_SIZE );
+ Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name);
+ #endif
+
+ #if CACHE_FUTURE_PACKETS_IN_BYTES
+ // Future recieved data (ahead of the expected sequence number)
+ conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection);
+ conn->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE;
+ #endif
+
+ SHORTLOCK(&glTCP_OutbountCons);
+ conn->Next = gTCP_OutbountCons;
+ gTCP_OutbountCons = conn;
+ SHORTREL(&glTCP_OutbountCons);
+
+ return &conn->Node;
+}
+
+/**
+ * \brief Wait for a packet and return it
+ * \note If \a Length is smaller than the size of the packet, the rest
+ * of the packet's data will be discarded.
+ */
+Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tTCPConnection *conn = Node->ImplPtr;
+ size_t len;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+ LOG("conn = %p {State:%i}", conn, conn->State);
+
+ // Check if connection is estabilishing
+ // - TODO: Sleep instead (maybe using VFS_SelectNode to wait for the
+ // data to be availiable
+ while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT )
+ Threads_Yield();
+
+ // If the conneciton is not open, then clean out the recieved buffer
+ if( conn->State != TCP_ST_OPEN )
+ {
+ Mutex_Acquire( &conn->lRecievedPackets );
+ len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
+ Mutex_Release( &conn->lRecievedPackets );
+
+ if( len == 0 ) {
+ VFS_MarkAvaliable(Node, 0);
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ LEAVE('i', len);
+ return len;
+ }
+
+ // Wait
+ VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, NULL, "TCP_Client_Read");
+
+ // Lock list and read as much as possible (up to `Length`)
+ Mutex_Acquire( &conn->lRecievedPackets );
+ len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
+
+ if( len == 0 || conn->RecievedBuffer->Length == 0 ) {
+ LOG("Marking as none avaliable (len = %i)", len);
+ VFS_MarkAvaliable(Node, 0);
+ }
+
+ // Release the lock (we don't need it any more)
+ Mutex_Release( &conn->lRecievedPackets );
+
+ LEAVE('i', len);
+ return len;
+}
+
+/**
+ * \brief Send a data packet on a connection
+ */
+void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const void *Data)
+{
+ char buf[sizeof(tTCPHeader)+Length];
+ tTCPHeader *packet = (void*)buf;
+
+ packet->SourcePort = htons(Connection->LocalPort);
+ packet->DestPort = htons(Connection->RemotePort);
+ packet->DataOffset = (sizeof(tTCPHeader)/4)*16;
+ packet->WindowSize = htons(TCP_WINDOW_SIZE);
+
+ packet->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
+ packet->SequenceNumber = htonl(Connection->NextSequenceSend);
+ packet->Flags = TCP_FLAG_PSH|TCP_FLAG_ACK; // Hey, ACK if you can!
+
+ memcpy(packet->Options, Data, Length);
+
+ Log_Debug("TCP", "Send sequence 0x%08x", Connection->NextSequenceSend);
+#if HEXDUMP_OUTGOING
+ Debug_HexDump("TCP_INT_SendDataPacket: Data = ", Data, Length);
+#endif
+
+ TCP_SendPacket( Connection, sizeof(tTCPHeader)+Length, packet );
+
+ Connection->NextSequenceSend += Length;
+}
+
+/**
+ * \brief Send some bytes on a connection
+ */
+Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tTCPConnection *conn = Node->ImplPtr;
+ size_t rem = Length;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+// #if DEBUG
+// Debug_HexDump("TCP_Client_Write: Buffer = ",
+// Buffer, Length);
+// #endif
+
+ // Check if connection is open
+ while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT )
+ Threads_Yield();
+
+ if( conn->State != TCP_ST_OPEN ) {
+ VFS_MarkError(Node, 1);
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ do
+ {
+ int len = (rem < TCP_MAX_PACKET_SIZE) ? rem : TCP_MAX_PACKET_SIZE;
+
+ #if 0
+ // Wait for space in the buffer
+ Semaphore_Signal( &Connection->SentBufferSpace, len );
+
+ // Save data to buffer (and update the length read by the ammount written)
+ len = RingBuffer_Write( &Connection->SentBuffer, Buffer, len);
+ #endif
+
+ // Send packet
+ TCP_INT_SendDataPacket(conn, len, Buffer);
+
+ Buffer += len;
+ rem -= len;
+ } while( rem > 0 );
+
+ LEAVE('i', Length);
+ return Length;
+}
+
+/**
+ * \brief Open a connection to another host using TCP
+ * \param Conn Connection structure
+ */
+void TCP_StartConnection(tTCPConnection *Conn)
+{
+ tTCPHeader hdr = {0};
+
+ Conn->State = TCP_ST_SYN_SENT;
+
+ hdr.SourcePort = htons(Conn->LocalPort);
+ hdr.DestPort = htons(Conn->RemotePort);
+ Conn->NextSequenceSend = rand();
+ hdr.SequenceNumber = htonl(Conn->NextSequenceSend);
+ hdr.DataOffset = (sizeof(tTCPHeader)/4) << 4;
+ hdr.Flags = TCP_FLAG_SYN;
+ hdr.WindowSize = htons(TCP_WINDOW_SIZE); // Max
+ hdr.Checksum = 0; // TODO
+
+ TCP_SendPacket( Conn, sizeof(tTCPHeader), &hdr );
+
+ Conn->NextSequenceSend ++;
+ Conn->State = TCP_ST_SYN_SENT;
+
+ return ;
+}
+
+/**
+ * \brief Control a client socket
+ */
+int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tTCPConnection *conn = Node->ImplPtr;
+
+ ENTER("pNode iID pData", Node, ID, Data);
+
+ switch(ID)
+ {
+ case 4: // Get/Set local port
+ if(!Data)
+ LEAVE_RET('i', conn->LocalPort);
+ if(conn->State != TCP_ST_CLOSED)
+ LEAVE_RET('i', -1);
+ if(!CheckMem(Data, sizeof(Uint16)))
+ LEAVE_RET('i', -1);
+
+ if(Threads_GetUID() != 0 && *(Uint16*)Data < 1024)
+ LEAVE_RET('i', -1);
+
+ conn->LocalPort = *(Uint16*)Data;
+ LEAVE_RET('i', conn->LocalPort);
+
+ case 5: // Get/Set remote port
+ if(!Data) LEAVE_RET('i', conn->RemotePort);
+ if(conn->State != TCP_ST_CLOSED) LEAVE_RET('i', -1);
+ if(!CheckMem(Data, sizeof(Uint16))) LEAVE_RET('i', -1);
+ conn->RemotePort = *(Uint16*)Data;
+ LEAVE_RET('i', conn->RemotePort);
+
+ case 6: // Set Remote IP
+ if( conn->State != TCP_ST_CLOSED )
+ LEAVE_RET('i', -1);
+ if( conn->Interface->Type == 4 )
+ {
+ if(!CheckMem(Data, sizeof(tIPv4))) LEAVE_RET('i', -1);
+ conn->RemoteIP.v4 = *(tIPv4*)Data;
+ }
+ else if( conn->Interface->Type == 6 )
+ {
+ if(!CheckMem(Data, sizeof(tIPv6))) LEAVE_RET('i', -1);
+ conn->RemoteIP.v6 = *(tIPv6*)Data;
+ }
+ LEAVE_RET('i', 0);
+
+ case 7: // Connect
+ if(conn->LocalPort == 0xFFFF)
+ conn->LocalPort = TCP_GetUnusedPort();
+ if(conn->RemotePort == -1)
+ LEAVE_RET('i', 0);
+
+ {
+ tTime timeout_end = now() + conn->Interface->TimeoutDelay;
+
+ TCP_StartConnection(conn);
+ // TODO: Wait for connection to open
+ while( conn->State == TCP_ST_SYN_SENT && timeout_end > now() ) {
+ Threads_Yield();
+ }
+ if( conn->State == TCP_ST_SYN_SENT )
+ LEAVE_RET('i', 0);
+ }
+
+ LEAVE_RET('i', 1);
+
+ // Get recieve buffer length
+ case 8:
+ LEAVE_RET('i', conn->RecievedBuffer->Length);
+ }
+
+ return 0;
+}
+
+void TCP_Client_Close(tVFS_Node *Node)
+{
+ tTCPConnection *conn = Node->ImplPtr;
+ tTCPHeader packet;
+
+ ENTER("pNode", Node);
+
+ if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN )
+ {
+ packet.SourcePort = htons(conn->LocalPort);
+ packet.DestPort = htons(conn->RemotePort);
+ packet.DataOffset = (sizeof(tTCPHeader)/4)*16;
+ packet.WindowSize = TCP_WINDOW_SIZE;
+
+ packet.AcknowlegementNumber = 0;
+ packet.SequenceNumber = htonl(conn->NextSequenceSend);
+ packet.Flags = TCP_FLAG_FIN;
+
+ TCP_SendPacket( conn, sizeof(tTCPHeader), &packet );
+ }
+
+ switch( conn->State )
+ {
+ case TCP_ST_CLOSE_WAIT:
+ conn->State = TCP_ST_LAST_ACK;
+ break;
+ case TCP_ST_OPEN:
+ conn->State = TCP_ST_FIN_WAIT1;
+ while( conn->State == TCP_ST_FIN_WAIT1 ) Threads_Yield();
+ break;
+ default:
+ Log_Warning("TCP", "Unhandled connection state in TCP_Client_Close");
+ break;
+ }
+
+ free(conn);
+
+ LEAVE('-');
+}
+
+/**
+ * \brief Checks if a value is between two others (after taking into account wrapping)
+ */
+int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue)
+{
+ if( MaxValue < 0xFFFFFFFF )
+ {
+ Lower %= MaxValue + 1;
+ Value %= MaxValue + 1;
+ Higher %= MaxValue + 1;
+ }
+
+ // Simple Case, no wrap ?
+ // Lower Value Higher
+ // | ... + ... + ... + ... |
+
+ if( Lower < Higher ) {
+ return Lower < Value && Value < Higher;
+ }
+ // Higher has wrapped below lower
+
+ // Value > Lower ?
+ // Higher Lower Value
+ // | ... + ... + ... + ... |
+ if( Value > Lower ) {
+ return 1;
+ }
+
+ // Value < Higher ?
+ // Value Higher Lower
+ // | ... + ... + ... + ... |
+ if( Value < Higher ) {
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - TCP Definitions
+ */
+#ifndef _TCP_H_
+#define _TCP_H_
+
+#include "ipstack.h"
+#include <adt.h> // tRingBuffer
+
+typedef struct sTCPHeader tTCPHeader;
+typedef struct sTCPListener tTCPListener;
+typedef struct sTCPStoredPacket tTCPStoredPacket;
+typedef struct sTCPConnection tTCPConnection;
+
+struct sTCPHeader
+{
+ Uint16 SourcePort;
+ Uint16 DestPort;
+ Uint32 SequenceNumber;
+ Uint32 AcknowlegementNumber;
+ #if 0
+ struct { // Lowest to highest
+ unsigned Reserved: 4;
+ unsigned DataOffset: 4; // Size of the header in 32-bit words
+ } __attribute__ ((packed));
+ #else
+ Uint8 DataOffset;
+ #endif
+ #if 0
+ struct { // Lowest to Highest
+ unsigned FIN: 1; // Last packet
+ unsigned SYN: 1; // Synchronise Sequence Numbers
+ unsigned RST: 1; // Reset Connection
+ unsigned PSH: 1; // Push Function
+ unsigned ACK: 1; // Acknowlegement field is significant
+ unsigned URG: 1; // Urgent pointer is significant
+ unsigned ECE: 1; // ECN-Echo
+ unsigned CWR: 1; // Congestion Window Reduced
+ } __attribute__ ((packed)) Flags;
+ #else
+ Uint8 Flags;
+ #endif
+ Uint16 WindowSize;
+
+ Uint16 Checksum;
+ Uint16 UrgentPointer;
+
+ Uint8 Options[];
+} __attribute__ ((packed));
+
+enum eTCPFlags
+{
+ TCP_FLAG_FIN = 0x01,
+ TCP_FLAG_SYN = 0x02,
+ TCP_FLAG_RST = 0x04,
+ TCP_FLAG_PSH = 0x08,
+ TCP_FLAG_ACK = 0x10,
+ TCP_FLAG_URG = 0x20,
+ TCP_FLAG_ECE = 0x40,
+ TCP_FLAG_CWR = 0x80
+};
+
+struct sTCPListener
+{
+ struct sTCPListener *Next; //!< Next server in the list
+ Uint16 Port; //!< Listening port (0 disables the server)
+ tInterface *Interface; //!< Listening Interface
+ tVFS_Node Node; //!< Server Directory node
+ int NextID; //!< Name of the next connection
+ tShortSpinlock lConnections; //!< Spinlock for connections
+ tTCPConnection *Connections; //!< Connections (linked list)
+ tTCPConnection *volatile NewConnections;
+ tTCPConnection *ConnectionsTail;
+};
+
+struct sTCPStoredPacket
+{
+ struct sTCPStoredPacket *Next;
+ size_t Length;
+ Uint32 Sequence;
+ Uint8 Data[];
+};
+
+enum eTCPConnectionState
+{
+ TCP_ST_CLOSED, // 0 - Connection invalid
+
+ TCP_ST_SYN_SENT, // 1 - SYN sent by local, waiting for SYN-ACK
+ TCP_ST_SYN_RCVD, // 2 - SYN recieved, SYN-ACK sent
+
+ TCP_ST_OPEN, // 3 - Connection open
+
+ // Local Close
+ TCP_ST_FIN_WAIT1, // 4 - FIN sent, waiting for reply (ACK or FIN)
+ TCP_ST_FIN_WAIT2, // 5 - sent FIN acked, waiting for FIN from peer
+ TCP_ST_CLOSING, // 6 - Waiting for ACK of FIN (FIN sent and recieved)
+ TCP_ST_TIME_WAIT, // 7 - Waiting for timeout after local close
+ // Remote close
+ TCP_ST_CLOSE_WAIT, // 8 - FIN recieved, waiting for user to close (error set, wait for node close)
+ TCP_ST_LAST_ACK, // 9 - FIN sent and recieved, waiting for ACK
+ TCP_ST_FINISHED // 10 - Essentially closed, all packets are invalid
+};
+
+struct sTCPConnection
+{
+ struct sTCPConnection *Next;
+ enum eTCPConnectionState State; //!< Connection state (see ::eTCPConnectionState)
+ Uint16 LocalPort; //!< Local port
+ Uint16 RemotePort; //!< Remote port
+ tInterface *Interface; //!< Listening Interface
+ tVFS_Node Node; //!< Node
+
+ Uint32 NextSequenceSend; //!< Next sequence value for outbound packets
+ Uint32 NextSequenceRcv; //!< Next expected sequence value for inbound
+
+ #if 0
+ /**
+ * \brief Non-ACKed packets
+ * \note Ring buffer
+ * \{
+ */
+ tMutex lNonACKedPackets;
+ tTCPStoredPacket *SentPackets; //!< Non-acknowleged packets
+ /**
+ * \}
+ */
+ #endif
+
+ /**
+ * \brief Unread Packets
+ * \note Ring buffer
+ * \{
+ */
+ tMutex lRecievedPackets;
+ tRingBuffer *RecievedBuffer;
+ /**
+ * \}
+ */
+
+ /**
+ * \brief Out of sequence packets
+ * \note Sorted list to improve times
+ * \todo Convert this to a ring buffer and a bitmap of valid bytes
+ * \{
+ */
+ #if CACHE_FUTURE_PACKETS_OR_BYTES == bytes
+ Uint32 HighestSequenceRcvd; //!< Highest sequence number (within window) recieved
+ Uint8 *FuturePacketData; //!< Future packet data (indexed by sequence number)
+ Uint8 *FuturePacketValidBytes; //!< Valid byte bitmap (WINDOW_SIZE/8 bytes)
+ #else
+ tShortSpinlock lFuturePackets; //!< Future packets spinlock
+ tTCPStoredPacket *FuturePackets; //!< Out of sequence packets
+ #endif
+ /**
+ * \}
+ */
+
+ union {
+ tIPv4 v4;
+ tIPv6 v6;
+ } RemoteIP; //!< Remote IP Address
+ // Type is determined by LocalInterface->Type
+};
+
+#endif
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - UDP Handling
+ */
+#include "ipstack.h"
+#include <api_drv_common.h>
+#include "udp.h"
+
+#define UDP_ALLOC_BASE 0xC000
+
+// === PROTOTYPES ===
+void UDP_Initialise();
+void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
+void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
+void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length);
+// --- Client Channels
+tVFS_Node *UDP_Channel_Init(tInterface *Interface);
+Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data);
+void UDP_Channel_Close(tVFS_Node *Node);
+// --- Helpers
+Uint16 UDP_int_AllocatePort();
+ int UDP_int_MarkPortAsUsed(Uint16 Port);
+void UDP_int_FreePort(Uint16 Port);
+
+// === GLOBALS ===
+tVFS_NodeType gUDP_NodeType = {
+ .Read = UDP_Channel_Read,
+ .Write = UDP_Channel_Write,
+ .IOCtl = UDP_Channel_IOCtl,
+ .Close = UDP_Channel_Close
+};
+tMutex glUDP_Channels;
+tUDPChannel *gpUDP_Channels;
+
+tMutex glUDP_Ports;
+Uint32 gUDP_Ports[0x10000/32];
+
+tSocketFile gUDP_SocketFile = {NULL, "udp", UDP_Channel_Init};
+
+// === CODE ===
+/**
+ * \fn void TCP_Initialise()
+ * \brief Initialise the TCP Layer
+ */
+void UDP_Initialise()
+{
+ IPStack_AddFile(&gUDP_SocketFile);
+ //IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable);
+ IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket);
+}
+
+/**
+ * \brief Scan a list of tUDPChannels and find process the first match
+ * \return 0 if no match was found, -1 on error and 1 if a match was found
+ */
+int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+ tUDPHeader *hdr = Buffer;
+ tUDPChannel *chan;
+ tUDPPacket *pack;
+ int len;
+
+ for(chan = List;
+ chan;
+ chan = chan->Next)
+ {
+ // Match local endpoint
+ if(chan->Interface && chan->Interface != Interface) continue;
+ if(chan->LocalPort != ntohs(hdr->DestPort)) continue;
+
+ // Check for remote port restriction
+ if(chan->Remote.Port && chan->Remote.Port != ntohs(hdr->SourcePort))
+ continue;
+ // Check for remote address restriction
+ if(chan->RemoteMask)
+ {
+ if(chan->Remote.AddrType != Interface->Type)
+ continue;
+ if(!IPStack_CompareAddress(Interface->Type, Address,
+ &chan->Remote.Addr, chan->RemoteMask)
+ )
+ continue;
+ }
+
+ Log_Log("UDP", "Recieved packet for %p", chan);
+ // Create the cached packet
+ len = ntohs(hdr->Length);
+ pack = malloc(sizeof(tUDPPacket) + len);
+ pack->Next = NULL;
+ memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type));
+ pack->Remote.Port = ntohs(hdr->SourcePort);
+ pack->Remote.AddrType = Interface->Type;
+ pack->Length = len;
+ memcpy(pack->Data, hdr->Data, len);
+
+ // Add the packet to the channel's queue
+ SHORTLOCK(&chan->lQueue);
+ if(chan->Queue)
+ chan->QueueEnd->Next = pack;
+ else
+ chan->QueueEnd = chan->Queue = pack;
+ SHORTREL(&chan->lQueue);
+ VFS_MarkAvaliable(&chan->Node, 1);
+ Mutex_Release(&glUDP_Channels);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+ * \brief Handles a packet from the IP Layer
+ */
+void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+ tUDPHeader *hdr = Buffer;
+
+ Log_Debug("UDP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort));
+ Log_Debug("UDP", "hdr->DestPort = %i", ntohs(hdr->DestPort));
+ Log_Debug("UDP", "hdr->Length = %i", ntohs(hdr->Length));
+ Log_Debug("UDP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
+
+ // Check registered connections
+ Mutex_Acquire(&glUDP_Channels);
+ UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer);
+ Mutex_Release(&glUDP_Channels);
+}
+
+/**
+ * \brief Handle an ICMP Unrechable Error
+ */
+void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
+{
+
+}
+
+/**
+ * \brief Send a packet
+ * \param Channel Channel to send the packet from
+ * \param Data Packet data
+ * \param Length Length in bytes of packet data
+ */
+void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length)
+{
+ tUDPHeader *hdr;
+
+ if(Channel->Interface && Channel->Interface->Type != AddrType) return ;
+
+ switch(AddrType)
+ {
+ case 4:
+ // Create the packet
+ hdr = malloc(sizeof(tUDPHeader)+Length);
+ hdr->SourcePort = htons( Channel->LocalPort );
+ hdr->DestPort = htons( Port );
+ hdr->Length = htons( sizeof(tUDPHeader) + Length );
+ hdr->Checksum = 0; // Checksum can be zero on IPv4
+ memcpy(hdr->Data, Data, Length);
+ // Pass on the the IPv4 Layer
+ // TODO: What if Channel->Interface is NULL here?
+ IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, sizeof(tUDPHeader)+Length, hdr);
+ // Free allocated packet
+ free(hdr);
+ break;
+ }
+}
+
+// --- Client Channels
+tVFS_Node *UDP_Channel_Init(tInterface *Interface)
+{
+ tUDPChannel *new;
+ new = calloc( sizeof(tUDPChannel), 1 );
+ new->Interface = Interface;
+ new->Node.ImplPtr = new;
+ new->Node.NumACLs = 1;
+ new->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ new->Node.Type = &gUDP_NodeType;
+
+ Mutex_Acquire(&glUDP_Channels);
+ new->Next = gpUDP_Channels;
+ gpUDP_Channels = new;
+ Mutex_Release(&glUDP_Channels);
+
+ return &new->Node;
+}
+
+/**
+ * \brief Read from the channel file (wait for a packet)
+ */
+Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ tUDPPacket *pack;
+ tUDPEndpoint *ep;
+ int ofs, addrlen;
+
+ if(chan->LocalPort == 0) {
+ Log_Notice("UDP", "Channel %p sent with no local port", chan);
+ return 0;
+ }
+
+ while(chan->Queue == NULL) Threads_Yield();
+
+ for(;;)
+ {
+ VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read");
+ SHORTLOCK(&chan->lQueue);
+ if(chan->Queue == NULL) {
+ SHORTREL(&chan->lQueue);
+ continue;
+ }
+ pack = chan->Queue;
+ chan->Queue = pack->Next;
+ if(!chan->Queue) {
+ chan->QueueEnd = NULL;
+ VFS_MarkAvaliable(Node, 0); // Nothing left
+ }
+ SHORTREL(&chan->lQueue);
+ break;
+ }
+
+ // Check that the header fits
+ addrlen = IPStack_GetAddressSize(pack->Remote.AddrType);
+ ep = Buffer;
+ ofs = 4 + addrlen;
+ if(Length < ofs) {
+ free(pack);
+ Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs);
+ return 0;
+ }
+
+ // Fill header
+ ep->Port = pack->Remote.Port;
+ ep->AddrType = pack->Remote.AddrType;
+ memcpy(&ep->Addr, &pack->Remote.Addr, addrlen);
+
+ // Copy packet data
+ if(Length > ofs + pack->Length) Length = ofs + pack->Length;
+ memcpy((char*)Buffer + ofs, pack->Data, Length - ofs);
+
+ // Free cached packet
+ free(pack);
+
+ return Length;
+}
+
+/**
+ * \brief Write to the channel file (send a packet)
+ */
+Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ const tUDPEndpoint *ep;
+ const void *data;
+ int ofs;
+ if(chan->LocalPort == 0) return 0;
+
+ ep = Buffer;
+ ofs = 2 + 2 + IPStack_GetAddressSize( ep->AddrType );
+
+ data = (const char *)Buffer + ofs;
+
+ UDP_SendPacketTo(chan, ep->AddrType, &ep->Addr, ep->Port, data, (size_t)Length - ofs);
+
+ return 0;
+}
+
+/**
+ * \brief Names for channel IOCtl Calls
+ */
+static const char *casIOCtls_Channel[] = {
+ DRV_IOCTLNAMES,
+ "getset_localport",
+ "getset_remoteport",
+ "getset_remotemask",
+ "set_remoteaddr",
+ NULL
+ };
+/**
+ * \brief Channel IOCtls
+ */
+int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_MISC, "UDP Channel", 0x100, casIOCtls_Channel);
+
+ case 4: // getset_localport (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->LocalPort);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ // Set port
+ chan->LocalPort = *(Uint16*)Data;
+ // Permissions check (Ports lower than 1024 are root-only)
+ if(chan->LocalPort != 0 && chan->LocalPort < 1024) {
+ if( Threads_GetUID() != 0 ) {
+ LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort);
+ chan->LocalPort = 0;
+ LEAVE_RET('i', -1);
+ }
+ }
+ // Allocate a random port if requested
+ if( chan->LocalPort == 0 )
+ chan->LocalPort = UDP_int_AllocatePort();
+ else
+ {
+ // Else, mark the requested port as used
+ if( UDP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) {
+ LOG("Port %i us currently in use", chan->LocalPort);
+ chan->LocalPort = 0;
+ LEAVE_RET('i', 0);
+ }
+ LEAVE_RET('i', 1);
+ }
+ LEAVE_RET('i', 1);
+
+ case 5: // getset_remoteport (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->Remote.Port);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ chan->Remote.Port = *(Uint16*)Data;
+ return 1;
+
+ case 6: // getset_remotemask (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->RemoteMask);
+ if(!CheckMem(Data, sizeof(int))) LEAVE_RET('i', -1);
+ if( !chan->Interface ) {
+ LOG("Can't set remote mask on NULL interface");
+ LEAVE_RET('i', -1);
+ }
+ if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) )
+ LEAVE_RET('i', -1);
+ chan->RemoteMask = *(int*)Data;
+ return 1;
+
+ case 7: // set_remoteaddr (returns bool success)
+ if( !chan->Interface ) {
+ LOG("Can't set remote address on NULL interface");
+ LEAVE_RET('i', -1);
+ }
+ if(!CheckMem(Data, IPStack_GetAddressSize(chan->Interface->Type))) {
+ LOG("Invalid pointer");
+ LEAVE_RET('i', -1);
+ }
+ memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type));
+ return 0;
+ }
+ LEAVE_RET('i', 0);
+}
+
+/**
+ * \brief Close and destroy an open channel
+ */
+void UDP_Channel_Close(tVFS_Node *Node)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ tUDPChannel *prev;
+
+ // Remove from the main list first
+ Mutex_Acquire(&glUDP_Channels);
+ if(gpUDP_Channels == chan)
+ gpUDP_Channels = gpUDP_Channels->Next;
+ else
+ {
+ for(prev = gpUDP_Channels;
+ prev->Next && prev->Next != chan;
+ prev = prev->Next);
+ if(!prev->Next)
+ Log_Warning("UDP", "Bookeeping Fail, channel %p is not in main list", chan);
+ else
+ prev->Next = prev->Next->Next;
+ }
+ Mutex_Release(&glUDP_Channels);
+
+ // Clear Queue
+ SHORTLOCK(&chan->lQueue);
+ while(chan->Queue)
+ {
+ tUDPPacket *tmp;
+ tmp = chan->Queue;
+ chan->Queue = tmp->Next;
+ free(tmp);
+ }
+ SHORTREL(&chan->lQueue);
+
+ // Free channel structure
+ free(chan);
+}
+
+/**
+ * \return Port Number on success, or zero on failure
+ */
+Uint16 UDP_int_AllocatePort()
+{
+ int i;
+ Mutex_Acquire(&glUDP_Ports);
+ // Fast Search
+ for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 )
+ if( gUDP_Ports[i/32] != 0xFFFFFFFF )
+ break;
+ if(i == 0x10000) return 0;
+ for( ;; i++ )
+ {
+ if( !(gUDP_Ports[i/32] & (1 << (i%32))) )
+ return i;
+ }
+ Mutex_Release(&glUDP_Ports);
+}
+
+/**
+ * \brief Allocate a specific port
+ * \return Boolean Success
+ */
+int UDP_int_MarkPortAsUsed(Uint16 Port)
+{
+ Mutex_Acquire(&glUDP_Ports);
+ if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) {
+ return 0;
+ Mutex_Release(&glUDP_Ports);
+ }
+ gUDP_Ports[Port/32] |= 1 << (Port%32);
+ Mutex_Release(&glUDP_Ports);
+ return 1;
+}
+
+/**
+ * \brief Free an allocated port
+ */
+void UDP_int_FreePort(Uint16 Port)
+{
+ Mutex_Acquire(&glUDP_Ports);
+ gUDP_Ports[Port/32] &= ~(1 << (Port%32));
+ Mutex_Release(&glUDP_Ports);
+}
--- /dev/null
+/*
+ * Acess2 IP Stack
+ * - UDP Definitions
+ */
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#include "ipstack.h"
+#include "ipv4.h"
+
+typedef struct sUDPHeader tUDPHeader;
+typedef struct sUDPEndpoint tUDPEndpoint;
+typedef struct sUDPPacket tUDPPacket;
+typedef struct sUDPChannel tUDPChannel;
+
+struct sUDPHeader
+{
+ Uint16 SourcePort;
+ Uint16 DestPort;
+ Uint16 Length;
+ Uint16 Checksum;
+ Uint8 Data[];
+};
+
+struct sUDPEndpoint
+{
+ Uint16 Port;
+ Uint16 AddrType;
+ union {
+ tIPv4 v4;
+ tIPv6 v6;
+ } Addr;
+};
+
+struct sUDPPacket
+{
+ struct sUDPPacket *Next;
+ tUDPEndpoint Remote;
+ size_t Length;
+ Uint8 Data[];
+};
+
+struct sUDPChannel
+{
+ struct sUDPChannel *Next;
+ tInterface *Interface;
+ Uint16 LocalPort;
+
+ tUDPEndpoint Remote; // Only accept packets form this address/port pair
+ int RemoteMask; // Mask on the address
+
+ tVFS_Node Node;
+ tShortSpinlock lQueue;
+ tUDPPacket * volatile Queue;
+ tUDPPacket *QueueEnd;
+};
+
+#endif
+
--- /dev/null
+CATEGORY = Input
+
+-include ../../Makefile.tpl
--- /dev/null
+/*
+ * Acess2
+ * - By thePowersGang (John Hodge)
+ *
+ * 8042 (or comaptible) Driver
+ */
+#include <acess.h>
+#include "common.h"
+
+// === PROTOTYPES ===
+void KBC8042_Init(void);
+void KBC8042_KeyboardHandler(int IRQ, void *Ptr);
+void KBC8042_MouseHandler(int IRQ, void *Ptr);
+void KBC8042_EnableMouse(void);
+static inline void KBC8042_SendDataAlt(Uint8 data);
+static inline void KBC8042_SendData(Uint8 data);
+static inline Uint8 KBC8042_ReadData(void);
+static void KBC8042_SendMouseCommand(Uint8 cmd);
+
+// === CODE ===
+void KBC8042_Init(void)
+{
+ IRQ_AddHandler(1, KBC8042_KeyboardHandler, NULL);
+ IRQ_AddHandler(12, KBC8042_MouseHandler, NULL); // Set IRQ
+
+ {
+ Uint8 temp;
+ // Attempt to get around a strange bug in Bochs/Qemu by toggling
+ // the controller on and off
+ temp = inb(0x61);
+ outb(0x61, temp | 0x80);
+ outb(0x61, temp & 0x7F);
+ inb(0x60); // Clear keyboard buffer
+ }
+}
+
+void KBC8042_KeyboardHandler(int IRQ, void *Ptr)
+{
+ Uint8 scancode;
+
+// Log("KBC8042_KeyboardHandler: (IRQ=%i, Ptr=%p)", IRQ, Ptr);
+
+ scancode = inb(0x60);
+ KB_HandleScancode( scancode );
+}
+
+void KBC8042_MouseHandler(int IRQ, void *Ptr)
+{
+ PS2Mouse_HandleInterrupt( inb(0x60) );
+}
+
+void KBC8042_SetLEDs(Uint8 leds)
+{
+ while( inb(0x64) & 2 ); // Wait for bit 2 to unset
+ outb(0x60, 0xED); // Send update command
+
+ while( inb(0x64) & 2 ); // Wait for bit 2 to unset
+ outb(0x60, leds);
+}
+
+void KBC8042_EnableMouse(void)
+{
+ Uint8 status;
+ Log_Log("8042", "Enabling Mouse...");
+
+ // Enable AUX PS/2
+ KBC8042_SendDataAlt(0xA8);
+
+ // Enable AUX PS/2 (Compaq Status Byte)
+ KBC8042_SendDataAlt(0x20); // Send Command
+ status = KBC8042_ReadData(); // Get Status
+ status &= ~0x20; // Clear "Disable Mouse Clock"
+ status |= 0x02; // Set IRQ12 Enable
+ KBC8042_SendDataAlt(0x60); // Send Command
+ KBC8042_SendData(status); // Set Status
+
+ //mouseSendCommand(0xF6); // Set Default Settings
+ KBC8042_SendMouseCommand(0xF4); // Enable Packets
+}
+
+static inline void KBC8042_SendDataAlt(Uint8 data)
+{
+ int timeout=100000;
+ while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear
+ outb(0x64, data); // Send Command
+}
+static inline void KBC8042_SendData(Uint8 data)
+{
+ int timeout=100000;
+ while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear
+ outb(0x60, data); // Send Command
+}
+static inline Uint8 KBC8042_ReadData(void)
+{
+ int timeout=100000;
+ while( timeout-- && (inb(0x64) & 1) == 0); // Wait for Flag to set
+ return inb(0x60);
+}
+static inline void KBC8042_SendMouseCommand(Uint8 cmd)
+{
+ KBC8042_SendDataAlt(0xD4);
+ KBC8042_SendData(cmd);
+}
+
--- /dev/null
+#
+#
+
+OBJ = main.o kb.o ps2mouse.o
+OBJ += 8042.o pl050.o
+NAME = PS2KbMouse
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2
+ *
+ * PS2 Keyboard/Mouse Driver
+ *
+ * common.h
+ * - Shared definitions
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+extern int KB_Install(char **Arguments);
+extern int PS2Mouse_Install(char **Arguments);
+
+extern void KBC8042_Init(void);
+extern void KBC8042_EnableMouse(void);
+
+extern void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ);
+extern void PL050_EnableMouse(void);
+
+extern void KB_HandleScancode(Uint8 scancode);
+extern void PS2Mouse_HandleInterrupt(Uint8 InputByte);
+
+extern void (*gpMouse_EnableFcn)(void);
+
+#endif
--- /dev/null
+/*
+ * Acess2
+ * PS2 Keyboard Driver
+ */
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <api_drv_common.h>
+#include <api_drv_keyboard.h>
+#include "kb_kbdus.h"
+
+// === CONSTANTS ===
+#define KB_BUFFER_SIZE 1024
+#define USE_KERNEL_MAGIC 1
+
+// === IMPORTS ===
+extern void Threads_ToggleTrace(int TID);
+extern void Threads_Dump(void);
+extern void Heap_Stats(void);
+
+// === PROTOTYPES ===
+ int KB_Install(char **Arguments);
+void KB_HandleScancode(Uint8 scancode);
+void KB_UpdateLEDs(void);
+ int KB_IOCtl(tVFS_Node *Node, int Id, void *Data);
+
+// === GLOBALS ===
+tVFS_NodeType gKB_NodeType = {
+ .IOCtl = KB_IOCtl
+};
+tDevFS_Driver gKB_DevInfo = {
+ NULL, "PS2Keyboard",
+ { .Type = &gKB_NodeType }
+};
+tKeybardCallback gKB_Callback = NULL;
+Uint32 **gpKB_Map = gpKBDUS;
+Uint8 gbaKB_States[3][256];
+ int gbKB_ShiftState = 0;
+ int gbKB_CapsState = 0;
+ int gbKB_KeyUp = 0;
+ int giKB_KeyLayer = 0;
+#if USE_KERNEL_MAGIC
+ int gbKB_MagicState = 0;
+ int giKB_MagicAddress = 0;
+ int giKB_MagicAddressPos = 0;
+#endif
+
+// === CODE ===
+/**
+ * \brief Install the keyboard driver
+ */
+int KB_Install(char **Arguments)
+{
+ DevFS_AddDevice( &gKB_DevInfo );
+ //Log("KB_Install: Installed");
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Called on a keyboard IRQ
+ * \param IRQNum IRQ number (unused)
+ */
+void KB_HandleScancode(Uint8 scancode)
+{
+ Uint32 ch;
+ int bCaseSwitch = (gbKB_ShiftState != 0) != (gbKB_CapsState != 0);
+
+ //Log_Debug("Keyboard", "scancode = %02x", scancode);
+
+ // Ignore ACKs
+ if(scancode == 0xFA) {
+ // Oh man! This is anarchic (I'm leaving it here to represent
+ // the mess that Acess once was)
+ //kb_lastChar = KB_ACK;
+ return;
+ }
+
+ // Layer +1
+ if(scancode == 0xE0) {
+ giKB_KeyLayer = 1;
+ return;
+ }
+ // Layer +2
+ if(scancode == 0xE1) {
+ giKB_KeyLayer = 2;
+ return;
+ }
+
+ #if KB_ALT_SCANCODES
+ if(scancode == 0xF0)
+ {
+ gbKB_KeyUp = 1;
+ return;
+ }
+ #else
+ if(scancode & 0x80)
+ {
+ scancode &= 0x7F;
+ gbKB_KeyUp = 1;
+ }
+ #endif
+
+ if( gKB_Callback )
+ gKB_Callback( (giKB_KeyLayer << 8) | scancode | KEY_ACTION_RAWSYM );
+
+ // Translate
+ ch = gpKB_Map[giKB_KeyLayer*2+bCaseSwitch][scancode];
+ // - Unknown characters in the shift layer fall through to lower
+ if(bCaseSwitch && ch == 0)
+ ch = gpKB_Map[giKB_KeyLayer*2][scancode];
+ // Check for unknown key
+ if(!ch)
+ {
+ if(!gbKB_KeyUp)
+ Log_Warning("Keyboard", "UNK %i %x", giKB_KeyLayer, scancode);
+// return ;
+ // Can pass through to ensure each raw message has a up/down with it
+ }
+
+ // Key Up?
+ if (gbKB_KeyUp)
+ {
+ gbKB_KeyUp = 0;
+ gbaKB_States[giKB_KeyLayer][scancode] = 0; // Unset key state flag
+
+ #if USE_KERNEL_MAGIC
+ if(ch == KEY_LCTRL) gbKB_MagicState &= ~1;
+ if(ch == KEY_LALT) gbKB_MagicState &= ~2;
+ #endif
+
+ if(ch == KEY_LSHIFT) gbKB_ShiftState &= ~1;
+ if(ch == KEY_RSHIFT) gbKB_ShiftState &= ~2;
+
+ // Call callback
+ if(gKB_Callback) gKB_Callback( ch | KEY_ACTION_RELEASE );
+
+ // Reset Layer
+ giKB_KeyLayer = 0;
+ return;
+ }
+
+ // Refire?
+ if( gbaKB_States[giKB_KeyLayer][scancode] == 1 )
+ {
+ if(gKB_Callback) gKB_Callback(ch | KEY_ACTION_REFIRE);
+ giKB_KeyLayer = 0;
+ return ;
+ }
+
+ // Set the bit relating to the key
+ gbaKB_States[giKB_KeyLayer][scancode] = 1;
+ // Set shift key bits
+ if(ch == KEY_LSHIFT) gbKB_ShiftState |= 1;
+ if(ch == KEY_RSHIFT) gbKB_ShiftState |= 2;
+
+ // Check for Caps Lock
+ if(ch == KEY_CAPSLOCK) {
+ gbKB_CapsState = !gbKB_CapsState;
+ KB_UpdateLEDs();
+ }
+
+ // --- Check for Kernel Magic Combos
+ #if USE_KERNEL_MAGIC
+ if(ch == KEY_LCTRL) {
+ gbKB_MagicState |= 1;
+ //Log_Log("Keyboard", "Kernel Magic LCTRL Down\n");
+ }
+ if(ch == KEY_LALT) {
+ gbKB_MagicState |= 2;
+ //Log_Log("Keyboard", "Kernel Magic LALT Down\n");
+ }
+ if(gbKB_MagicState == 3)
+ {
+ switch(ch)
+ {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': case 'a': case 'b':
+ case 'c': case 'd': case 'e': case 'f':
+ {
+ char str[4] = {'0', 'x', ch, 0};
+ if(giKB_MagicAddressPos == BITS/4) return;
+ giKB_MagicAddress |= atoi(str) << giKB_MagicAddressPos;
+ giKB_MagicAddressPos ++;
+ }
+ return;
+
+ // Instruction Tracing
+ case 't':
+ Log("Toggle instruction tracing on %i\n", giKB_MagicAddress);
+ Threads_ToggleTrace( giKB_MagicAddress );
+ giKB_MagicAddress = 0; giKB_MagicAddressPos = 0;
+ return;
+
+ // Thread List Dump
+ case 'p': Threads_Dump(); return;
+ // Heap Statistics
+ case 'h': Heap_Stats(); return;
+ // Dump Structure
+ case 's': return;
+ }
+ }
+ #endif
+
+ if(gKB_Callback)
+ gKB_Callback(ch | KEY_ACTION_PRESS);
+
+ // Reset Layer
+ giKB_KeyLayer = 0;
+}
+
+/**
+ * \fn void KB_UpdateLEDs(void)
+ * \brief Updates the status of the keyboard LEDs
+ */
+void KB_UpdateLEDs(void)
+{
+ Uint8 leds;
+
+ leds = (gbKB_CapsState ? 4 : 0);
+
+ // TODO: Update LEDS
+ Log_Warning("Keyboard", "TODO: Update LEDs");
+}
+
+static const char *csaIOCTL_NAMES[] = {DRV_IOCTLNAMES, DRV_KEYBAORD_IOCTLNAMES, NULL};
+
+/**
+ * \fn int KB_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief Calls an IOCtl Command
+ */
+int KB_IOCtl(tVFS_Node *Node, int Id, void *Data)
+{
+ switch(Id)
+ {
+ BASE_IOCTLS(DRV_TYPE_KEYBOARD, "KB", 0x100, csaIOCTL_NAMES);
+
+ // Sets the Keyboard Callback
+ case KB_IOCTL_SETCALLBACK:
+ // Sanity Check
+ if((Uint)Data < KERNEL_BASE) return 0;
+ // Can only be set once
+ if(gKB_Callback != NULL) return 0;
+ // Set Callback
+ gKB_Callback = Data;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
--- /dev/null
+\r
+#ifndef _KBDUS_H\r
+#define _KBDUS_H\r
+\r
+// - Base (NO PREFIX)\r
+Uint32 gpKBDUS1[256] = {\r
+ 0,\r
+ KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', // 0x01 - 0x0e\r
+ '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x0f - 0x1c\r
+ KEY_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', // 0x1d - 0x28\r
+ '`', KEY_LSHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, // 0x29 - 0x3e\r
+ KEY_KPSTAR,\r
+ KEY_LALT, ' ', KEY_CAPSLOCK,\r
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,\r
+ KEY_NUMLOCK, KEY_SCROLLLOCK,\r
+ KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS,\r
+ KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, KEY_KPPLUS,\r
+ KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN,\r
+ KEY_KPINS, KEY_KPDEL,\r
+ 0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0,\r
+/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+};\r
+// Shift Key pressed\r
+Uint32 gpKBDUS1s[256] = {\r
+ 0,\r
+ KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', // 0x01 - 0x0e\r
+ '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', // 0x0f - 0x1c\r
+ KEY_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':','"', // 0x1d - 0x28\r
+ '~', KEY_LSHIFT,'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT, // 0x29 - 0x3e\r
+ 0\r
+ };\r
+// - 0xE0 Prefixed\r
+Uint32 gpKBDUS2[256] = {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F\r
+/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_KPENTER, KEY_RCTRL, 0, 0,\r
+/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*30*/ 0, 0, 0, 0, 0, KEY_KPSLASH, 0, 0, KEY_RALT, 0, 0, 0, 0, 0, 0, 0,\r
+/*40*/ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END,\r
+/*50*/ KEY_DOWN, KEY_PGDOWN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, 0, KEY_LWIN, KEY_RWIN, KEY_MENU, 0, 0,\r
+/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+};\r
+// - 0xE1 Prefixed\r
+Uint32 gpKBDUS3[256] = {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F\r
+/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAUSE, 0, 0,\r
+/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+};\r
+\r
+\r
+Uint32 *gpKBDUS[6] = { gpKBDUS1, gpKBDUS1s, gpKBDUS2, gpKBDUS2, gpKBDUS3, gpKBDUS3 };\r
+\r
+#endif\r
--- /dev/null
+/*
+ * Acess2
+ *
+ * PS/2 Keboard / Mouse Driver
+ */
+#include <acess.h>
+#include <modules.h>
+#include "common.h"
+
+// === IMPORTS ===
+// TODO: Allow runtime/compile-time switching
+// Maybe PCI will have it?
+// Integrator-CP
+#if 0
+#define KEYBOARD_IRQ 3
+#define KEYBOARD_BASE 0x18000000
+#define MOUSE_IRQ 4
+#define MOUSE_BASE 0x19000000
+#endif
+// Realview
+#if 1
+#define KEYBOARD_IRQ 20
+#define KEYBOARD_BASE 0x10006000
+#define MOUSE_IRQ 21
+#define MOUSE_BASE 0x10007000
+#endif
+
+// === PROTOTYPES ===
+ int PS2_Install(char **Arguments);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x0100, Input_PS2KbMouse, PS2_Install, NULL, NULL); // Shuts the makefile up
+MODULE_DEFINE(0, 0x0100, PS2Keyboard, KB_Install, NULL, "Input_PS2KbMouse", NULL);
+MODULE_DEFINE(0, 0x0100, PS2Mouse, PS2Mouse_Install, NULL, "Input_PS2KbMouse", NULL);
+
+// === CODE ===
+int PS2_Install(char **Arguments)
+{
+ #if ARCHDIR_is_x86 || ARCHDIR_is_x86_64
+ KBC8042_Init();
+ gpMouse_EnableFcn = KBC8042_EnableMouse;
+ #elif ARCHDIR_is_armv7
+ PL050_Init(KEYBOARD_BASE, KEYBOARD_IRQ, MOUSE_BASE, MOUSE_IRQ);
+ gpMouse_EnableFcn = PL050_EnableMouse;
+ #endif
+
+ return MODULE_ERR_OK;
+}
--- /dev/null
+/*
+ * Acess2
+ * - By thePowersGang (John Hodge)
+ *
+ * PL050 (or comaptible) Driver
+ */
+#define DEBUG 1
+
+#include <acess.h>
+#include "common.h"
+
+// === CONSTANTS ===
+#define PL050_TXBUSY 0x20
+
+// === PROTOTYPES ===
+void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ);
+void PL050_KeyboardHandler(int IRQ, void *Ptr);
+void PL050_MouseHandler(int IRQ, void *Ptr);
+void PL050_EnableMouse(void);
+static inline void PL050_WriteMouseData(Uint8 data);
+static inline void PL050_WriteKeyboardData(Uint8 data);
+static inline Uint8 PL050_ReadMouseData(void);
+static inline Uint8 PL050_ReadKeyboardData(void);
+
+// === GLOBALS ===
+Uint32 *gpPL050_KeyboardBase;
+Uint32 *gpPL050_MouseBase;
+
+// === CODE ===
+void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ)
+{
+ if( KeyboardBase ) {
+ LOG("KeyboardBase = 0x%x", KeyboardBase);
+ gpPL050_KeyboardBase = (void*)MM_MapHWPages(KeyboardBase, 1);
+ LOG("gpPL050_KeyboardBase = %p", gpPL050_KeyboardBase);
+ IRQ_AddHandler(KeyboardIRQ, PL050_KeyboardHandler, NULL);
+
+ gpPL050_KeyboardBase[0] = 0x10;
+ }
+ if( MouseBase ) {
+ gpPL050_MouseBase = (void*)MM_MapHWPages(MouseBase, 1);
+ IRQ_AddHandler(MouseIRQ, PL050_MouseHandler, NULL);
+
+ gpPL050_MouseBase[0] = 0x10;
+ }
+}
+
+void PL050_KeyboardHandler(int IRQ, void *Ptr)
+{
+ Uint8 scancode;
+
+ scancode = PL050_ReadKeyboardData();
+ KB_HandleScancode( scancode );
+}
+
+void PL050_MouseHandler(int IRQ, void *Ptr)
+{
+ PS2Mouse_HandleInterrupt( PL050_ReadMouseData() );
+}
+
+void PL050_SetLEDs(Uint8 leds)
+{
+ PL050_WriteKeyboardData(0xED);
+ PL050_WriteKeyboardData(leds);
+}
+
+void PL050_EnableMouse(void)
+{
+ Log_Log("PL050", "Enabling Mouse...");
+
+ //PL050_WriteMouseData(0xD4);
+ //PL050_WriteMouseData(0xF6); // Set Default Settings
+ PL050_WriteMouseData(0xD4);
+ PL050_WriteMouseData(0xF4); // Enable Packets
+ LOG("Done");
+}
+
+static inline void PL050_WriteMouseData(Uint8 Data)
+{
+ int timeout = 10000;
+
+ if( !gpPL050_MouseBase ) {
+ Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)");
+ return ;
+ }
+
+ ENTER("xData", Data);
+
+ while( --timeout && (gpPL050_MouseBase[1] & PL050_TXBUSY) );
+ if(timeout)
+ gpPL050_MouseBase[2] = Data;
+ else
+ Log_Error("PL050", "Write to mouse timed out");
+ LEAVE('-');
+}
+
+static inline Uint8 PL050_ReadMouseData(void)
+{
+ if( !gpPL050_MouseBase ) {
+ Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)");
+ return 0;
+ }
+ return gpPL050_MouseBase[2];
+}
+static inline void PL050_WriteKeyboardData(Uint8 Data)
+{
+ int timeout = 10000;
+
+ if( !gpPL050_KeyboardBase ) {
+ Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)");
+ return ;
+ }
+
+ while( --timeout && gpPL050_KeyboardBase[1] & PL050_TXBUSY );
+ if(timeout)
+ gpPL050_KeyboardBase[2] = Data;
+ else
+ Log_Error("PL050", "Write to keyboard timed out");
+}
+static inline Uint8 PL050_ReadKeyboardData(void)
+{
+ if( !gpPL050_KeyboardBase ) {
+ Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)");
+ return 0;
+ }
+
+ return gpPL050_KeyboardBase[2];
+}
+
--- /dev/null
+/*\r
+ * Acess2 Mouse Driver\r
+ */\r
+#define DEBUG 0\r
+#include <acess.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <api_drv_common.h>\r
+#include <api_drv_joystick.h>\r
+#include "common.h"\r
+\r
+// == CONSTANTS ==\r
+#define NUM_AXIES 2 // X+Y\r
+#define NUM_BUTTONS 5 // Left, Right, Scroll Click, Scroll Up, Scroll Down\r
+\r
+// == PROTOTYPES ==\r
+// - Internal -\r
+ int PS2Mouse_Install(char **Arguments);\r
+void PS2Mouse_HandleInterrupt(Uint8 InputByte);\r
+// - Filesystem -\r
+Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
+int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
+\r
+// == GLOBALS ==\r
+void (*gpMouse_EnableFcn)(void);\r
+// - Settings\r
+ int giMouse_Sensitivity = 1;\r
+ int giMouse_MaxX = 640, giMouse_MaxY = 480;\r
+// - File Data\r
+Uint8 gMouse_FileData[sizeof(tJoystick_FileHeader) + NUM_AXIES*sizeof(tJoystick_Axis) + NUM_BUTTONS];\r
+tJoystick_FileHeader *gMouse_FileHeader = (void *)gMouse_FileData;\r
+tJoystick_Axis *gMouse_Axies;\r
+Uint8 *gMouse_Buttons;\r
+tJoystick_Callback gMouse_Callback;\r
+ int gMouse_CallbackArg;\r
+ int giMouse_AxisLimits[2];\r
+// - Internal State\r
+ int giMouse_Cycle = 0; // IRQ Position\r
+Uint8 gaMouse_Bytes[4] = {0,0,0,0};\r
+// - Driver definition\r
+tVFS_NodeType gMouse_NodeType = {\r
+ .Read = PS2Mouse_Read,\r
+ .IOCtl = PS2Mouse_IOCtl\r
+};\r
+tDevFS_Driver gMouse_DriverStruct = {\r
+ NULL, "PS2Mouse",\r
+ {\r
+ .NumACLs = 1, .ACLs = &gVFS_ACL_EveryoneRX,\r
+ .Type = &gMouse_NodeType\r
+ }\r
+};\r
+\r
+// == CODE ==\r
+int PS2Mouse_Install(char **Arguments)\r
+{\r
+ \r
+\r
+ // Set up variables\r
+ gMouse_Axies = (void*)&gMouse_FileData[4];\r
+ gMouse_Buttons = (void*)&gMouse_Axies[NUM_AXIES];\r
+\r
+ gMouse_FileHeader->NAxies = 2; gMouse_FileHeader->NButtons = 3;\r
+ gMouse_Axies[0].MinValue = -10; gMouse_Axies[0].MaxValue = 10;\r
+ gMouse_Axies[1].MinValue = -10; gMouse_Axies[1].MaxValue = 10;\r
+ \r
+ // Initialise Mouse Controller\r
+ giMouse_Cycle = 0; // Set Current Cycle position\r
+ gpMouse_EnableFcn();\r
+ \r
+ DevFS_AddDevice(&gMouse_DriverStruct);\r
+ \r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/* Handle Mouse Interrupt\r
+ */\r
+void PS2Mouse_HandleInterrupt(Uint8 InputByte)\r
+{\r
+ Uint8 flags;\r
+ int d[2], d_accel[2];\r
+ int i;\r
+ \r
+ // Gather mouse data\r
+ gaMouse_Bytes[giMouse_Cycle] = InputByte;\r
+ LOG("gaMouse_Bytes[%i] = 0x%02x", gMouse_Axies[0].MaxValue);\r
+ // - If bit 3 of the first byte is not set, it's not a valid packet?\r
+ if(giMouse_Cycle == 0 && !(gaMouse_Bytes[0] & 0x08) )\r
+ return ;\r
+ giMouse_Cycle++;\r
+ if(giMouse_Cycle < 3)\r
+ return ;\r
+\r
+ giMouse_Cycle = 0;\r
+\r
+ // Actual Processing (once we have all bytes) \r
+ flags = gaMouse_Bytes[0];\r
+\r
+ LOG("flags = 0x%x", flags);\r
+ \r
+ // Check for X/Y Overflow\r
+ if(flags & 0xC0) return;\r
+ \r
+ // Calculate dX and dY\r
+ d[0] = gaMouse_Bytes[1]; if(flags & 0x10) d[0] = -(256-d[0]); // x\r
+ d[1] = gaMouse_Bytes[2]; if(flags & 0x20) d[1] = -(256-d[1]); // y\r
+ d[1] = -d[1]; // Y is negated\r
+ LOG("RAW dx=%i, dy=%i\n", d[0], d[1]);\r
+ // Apply scaling\r
+ // TODO: Apply a form of curve to the mouse movement (dx*log(dx), dx^k?)\r
+ // TODO: Independent sensitivities?\r
+ // TODO: Disable acceleration via a flag?\r
+ d_accel[0] = d[0]*giMouse_Sensitivity;\r
+ d_accel[1] = d[1]*giMouse_Sensitivity;\r
+ \r
+ // Set Buttons (Primary)\r
+ for( i = 0; i < 3; i ++ )\r
+ {\r
+ Uint8 newVal = (flags & (1 << i)) ? 0xFF : 0;\r
+ if(newVal != gMouse_Buttons[i]) {\r
+ if( gMouse_Callback )\r
+ gMouse_Callback(gMouse_CallbackArg, 0, i, newVal - gMouse_Buttons[i]);\r
+ gMouse_Buttons[i] = newVal;\r
+ }\r
+ }\r
+ \r
+ // Update X and Y Positions\r
+ for( i = 0; i < 2; i ++ )\r
+ {\r
+ Sint16 newCursor = 0;\r
+ if( giMouse_AxisLimits[i] )\r
+ newCursor = MIN( MAX(0, gMouse_Axies[i].CursorPos + d_accel[i]), giMouse_AxisLimits[i] );;\r
+ \r
+ if( gMouse_Callback )\r
+ {\r
+ if(giMouse_AxisLimits[i] && gMouse_Axies[i].CursorPos != newCursor)\r
+ gMouse_Callback(gMouse_CallbackArg, 1, i, newCursor - gMouse_Axies[i].CursorPos);\r
+ if(!giMouse_AxisLimits[i] && gMouse_Axies[i].CurValue != d_accel[i])\r
+ gMouse_Callback(gMouse_CallbackArg, 1, i, d_accel[i] - gMouse_Axies[i].CurValue);\r
+ }\r
+ \r
+ gMouse_Axies[i].CurValue = d_accel[i];\r
+ gMouse_Axies[i].CursorPos = newCursor;\r
+ }\r
+\r
+// Log("Mouse at %ix%i", gMouse_Axies[0].CursorPos, gMouse_Axies[1].CursorPos);\r
+ \r
+ VFS_MarkAvaliable(&gMouse_DriverStruct.RootNode, 1);\r
+}\r
+\r
+/* Read mouse state (coordinates)\r
+ */\r
+Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{\r
+ if(Offset > sizeof(gMouse_FileData)) return 0;\r
+ if(Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData);\r
+ if(Offset + Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData) - Offset;\r
+\r
+ memcpy(Buffer, &gMouse_FileData[Offset], Length);\r
+ \r
+ VFS_MarkAvaliable(Node, 0);\r
+ return Length;\r
+}\r
+\r
+static const char *csaIOCtls[] = {DRV_IOCTLNAMES, DRV_JOY_IOCTLNAMES, NULL};\r
+/* Handle messages to the device\r
+ */\r
+int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+{\r
+ tJoystick_NumValue *info = Data;\r
+\r
+ switch(ID)\r
+ {\r
+ BASE_IOCTLS(DRV_TYPE_JOYSTICK, "PS2Mouse", 0x100, csaIOCtls);\r
+\r
+ case JOY_IOCTL_SETCALLBACK: // TODO: Implement\r
+ return -1;\r
+ \r
+ case JOY_IOCTL_SETCALLBACKARG: // TODO: Implement\r
+ return -1;\r
+ \r
+ case JOY_IOCTL_GETSETAXISLIMIT:\r
+ if(!info) return 0;\r
+ if(info->Num < 0 || info->Num >= 2) return 0;\r
+ if(info->Value != -1)\r
+ giMouse_AxisLimits[info->Num] = info->Value;\r
+ return giMouse_AxisLimits[info->Num];\r
+ \r
+ case JOY_IOCTL_GETSETAXISPOSITION:\r
+ if(!info) return 0;\r
+ if(info->Num < 0 || info->Num >= 2) return 0;\r
+ if(info->Value != -1)\r
+ gMouse_Axies[info->Num].CursorPos = info->Value;\r
+ return gMouse_Axies[info->Num].CursorPos;\r
+\r
+ case JOY_IOCTL_GETSETAXISFLAGS:\r
+ return -1;\r
+ \r
+ case JOY_IOCTL_GETSETBUTTONFLAGS:\r
+ return -1;\r
+\r
+ default:\r
+ return 0;\r
+ }\r
+}\r
+\r
--- /dev/null
+#
+# EDI - Extensible Driver Interface
+#
+# Acess Interface
+
+
+OBJ = main.o edi.o
+NAME = EDI
+
+-include ../Makefile.tpl
--- /dev/null
+/*! \file acess-edi.h
+ * \brief Acess Specific EDI Objects
+ *
+ * Contains documentation and information for
+ * - Timers
+ */
+
+/* Copyright (c) 2006 John Hodge
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#ifndef ACESS_EDI_H
+#define ACESS_EDI_H
+
+#include "edi_objects.h"
+
+/// \brief Name of Acess EDI Time Class
+#define ACESS_TIMER_CLASS "ACESSEDI-TIMER"
+
+#ifndef IMPLEMENTING_EDI
+/*! \brief int32_t ACESSEDI-TIMER.init_timer(uint32_t Delay, void (*Callback)(int), int Arg);
+ *
+ * Takes a timer pointer and intialises the timer object to fire after \a Delay ms
+ * When the timer fires, \a Callback is called with \a Arg passed to it.
+ */
+EDI_DEFVAR int32_t (*init_timer)(object_pointer port_object, uint32_t Delay, void (*fcn)(int), int arg);
+
+/*! \brief void ACESSEDI-TIMER.disable_timer();
+ *
+ * Disables the timer and prevents it from firing
+ * After this has been called, the timer can then be initialised again.
+ */
+EDI_DEFVAR void (*disable_timer)(object_pointer port_object);
+
+
+#endif // defined(IMPLEMENTING_EDI)
+
+#endif
--- /dev/null
+#ifndef EDI_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#define EDI_H
+/*! \file edi.h
+ * \brief The unitive EDI header to include others, start EDI, and stop EDI.
+ *
+ * Data structures and algorithms this header represents:
+ * DATA STRUCTURE: CLASS QUOTAS - The runtime and the driver have the right to set a quota on how many objects of a given class
+ * owned by that party the other may construct. These quotas are kept internally by the driver or runtime, are optional and are
+ * exposed to the other party via the quota() function (for quotas of runtime-owned classes) and the k_quota() function pointer given
+ * to the runtime by the driver.
+ *
+ * ALGORITHMS: INITIALIZATION AND SHUTDOWN - On initialization of the runtime's EDI environment for this driver it calls the
+ * driver's driver_init() routine (which must match driver_init_t) to initialize the driver with a list of EDI objects the runtime
+ * thinks the driver should run with. The driver then initializes. This can include calling edi_negotiate_resources() to try and
+ * obtain more or different objects. Eventually driver_init() returns an edi_initialization_t structure containing its quota
+ * function and the list of classes belonging to the driver which the runtime can construct. Either the driver or the runtime can
+ * shut down EDI by calling edi_shutdown(), which in turn calls the driver's driver_finish() routine. On shutdown all objects, of
+ * classes belonging to both the runtime and driver, are destroyed. */
+
+#include "edi_objects.h"
+#include "edi_dma_streams.h"
+#include "edi_pthreads.h"
+#include "edi_port_io.h"
+#include "edi_memory_mapping.h"
+#include "edi_devices.h"
+#include "edi_interrupts.h"
+
+/*! \brief A pointer to a function the runtime can call if it fails to construct one of the driver's classes to find out what the
+ * runtime's quota is for that class.
+ *
+ * A pointer to a function which takes an edi_string_t as a parameter and returns in int32_t. This function follows the same
+ * semantics as the quota() function, returning the number of objects of the given class that can be constructed, -1 for infinity or
+ * -2 for an erroneous class name. It is used to tell the runtime the location of such a function in the driver so that the runtime
+ * can check quotas on driver-owned classes. */
+typedef int32_t (*k_quota_t)(edi_string_t resource_class);
+/*!\struct edi_initialization_t
+ * \brief Structure containing driver classes available to the runtime and the driver's quota function after the driver has initialized.
+ *
+ * Structure containing driver classes available to runtime, the driver's quota function and the driver's name provided to the runtime
+ * after the driver has initialized. driver_bus, vendor_id, and device_id are all optional fields which coders should consider
+ * supplementary information. Kernels can require these fields if they so please, but doing so for devices which don't run on a Vendor
+ * ID/Product ID supporting bus is rather unwise. */
+typedef struct {
+ /*!\brief The number of driver classes in the driver_classes array. */
+ int32_t num_driver_classes;
+ /*!\brief An array of declarations of driver classes available to the runtime.
+ *
+ * This array should not necessarily contain the entire list of EDI classes implemented by the driver. Instead, it should
+ * contain a list of those classes which the driver has correctly initialized itself to provide instances of with full
+ * functionality. */
+ edi_class_declaration_t *driver_classes;
+ /*!\brief The driver's quota function. */
+ k_quota_t k_quota;
+ /*!\brief The driver's name. */
+ edi_string_t driver_name;
+ /*!\brief The bus of the device this driver wants to drive, if applicable.
+ *
+ * The driver does not have to supply this field, and can also supply "MULTIPLE BUSES" here to indicate that it drives devices
+ * on multiple buses. */
+ edi_string_t driver_bus;
+ /*!\brief The driver's vendor ID, if applicable.
+ *
+ * The driver does not need to supply this field, and should supply -1 to indicate that it does not wish to. */
+ int16_t vendor_id;
+ /*!\brief The driver's device ID, if applicable.
+ *
+ * The driver does not need to supply this field, but can supply it along with vendor_id. If either vendor_id or this field are
+ * set to -1 the runtime should consider this field not supplied. */
+ int16_t driver_id;
+} edi_initialization_t;
+/*!\brief A pointer to a driver's initialization function.
+ *
+ * The protocol for the driver's initialization function. The runtime gives the driver a set of EDI objects representing the
+ * resources it thinks the driver should run with. This function returns an edi_initialization_t structure containing declarations
+ * of the EDI classes the driver can make available to the runtime after initialization. If any member of that structure contains 0
+ * or NULL, it is considered invalid and the runtime should destroy the driver without calling its driver_finish() routine. */
+typedef edi_initialization_t (*driver_init_t)(int32_t num_resources,edi_object_metadata_t *resources);
+/*!\brief Requests more resources from the runtime. Can be called during driver initialization.
+ *
+ * Called to negotiate with the runtime for the right to create further EDI objects/obtain further resources owned by the runtime.
+ * When the driver calls this routine, the runtime decides whether to grant more resources. If yes, this call returns true, and the
+ * driver can proceed to try and create the objects it desires, in addition to destroying EDI objects it doesn't want. Otherwise,
+ * it returns false.
+ * The driver must deal with whatever value this routine returns. */
+bool edi_negotiate_resources();
+
+/*! \brief Returns the driver's quota of objects for a given runtime-owned class.
+ *
+ * This function takes an edi_string_t with the name of a runtime-owned class in it and returns the number of objects of that class
+ * which drivers can construct, -1 for infinity, or -2 for an erroneous class name. */
+int32_t quota(edi_string_t resource_class);
+/*! \brief Sends a string to the operating systems debug output or logging facilities. */
+void edi_debug_write(uint32_t debug_string_length, char *debug_string);
+/*! \brief This call destroys all objects and shuts down the entire EDI environment of the driver.
+ *
+ * This function shuts down EDI as described in INITIALIZATION AND SHUTDOWN above. All objects are destroyed, EDI functions can no
+ * longer be successfully called, etc. This function only succeeds when EDI has already been initialized, so it returns -1 when EDI
+ * hasn't been, 1 on success, or 0 for all other errors. */
+int32_t shutdown_edi(void);
+
+/*!\brief A pointer to the driver's finishing/shutdown function.
+ *
+ * The protocol for the driver's shutting down. This function should do anything the driver wants done before it dies. */
+typedef void (*driver_finish_t)();
+
+#endif
--- /dev/null
+#ifndef EDI_DEVICES_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+/* Edited by thePowersGang (John Hodge) June 2009
+ * - Add #ifdef EDI_MAIN_FILE
+ */
+
+#define EDI_DEVICES_H
+
+/*! \file edi_devices.h
+ * \brief Declaration and description of simple classes for implementation by EDI drivers to represent hardware devices.
+ *
+ * Data structures and algorithms this header represents:
+ *
+ * DATA STRUCTURE AND ALGORITHM: BASIC DEVICES - There are two functions, select() for waiting on devices and ioctl() for
+ * controlling them, common to many POSIX devices. Implementations of EDI-CHARACTER-DEVICE or EDI-BLOCK-DEVICE may implement either of
+ * these or both, and users of such objects much query for the methods to see if they're supported. Obviously, runtime or driver
+ * developers don't *need* to support these.
+ *
+ * DATA STRUCTURE AND ALGORITHM: CHARACTER DEVICES - The class EDI-CHARACTER-DEVICE provides a very basic interface to character
+ * devices, which read and write streams of characters. As such, this class only provides read() and write(). The calls attempt a
+ * likeness to POSIX.
+ *
+ * DATA STRUCTURE AND ALGORITHM: BLOCK DEVICES - The class EDI-BLOCK-DEVICE provides a very basic interface to block devices, which
+ * can read(), write() and seek() to blocks of a specific size in an array of blocks with a specific size. Its declarations and
+ * semantics should behave like those of most POSIX operating systems.
+ *
+ * Note that EDI runtimes should not implement these classes. Their declarations are provided for drivers to implement. */
+
+#include "edi_objects.h"
+
+/* Methods common to all EDI device classes specified in this header. */
+
+/*!\brief EAGAIN returned by functions for block and character devices.
+ *
+ * Means that the amount of data the device has ready is less than count. */
+#define EAGAIN -1
+/*!\brief EBADOBJ returned by functions for block and character devices.
+ *
+ * Means that the object passed as the method's this point was not a valid object of the needed class. */
+#define EBADOBJ -2
+/*!\brief EINVAL returned by functions for block and character devices.
+ *
+ * Means that the method got passed invalid parameters. */
+#ifdef EINVAL
+# undef EINVAL
+#endif
+#define EINVAL -3
+
+/*!\brief select() type to wait until device is writable. */
+#define EDI_SELECT_WRITABLE 0
+/*!\brief select() type to wait until device is readable. */
+#define EDI_SELECT_READABLE 1
+
+/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to the given whence value. */
+#define EDI_SEEK_SET 0
+/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to its current value + whence. */
+#define EDI_SEEK_CURRENT 1
+
+#ifdef EDI_MAIN_FILE
+/*!\brief Arguments to EDI's basic select() function. */
+edi_variable_declaration_t select_arguments[2] = {{"pointer void","device",1},
+ {"unsigned int32_t","select_type",1}};
+/*!\brief Declaration of EDI's basic select() function.
+ *
+ * Contrary to the POSIX version, this select() puts its error codes in its return value. */
+edi_function_declaration_t select_declaration = {"int32_t","edi_device_select",0,2,select_arguments,NULL};
+#else
+extern edi_function_declaration_t select_declaration; // Declare for non main files
+#endif
+
+#ifdef EDI_MAIN_FILE
+/*!\brief Arguments to EDI's basic ioctl() function. */
+edi_variable_declaration_t ioctl_arguments[3] = {{"pointer void","device",1},{"int32_t","request",1},{"pointer void","argp",1}};
+/*!\brief Declaration of EDI's basic ioctl() function.
+ *
+ * Contrary to the POSIX version, this ioctl() puts its error codes in its return value. */
+edi_function_declaration_t ioctl_declaration = {"int32_t","edi_device_ioctl",0,3,ioctl_arguments,NULL};
+#else
+extern edi_class_declaration_t ioctl_declaration; // Declare for non main files
+#endif
+
+#ifdef EDI_MAIN_FILE
+/*!\brief Declaration of the arguments EDI-CHARACTER-DEVICE's read() and write() methods. */
+edi_variable_declaration_t chardev_read_write_arguments[3] = {{"pointer void","chardev",1},
+ {"pointer void","buffer",1},
+ {"unsigned int32_t","char_count",1}};
+/*!\brief Declarations of the methods of EDI-CHARACTER-DEVICE, read() and write().
+ *
+ * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-CHARACTER-DEVICE should
+ * fill in these entries with pointers to their own functions. */
+EDI_DEFVAR edi_function_declaration_t chardev_methods[2]= {{"int32_t","edi_chardev_read",0,3,chardev_read_write_arguments,NULL},
+ {"int32_t","edi_chardev_write",0,3,chardev_read_write_arguments,NULL}};
+/*!\brief Declaration of the EDI-CHARACTER-DEVICE class.
+ *
+ * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent
+ * before passing the filled-in structure to the EDI runtime. */
+EDI_DEFVAR edi_class_declaration_t chardev_class = {"EDI-CHARACTER-DEVICE",0,2,chardev_methods,NULL,NULL,NULL};
+#else
+extern edi_class_declaration_t chardev_class; // Declare for non main files
+#endif
+
+#ifdef EDI_MAIN_FILE
+/*!\brief Arguments to EDI-BLOCK-DEVICE's read() and write() methods. */
+edi_variable_declaration_t blockdev_read_write_arguments[3] = {{"pointer void","blockdev",1},
+ {"pointer void","buffer",1},
+ {"unsigned int32_t","blocks",1}};
+/*!\brief Arguments to EDI-BLOCK-DEVICE's seek() method. */
+edi_variable_declaration_t blockdev_seek_arguments[3] = {{"pointer void","blockdev",1},
+ {"int32_t","offset",1},
+ {"int32_t","whence",1}};
+/*!\brief Declaration of the methods of EDI-BLOCK-DEVICE, read(), write(), seek(), and get_block_size().
+ *
+ * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-BLOCK-DEVICE should fill
+ * these entries in with pointers to their own functions. */
+edi_function_declaration_t blockdev_methods[4] = {{"int32_t","edi_blockdev_read",0,3,blockdev_read_write_arguments,NULL},
+ {"int32_t","edi_blockdev_write",0,3,blockdev_read_write_arguments,NULL},
+ {"int32_t","edi_blockdev_seek",0,3,blockdev_seek_arguments,NULL},
+ {"unsigned int32_t","edi_blockdev_get_block_size",0,0,NULL,NULL}};
+/*!\brief Declaration of the EDI-BLOCK-DEVICE class.
+ *
+ * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent
+ * before passing the filled-in structure to the EDI runtime. */
+edi_class_declaration_t blockdev_class = {"EDI-BLOCK-DEVICE",0,4,blockdev_methods,NULL,NULL,NULL};
+#else
+extern edi_class_declaration_t blockdev_class; // Declare for non main files
+#endif
+
+#endif
--- /dev/null
+#ifndef EDI_DMA_STREAMS_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#define EDI_DMA_STREAMS_H
+
+/*! \file edi_dma_streams.h
+ * \brief EDI's stream subclass for handling Direct Memory Access hardware.
+ *
+ * Data structures and algorithms this header represents:
+ *
+ * DATA STRUCTURE: DMA STREAMS - DMA streams are objects of the class EDI-STREAM-DMA used to pass data between a buffer of
+ * memory and the computer's DMA hardware. It is the responsibility of the object to allocate memory for its stream memory buffer
+ * which can be used with DMA hardware and to program the DMA hardware for transmissions. DMA streams can be bidirectional if the
+ * correct DMA mode is used. */
+
+#include "edi_objects.h"
+
+#define DMA_STREAM_CLASS "EDI-STREAM-DMA"
+
+/*! \brief The name of the EDI DMA stream class.
+ *
+ * An edi_string_t with the class name "EDI-STREAM-DMA" in it. */
+#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
+const edi_string_t dma_stream_class = DMA_STREAM_CLASS;
+#else
+extern const edi_string_t dma_stream_class;
+#endif
+
+#ifndef IMPLEMENTING_EDI
+/*! \brief int32_t EDI-STREAM-DMA.init_dma_stream(unsigned int32_t channel,unsigned int32_t mode,unsigned int32_t buffer_pages);
+ *
+ * Pointer to the init_dma_stream() method of class EDI-STREAM-DMA, which initializes a DMA stream with a DMA channel, DMA mode, and
+ * the number of DMA-accessible memory pages to keep as a buffer. It will only work once per stream object. It's possible return
+ * values are 1 for sucess, -1 for invalid DMA channel, -2 for invalid DMA mode, -3 for inability to allocate enough buffer pages and
+ * 0 for all other errors. */
+EDI_DEFVAR int32_t (*init_dma_stream)(object_pointer stream, uint32_t channel, uint32_t mode, uint32_t buffer_pages);
+/*! \brief int32_t EDI-STREAM-DMA.transmit(data_pointer *anchor,unsigned int32 num_bytes,bool sending);
+ *
+ * Pointer to the dma_stream_transmit() method of class EDI-STREAM-DMA, which transmits the given number of bytes of data through
+ * the DMA stream to/from the given anchor (either source or destination), in the given direction. It returns 1 on success, -1 on
+ * an uninitialized or invalid DMA stream object, -2 when the anchor was NULL or otherwise invalid, -3 if the DMA stream can't
+ * transmit in the given direction, and 0 for all other errors. */
+EDI_DEFVAR int32_t (*dma_stream_transmit)(object_pointer stream, data_pointer anchor, uint32_t num_bytes, bool sending);
+#endif
+
+#endif
--- /dev/null
+#ifndef EDI_INTERRUPTS_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#define EDI_INTERRUPTS_H
+
+/*! \file edi_interrupts.h
+ * \brief Declaration and description of EDI's interrupt handling class.
+ *
+ * Data structures and algorithms this header represents:
+ * DATA STRUCTURE AND ALGORITHM: INTERRUPT OBJECTS - The class EDI-INTERRUPT encapsulates the handling of machine interrupts.
+ * It is initialized with an interrupt number to handle and a handler routine to call when that interrupt occurs. Only a couple of
+ * guarantees are made to the driver regarding the runtime's implementation of interrupt handling: 1) That the driver's handler is
+ * called for every time the interrupt associated with a valid and initialized interrupt object occurs, in the order of the
+ * occurences, 2) That the runtime handle the architecture-specific (general to the entire machine, not just this device)
+ * end-of-interrupt code when the driver is called without first returning from the machine interrupt. Note that the runtime hands
+ * out interrupt numbers at its own discretion and policy. */
+
+#include "edi_objects.h"
+
+/*! \brief Macro constant containing the name of the interrupt class
+ */
+#define INTERRUPTS_CLASS "EDI-INTERRUPT"
+/*! \brief The name of EDI's interrupt-handling class.
+ *
+ * An edi_string_t holding the name of the runtime-implemented interrupt object class. It's value is "EDI-INTERRUPT". */
+#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
+const edi_string_t interrupts_class = INTERRUPTS_CLASS;
+#else
+extern const edi_string_t interrupts_class;
+#endif
+
+/*! \brief A pointer to an interrupt handling function.
+ *
+ * A pointer to a function called to handle interrupts. Its unsigned int32_t parameter is the interrupt number that is being
+ * handled. */
+typedef void (*interrupt_handler_t)(uint32_t interrupt_number);
+
+#ifndef IMPLEMENTING_EDI
+/*! \brief Initializes an interrupt object with an interrupt number and a pointer to a handler function.
+ *
+ * A pointer to the init_interrupt() method of class EDI-INTERRUPT. This method initializes a newly-created interrupt object with an
+ * interrupt number and a pointer to the driver's handler of type interrupt_handler_t. It can only be called once per object, and
+ * returns 1 on success, fails with -1 when the interrupt number is invalid or unacceptable to the runtime, fails with -2 when the
+ * pointer to the driver's interrupt handler is invalid, and fails with -3 for all other errors. */
+EDI_DEFVAR int32_t (*init_interrupt)(object_pointer interrupt, uint32_t interrupt_number, interrupt_handler_t handler);
+/*! \brief Get this interrupt object's interrupt number. */
+EDI_DEFVAR uint32_t (*interrupt_get_irq)(object_pointer interrupt);
+/*! \brief Set a new handler for this interrupt object. */
+EDI_DEFVAR void (*interrupt_set_handler)(object_pointer interrupt, interrupt_handler_t handler);
+/*! \brief Return from this interrupt, letting the runtime run any necessary End-Of-Interrupt code.
+ *
+ * A pointer to the interrupt_return() method of class EDI-INTERRUPT. This method returns from the interrupt designated by the
+ * calling interrupt object. If there is a machine-wide end-of-interrupt procedure and the driver was called during the handling of
+ * the machine interrupt (as opposed to delaying the handling and letting the runtime EOI), the runtime runs it during this method.
+ * This method has no return value, since once it's called control leaves the calling thread. */
+EDI_DEFVAR void (*interrupt_return)(object_pointer interrupt);
+#endif
+
+#endif
--- /dev/null
+#ifndef EDI_MEMORY_MAPPING_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#define EDI_MEMORY_MAPPING_H
+
+/*! \file edi_memory_mapping.h
+ * \brief Declaration and description of EDI's class for mapping physical pages into the driver's address space.
+ *
+ * Data structures and algorithms this header represents:
+ * ALGORITHM: MEMORY MAPPINGS - Memory mapping objects of the class EDI-MEMORY-MAPPING are used to give virtual (driver-visible)
+ * addresses to sections of physical memory. These can either be memory mappings belonging to hardware devices or plain RAM which
+ * the driver wants page-aligned. A memory mapping object is initialized with the physical address for the memory mapping and the
+ * number of pages the mapping takes up, or simply the desired length of the a physically contiguous buffer in pages. The class's
+ * two methods map the section of memory into and out of the driver's virtual address space. */
+
+#include "edi_objects.h"
+
+/*! \brief The name of EDI's memory mapping class.
+ *
+ * An edi_string_t with the name of the memory mapping class, "EDI-MEMORY-MAPPING". */
+#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
+const edi_string_t memory_mapping_class = "EDI-MEMORY-MAPPING";
+#else
+extern const edi_string_t memory_mapping_class;
+#endif
+
+/*! \brief Flag representing Strong Uncacheable caching method. */
+#define CACHING_STRONG_UNCACHEABLE 0
+/*! \brief Flag representing Uncacheable caching method. */
+#define CACHING_UNCACHEABLE 1
+/*! \brief Flag representing Write combining caching method. */
+#define CACHING_WRITE_COMBINING 2
+/*! \brief Flag representing Write Through caching method. */
+#define CACHING_WRITE_THROUGH 3
+/*! \brief Flag representing Write Back caching method. */
+#define CACHING_WRITE_BACK 3
+/*! \brief Flag representing Write Protected caching method. */
+#define CACHING_WRITE_PROTECTED 3
+
+#ifndef IMPLEMENTING_EDI
+/*! \brief Initialize an EDI-MEMORY-MAPPING object with a physical address range.
+ *
+ * This method takes the start_physical_address of a memory mapping and the number of pages in that mapping and uses these arguments
+ * to initialize an EDI-MEMORY-MAPPING object. It can only be called once per object. It returns 1 when successful, -1 when an
+ * invalid physical address is given (one that the runtime knows is neither a physical memory mapping belonging to a device nor
+ * normal RAM), -2 when the number of pages requested is bad (for the same reasons as the starting address can be bad), and 0 for
+ * all other errors.
+ *
+ * Note that this method can't be invoked on an object which has already initialized via init_memory_mapping_with_pages(). */
+EDI_DEFVAR int32_t (*init_memory_mapping_with_address)(object_pointer mapping, data_pointer start_physical_address, uint32_t pages);
+/*! \brief Initialize an EDI-MEMORY-MAPPING object by requesting a number of new physical pages.
+ *
+ * This method takes a desired number of physical pages for a memory mapping, and uses that number to initialize an
+ * EDI-MEMORY-MAPPING object by creating a buffer of contiguous physical pages. It can only be called once per object. It returns
+ * 1 when successful, -1 when the request for pages cannot be fulfilled, and 0 for all other errors.
+ *
+ * Note that this method cannot be called if init_memory_mapping_with_address() has already been used on the given object. */
+EDI_DEFVAR int32_t (*init_memory_mapping_with_pages)(object_pointer mapping, uint32_t pages);
+/*! \brief Map the memory-mapping into this driver's visible address space.
+ *
+ * This asks the runtime to map a given memory mapping into the driver's virtual address space. Its parameter is the address of a
+ * data_pointer to place the virtual address of the mapping into. This method returns 1 on success, -1 on an invalid argument, -2
+ * for an uninitialized object, and 0 for all other errors. */
+EDI_DEFVAR int32_t (*map_in_mapping)(object_pointer mapping, data_pointer *address_mapped_to);
+/*! \brief Unmap the memory mapping from this driver's visible address space.
+ *
+ * This method tries to map the given memory mapping out of the driver's virtual address space. It returns 1 for success, -1
+ * for an uninitialized memory mapping object, -2 if the mapping isn't mapped into the driver's address space already, and 0
+ * for all other errors. */
+EDI_DEFVAR int32_t (*map_out_mapping)(object_pointer mapping);
+
+/*! \brief Set the caching flags for a memory mapping. */
+EDI_DEFVAR void (*mapping_set_caching_method)(object_pointer mapping, uint32_t caching_method);
+/*! \brief Get the current caching method for a memory mapping. */
+EDI_DEFVAR uint32_t (*mapping_get_caching_method)(object_pointer mapping);
+/*! \brief Flush write-combining buffers on CPU to make sure changes to memory mapping actually get written. Only applies to a Write Combining caching method (I think.).*/
+EDI_DEFVAR void (*flush_write_combining_mapping)(object_pointer mapping);
+#endif
+
+#endif
--- /dev/null
+#ifndef EDI_OBJECTS_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#define EDI_OBJECTS_H
+
+/*! \file edi_objects.h
+ * \brief The header file for basic EDI types and the object system.
+ *
+ * This file contains declarations of EDI's primitive data types, as well as structures and functions for with the object system.
+ * It represents these data structures and algorithms:
+ *
+ * DATA STRUCTURE: THE CLASS LIST - EDI implementing runtime's must keep an internal list of classes implemented by the runtime
+ * and separate lists of classes implemented by each driver. Whoever implements a class is said to "own" that class. The
+ * internal format of this list is up to the runtime coders, but it must be possible to recreate the original list of
+ * edi_class_declaration structures the driver declared to the runtime from it. This list is declared to the runtime in an
+ * initialization function in the header edi.h. The object_class member of an edi_object_metadata structure must point to that
+ * object's class's entry in this list.
+ *
+ * ALGORITHM AND DATA STRUCTURE: CLASSES AND INHERITANCE - Classes are described using edi_class_declaration_t structures and
+ * follow very simple rules. All data is private and EDI provides no way to access instance data, so there are no member
+ * variable declarations. However, if the data isn't memory-protected (for example, driver data on the driver heap) EDI allows
+ * the possibility of pointer access to data, since runtime and driver coders could make use of that behavior. Classes may have
+ * one ancestor by declaring so in their class declaration structure, and if child methods are different then parent methods
+ * the children always override their parents. An EDI runtime must also be able to check the existence and ownership of a given
+ * class given its name in an edi_string_t.
+ *
+ * ALGORITHM: OBJECT CREATION AND DESTRUCTION - An EDI runtime should be able to call the constructor of a named class, put the
+ * resulting object_pointer into an edi_object_metadata_t and return that structure. The runtime should also be able to call an
+ * object's class's destructor when given a pointer to a valid edi_metadata_t for an already-existing object. Data equivalent
+ * to an edi_object_metadata_t should also be tracked by the runtime for every object in existence in case of sudden EDI shutdown
+ * (see edi.h).
+ *
+ * ALGORITHM: RUNTIME TYPE INFORMATION - When passed the data_pointer member of an edi_object_metadata_t to a valid object, an
+ * EDI runtime must be able to return an edi_string_t containing the name of that object's class and to return function_pointers
+ * to methods when the required information to find the correct method is given by calling a class's method getting function.*/
+
+/* If the EDI headers are linked with the standard C library, they use its type definitions. Otherwise, equivalent definitions are
+ * made.*/
+#if __STDC_VERSION__ == 199901L
+# include <stdbool.h>
+# include <stdint.h>
+#else
+# ifndef NULL
+# define NULL ((void*)0)
+# endif
+typedef unsigned char bool;
+# define true 1
+# define false 0
+typedef char int8_t;
+typedef short int16_t;
+typedef long int32_t;
+typedef long long int64_t;
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+#endif
+
+/*! \brief Define a variable in the header
+ */
+#ifdef EDI_MAIN_FILE
+# define EDI_DEFVAR
+#else
+# define EDI_DEFVAR extern
+#endif
+
+/*! \brief A pointer to the in-memory instance of an object.
+ *
+ * This type is sized just like a general C pointer type (whatever*) for the target architecture. It's passed as a first parameter
+ * to all methods, thus allowing EDI classes to be implemented as C++ classes and providing some protection from confusing objects
+ * with normal pointers. Equivalent to a C++ this pointer or an Object Pascal Self argument. */
+typedef void *object_pointer;
+/*! \brief A basic pointer type pointing to arbitrary data in an arbitrary location. */
+typedef void *data_pointer;
+/*! \brief A basic function pointer type.
+ *
+ * A pointer to a piece of code which can be called and return to its caller, used to distinguish between pointers to code and
+ * pointers to data. Its size is hardware-dependent. */
+typedef void (*function_pointer)(void);
+/*! \brief The length of an EDI string without its null character. */
+#define EDI_STRING_LENGTH 31
+/*! \brief A type representing a 31-character long string with a terminating NULL character at the end. All of EDI uses this type
+ * for strings.
+ *
+ * A null-terminated string type which stores characters in int8s. It allows for 31 characters in each string, with the final
+ * character being the NULL terminator. Functions which use this type must check that its final character is NULL, a string which
+ * doesn't not have this property is invalid and insecure. I (the author of EDI) know and understand that this form of a string
+ * suffers from C programmer's disease, but I can't use anything else without either making string use far buggier or dragging
+ * everyone onto a better language than C. */
+typedef int8_t edi_string_t[0x20];
+/*! \brief A type representing a pointer form of #edi_string_t suitable for function returns
+ */
+typedef int8_t *edi_string_ptr_t;
+
+/*! \var EDI_BASE_TYPES
+ * \brief A constant array of edi_string_t's holding every available EDI primitive type. */
+/*! \var EDI_TYPE_MODIFIERS
+ * \brief A constant array of edi_string_t's holding available modifiers for EDI primitive types. */
+#ifdef IMPLEMENTING_EDI
+ const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg","edi_string_t"};
+ const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"};
+#else
+ //extern const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg", "edi_string_t"};
+ //extern const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"};
+ extern const edi_string_t EDI_BASE_TYPES[9];
+ extern const edi_string_t EDI_TYPE_MODIFIERS[2];
+#endif
+
+/*! \struct edi_object_metadata_t
+ * \brief A packed structure holding all data to identify an object to the EDI object system. */
+typedef struct {
+ /*! \brief Points to the instance data of the object represented by this structure.
+ *
+ * An object_pointer to the object this structure refers to. The this pointer, so to speak. */
+ object_pointer object;
+ /*! \brief Points the internal record kept by the runtime describing the object's class.
+ *
+ * Points to wherever the runtime has stored the class data this object was built from. The class data doesn't need to be
+ * readable to the driver, and so this pointer can point to an arbitrary runtime-reachable location. */
+ data_pointer object_class;
+} edi_object_metadata_t;
+
+/*! \struct edi_variable_declaration_t
+ * \brief The data structure used to describe a variable declaration to the EDI object system.
+ *
+ * The data structure used to describe a variable declaration to the EDI object system. The context of the declaration depends on
+ * where the data structure appears, ie: alone, in a class declaration, in a parameter list, etc. */
+typedef struct {
+ /*! \brief The type of the declared variable.
+ *
+ * The type of the variable, which must be a valid EDI primitive type as specified in the constant EDI_BASE_TYPES and
+ * possibly modified by a modifier specified in the constant EDI_TYPE_MODIFIERS. */
+ edi_string_t type;
+ /*! \brief The name of the declared variable. */
+ edi_string_t name;
+ /*! \brief Number of array entries if this variable is an array declaration.
+ *
+ * An int32_t specifying the number of variables of 'type' in the array 'name'. For a single variable this value should
+ * simply be set to 1, for values greater than 1 a packed array of contiguous variables is being declared, and a value of 0
+ * is invalid. */
+ int32_t array_length;
+} edi_variable_declaration_t;
+
+/*! \struct edi_function_declaration_t
+ * \brief The data structure used to declare a function to the EDI object system. */
+typedef struct {
+ /*! \brief The return type of the function. The same type rules which govern variable definitions apply here. */
+ edi_string_t return_type;
+ /*! \brief The name of the declared function. */
+ edi_string_t name;
+ /*! \brief The version number of the function, used to tell different implementations of the same function apart. */
+ uint32_t version;
+ /*! \brief The number of arguments passed to the function.
+ *
+ * The number of entries in the member arguments that the object system should care about. Caring about less misses
+ * parameters to functions, caring about more results in buffer overflows. */
+ uint32_t num_arguments;
+ /*! \brief An array of the declared function's arguments.
+ *
+ * A pointer to an array num_arguments long containing edi_variable_declaration_t's for each argument to the declared
+ * function.*/
+ edi_variable_declaration_t *arguments;
+ /*!\brief A pointer to the declared function's code in memory. */
+ function_pointer code;
+} edi_function_declaration_t;
+
+/*! \brief A pointer to a function for constructing instances of a class.
+ *
+ * A pointer to a function which takes no parameters and returns an object_pointer pointing to the newly made instance of a class.
+ * It is the constructor's responsibility to allocate memory for the new object. Each EDI class needs one of these. */
+typedef object_pointer (*edi_constructor_t)(void);
+/*! \brief A pointer to a function for destroying instances of a class.
+ *
+ * A pointer to a function which takes an object_pointer as a parameter and returns void. This is the destructor counterpart to a
+ * class's edi_constructor_t, it destroys the object pointed to by its parameter and frees the object's memory. Every class must
+ * have one */
+typedef void (*edi_destructor_t)(object_pointer);
+
+/*! \brief Information the driver must give the runtime about its classes so EDI can construct them and call their methods.
+ *
+ * A structure used to declare a class to an EDI runtime so instances of it can be constructed by the EDI object system. */
+typedef struct {
+ /*! \brief The name of the class declared by the structure. */
+ edi_string_t name;
+ /*! \brief The version of the class. This number is used to tell identically named but differently
+ * implemented classes apart.*/
+ uint32_t version;
+ /*! \brief The number of methods in the 'methods' function declaration array. */
+ uint32_t num_methods;
+ /*! \brief An array of edi_function_declaration_t declaring the methods of this class. */
+ edi_function_declaration_t *methods;
+ /*! \brief Allocates the memory for a new object of the declared class and constructs the object. Absolutely required.*/
+ edi_constructor_t constructor;
+ /*! \brief Destroys the given object of the declared class and frees its memory. Absolutely required. */
+ edi_destructor_t destructor;
+ /*! \brief A pointer to another EDI class declaration structure specifying the declared class's parent class.
+ *
+ * Points to a parent class declared in another class declaration. It can be NULL to mean this class has no parent. */
+ struct edi_class_declaration_t *parent;
+} edi_class_declaration_t;
+
+/*! \brief Checks the existence of the named class.
+ *
+ * This checks for the existence on THE CLASS LIST of the class named by its edi_string_t parameter and returns a signed int32_t. If
+ * the class isn't found (ie: it doesn't exist as far as EDI is concerned) -1 is returned, if the class is owned by the driver
+ * (implemented by the driver and declared to the runtime by the driver) 0, and if the class is owned by the runtime (implemented by
+ * the runtime) 1. */
+int32_t check_class_existence(edi_string_t class_name);
+/*! \brief Constructs an object of the named class and returns its object_pointer and a data_pointer to its class data.
+ *
+ * Given a valid class name in an edi_string_t this function constructs the specified class and returns an edi_metadata_t describing
+ * the new object as detailed in OBJECT CREATION AND DESTRUCTION. If the construction fails it returns a structure full of NULL
+ * pointers. */
+edi_object_metadata_t construct_object(edi_string_t class_name);
+/*! \brief Destroys the given object using its class data.
+ *
+ * As specified in OBJECT CREATION AND DESTRUCTION this function should destroy an object when given its valid edi_metadata_t. The
+ * destruction is accomplished by calling the class's destructor. */
+void destroy_object(edi_object_metadata_t object);
+/*! \brief Obtains a function pointer to a named method of a given class.
+ *
+ * When given a valid data_pointer object_class from an edi_object_metadata_t and an edi_string_t representing the name of the
+ * desired method retrieves a function_pointer to the method's machine code in memory. If the desired method isn't found, NULL is
+ * returned. */
+function_pointer get_method_by_name(data_pointer object_class,edi_string_t method_name);
+/*! \brief Obtains a function pointer to a method given by a declaration of the given class if the class's method matches the
+ * declaration.
+ *
+ * Works just like get_method_by_name(), but by giving an edi_function_declaration_t for the desired method instead of just its name.
+ * Performs detailed checking against THE CLASS LIST to make sure that the method returned exactly matches the declaration passed
+ * in. */
+function_pointer get_method_by_declaration(data_pointer object_class,edi_function_declaration_t declaration);
+
+/* Runtime typing information. */
+/*! \brief Returns the name of the class specified by a pointer to class data.
+ *
+ * Given the data_pointer to an object's class data as stored in an edi_object_metadata_t retrieves the name of the object's class
+ * and returns it in an edi_string_t. */
+edi_string_ptr_t get_object_class(data_pointer object_class);
+/*! \brief Returns the name of a class's parent class.
+ *
+ * When given an edi_string_t with a class name in it, returns another edi_string_t containing the name of the class's parent, or an
+ * empty string. */
+edi_string_ptr_t get_class_parent(edi_string_t some_class);
+/*! \brief Returns the internal class data of a named class (if it exists) or NULL.
+ *
+ * When given an edi_string_t with a valid class name in it, returns a pointer to the runtime's internal class data for that class.
+ * Otherwise, it returns NULL. */
+data_pointer get_internal_class(edi_string_t some_class);
+
+#endif
--- /dev/null
+#ifndef EDI_PORT_IO_H
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+/* Modified by thePowersGang (John Hodge)
+ * - Surround variable definitions with an #ifdef IMPLEMENTING_EDI
+ */
+
+#define EDI_PORT_IO_H
+
+/*! \file edi_port_io.h
+ * \brief Declaration and description of EDI's port I/O class.
+ *
+ * Data structures and algorithms this header represents:
+ *
+ * DATA STRUCTURE AND ALGORITHM: PORT I/O OBJECTS - A class named "EDI-IO-PORT" is defined as an encapsulation of the port I/O
+ * used on some machine architectures. Each object of this class represents a single I/O port which can be read from and written to
+ * in various sizes. Each port can be held by one object only at a time. */
+
+#include "edi_objects.h"
+
+/*! \brief Macro to create methods for reading from ports.
+ *
+ * This macro creates four similar methods, differing in the size of the type they read from the I/O port held by the object. Their
+ * parameter is a pointer to the output type, which is filled with the value read from the I/O port. They return 1 for success, -1
+ * for an uninitialized I/O port object, and 0 for other errors. */
+#define port_read_method(type,name) int32_t (*name)(object_pointer port_object, type *out)
+/*! \brief Macro to create methods for writing to ports.
+ *
+ * This macro creates four more similar methods, differing in the size of the type they write to the I/O port held by the object.
+ * Their parameter is the value to write to the port. They return 1 for success, -1 for an uninitialized I/O port object and 0 for
+ * other errors. */
+#define port_write_method(type,name) int32_t (*name)(object_pointer port_object, type in)
+
+/*! \brief Name of EDI I/O port class. (Constant)
+ *
+ * A CPP constant with the value of #io_port_class */
+#define IO_PORT_CLASS "EDI-IO-PORT"
+/*! \brief Name of EDI I/O port class.
+ *
+ * An edi_string_t containing the class name "EDI-IO-PORT". */
+#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
+const edi_string_t io_port_class = IO_PORT_CLASS;
+#else
+extern const edi_string_t io_port_class;
+#endif
+
+#ifndef IMPLEMENTING_EDI
+/*! \brief int32_t EDI-IO-PORT.init_io_port(unsigned int16_t port);
+ *
+ * This method takes an unsigned int16_t representing a particular I/O port and initializes the invoked EDI-IO-PORT object with it.
+ * The method returns 1 if successful, -1 if the I/O port could not be obtained for the object, and 0 for all other errors. */
+EDI_DEFVAR int32_t (*init_io_port)(object_pointer port_object, uint16_t port);
+/*! \brief Get the port number from a port object. */
+EDI_DEFVAR uint16_t (*get_port_number)(object_pointer port);
+/*! \brief Method created by port_read_method() in order to read bytes (int8s) from I/O ports. */
+EDI_DEFVAR int32_t (*read_byte_io_port)(object_pointer port_object, int8_t *out);
+/*! \brief Method created by port_read_method() in order to read words (int16s) from I/O ports. */
+EDI_DEFVAR int32_t (*read_word_io_port)(object_pointer port_object, int16_t *out);
+/*! \brief Method created by port_read_method() in order to read longwords (int32s) from I/O ports. */
+EDI_DEFVAR int32_t (*read_long_io_port)(object_pointer port_object, int32_t *out);
+/*! \brief Method created by port_read_method() in order to read long longwords (int64s) from I/O ports. */
+EDI_DEFVAR int32_t (*read_longlong_io_port)(object_pointer port_object,int64_t *out);
+/*! \brief Method of EDI-IO-PORT to read long strings of data from I/O ports.
+ *
+ * Reads arbitrarily long strings of data from the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2
+ * for a bad pointer to the destination buffer, and 0 for all other errors. */
+EDI_DEFVAR int32_t (*read_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *out);
+/*! \brief Method created by port_write_method() in order to write bytes (int8s) to I/O ports. */
+EDI_DEFVAR int32_t (*write_byte_io_port)(object_pointer port_object, int8_t in);
+/*! \brief Method created by port_write_method() in order to write words (int16s) to I/O ports. */
+EDI_DEFVAR int32_t (*write_word_io_port)(object_pointer port_object, int16_t in);
+/*! \brief Method created by port_write_method() in order to write longwords (int32s) to I/O ports. */
+EDI_DEFVAR int32_t (*write_long_io_port)(object_pointer port_object, int32_t in);
+/*! \brief Method created by port_write_method() in order to write long longwords (int64s) to I/O ports. */
+EDI_DEFVAR int32_t (*write_longlong_io_port)(object_pointer port_object, int64_t in);
+/*! \brief Method of EDI-IO-PORT to write long strings of data to I/O ports.
+ *
+ * Writes arbitrarily long strings of data to the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2
+ * for a bad pointer to the source buffer, and 0 for all other errors. */
+EDI_DEFVAR int32_t (*write_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *in);
+
+#endif // defined(IMPLEMENTING_EDI)
+
+#endif
--- /dev/null
+#ifndef EDI_PTHREADS
+
+/* Copyright (c) 2006 Eli Gottlieb.
+ * Permission is granted to copy, distribute and/or modify this document
+ * under the terms of the GNU Free Documentation License, Version 1.2
+ * or any later version published by the Free Software Foundation;
+ * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ * Texts. A copy of the license is included in the file entitled "COPYING". */
+
+#define EDI_PTHREADS
+/*!\file edi_pthreads.h
+ * \brief A basic subset of POSIX Threads functionality, providing threading and thread synchronization.
+ *
+ * A very basic POSIX Threads interface. Note that pthreads are not a class, because none of these calls really gels with
+ * object-oriented programming. Also, if drivers aren't processes or threads under the implementing operating system a small
+ * threading system must be implemented in-runtime just to multiplex the pthreads of EDI drivers. Sorry about that.
+ *
+ * Data structures and algorithms this header represents:
+ *
+ * ALGORITHM AND DATA STRUCTURE: POSIX Threading - The runtime must provide enough of a POSIX threading interface to implement
+ * the calls described here. The actual multithreading must be performed by the runtime, and the runtime can implement that
+ * multithreading however it likes as long as the given POSIX Threads subset works. There is, however, a caveat: since the runtime
+ * calls the driver like it would a library, the driver must perceive all calls made to it by the runtime as running under one thread.
+ * From this thread the driver can create others. Such behavior is a quirk of EDI, and does not come from the POSIX standard.
+ * However, it is necessary to provide the driver with a thread for its own main codepaths. For further details on a given POSIX
+ * Threading routine, consult its Unix manual page. */
+
+#include "edi_objects.h"
+
+/* Placeholder type definitions. Users of the PThreads interface only ever need to define pointers to these types. */
+/*!\brief Opaque POSIX Threading thread attribute type. */
+typedef void pthread_attr_t;
+/*!\brief Opaque POSIX Threading mutex (mutual exclusion semaphore) type. */
+typedef void pthread_mutex_t;
+/*!\brief Opaque POSIX Threading mutex attribute type. */
+typedef void pthread_mutex_attr_t;
+
+/*!\struct sched_param
+ * \brief POSIX Threading scheduler parameters for a thread. */
+typedef struct {
+ /*!\brief The priority of the thread. */
+ int32_t sched_priority;
+} sched_param;
+
+/*!\brief POSIX Threading thread identifier. */
+typedef uint32_t pthread_t;
+/*!\brief POSIX Threading thread function type.
+ *
+ * A function pointer to a thread function, with the required signature of a thread function. A thread function takes one untyped
+ * pointer as an argument and returns an untyped pointer. Such a function is a thread's main routine: it's started with the thread,
+ * and the thread exits if it returns. */
+typedef void *(*pthread_function_t)(void*);
+
+/*!\brief Insufficient resources. */
+#define EAGAIN -1
+/*!\brief Invalid parameter. */
+#define EINVAL -2
+/*!\brief Permission denied. */
+#define EPERM -3
+/*!\brief Operation not supported. */
+#define ENOTSUP -4
+/*!\brief Priority scheduling for POSIX/multiple schedulers is not implemented. */
+#define ENOSYS -5
+/*!\brief Out of memory. */
+#define ENOMEM -6
+/*!\brief Deadlock. Crap. */
+#define EDEADLK -7
+/*!\brief Busy. Mutex already locked. */
+#define EBUSY -8
+
+/*!\brief Scheduling policy for regular, non-realtime scheduling. The default. */
+#define SCHED_OTHER 0
+/*!\brief Real-time, first-in first-out scheduling policy. Requires special (superuser, where such a thing exists) permissions. */
+#define SCHED_FIFO 1
+/*!\brief Real-time, round-robin scheduling policy. Requires special (superuser, where such a thing exists) permissions. */
+#define SCHED_RR 0
+
+/*!\brief Creates a new thread with the given attributes, thread function and arguments, giving back the thread ID of the new
+ * thread.
+ *
+ * pthread_create() creates a new thread of control that executes concurrently with the calling thread. The new thread applies the
+ * function start_routine, passing it arg as its argument. The attr argument specifies thread attributes to apply to the new thread;
+ * it can also be NULL for the default thread attributes (joinable with default scheduling policy). On success this function returns
+ * 0 and places the identifier of the new thread into thread_id. On an error, pthread_create() can return EAGAIN if insufficient
+ * runtime resources are available to create the requested thread, EINVAL a value specified by attributes is invalid, or EPERM if the
+ * caller doesn't have permissions to set the given attributes.
+ *
+ * For further information: man 3 pthread_create */
+int32_t pthread_create(pthread_t *thread_id, const pthread_attr_t *attributes, pthread_function_t thread_function, void *arguments);
+/*!\brief Terminates the execution of the calling thread. The thread's exit code with by status, and this routine never returns. */
+void pthread_exit(void *status);
+/*!\brief Returns the thread identifier of the calling thread. */
+pthread_t pthread_self();
+/*!\brief Compares two thread identifiers.
+ *
+ * Determines of the given two thread identifiers refer to the same thread. If so, returns non-zero. Otherwise, 0 is returned. */
+int32_t pthread_equal(pthread_t thread1, pthread_t thread2);
+/*!\brief Used by the calling thread to relinquish use of the processor. The thread then waits in the run queue to be scheduled
+ * again. */
+void pthread_yield();
+
+/*!\brief Gets the scheduling policy of the given attributes.
+ *
+ * Places the scheduling policy for attributes into policy. Returns 0 on success, EINVAL if attributes was invalid, and ENOSYS if
+ * priority scheduling/multiple scheduler support is not implemented. */
+int32_t pthread_attr_getschedpolicy(const pthread_attr_t *attributes, int32_t *policy);
+/*!\brief Sets the scheduling policy of the given attributes.
+ *
+ * Requests a switch of scheduling policy to policy for the given attributes. Can return 0 for success, EINVAL if the given policy
+ * is not one of SCHED_OTHER, SCHED_FIFO or SCHED_RR or ENOTSUP if policy is either SCHED_FIFO or SCHED_RR and the driver is not
+ * running with correct privileges. */
+int32_t pthread_attr_setschedpolicy(pthread_attr_t *attributes, int32_t policy);
+
+/*!\brief Gets the scheduling paramaters (priority) from the given attributes.
+ *
+ * On success, stores scheduling parameters in param from attributes, and returns 0. Otherwise, returns non-zero error code, such
+ * as EINVAL if the attributes object is invalid. */
+int32_t pthread_attr_getschedparam(const pthread_attr_t *attributes, sched_param *param);
+/*!\brief Sets the scheduling parameters (priority) of the given attributes.
+ *
+ * Requests that the runtime set the scheduling parameters (priority) of attributes from param. Returns 0 for success, EINVAL for an
+ * invalid attributes object, ENOSYS when multiple schedulers/priority scheduling is not implemented, and ENOTSUP when the value of
+ * param isn't supported/allowed. */
+int32_t pthread_attr_setschedparam(pthread_attr_t *attributes, const sched_param *param);
+
+/*!\brief The thread obtains its scheduling properties explicitly from its attributes structure. */
+#define PTHREAD_EXPLICIT_SCHED 1
+/*!\brief The thread inherits its scheduling properties from its parent thread. */
+#define PTHREAD_INHERIT_SCHED 0
+
+/*!\brief Returns the inheritsched attribute of the given attributes.
+ *
+ * On success, returns 0 and places the inheritsched attribute from attributes into inherit. This attribute specifies where the
+ * thread's scheduling properites shall come from, and can be set to PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure it
+ * returns EINVAL if attributes was invalid or ENOSYS if multiple schedulers/priority scheduling isn't implemented. */
+int32_t pthread_attr_getinheritsched(const pthread_attr_t *attributes, int32_t *inherit);
+/*!\brief Sets the inheritsched attribute of the given attributes.
+ *
+ * On success, places inherit into the inheritsched attribute of attributes and returns 0. inherit must either contain
+ * PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure, this routine returns EINVAL if attributes is invalid, ENOSYS when
+ * multiple schedulers/priority scheduling isn't implemented, and ENOSUP if the inheritsched attribute isn't supported. */
+int32_t pthread_attr_setinheritsched(pthread_attr_t *attributes, int32_t inherit);
+
+/*!\brief Creates a new POSIX Threads mutex, which will initially be unlocked.
+ *
+ * Creates a new mutex with the given attributes. If attributes is NULL, the default attributes will be used. The mutex starts out
+ * unlocked. On success, the new mutex resides in the mutex structure pointed to by mutex, and this routine routines 0. On failure,
+ * it returns EAGAIN if the system lacked sufficient non-memory resources to initialize the mutex, EBUSY if the given mutex is
+ * already initialized and in use, EINVAL if either parameter is invalid, and ENOMEM if the system lacks the memory for a new
+ * mutex. Note: All EDI mutexes are created with the default attributes, and are of type PTHREAD_MUTEX_ERRORCHECK. This means
+ * undefined behavior can never result from an badly placed function call. */
+int32_t pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attributes);
+/*!\brief Locks the given mutex.
+ *
+ * Locks the given mutex. If the mutex is already locked, blocks the calling thread until it can acquire the lock. When this
+ * routine returns successfully, it will return 0 and the calling thread will own the lock of the mutex. If the call fails, it can
+ * return EINVAL when mutex is invalid or EDEADLK if the calling thread already owns the mutex. */
+int32_t pthread_mutex_lock(pthread_mutex_t *mutex);
+/*!\brief Unlocks the given mutex.
+ *
+ * Unlocks the given mutex, returning 0 on success. On failure, it can return EINVAL when mutex is invalid or EPERM when the
+ * calling thread doesn't own the mutex. */
+int32_t pthread_mutex_unlock(pthread_mutex_t *mutex);
+/*!\brief Tries to lock the given mutex, returning immediately even if the mutex is already locked.
+ *
+ * Attempts to lock the given mutex, but returns immediately if it can't acquire a lock. Returns 0 when it has acquired a lock,
+ * EBUSY if the mutex is already locked, or EINVAL if mutex is invalid. */
+int32_t pthread_mutex_trylock(pthread_mutex_t *mutex);
+/*!\brief Destroys the given mutex, or at least the internal structure of it.
+ *
+ * Deletes the given mutex, making mutex invalid until it should be initialized by pthread_mutex_init(). Returns 0 on success,
+ * EINVAL when mutex is invalid, or EBUSY when mutex is locked or referenced by another thread. */
+int32_t pthread_mutex_destroy (pthread_mutex_t *mutex);
+
+#endif
--- /dev/null
+#ifndef HELPERS_H
+
+#define HELPERS_H
+
+#include <edi.h>
+
+// Locally Defined
+bool edi_string_equal(edi_string_t x,edi_string_t y);
+bool descends_from(data_pointer object_class,edi_string_t desired_class);
+data_pointer get_actual_class(edi_string_t ancestor,int32_t num_objects,edi_object_metadata_t *objects);
+
+// Local Copy/set
+void *memcpyd(void *dest, void *src, unsigned int count);
+
+// Implementation Defined Common functions
+void *memcpy(void *dest, void *src, unsigned int count);
+void *memmove(void *dest, void *src, unsigned int count);
+void *realloc(void *ptr, unsigned int size);
+
+#endif
--- /dev/null
+/*
+ * AcessOS EDI Interface
+ * - IRQ Class
+ *
+ * By John Hodge (thePowersGang)
+ *
+ * This file has been released into the public domain.
+ * You are free to use it as you wish.
+ */
+#include "edi/edi.h"
+
+// === TYPES ===
+typedef struct {
+ uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap)
+ uint16_t Num;
+ interrupt_handler_t Handler;
+} tEdiIRQ;
+
+// === PROTOTYPES ===
+void EDI_Int_IRQ_Handler(tRegs *Regs);
+
+// === GLOBALS ===
+tEdiIRQ gEdi_IRQObjects[16];
+
+// === FUNCTIONS ===
+/**
+ * \fn object_pointer Edi_Int_IRQ_Construct(void)
+ * \brief Creates a new IRQ Object
+ * \return Pointer to object
+ */
+object_pointer Edi_Int_IRQ_Construct(void)
+{
+ int i;
+ // Search for a free irq
+ for( i = 0; i < 16; i ++ )
+ {
+ if(gEdi_IRQObjects[i].State) continue;
+ gEdi_IRQObjects[i].State = 1;
+ gEdi_IRQObjects[i].Num = 0;
+ gEdi_IRQObjects[i].Handler = NULL;
+ return &gEdi_IRQObjects[i];
+ }
+ return NULL;
+}
+
+/**
+ * \fn void Edi_Int_IRQ_Destruct(object_pointer Object)
+ * \brief Destruct an IRQ Object
+ * \param Object Object to destroy
+ */
+void Edi_Int_IRQ_Destruct(object_pointer Object)
+{
+ tEdiIRQ *obj;
+
+ VALIDATE_PTR(Object,);
+ obj = GET_DATA(Object);
+
+ if( !obj->State ) return;
+
+ if( obj->Handler )
+ irq_uninstall_handler( obj->Num );
+
+ if( obj->State & 0x8000 ) { // If in heap, free
+ free(Object);
+ } else { // Otherwise, mark as unallocated
+ obj->State = 0;
+ }
+}
+
+/**
+ * \fn int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler)
+ * \brief Initialises an IRQ
+ * \param Object Object Pointer (this)
+ * \param Num IRQ Number to use
+ * \param Handler Callback for IRQ
+ */
+int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler)
+{
+ tEdiIRQ *obj;
+
+ //LogF("Edi_Int_IRQ_InitInt: (Object=0x%x, Num=%i, Handler=0x%x)\n", Object, Num, Handler);
+
+ VALIDATE_PTR(Object,0);
+ obj = GET_DATA(Object);
+
+ if( !obj->State ) return 0;
+
+ if(Num > 15) return 0;
+
+ // Install the IRQ if a handler is passed
+ if(Handler) {
+ if( !irq_install_handler(Num, Edi_Int_IRQ_Handler) )
+ return 0;
+ obj->Handler = Handler;
+ }
+
+ obj->Num = Num;
+ obj->State &= ~0x3FFF;
+ obj->State |= 2; // Set initialised flag
+ return 1;
+}
+
+/**
+ * \fn uint16_t Edi_Int_IRQ_GetInt(object_pointer Object)
+ * \brief Returns the irq number associated with the object
+ * \param Object IRQ Object to get number from
+ * \return IRQ Number
+ */
+uint16_t Edi_Int_IRQ_GetInt(object_pointer Object)
+{
+ tEdiIRQ *obj;
+
+ VALIDATE_PTR(Object,0);
+ obj = GET_DATA(Object);
+
+ if( !obj->State ) return 0;
+ return obj->Num;
+}
+
+/**
+ * \fn void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler)
+ * \brief Set the IRQ handler for an IRQ object
+ * \param Object IRQ Object to alter
+ * \param Handler Function to use as handler
+ */
+void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler)
+{
+ tEdiIRQ *obj;
+
+ // Get Data Pointer
+ VALIDATE_PTR(Object,);
+ obj = GET_DATA(Object);
+
+ // Sanity Check arguments
+ if( !obj->State ) return ;
+
+ // Only register the mediator if it is not already
+ if( Handler && !obj->Handler )
+ if( !irq_install_handler(obj->Num, Edi_Int_IRQ_Handler) )
+ return ;
+ obj->Handler = Handler;
+}
+
+/**
+ * \fn void EDI_Int_IRQ_Return(object_pointer Object)
+ * \brief Return from interrupt
+ * \param Object IRQ Object
+ * \note Due to the structure of acess interrupts, this is a dummy
+ */
+void EDI_Int_IRQ_Return(object_pointer Object)
+{
+}
+
+/**
+ * \fn void Edi_Int_IRQ_Handler(struct regs *Regs)
+ * \brief EDI IRQ Handler - Calls the handler
+ * \param Regs Register state at IRQ call
+ */
+void Edi_Int_IRQ_Handler(struct regs *Regs)
+{
+ int i;
+ for( i = 0; i < 16; i ++ )
+ {
+ if(!gEdi_IRQObjects[i].State) continue; // Unused, Skip
+ if(gEdi_IRQObjects[i].Num != Regs->int_no) continue; // Another IRQ, Skip
+ if(!gEdi_IRQObjects[i].Handler) continue; // No Handler, Skip
+ gEdi_IRQObjects[i].Handler( Regs->int_no ); // Call Handler
+ return;
+ }
+}
+
+
+// === CLASS DECLARATION ===
+static edi_function_declaration_t scEdi_Int_Functions_IRQ[] = {
+ {"int32_t", "init_interrupt", 1, 3, NULL, //scEdi_Int_Variables_IO[0],
+ (function_pointer)Edi_Int_IRQ_InitInt
+ },
+ {"uint32_t", "interrupt_get_irq", 1, 1, NULL, //scEdi_Int_Variables_IO[1],
+ (function_pointer)Edi_Int_IRQ_GetInt
+ },
+ {"void", "interrupt_set_handler", 1, 2, NULL, //scEdi_Int_Variables_IO[2],
+ (function_pointer)Edi_Int_IRQ_GetInt
+ },
+ {"void", "interrupt_return", 1, 1, NULL, //scEdi_Int_Variables_IO[3],
+ (function_pointer)Edi_Int_IRQ_GetInt
+ }
+ };
+static edi_class_declaration_t scEdi_Int_Class_IRQ =
+ {
+ INTERRUPTS_CLASS, 1, 12,
+ scEdi_Int_Functions_IRQ,
+ Edi_Int_IRQ_Construct,
+ Edi_Int_IRQ_Destruct,
+ NULL
+ };
--- /dev/null
+/*
+ * AcessOS EDI Interface
+ * - IO Port Class
+ *
+ * By John Hodge (thePowersGang)
+ *
+ * This file has been released into the public domain.
+ * You are free to use it as you wish.
+ */
+#include "edi/edi.h"
+
+// === TYPES ===
+typedef struct {
+ uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap)
+ uint16_t Num;
+} tEdiPort;
+
+// === GLOBALS ===
+#define NUM_PREALLOC_PORTS 128
+tEdiPort gEdi_PortObjects[NUM_PREALLOC_PORTS];
+
+// === FUNCTIONS ===
+/**
+ * \fn object_pointer Edi_Int_IO_Construct(void)
+ * \brief Creates a new IO Port Object
+ * \return Pointer to object
+ */
+object_pointer Edi_Int_IO_Construct(void)
+{
+ tEdiPort *ret;
+ int i;
+ // Search for a free preallocated port
+ for( i = 0; i < NUM_PREALLOC_PORTS; i ++ )
+ {
+ if(gEdi_PortObjects[i].State) continue;
+ gEdi_PortObjects[i].State = 1;
+ gEdi_PortObjects[i].Num = 0;
+ return &gEdi_PortObjects[i];
+ }
+ // Else, use heap space
+ ret = malloc( sizeof(tEdiPort) );
+ ret->State = 0x8001;
+ ret->Num = 0;
+ return ret;
+}
+
+/**
+ * \fn void Edi_Int_IO_Destruct(object_pointer Object)
+ * \brief Destruct an IO Port Object
+ * \param Object Object to destroy
+ */
+void Edi_Int_IO_Destruct(object_pointer Object)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object,);
+ obj = GET_DATA(Object);
+
+ if(obj->State & 0x8000) { // If in heap, free
+ free(Object);
+ } else { // Otherwise, mark as unallocated
+ obj->State = 0;
+ }
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port)
+ * \brief Initialises an IO Port
+ * \param Object Object Pointer (this)
+ * \param Port Port Number to use
+ */
+int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+
+ if( !obj->State ) return 0;
+ obj->Num = Port;
+ obj->State &= ~0x3FFF;
+ obj->State |= 2; // Set initialised flag
+ return 1;
+}
+
+/**
+ * \fn uint16_t Edi_Int_IO_GetPortNum(object_pointer Object)
+ * \brief Returns the port number associated with the object
+ * \param Object Port Object to get number from
+ * \return Port Number
+ */
+uint16_t Edi_Int_IO_GetPortNum(object_pointer Object)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ // Check if valid
+ if( !obj->State ) return 0;
+ // Return Port No
+ return obj->Num;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out)
+ * \brief Read a byte from an IO port
+ * \param Object Port Object
+ * \param out Pointer to put read data
+ */
+int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "inb %%dx, %%al" : "=a" (*out) : "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out)
+ * \brief Read a word from an IO port
+ * \param Object Port Object
+ * \param out Pointer to put read data
+ */
+int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out)
+{
+
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "inw %%dx, %%ax" : "=a" (*out) : "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out)
+ * \brief Read a double word from an IO port
+ * \param Object Port Object
+ * \param out Pointer to put read data
+ */
+int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out)
+{
+
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out) : "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out)
+ * \brief Read a quad word from an IO port
+ * \param Object Port Object
+ * \param out Pointer to put read data
+ */
+int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out)
+{
+ uint32_t *out32 = (uint32_t*)out;
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out32) : "d" ( obj->Num ) );
+ __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*(out32+1)) : "d" ( obj->Num+4 ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out)
+ * \brief Read a byte from an IO port
+ * \param Object Port Object
+ * \param Length Number of bytes to read
+ * \param out Pointer to put read data
+ */
+int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "rep insb" : : "c" (Length), "D" (out), "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in)
+ * \brief Write a byte from an IO port
+ * \param Object Port Object
+ * \param in Data to write
+ */
+int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "outb %%al, %%dx" : : "a" (in), "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in)
+ * \brief Write a word from an IO port
+ * \param Object Port Object
+ * \param in Data to write
+ */
+int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "outw %%ax, %%dx" : : "a" (in), "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in)
+ * \brief Write a double word from an IO port
+ * \param Object Port Object
+ * \param in Data to write
+ */
+int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (in), "d" ( obj->Num ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in)
+ * \brief Write a quad word from an IO port
+ * \param Object Port Object
+ * \param in Data to write
+ */
+int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in)
+{
+ uint32_t *in32 = (uint32_t*)∈
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*in32), "d" ( obj->Num ) );
+ __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*(in32+1)), "d" ( obj->Num+4 ) );
+
+ return 1;
+}
+
+/**
+ * \fn int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in)
+ * \brief Read a byte from an IO port
+ * \param Object Port Object
+ * \param Length Number of bytes to write
+ * \param in Pointer to of data to write
+ */
+int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in)
+{
+ tEdiPort *obj;
+ // Get Data Pointer
+ VALIDATE_PTR(Object, 0);
+ obj = GET_DATA(Object);
+ if( !obj->State ) return 0;
+ if( obj->State & 1 ) return -1; // Unintialised
+
+ __asm__ __volatile__ ( "rep outsb" : : "c" (Length), "D" (in), "d" ( obj->Num ) );
+
+ return 1;
+}
+
+// === CLASS DECLARATION ===
+/*static edi_variable_declaration_t *scEdi_Int_Variables_IO[] = {
+ {
+ {"pointer", "port_object", 0},
+ {"uint16_t", "port", 0}
+ },
+ {
+ {"pointer", "port_object", 0}
+ },
+ {
+ {"pointer", "port_object", 0},
+ {"pointer int8_t", "out", 0}
+ }
+};*/
+static edi_function_declaration_t scEdi_Int_Functions_IO[] = {
+ {"int32_t", "init_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[0],
+ (function_pointer)Edi_Int_IO_InitPort
+ },
+ {"uint16_t", "get_port_number", 1, 1, NULL, //scEdi_Int_Variables_IO[1],
+ (function_pointer)Edi_Int_IO_GetPortNum
+ },
+ {"int32_t", "read_byte_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[2],
+ (function_pointer)Edi_Int_IO_ReadByte
+ },
+ {"int32_t", "read_word_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"pointer int16_t", "out", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_ReadWord
+ },
+ {"int32_t", "read_long_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"pointer int32_t", "out", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_ReadDWord
+ },
+ {"int32_t", "read_longlong_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"pointer int64_t", "out", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_ReadQWord
+ },
+ {"int32_t", "read_string_io_port", 1, 3, NULL/*{
+ {"pointer", "port_object", 0},
+ {"int32_T", "data_length", 0},
+ {"pointer int64_t", "out", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_ReadString
+ },
+
+ {"int32_t", "write_byte_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"int8_t", "in", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_WriteByte},
+ {"int32_t", "write_word_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"int16_t", "in", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_WriteWord},
+ {"int32_t", "write_long_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"int32_t", "in", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_WriteDWord},
+ {"int32_t", "write_longlong_io_port", 1, 2, NULL/*{
+ {"pointer", "port_object", 0},
+ {"int64_t", "in", 0}
+ }*/,
+ (function_pointer)Edi_Int_IO_WriteQWord}
+ };
+static edi_class_declaration_t scEdi_Int_Class_IO =
+ {
+ IO_PORT_CLASS, 1, 12,
+ scEdi_Int_Functions_IO,
+ Edi_Int_IO_Construct,
+ Edi_Int_IO_Destruct,
+ NULL
+ };
--- /dev/null
+/*
+ * Acess2 EDI Layer
+ */
+#define DEBUG 0
+#define VERSION ((0<<8)|1)
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#define IMPLEMENTING_EDI 1
+#include "edi/edi.h"
+
+#define VALIDATE_PTR(_ptr,_err) do{if(!(_ptr))return _err; }while(0)
+#define GET_DATA(_ptr) (Object)
+
+#include "edi_io.inc.c"
+#include "edi_int.inc.c"
+
+// === STRUCTURES ===
+typedef struct sAcessEdiDriver {
+ struct sAcessEdiDriver *Next;
+ tDevFS_Driver Driver;
+ int FileCount;
+ struct {
+ char *Name;
+ tVFS_Node Node;
+ } *Files;
+ edi_object_metadata_t *Objects;
+ edi_initialization_t Init;
+ driver_finish_t Finish;
+} tAcessEdiDriver;
+
+// === PROTOTYPES ===
+ int EDI_Install(char **Arguments);
+ int EDI_DetectDriver(void *Base);
+ int EDI_LoadDriver(void *Base);
+vfs_node *EDI_FS_ReadDir(vfs_node *Node, int Pos);
+vfs_node *EDI_FS_FindDir(vfs_node *Node, char *Name);
+ int EDI_FS_CharRead(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+ int EDI_FS_CharWrite(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+ int EDI_FS_IOCtl(vfs_node *Node, int Id, void *Data);
+data_pointer EDI_GetInternalClass(edi_string_t ClassName);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, EDI, EDI_Install, NULL, NULL);
+tModuleLoader gEDI_Loader = {
+ NULL, "EDI", EDI_DetectDriver, EDI_LoadDriver, NULL
+};
+tSpinlock glEDI_Drivers;
+tAcessEdiDriver *gEdi_Drivers = NULL;
+edi_class_declaration_t *gcEdi_IntClasses[] = {
+ &scEdi_Int_Class_IO, &scEdi_Int_Class_IRQ
+};
+#define NUM_INT_CLASSES (sizeof(gcEdi_IntClasses)/sizeof(gcEdi_IntClasses[0]))
+char *csCharNumbers[] = {"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9"};
+char *csBlockNumbers[] = {"blk0", "blk1", "blk2", "blk3", "blk4", "blk5", "blk6", "blk7", "blk8", "blk9"};
+
+// === CODE ===
+/**
+ * \fn int EDI_Install(char **Arguments)
+ * \brief Stub intialisation routine
+ */
+int EDI_Install(char **Arguments)
+{
+ Module_RegisterLoader( &gEDI_Loader );
+ return 1;
+}
+
+/**
+ * \brief Detects if a driver should be loaded by the EDI subsystem
+ */
+int EDI_DetectDriver(void *Base)
+{
+ if( Binary_FindSymbol(BASE, "driver_init", NULL) == 0 )
+ return 0;
+
+ return 1;
+}
+
+/**
+ * \fn int Edi_LoadDriver(void *Base)
+ * \brief Load an EDI Driver from a loaded binary
+ * \param Base Binary Handle
+ * \return 0 on success, non zero on error
+ */
+int EDI_LoadDriver(void *Base)
+{
+ driver_init_t init;
+ driver_finish_t finish;
+ tAcessEdiDriver *info;
+ int i, j;
+ int devfsId;
+ edi_class_declaration_t *classes;
+
+ ENTER("pBase", Base);
+
+ // Get Functions
+ if( !Binary_FindSymbol(Base, "driver_init", (Uint*)&init)
+ || !Binary_FindSymbol(Base, "driver_finish", (Uint*)&finish) )
+ {
+ Warning("[EDI ] Driver %p does not provide both `driver_init` and `driver_finish`\n", Base);
+ Binary_Unload(Base);
+ return 0;
+ }
+
+ // Allocate Driver Information
+ info = malloc( sizeof(tAcessEdiDriver) );
+ info->Finish = finish;
+
+ // Initialise Driver
+ info->Init = init( 0, NULL ); // TODO: Implement Resources
+
+ LOG("info->Init.driver_name = '%s'", info->Init.driver_name);
+ LOG("info->Init.num_driver_classes = %i", info->Init.num_driver_classes);
+
+ // Count mappable classes
+ classes = info->Init.driver_classes;
+ info->FileCount = 0;
+ info->Objects = NULL;
+ for( i = 0, j = 0; i < info->Init.num_driver_classes; i++ )
+ {
+ if( strncmp(classes[i].name, "EDI-CHARACTER-DEVICE", 32) == 0 )
+ {
+ data_pointer *obj;
+ // Initialise Object Instances
+ for( ; (obj = classes[i].constructor()); j++ ) {
+ LOG("%i - Constructed '%s'", j, classes[i].name);
+ info->FileCount ++;
+ info->Objects = realloc(info->Objects, sizeof(*info->Objects)*info->FileCount);
+ info->Objects[j].object = obj;
+ info->Objects[j].object_class = &classes[i];
+ }
+ }
+ else
+ LOG("%i - %s", i, classes[i].name);
+ }
+
+ if(info->FileCount)
+ {
+ int iNumChar = 0;
+ // Create VFS Nodes
+ info->Files = malloc( info->FileCount * sizeof(*info->Files) );
+ memset(info->Files, 0, info->FileCount * sizeof(*info->Files));
+ j = 0;
+ for( j = 0; j < info->FileCount; j++ )
+ {
+ classes = info->Objects[j].object_class;
+ if( strncmp(classes->name, "EDI-CHARACTER-DEVICE", 32) == 0 )
+ {
+ LOG("%i - %s", j, csCharNumbers[iNumChar]);
+ info->Files[j].Name = csCharNumbers[iNumChar];
+ info->Files[j].Node.NumACLs = 1;
+ info->Files[j].Node.ACLs = &gVFS_ACL_EveryoneRW;
+ info->Files[j].Node.ImplPtr = &info->Objects[j];
+ info->Files[j].Node.Read = EDI_FS_CharRead;
+ info->Files[j].Node.Write = EDI_FS_CharWrite;
+ info->Files[j].Node.IOCtl = EDI_FS_IOCtl;
+ info->Files[j].Node.CTime =
+ info->Files[j].Node.MTime =
+ info->Files[j].Node.ATime = now();
+
+ iNumChar ++;
+ continue;
+ }
+ }
+
+ // Create DevFS Driver
+ info->Driver.ioctl = EDI_FS_IOCtl;
+ memsetda(&info->Driver.rootNode, 0, sizeof(vfs_node) / 4);
+ info->Driver.Name = info->Init.driver_name;
+ info->Driver.RootNode.Flags = VFS_FFLAG_DIRECTORY;
+ info->Driver.RootNode.NumACLs = 1;
+ info->Driver.RootNode.ACLs = &gVFS_ACL_EveryoneRX;
+ info->Driver.RootNode.Length = info->FileCount;
+ info->Driver.RootNode.ImplPtr = info;
+ info->Driver.RootNode.ReadDir = EDI_FS_ReadDir;
+ info->Driver.RootNode.FindDir = EDI_FS_FindDir;
+ info->Driver.RootNode.IOCtl = EDI_FS_IOCtl;
+
+ // Register
+ devfsId = dev_addDevice( &info->Driver );
+ if(devfsId == -1) {
+ free(info->Files); // Free Files
+ info->Finish(); // Clean up driver
+ free(info); // Free info structure
+ Binary_Unload(iDriverBase); // Unload library
+ return -3; // Return error
+ }
+ }
+
+ // Append to loaded list;
+ LOCK(&glEDI_Drivers);
+ info->Next = gEDI_Drivers;
+ gEDI_Drivers = info;
+ RELEASE(&glEDI_Drivers);
+
+ LogF("[EDI ] Loaded Driver '%s' (%s)\n", info->Init.driver_name, Path);
+ LEAVE('i', 1);
+ return 1;
+}
+
+// --- Filesystem Interaction ---
+/**
+ * \brief Read from a drivers class list
+ * \param Node Driver's Root Node
+ * \param Pos Index of file to get
+ */
+char *EDI_FS_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tAcessEdiDriver *info;
+
+ // Sanity Check
+ if(!Node) return NULL;
+
+ // Get Information Structure
+ info = (void *) Node->ImplPtr;
+ if(!info) return NULL;
+
+ // Check Position
+ if(Pos < 0) return NULL;
+ if(Pos >= info->FileCount) return NULL;
+
+ return strdup( info->Files[Pos].Name );
+}
+
+/**
+ * \fn tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name)
+ * \brief Find a named file in a driver
+ * \param Node Driver's Root Node
+ * \param Name Name of file to find
+ */
+tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name)
+{
+ tAcessEdiDriver *info;
+ int i;
+
+ // Sanity Check
+ if(!Node) return NULL;
+ if(!Name) return NULL;
+
+ // Get Information Structure
+ info = (void *) Node->ImplPtr;
+ if(!info) return NULL;
+
+ for( i = 0; i < info->FileCount; i++ )
+ {
+ if(strcmp(info->Files[i].name, Name) == 0)
+ return &info->Files[i].Node;
+ }
+
+ return NULL;
+}
+
+/**
+ * \fn Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from an EDI Character Device
+ * \param Node File Node
+ * \param Offset Offset into file (ignored)
+ * \param Length Number of characters to read
+ * \param Buffer Destination for data
+ * \return Number of characters read
+ */
+Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ edi_object_metadata_t *meta;
+ edi_class_declaration_t *class;
+
+ // Sanity Check
+ if(!Node || !Buffer) return 0;
+ if(Length <= 0) return 0;
+ // Get Object Metadata
+ meta = (void *) Node->ImplPtr;
+ if(!meta) return 0;
+
+ // Get Class
+ class = meta->object_class;
+ if(!class) return 0;
+
+ // Read from object
+ if( ((tAnyFunction) class->methods[0].code)( meta->object, Buffer, Length ))
+ return Length;
+
+ return 0;
+}
+
+/**
+ * \fn Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Write to an EDI Character Device
+ * \param Node File Node
+ * \param Offset Offset into file (ignored)
+ * \param Length Number of characters to write
+ * \param Buffer Source for data
+ * \return Number of characters written
+ */
+Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ edi_object_metadata_t *meta;
+ edi_class_declaration_t *class;
+
+ // Sanity Check
+ if(!Node || !Buffer) return 0;
+ if(Length <= 0) return 0;
+ // Get Object Metadata
+ meta = (void *) Node->ImplPtr;
+ if(!meta) return 0;
+
+ // Get Class
+ class = meta->object_class;
+ if(!class) return 0;
+
+ // Write to object
+ if( ((tAnyFunction) class->methods[1].code)( meta->object, Buffer, Length ))
+ return Length;
+
+ return 0;
+}
+
+/**
+ * \fn int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief Perfom an IOCtl call on the object
+ */
+int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data)
+{
+ return 0;
+}
+
+// --- EDI Functions ---
+/**
+ * \fn data_pointer EDI_GetDefinedClass(edi_string_t ClassName)
+ * \brief Gets the structure of a driver defined class
+ * \param ClassName Name of class to find
+ * \return Class definition or NULL
+ */
+data_pointer EDI_GetDefinedClass(edi_string_t ClassName)
+{
+ int i;
+ tAcessEdiDriver *drv;
+ edi_class_declaration_t *classes;
+
+ for(drv = gEdi_Drivers;
+ drv;
+ drv = drv->Next )
+ {
+ classes = drv->Init.driver_classes;
+ for( i = 0; i < drv->Init.num_driver_classes; i++ )
+ {
+ if( strncmp(classes[i].name, ClassName, 32) == 0 )
+ return &classes[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * \fn int32_t EDI_CheckClassExistence(edi_string_t ClassName)
+ * \brief Checks if a class exists
+ * \param ClassName Name of class
+ * \return 1 if the class exists, -1 otherwise
+ */
+int32_t EDI_CheckClassExistence(edi_string_t ClassName)
+{
+ //LogF("check_class_existence: (ClassName='%s')\n", ClassName);
+ if(EDI_GetInternalClass(ClassName))
+ return 1;
+
+ if(EDI_GetDefinedClass(ClassName)) // Driver Defined
+ return 1;
+
+ return -1;
+}
+
+/**
+ * \fn edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName)
+ * \brief Construct an instance of an class (an object)
+ * \param ClassName Name of the class to construct
+ */
+edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName)
+{
+ edi_object_metadata_t ret = {0, 0};
+ edi_class_declaration_t *class;
+
+ //LogF("EDI_ConstructObject: (ClassName='%s')\n", ClassName);
+
+ // Get class definition
+ if( !(class = EDI_GetInternalClass(ClassName)) ) // Internal
+ if( !(class = EDI_GetDefinedClass(ClassName)) ) // Driver Defined
+ return ret; // Return ERROR
+
+ // Initialise
+ ret.object = class->constructor();
+ if( !ret.object )
+ return ret; // Return ERROR
+
+ // Set declaration pointer
+ ret.object_class = class;
+
+ //LogF("EDI_ConstructObject: RETURN {0x%x,0x%x}\n", ret.object, ret.object_class);
+ return ret;
+}
+
+/**
+ * \fn void EDI_DestroyObject(edi_object_metadata_t Object)
+ * \brief Destroy an instance of a class
+ * \param Object Object to destroy
+ */
+void EDI_DestroyObject(edi_object_metadata_t Object)
+{
+ if( !Object.object ) return;
+ if( !Object.object_class ) return;
+
+ ((edi_class_declaration_t*)(Object.object_class))->destructor( &Object );
+}
+
+/**
+ * \fn function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName)
+ * \brief Get a method of a class by it's name
+ * \param ObjectClass Pointer to a ::edi_object_metadata_t of the object
+ * \param MethodName Name of the desired method
+ * \return Function address or NULL
+ */
+function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName)
+{
+ edi_class_declaration_t *dec = ObjectClass;
+ int i;
+
+ //LogF("get_method_by_name: (ObjectClass=0x%x, MethodName='%s')\n", ObjectClass, MethodName);
+
+ if(!ObjectClass) return NULL;
+
+ for(i = 0; i < dec->num_methods; i++)
+ {
+ if(strncmp(MethodName, dec->methods[i].name, 32) == 0)
+ return dec->methods[i].code;
+ }
+ return NULL;
+}
+
+#if 0
+function_pointer get_method_by_declaration(data_pointer Class, edi_function_declaration_t Declaration);
+#endif
+
+/**
+ * \fn edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName)
+ * \brief Get the parent of the named class
+ * \todo Implement
+ */
+edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName)
+{
+ WarningEx("EDI", "`get_class_parent` is unimplemented");
+ return NULL;
+}
+
+/**
+ * \fn data_pointer EDI_GetInternalClass(edi_string_t ClassName)
+ * \brief Get a builtin class
+ * \param ClassName Name of class to find
+ * \return Pointer to the ::edi_class_declaration_t of the class
+ */
+data_pointer EDI_GetInternalClass(edi_string_t ClassName)
+{
+ int i;
+ //LogF("get_internal_class: (ClassName='%s')\n", ClassName);
+ for( i = 0; i < NUM_INT_CLASSES; i++ )
+ {
+ if( strncmp( gcEdi_IntClasses[i]->name, ClassName, 32 ) == 0 ) {
+ return gcEdi_IntClasses[i];
+ }
+ }
+ //LogF("get_internal_class: RETURN NULL\n");
+ return NULL;
+}
+
+/**
+ * \fn edi_string_ptr_t EDI_GetObjectClass(data_pointer Object)
+ * \brief Get the name of the object of \a Object
+ * \param Object Object to get name of
+ * \return Pointer to the class name
+ */
+edi_string_ptr_t EDI_GetObjectClass(data_pointer ObjectClass)
+{
+ edi_object_metadata_t *Metadata = ObjectClass;
+ // Sanity Check
+ if(!ObjectClass) return NULL;
+ if(!(edi_class_declaration_t*) Metadata->object_class) return NULL;
+
+ // Return Class Name
+ return ((edi_class_declaration_t*) Metadata->object_class)->name;
+}
+
+// === EXPORTS ===
+EXPORTAS(EDI_CheckClassExistence, check_class_existence);
+EXPORTAS(EDI_ConstructObject, construct_object);
+EXPORTAS(EDI_DestroyObject, destroy_object);
+EXPORTAS(EDI_GetMethodByName, get_method_by_name);
+EXPORTAS(EDI_GetClassParent, get_class_parent);
+EXPORTAS(EDI_GetInternalClass, get_internal_class);
+EXPORTAS(EDI_GetObjectClass, get_object_class);
--- /dev/null
+-include ../../Makefile.tpl
--- /dev/null
+#
+#
+
+CPPFLAGS = -I./include
+OBJ = main.o logging.o strmem.o imc.o mem.o buf.o cb.o
+OBJ += meta_mgmt.o meta_gio.o
+OBJ += physio.o physio/meta_bus.o physio/meta_intr.o
+NAME = UDI
+
+-include ../Makefile.tpl
--- /dev/null
+
+<Love4Boobies> logical volume metalanguage - for file system drivers to use, network protocol
+ metalanguage and audio support
+<Love4Boobies> madeofstaples expressed his interest in reviewing the OpenUSBDI specification to update
+ it for USB 3.0
+
+
+=== Initialisation ===
+udi_init_t
+ > udi_init_info
+ > This is the only defined symbol in a UDI driver
+
+udi_primary_init_t
+- UDI_MAX_SCRATCH
+- UDI_OP_LONG_EXEC
+
+udi_secondary_init_t
+
+udi_ops_init_t
+
+udi_cb_init_t
+
+udi_cb_select_t
+
+udi_gcb_init_t
+
+udi_init_context_t
+
+udi_limits_t
+- UDI_MIN_ALLOC_LIMIT
+- UDI_MIN_TRACE_LOG_LIMIT
+- UDI_MIN_INSTANCE_ATTR_LIMIT
+
+udi_chan_context_t
+
+udi_child_chan_context_t
--- /dev/null
+/**
+ * \file buf.c
+ * \author John Hodge (thePowersGang)
+ *
+ * Buffer Manipulation
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === EXPORTS ===
+EXPORT(udi_buf_copy);
+EXPORT(udi_buf_write);
+EXPORT(udi_buf_read);
+EXPORT(udi_buf_free);
+
+// === CODE ===
+void udi_buf_copy(
+ udi_buf_copy_call_t *callback,
+ udi_cb_t *gcb,
+ udi_buf_t *src_buf,
+ udi_size_t src_off,
+ udi_size_t src_len,
+ udi_buf_t *dst_buf,
+ udi_size_t dst_off,
+ udi_size_t dst_len,
+ udi_buf_path_t path_handle
+ )
+{
+ UNIMPLEMENTED();
+}
+
+/**
+ * \brief Write to a buffer
+ * \param callback Function to call once the write has completed
+ * \param gcb Control Block
+ * \param src_mem Source Data
+ * \param src_len Length of source data
+ * \param dst_buf Destination buffer
+ * \param dst_off Destination offset in the buffer
+ * \param dst_len Length of destination area (What the?, Redundant
+ * Department of redundacny department)
+ * \param path_handle ???
+ */
+void udi_buf_write(
+ udi_buf_write_call_t *callback,
+ udi_cb_t *gcb,
+ const void *src_mem,
+ udi_size_t src_len,
+ udi_buf_t *dst_buf,
+ udi_size_t dst_off,
+ udi_size_t dst_len,
+ udi_buf_path_t path_handle
+ )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_buf_read(
+ udi_buf_t *src_buf,
+ udi_size_t src_off,
+ udi_size_t src_len,
+ void *dst_mem )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_buf_free(udi_buf_t *buf)
+{
+ UNIMPLEMENTED();
+}
--- /dev/null
+/**
+ * \file cb.c
+ * \author John Hodge (thePowersGang)
+ * \brief Control block code
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === CODE ===
+void udi_cb_alloc (
+ udi_cb_alloc_call_t *callback, //!< Function to be called when the CB is allocated
+ udi_cb_t *gcb, //!< Parent Control Block
+ udi_index_t cb_idx,
+ udi_channel_t default_channel
+ )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_cb_alloc_dynamic(
+ udi_cb_alloc_call_t *callback,
+ udi_cb_t *gcb,
+ udi_index_t cb_idx,
+ udi_channel_t default_channel,
+ udi_size_t inline_size,
+ udi_layout_t *inline_layout
+ )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_cb_alloc_batch(
+ udi_cb_alloc_batch_call_t *callback, //!<
+ udi_cb_t *gcb, //!<
+ udi_index_t cb_idx,
+ udi_index_t count,
+ udi_boolean_t with_buf,
+ udi_size_t buf_size,
+ udi_buf_path_t path_handle
+ )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_cb_free(udi_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb)
+{
+ UNIMPLEMENTED();
+}
+
+// === EXPORTS ===
+EXPORT(udi_cb_alloc);
+EXPORT(udi_cb_alloc_dynamic);
+EXPORT(udi_cb_alloc_batch);
+EXPORT(udi_cb_free);
+EXPORT(udi_cancel);
--- /dev/null
+/**
+ * \file imc.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === EXPORTS ===
+EXPORT(udi_channel_anchor);
+EXPORT(udi_channel_spawn);
+EXPORT(udi_channel_set_context);
+EXPORT(udi_channel_op_abort);
+EXPORT(udi_channel_close);
+EXPORT(udi_channel_event_ind);
+EXPORT(udi_channel_event_complete);
+
+// === CODE ===
+/**
+ */
+void udi_channel_anchor(
+ udi_channel_anchor_call_t *callback, udi_cb_t *gcb,
+ udi_channel_t channel, udi_index_t ops_idx, void *channel_context
+ )
+{
+ Warning("%s Unimplemented", __func__);
+}
+
+/**
+ */
+extern void udi_channel_spawn(
+ udi_channel_spawn_call_t *callback, udi_cb_t *gcb,
+ udi_channel_t channel, udi_index_t spawn_idx,
+ udi_index_t ops_idx, void *channel_context
+ )
+{
+ Warning("%s Unimplemented", __func__);
+}
+
+/**
+ *
+ */
+void udi_channel_set_context(
+ udi_channel_t target_channel, void *channel_context
+ )
+{
+ Warning("%s Unimplemented", __func__);
+}
+
+void udi_channel_op_abort(
+ udi_channel_t target_channel, udi_cb_t *orig_cb
+ )
+{
+ Warning("%s Unimplemented", __func__);
+}
+
+void udi_channel_close(udi_channel_t channel)
+{
+ Warning("%s Unimplemented", __func__);
+}
+
+void udi_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+ udi_channel_event_complete(cb, UDI_OK);
+}
+
+void udi_channel_event_complete(udi_channel_event_cb_t *cb, udi_status_t status)
+{
+ Warning("%s Unimplemented", __func__);
+}
--- /dev/null
+/**
+ * \file physio/meta_bus.h
+ */
+#ifndef _PHYSIO_META_BUS_H_
+#define _PHYSIO_META_BUS_H_
+
+#include <udi.h>
+#include <udi_physio.h>
+
+typedef const struct udi_bus_device_ops_s udi_bus_device_ops_t;
+typedef const struct udi_bus_bridge_ops_s udi_bus_bridge_ops_t;
+typedef struct udi_bus_bind_cb_s udi_bus_bind_cb_t;
+typedef void udi_bus_unbind_req_op_t(udi_bus_bind_cb_t *cb);
+typedef void udi_bus_unbind_ack_op_t(udi_bus_bind_cb_t *cb);
+typedef void udi_bus_bind_req_op_t(udi_bus_bind_cb_t *cb);
+typedef void udi_bus_bind_ack_op_t(
+ udi_bus_bind_cb_t *cb,
+ udi_dma_constraints_t dma_constraints,
+ udi_ubit8_t preferred_endianness,
+ udi_status_t status
+ );
+
+
+struct udi_bus_device_ops_s
+{
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_bus_bind_ack_op_t *bus_bind_ack_op;
+ udi_bus_unbind_ack_op_t *bus_unbind_ack_op;
+ udi_intr_attach_ack_op_t *intr_attach_ack_op;
+ udi_intr_detach_ack_op_t *intr_detach_ack_op;
+};
+/* Bus Device Ops Vector Number */
+#define UDI_BUS_DEVICE_OPS_NUM 1
+
+struct udi_bus_bridge_ops_s
+{
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_bus_bind_req_op_t *bus_bind_req_op;
+ udi_bus_unbind_req_op_t *bus_unbind_req_op;
+ udi_intr_attach_req_op_t *intr_attach_req_op;
+ udi_intr_detach_req_op_t *intr_detach_req_op;
+};
+/* Bus Bridge Ops Vector Number */
+#define UDI_BUS_BRIDGE_OPS_NUM
+
+struct udi_bus_bind_cb_s
+{
+ udi_cb_t gcb;
+};
+/* Bus Bind Control Block Group Number */
+#define UDI_BUS_BIND_CB_NUM 1
+
+
+extern void udi_bus_bind_req(udi_bus_bind_cb_t *cb);
+
+extern void udi_bus_bind_ack(
+ udi_bus_bind_cb_t *cb,
+ udi_dma_constraints_t dma_constraints,
+ udi_ubit8_t preferred_endianness,
+ udi_status_t status
+ );
+/* Values for preferred_endianness */
+#define UDI_DMA_BIG_ENDIAN (1U<<5)
+#define UDI_DMA_LITTLE_ENDIAN (1U<<6)
+#define UDI_DMA_ANY_ENDIAN (1U<<0)
+
+extern void udi_bus_unbind_req(udi_bus_bind_cb_t *cb);
+extern void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb);
+
+
+
+
+
+#endif
--- /dev/null
+/**
+ * \file physio/meta_intr.h
+ */
+#ifndef _PHYSIO_META_INTR_H_
+#define _PHYSIO_META_INTR_H_
+
+#include <udi.h>
+#include <udi_physio.h>
+#include "pio.h"
+
+typedef struct udi_intr_attach_cb_s udi_intr_attach_cb_t;
+typedef void udi_intr_attach_req_op_t(udi_intr_attach_cb_t *intr_attach_cb);
+typedef void udi_intr_attach_ack_op_t(
+ udi_intr_attach_cb_t *intr_attach_cb,
+ udi_status_t status
+ );
+typedef struct udi_intr_detach_cb_s udi_intr_detach_cb_t;
+typedef void udi_intr_detach_req_op_t(udi_intr_detach_cb_t *intr_detach_cb);
+typedef void udi_intr_detach_ack_op_t(udi_intr_detach_cb_t *intr_detach_cb);
+typedef const struct udi_intr_handler_ops_s udi_intr_handler_ops_t;
+typedef const struct udi_intr_dispatcher_ops_s udi_intr_dispatcher_ops_t;
+typedef struct udi_intr_event_cb_s udi_intr_event_cb_t;
+typedef void udi_intr_event_ind_op_t(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags);
+typedef void udi_intr_event_rdy_op_t(udi_intr_event_cb_t *intr_event_cb);
+
+
+struct udi_intr_attach_cb_s
+{
+ udi_cb_t gcb;
+ udi_index_t interrupt_idx;
+ udi_ubit8_t min_event_pend;
+ udi_pio_handle_t preprocessing_handle;
+};
+/* Bridge Attach Control Block Group Number */
+#define UDI_BUS_INTR_ATTACH_CB_NUM 2
+
+struct udi_intr_detach_cb_s
+{
+ udi_cb_t gcb;
+ udi_index_t interrupt_idx;
+};
+/* Bridge Detach Control Block Group Number */
+#define UDI_BUS_INTR_DETACH_CB_NUM 3
+
+struct udi_intr_handler_ops_s
+{
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_intr_event_ind_op_t *intr_event_ind_op;
+};
+/* Interrupt Handler Ops Vector Number */
+#define UDI_BUS_INTR_HANDLER_OPS_NUM 3
+
+struct udi_intr_dispatcher_ops_s
+{
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_intr_event_rdy_op_t *intr_event_rdy_op;
+};
+/* Interrupt Dispatcher Ops Vector Number */
+#define UDI_BUS_INTR_DISPATCH_OPS_NUM 4
+
+struct udi_intr_event_cb_s
+{
+ udi_cb_t gcb;
+ udi_buf_t *event_buf;
+ udi_ubit16_t intr_result;
+};
+/* Flag values for interrupt handling */
+#define UDI_INTR_UNCLAIMED (1U<<0)
+#define UDI_INTR_NO_EVENT (1U<<1)
+/* Bus Interrupt Event Control Block Group Number */
+#define UDI_BUS_INTR_EVENT_CB_NUM 4
+
+
+
+extern void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb);
+extern void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status);
+extern void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status);
+
+extern void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb);
+extern void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb);
+extern void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb);
+
+
+extern void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags);
+/**
+ * \brief Values for ::udi_intr_event_ind \a flags
+ * \{
+ */
+#define UDI_INTR_MASKING_NOT_REQUIRED (1U<<0)
+#define UDI_INTR_OVERRUN_OCCURRED (1U<<1)
+#define UDI_INTR_PREPROCESSED (1U<<2)
+/**
+ * \}
+ */
+
+extern void udi_intr_event_rdy(udi_intr_event_cb_t *intr_event_cb);
+
+
+
+#endif
--- /dev/null
+/**
+ * \file physio/pio.h
+ */
+#ifndef _PHYSIO_PIO_H_
+#define _PHYSIO_PIO_H_
+
+#include <udi.h>
+#include <udi_physio.h>
+
+
+typedef _udi_handle_t udi_pio_handle_t;
+/* Null handle value for udi_pio_handle_t */
+#define UDI_NULL_PIO_HANDLE _NULL_HANDLE
+
+#endif
--- /dev/null
+/**
+ * \file udi.h
+ */
+#ifndef _UDI_H_
+#define _UDI_H_
+
+// Use the core acess file to use the specific size types (plus va_arg)
+#include <acess.h>
+
+#include "udi/arch/x86.h"
+
+/**
+ * \name Values and Flags for udi_status_t
+ * \{
+ */
+#define UDI_STATUS_CODE_MASK 0x0000FFFF
+#define UDI_STAT_META_SPECIFIC 0x00008000
+#define UDI_SPECIFIC_STATUS_MASK 0x00007FFF
+#define UDI_CORRELATE_OFFSET 16
+#define UDI_CORRELATE_MASK 0xFFFF0000
+/* Common Status Values */
+#define UDI_OK 0
+#define UDI_STAT_NOT_SUPPORTED 1
+#define UDI_STAT_NOT_UNDERSTOOD 2
+#define UDI_STAT_INVALID_STATE 3
+#define UDI_STAT_MISTAKEN_IDENTITY 4
+#define UDI_STAT_ABORTED 5
+#define UDI_STAT_TIMEOUT 6
+#define UDI_STAT_BUSY 7
+#define UDI_STAT_RESOURCE_UNAVAIL 8
+#define UDI_STAT_HW_PROBLEM 9
+#define UDI_STAT_NOT_RESPONDING 10
+#define UDI_STAT_DATA_UNDERRUN 11
+#define UDI_STAT_DATA_OVERRUN 12
+#define UDI_STAT_DATA_ERROR 13
+#define UDI_STAT_PARENT_DRV_ERROR 14
+#define UDI_STAT_CANNOT_BIND 15
+#define UDI_STAT_CANNOT_BIND_EXCL 16
+#define UDI_STAT_TOO_MANY_PARENTS 17
+#define UDI_STAT_BAD_PARENT_TYPE 18
+#define UDI_STAT_TERMINATED 19
+#define UDI_STAT_ATTR_MISMATCH 20
+/**
+ * \}
+ */
+
+/**
+ * \name Data Layout Specifiers
+ * \{
+ */
+typedef const udi_ubit8_t udi_layout_t;
+/* Specific-Length Layout Type Codes */
+#define UDI_DL_UBIT8_T 1
+#define UDI_DL_SBIT8_T 2
+#define UDI_DL_UBIT16_T 3
+#define UDI_DL_SBIT16_T 4
+#define UDI_DL_UBIT32_T 5
+#define UDI_DL_SBIT32_T 6
+#define UDI_DL_BOOLEAN_T 7
+#define UDI_DL_STATUS_T 8
+/* Abstract Element Layout Type Codes */
+#define UDI_DL_INDEX_T 20
+/* Opaque Handle Element Layout Type Codes */
+#define UDI_DL_CHANNEL_T 30
+#define UDI_DL_ORIGIN_T 32
+/* Indirect Element Layout Type Codes */
+#define UDI_DL_BUF 40
+#define UDI_DL_CB 41
+#define UDI_DL_INLINE_UNTYPED 42
+#define UDI_DL_INLINE_DRIVER_TYPED 43
+#define UDI_DL_MOVABLE_UNTYPED 44
+/* Nested Element Layout Type Codes */
+#define UDI_DL_INLINE_TYPED 50
+#define UDI_DL_MOVABLE_TYPED 51
+#define UDI_DL_ARRAY 52
+#define UDI_DL_END 0
+/**
+ * \}
+ */
+
+
+// === INCLUDE SUB-SECTIONS ===
+#include "udi/cb.h" // Control Blocks
+#include "udi/log.h" // Logging
+#include "udi/attr.h" // Attributes
+#include "udi/strmem.h" // String/Memory
+#include "udi/buf.h" // Buffers
+#include "udi/mem.h" // Memory Management
+#include "udi/imc.h" // Inter-module Communication
+#include "udi/meta_mgmt.h" // Management Metalanguage
+#include "udi/meta_gio.h" // General IO Metalanguage
+#include "udi/init.h" // Init
+
+#endif
--- /dev/null
+
+#ifndef _UDI_ARCH_x86_H_
+#define _UDI_ARCH_x86_H_
+
+typedef Sint8 udi_sbit8_t; /* signed 8-bit: -2^7..2^7-1 */
+typedef Sint16 udi_sbit16_t; /* signed 16-bit: -2^15..2^15-1 */
+typedef Sint32 udi_sbit32_t; /* signed 32-bit: -2^31..2^31-1 */
+typedef Uint8 udi_ubit8_t; /* unsigned 8-bit: 0..28-1 */
+typedef Uint16 udi_ubit16_t; /* unsigned 16-bit: 0..216-1 */
+typedef Uint32 udi_ubit32_t; /* unsigned 32-bit: 0..232-1 */
+
+typedef udi_ubit8_t udi_boolean_t; /* 0=False; 1..28-1=True */
+#define FALSE 0
+#define TRUE 1
+
+typedef size_t udi_size_t; /* buffer size */
+typedef size_t udi_index_t; /* zero-based index type */
+
+typedef void *_udi_handle_t;
+#define _NULL_HANDLE NULL
+
+/* Channel Handle */
+typedef _udi_handle_t *udi_channel_t;
+#define UDI_NULL_CHANNEL _NULL_HANDLE
+
+/**
+ * \brief Buffer Path
+ */
+typedef _udi_handle_t udi_buf_path_t;
+#define UDI_NULL_BUF_PATH _NULL_HANDLE
+
+typedef _udi_handle_t udi_origin_t;
+#define UDI_NULL_ORIGIN _NULL_HANDLE
+
+typedef Sint64 udi_timestamp_t;
+
+#define UDI_HANDLE_IS_NULL(handle, handle_type) (handle == NULL)
+#define UDI_HANDLE_ID(handle, handle_type) ((Uint32)handle)
+
+/**
+ * \name va_arg wrapper
+ * \{
+ */
+#define UDI_VA_ARG(pvar, type, va_code) va_arg(pvar,type)
+#define UDI_VA_UBIT8_T
+#define UDI_VA_SBIT8_T
+#define UDI_VA_UBIT16_T
+#define UDI_VA_SBIT16_T
+#define UDI_VA_UBIT32_T
+#define UDI_VA_SBIT32_T
+#define UDI_VA_BOOLEAN_T
+#define UDI_VA_INDEX_T
+#define UDI_VA_SIZE_T
+#define UDI_VA_STATUS_T
+#define UDI_VA_CHANNEL_T
+#define UDI_VA_ORIGIN_T
+#define UDI_VA_POINTER
+/**
+ * \}
+ */
+
+/**
+ * \brief Status Type
+ */
+typedef udi_ubit32_t udi_status_t;
+
+#endif
--- /dev/null
+/**
+ * \file udi_attr.h
+ */
+#ifndef _UDI_ATTR_H_
+#define _UDI_ATTR_H_
+
+typedef struct udi_instance_attr_list_s udi_instance_attr_list_t;
+typedef udi_ubit8_t udi_instance_attr_type_t;
+
+/* Instance attribute limits */
+#define UDI_MAX_ATTR_NAMELEN 32
+#define UDI_MAX_ATTR_SIZE 64
+
+/**
+ * \brief Instance Attribute
+ */
+struct udi_instance_attr_list_s
+{
+ char attr_name[UDI_MAX_ATTR_NAMELEN];
+ udi_ubit8_t attr_value[UDI_MAX_ATTR_SIZE];
+ udi_ubit8_t attr_length;
+ udi_instance_attr_type_t attr_type;
+};
+
+
+/**
+ * \brief Instance Attribute Types
+ * \see ::udi_instance_attr_type_t
+ */
+enum
+{
+ UDI_ATTR_NONE,
+ UDI_ATTR_STRING,
+ UDI_ATTR_ARRAY8,
+ UDI_ATTR_UBIT32,
+ UDI_ATTR_BOOLEAN,
+ UDI_ATTR_FILE
+};
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_buf.h
+ */
+#ifndef _UDI_BUF_H_
+#define _UDI_BUF_H_
+
+
+typedef struct udi_buf_s udi_buf_t;
+typedef struct udi_xfer_constraints_s udi_xfer_constraints_t;
+typedef void udi_buf_copy_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf);
+typedef void udi_buf_write_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf);
+
+/**
+ * \brief Describes a buffer
+ * \note Semi-Opaque
+ */
+struct udi_buf_s
+{
+ udi_size_t buf_size;
+ Uint8 Data[]; //!< ENVIRONMENT ONLY
+};
+
+/**
+ * \brief
+ */
+struct udi_xfer_constraints_s
+{
+ udi_ubit32_t udi_xfer_max;
+ udi_ubit32_t udi_xfer_typical;
+ udi_ubit32_t udi_xfer_granularity;
+ udi_boolean_t udi_xfer_one_piece;
+ udi_boolean_t udi_xfer_exact_size;
+ udi_boolean_t udi_xfer_no_reorder;
+};
+
+// --- MACROS ---
+/**
+ * \brief Allocates a buffer
+ */
+#define UDI_BUF_ALLOC(callback, gcb, init_data, size, path_handle) \
+ udi_buf_write(callback, gcb, init_data, size, NULL, 0, 0, path_handle)
+
+/**
+ * \brief Inserts data into a buffer
+ */
+#define UDI_BUF_INSERT(callback, gcb, new_data, size, dst_buf, dst_off) \
+ udi_buf_write(callback, gcb, new_data, size, dst_buf, dst_off, 0, UDI_NULL_BUF_PATH)
+
+/**
+ * \brief Removes data from a buffer (data afterwards will be moved forewards)
+ */
+#define UDI_BUF_DELETE(callback, gcb, size, dst_buf, dst_off) \
+ udi_buf_write(callback, gcb, NULL, 0, dst_buf, dst_off, size, UDI_NULL_BUF_PATH)
+
+/**
+ * \brief Duplicates \a src_buf
+ */
+#define UDI_BUF_DUP(callback, gcb, src_buf, path_handle) \
+ udi_buf_copy(callback, gcb, src_buf, 0, (src_buf)->buf_size, NULL, 0, 0, path_handle)
+
+
+/**
+ * \brief Copies data from one buffer to another
+ */
+extern void udi_buf_copy(
+ udi_buf_copy_call_t *callback,
+ udi_cb_t *gcb,
+ udi_buf_t *src_buf,
+ udi_size_t src_off,
+ udi_size_t src_len,
+ udi_buf_t *dst_buf,
+ udi_size_t dst_off,
+ udi_size_t dst_len,
+ udi_buf_path_t path_handle );
+
+/**
+ * \brief Copies data from driver space to a buffer
+ */
+extern void udi_buf_write(
+ udi_buf_write_call_t *callback,
+ udi_cb_t *gcb,
+ const void *src_mem,
+ udi_size_t src_len,
+ udi_buf_t *dst_buf,
+ udi_size_t dst_off,
+ udi_size_t dst_len,
+ udi_buf_path_t path_handle
+ );
+
+/**
+ * \brief Reads data from a buffer into driver space
+ */
+extern void udi_buf_read(
+ udi_buf_t *src_buf,
+ udi_size_t src_off,
+ udi_size_t src_len,
+ void *dst_mem );
+
+/**
+ * \brief Frees a buffer
+ */
+extern void udi_buf_free(udi_buf_t *buf);
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_cb.h
+ */
+#ifndef _UDI_CB_H_
+#define _UDI_CB_H_
+
+typedef struct udi_cb_s udi_cb_t;
+typedef void udi_cb_alloc_call_t(udi_cb_t *gcb, udi_cb_t *new_cb);
+typedef void udi_cb_alloc_batch_call_t(udi_cb_t *gcb, udi_cb_t *first_new_cb);
+typedef void udi_cancel_call_t(udi_cb_t *gcb);
+
+#define UDI_GCB(mcb) (&(mcb)->gcb)
+#define UDI_MCB(gcb, cb_type) ((cb_type *)(gcb))
+
+/**
+ * \brief Describes a generic control block
+ * \note Semi-opaque
+ */
+struct udi_cb_s
+{
+ /**
+ * \brief Channel associated with the control block
+ */
+ udi_channel_t channel;
+ /**
+ * \brief Current state
+ * \note Driver changable
+ */
+ void *context;
+ /**
+ * \brief CB's scratch area
+ */
+ void *scratch;
+ /**
+ * \brief ???
+ */
+ void *initiator_context;
+ /**
+ * \brief Request Handle?
+ */
+ udi_origin_t origin;
+};
+
+extern void udi_cb_alloc (
+ udi_cb_alloc_call_t *callback,
+ udi_cb_t *gcb,
+ udi_index_t cb_idx,
+ udi_channel_t default_channel
+ );
+
+extern void udi_cb_alloc_dynamic(
+ udi_cb_alloc_call_t *callback,
+ udi_cb_t *gcb,
+ udi_index_t cb_idx,
+ udi_channel_t default_channel,
+ udi_size_t inline_size,
+ udi_layout_t *inline_layout
+ );
+
+extern void udi_cb_alloc_batch(
+ udi_cb_alloc_batch_call_t *callback,
+ udi_cb_t *gcb,
+ udi_index_t cb_idx,
+ udi_index_t count,
+ udi_boolean_t with_buf,
+ udi_size_t buf_size,
+ udi_buf_path_t path_handle
+ );
+
+extern void udi_cb_free(udi_cb_t *cb);
+
+extern void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb);
+
+
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_imc.h
+ * \brief Inter-Module Communication
+ */
+#ifndef _UDI_IMC_H_
+#define _UDI_IMC_H_
+
+typedef void udi_channel_anchor_call_t(udi_cb_t *gcb, udi_channel_t anchored_channel);
+typedef void udi_channel_spawn_call_t(udi_cb_t *gcb, udi_channel_t new_channel);
+
+typedef struct udi_channel_event_cb_s udi_channel_event_cb_t;
+
+typedef void udi_channel_event_ind_op_t(udi_channel_event_cb_t *cb);
+
+/**
+ * \brief Anchors a channel end to the current region
+ */
+extern void udi_channel_anchor(
+ udi_channel_anchor_call_t *callback, udi_cb_t *gcb,
+ udi_channel_t channel, udi_index_t ops_idx, void *channel_context
+ );
+
+/**
+ * \brief Created a new channel between two regions
+ */
+extern void udi_channel_spawn(
+ udi_channel_spawn_call_t *callback,
+ udi_cb_t *gcb,
+ udi_channel_t channel,
+ udi_index_t spawn_idx,
+ udi_index_t ops_idx,
+ void *channel_context
+ );
+
+/**
+ * \brief Attaches a new context pointer to the current channel
+ */
+extern void udi_channel_set_context(
+ udi_channel_t target_channel,
+ void *channel_context
+ );
+/**
+ * \brief
+ */
+extern void udi_channel_op_abort(
+ udi_channel_t target_channel,
+ udi_cb_t *orig_cb
+ );
+
+/**
+ * \brief Closes an open channel
+ */
+extern void udi_channel_close(udi_channel_t channel);
+
+/**
+ * \brief Describes a channel event
+ */
+struct udi_channel_event_cb_s
+{
+ udi_cb_t gcb;
+ udi_ubit8_t event;
+ union {
+ struct {
+ udi_cb_t *bind_cb;
+ } internal_bound;
+ struct {
+ udi_cb_t *bind_cb;
+ udi_ubit8_t parent_ID;
+ udi_buf_path_t *path_handles;
+ } parent_bound;
+ udi_cb_t *orig_cb;
+ } params;
+};
+/* Channel event types */
+#define UDI_CHANNEL_CLOSED 0
+#define UDI_CHANNEL_BOUND 1
+#define UDI_CHANNEL_OP_ABORTED 2
+
+/**
+ * \brief Proxy function
+ */
+extern void udi_channel_event_ind(udi_channel_event_cb_t *cb);
+
+/**
+ * \brief Called when channel event is completed
+ */
+extern void udi_channel_event_complete(
+ udi_channel_event_cb_t *cb, udi_status_t status
+ );
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_init.h
+ */
+#ifndef _UDI_INIT_H_
+#define _UDI_INIT_H_
+
+typedef struct udi_init_s udi_init_t;
+typedef struct udi_primary_init_s udi_primary_init_t;
+typedef struct udi_secondary_init_s udi_secondary_init_t;
+typedef struct udi_ops_init_s udi_ops_init_t;
+typedef struct udi_cb_init_s udi_cb_init_t;
+typedef struct udi_cb_select_s udi_cb_select_t;
+typedef struct udi_gcb_init_s udi_gcb_init_t;
+
+typedef struct udi_init_context_s udi_init_context_t;
+typedef struct udi_limits_s udi_limits_t;
+typedef struct udi_chan_context_s udi_chan_context_t;
+typedef struct udi_child_chan_context_s udi_child_chan_context_t;
+
+typedef void udi_op_t(void);
+typedef udi_op_t * const udi_ops_vector_t;
+
+/**
+ * \brief UDI Initialisation Structure
+ *
+ * Defines how to initialise and use a UDI driver
+ */
+struct udi_init_s
+{
+ /**
+ * \brief Defines the primary region
+ * \note For secondary modules this must be NULL
+ */
+ udi_primary_init_t *primary_init_info;
+
+ /**
+ * \brief Defines all secondary regions
+ * Pointer to a list (so, essentially an array) of ::udi_secondary_init_t
+ * It is terminated by an entry with ::udi_secondary_init_t.region_idx
+ * set to zero.
+ * \note If NULL, it is to be treated as an empty list
+ */
+ udi_secondary_init_t *secondary_init_list;
+
+ /**
+ * \brief Channel operations
+ * Pointer to a ::udi_ops_init_t.ops_idx == 0 terminated list that
+ * defines the channel opterations usage for each ops vector implemented
+ * in this module.
+ * \note Must contain at least one entry for each metalanguage used
+ */
+ udi_ops_init_t *ops_init_list;
+
+ /**
+ * \brief Control Blocks
+ */
+ udi_cb_init_t *cb_init_list;
+
+ /**
+ * \brief Generic Control Blocks
+ */
+ udi_gcb_init_t *gcb_init_list;
+
+ /**
+ * \brief Overrides for control blocks
+ * Allows a control block to override the ammount of scratch space it
+ * gets for a specific ops vector.
+ */
+ udi_cb_select_t *cb_select_list;
+};
+
+
+/**
+ * \name Flags for ::udi_primary_init_t.mgmt_op_flags
+ * \{
+ */
+
+/**
+ * \brief Tells the environment that this operation may take some time
+ * Used as a hint in scheduling tasks
+ */
+#define UDI_OP_LONG_EXEC 0x01
+
+/**
+ * \}
+ */
+
+/**
+ * \brief Describes the Primary Region
+ * Tells the environment how to set up the driver's primary region.
+ */
+struct udi_primary_init_s
+{
+ /**
+ * \brief Management Ops Vector
+ * Pointer to a list of functions for the Management Metalanguage
+ */
+ udi_mgmt_ops_t *mgmt_ops;
+
+ /**
+ * \brief Flags for \a mgmt_ops
+ * Each entry in \a mgmt_ops is acommanied by an entry in this array.
+ * Each entry contains the flags that apply to the specified ops vector.
+ * \see UDI_OP_LONG_EXEC
+ */
+ const udi_ubit8_t *mgmt_op_flags;
+
+ /**
+ * \brief Scratch space size
+ * Specifies the number of bytes to allocate for each control block
+ * passed by the environment.
+ * \note must not exceed ::UDI_MAX_SCRATCH
+ */
+ udi_size_t mgmt_scratch_requirement;
+
+ /**
+ * \todo What is this?
+ */
+ udi_ubit8_t enumeration_attr_list_length;
+
+ /**
+ * \brief Size in bytes to allocate to each instance of the primary
+ * region
+ * Essentially the size of the driver's instance state
+ * \note Must be at least sizeof(udi_init_context_t) and not more
+ * than UDI_MIN_ALLOC_LIMIT
+ */
+ udi_size_t rdata_size;
+
+ /**
+ * \brief Size in bytes to allocate for each call to ::udi_enumerate_req
+ * \note Must not exceed UDI_MIN_ALLOC_LIMIT
+ */
+ udi_size_t child_data_size;
+
+ /**
+ * \brief Number of path handles for each parent bound to this driver
+ * \todo What the hell are path handles?
+ */
+ udi_ubit8_t per_parent_paths;
+};
+
+/**
+ * \brief Tells the environment how to create a secondary region
+ */
+struct udi_secondary_init_s
+{
+ /**
+ * \brief Region Index
+ * Non-zero driver-dependent index value that identifies the region
+ * \note This corresponds to a "region" declaration in the udiprops.txt
+ * file.
+ */
+ udi_index_t region_idx;
+ /**
+ * \brief Number of bytes to allocate
+ *
+ * \note Again, must be between sizeof(udi_init_context_t) and
+ * UDI_MIN_ALLOC_LIMIT
+ */
+ udi_size_t rdata_size;
+};
+
+/**
+ * \brief Defines channel endpoints (ways of communicating with the driver)
+ *
+ */
+struct udi_ops_init_s
+{
+ /**
+ * \brief ops index number
+ * Used to uniquely this entry
+ * \note If this is zero, it marks the end of the list
+ */
+ udi_index_t ops_idx;
+ /**
+ * \brief Metalanguage Index
+ * Defines what metalanguage is used
+ */
+ udi_index_t meta_idx;
+ /**
+ * \brief Metalanguage Operation
+ * Defines what metalanguage operation is used
+ */
+ udi_index_t meta_ops_num;
+ /**
+ * \brief Size of the context area
+ * \note If non-zero, must be at least
+ */
+ udi_size_t chan_context_size;
+ /**
+ * \brief Pointer to the operations
+ * Pointer to a <<meta>>_<<role>>_ops_t structure
+ */
+ udi_ops_vector_t *ops_vector;
+ /**
+ * \brief Flags for each entry in \a ops_vector
+ */
+ //const udi_ubit8_t *op_flags;
+};
+
+/**
+ * \brief Defines control blocks
+ * Much the same as ::udi_ops_init_t
+ */
+struct udi_cb_init_s
+{
+ udi_index_t cb_idx;
+ udi_index_t meta_idx;
+ udi_index_t meta_cb_num;
+ udi_size_t scratch_requirement;
+ /**
+ * \brief Size of inline memory
+ */
+ udi_size_t inline_size;
+ /**
+ * \brief Layout of inline memory
+ */
+ udi_layout_t *inline_layout;
+};
+
+/**
+ * \brief Overrides the scratch size for an operation
+ */
+struct udi_cb_select_s
+{
+ udi_index_t ops_idx;
+ udi_index_t cb_idx;
+};
+
+/**
+ * \brief General Control Blocks
+ * These control blocks can only be used as general data storage, not
+ * for any channel operations.
+ */
+struct udi_gcb_init_s
+{
+ udi_index_t cb_idx;
+ udi_size_t scratch_requirement;
+};
+
+
+// ===
+// ===
+/**
+ * \brief Environement Imposed Limits
+ */
+struct udi_limits_s
+{
+ /**
+ * \brief Maximum legal ammount of memory that can be allocated
+ */
+ udi_size_t max_legal_alloc;
+
+ /**
+ * \brief Maximum ammount of guaranteed memory
+ */
+ udi_size_t max_safe_alloc;
+ /**
+ * \brief Maximum size of the final string from ::udi_trace_write
+ * or ::udi_log_write
+ */
+ udi_size_t max_trace_log_formatted_len;
+ /**
+ * \brief Maximum legal size of an instanct attribute value
+ */
+ udi_size_t max_instance_attr_len;
+ /**
+ * \brief Minumum time difference (in nanoseconds between unique values
+ * returned by ::udi_time_current
+ */
+ udi_ubit32_t min_curtime_res;
+ /**
+ * \brief Minimum resolution of timers
+ * \see ::udi_timer_start_repeating, ::udi_timer_start
+ */
+ udi_ubit32_t min_timer_res;
+} PACKED;
+
+/**
+ * \brief Primary Region Context data
+ */
+struct udi_init_context_s
+{
+ udi_index_t region_idx;
+ udi_limits_t limits;
+};
+
+/**
+ * \brief Channel context data
+ */
+struct udi_chan_context_s
+{
+ /**
+ * \brief Pointer to the driver instance's initial region data
+ */
+ void *rdata;
+} PACKED;
+
+/**
+ * \brief Child Channel context
+ */
+struct udi_child_chan_context_s
+{
+ /**
+ * \brief Pointer to the driver instance's initial region data
+ */
+ void *rdata;
+ /**
+ * \brief Some sort of unique ID number
+ */
+ udi_ubit32_t child_ID;
+};
+
+#endif
--- /dev/null
+/**
+ * \file udi_log.h
+ */
+#ifndef _UDI_LOG_H_
+#define _UDI_LOG_H_
+
+/**
+ * \brief Trace Event
+ */
+typedef udi_ubit32_t udi_trevent_t;
+
+/**
+ * \brief Log Callback
+ */
+typedef void udi_log_write_call_t(udi_cb_t *gcb, udi_status_t correlated_status);
+
+/**
+ * \name Log Severities
+ * \brief Values for severity
+ * \{
+ */
+#define UDI_LOG_DISASTER 1
+#define UDI_LOG_ERROR 2
+#define UDI_LOG_WARNING 3
+#define UDI_LOG_INFORMATION 4
+/**
+ * \}
+ */
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_mem.h
+ */
+#ifndef _UDI_MEM_H_
+#define _UDI_MEM_H_
+
+/**
+ * \brief Callback type for ::udi_mem_alloc
+ */
+typedef void udi_mem_alloc_call_t(udi_cb_t *gcb, void *new_mem);
+
+/**
+ * \brief Allocate memory
+ */
+extern void udi_mem_alloc(
+ udi_mem_alloc_call_t *callback,
+ udi_cb_t *gcb,
+ udi_size_t size,
+ udi_ubit8_t flags
+ );
+
+/**
+ * \brief Values for ::udi_mem_alloc \a flags
+ * \{
+ */
+#define UDI_MEM_NOZERO (1U<<0) //!< No need to zero the memory
+#define UDI_MEM_MOVABLE (1U<<1) //!< Globally accessable memory?
+/**
+ * \}
+ */
+
+/**
+ * \brief Free allocated memory
+ */
+extern void udi_mem_free(void *target_mem);
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_meta_gio.h
+ */
+#ifndef _UDI_META_GIO_H_
+#define _UDI_META_GIO_H_
+
+typedef const struct udi_gio_provider_ops_s udi_gio_provider_ops_t;
+typedef const struct udi_gio_client_ops_s udi_gio_client_ops_t;
+typedef struct udi_gio_bind_cb_s udi_gio_bind_cb_t;
+typedef struct udi_gio_xfer_cb_s udi_gio_xfer_cb_t;
+typedef struct udi_gio_rw_params_s udi_gio_rw_params_t;
+typedef struct udi_gio_event_cb_s udi_gio_event_cb_t;
+
+typedef void udi_gio_bind_req_op_t(udi_gio_bind_cb_t *cb);
+typedef void udi_gio_unbind_req_op_t(udi_gio_bind_cb_t *cb);
+typedef void udi_gio_xfer_req_op_t(udi_gio_bind_cb_t *cb);
+typedef void udi_gio_event_res_op_t(udi_gio_bind_cb_t *cb);
+
+typedef void udi_gio_bind_ack_op_t(
+ udi_gio_bind_cb_t *cb,
+ udi_ubit32_t device_size_lo,
+ udi_ubit32_t device_size_hi,
+ udi_status_t status
+ );
+typedef void udi_gio_unbind_ack_op_t(udi_gio_bind_cb_t *cb);
+typedef void udi_gio_xfer_ack_op_t(udi_gio_bind_cb_t *cb);
+typedef void udi_gio_xfer_nak_op_t(udi_gio_bind_cb_t *cb, udi_status_t status);
+typedef void udi_gio_event_ind_op_t(udi_gio_bind_cb_t *cb);
+
+typedef udi_ubit8_t udi_gio_op_t;
+/* Limit values for udi_gio_op_t */
+#define UDI_GIO_OP_CUSTOM 16
+#define UDI_GIO_OP_MAX 64
+/* Direction flag values for op */
+#define UDI_GIO_DIR_READ (1U<<6)
+#define UDI_GIO_DIR_WRITE (1U<<7)
+/* Standard Operation Codes */
+#define UDI_GIO_OP_READ UDI_GIO_DIR_READ
+#define UDI_GIO_OP_WRITE UDI_GIO_DIR_WRITE
+
+
+
+struct udi_gio_provider_ops_s
+{
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_gio_bind_req_op_t *gio_bind_req_op;
+ udi_gio_unbind_req_op_t *gio_unbind_req_op;
+ udi_gio_xfer_req_op_t *gio_xfer_req_op;
+ udi_gio_event_res_op_t *gio_event_res_op;
+};
+/* Ops Vector Number */
+#define UDI_GIO_PROVIDER_OPS_NUM 1
+
+struct udi_gio_client_ops_s
+{
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_gio_bind_ack_op_t *gio_bind_ack_op;
+ udi_gio_unbind_ack_op_t *gio_unbind_ack_op;
+ udi_gio_xfer_ack_op_t *gio_xfer_ack_op;
+ udi_gio_xfer_nak_op_t *gio_xfer_nak_op;
+ udi_gio_event_ind_op_t *gio_event_ind_op;
+};
+/* Ops Vector Number */
+#define UDI_GIO_CLIENT_OPS_NUM 2
+
+struct udi_gio_bind_cb_s
+{
+ udi_cb_t gcb;
+ udi_xfer_constraints_t xfer_constraints;
+};
+/* Control Block Group Number */
+#define UDI_GIO_BIND_CB_NUM 1
+
+
+struct udi_gio_xfer_cb_s
+{
+ udi_cb_t gcb;
+ udi_gio_op_t op;
+ void *tr_params;
+ udi_buf_t *data_buf;
+};
+/* Control Block Group Number */
+#define UDI_GIO_XFER_CB_NUM 2
+
+struct udi_gio_rw_params_s
+{
+ udi_ubit32_t offset_lo;
+ udi_ubit32_t offset_hi;
+};
+
+struct udi_gio_event_cb_s
+{
+ udi_cb_t gcb;
+ udi_ubit8_t event_code;
+ void *event_params;
+};
+/* Control Block Group Number */
+#define UDI_GIO_EVENT_CB_NUM 3
+
+
+extern void udi_gio_bind_req(udi_gio_bind_cb_t *cb);
+extern void udi_gio_bind_ack(
+ udi_gio_bind_cb_t *cb,
+ udi_ubit32_t device_size_lo,
+ udi_ubit32_t device_size_hi,
+ udi_status_t status
+ );
+
+extern void udi_gio_unbind_req(udi_gio_bind_cb_t *cb);
+extern void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb);
+
+extern void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb);
+extern void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb);
+extern void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status);
+
+extern void udi_gio_event_res(udi_gio_event_cb_t *cb);
+extern void udi_gio_event_ind(udi_gio_event_cb_t *cb);
+extern void udi_gio_event_res_unused(udi_gio_event_cb_t *cb);
+
+#endif
--- /dev/null
+/**
+ * \file udi_meta_mgmt.h
+ */
+#ifndef _UDI_META_MGMT_H_
+#define _UDI_META_MGMT_H_
+
+typedef struct udi_mgmt_ops_s udi_mgmt_ops_t;
+typedef struct udi_mgmt_cb_s udi_mgmt_cb_t;
+typedef struct udi_usage_cb_s udi_usage_cb_t;
+typedef struct udi_filter_element_s udi_filter_element_t;
+typedef struct udi_enumerate_cb_s udi_enumerate_cb_t;
+
+/**
+ * \name Specify Usage
+ * \{
+ */
+typedef void udi_usage_ind_op_t(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
+/* Values for resource_level */
+#define UDI_RESOURCES_CRITICAL 1
+#define UDI_RESOURCES_LOW 2
+#define UDI_RESOURCES_NORMAL 3
+#define UDI_RESOURCES_PLENTIFUL 4
+/* Proxy */
+extern void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
+/**
+ * \}
+ */
+
+typedef void udi_usage_res_op_t(udi_usage_cb_t *cb);
+
+/**
+ * \name Enumerate this driver
+ * \{
+ */
+typedef void udi_enumerate_req_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level);
+/* Values for enumeration_level */
+#define UDI_ENUMERATE_START 1
+#define UDI_ENUMERATE_START_RESCAN 2
+#define UDI_ENUMERATE_NEXT 3
+#define UDI_ENUMERATE_NEW 4
+#define UDI_ENUMERATE_DIRECTED 5
+#define UDI_ENUMERATE_RELEASE 6
+/* Proxy */
+extern void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level);
+/**
+ * \}
+ */
+
+/**
+ * \name Enumeration Acknowlagement
+ * \{
+ */
+typedef void udi_enumerate_ack_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_result, udi_index_t ops_idx);
+/* Values for enumeration_result */
+#define UDI_ENUMERATE_OK 0
+#define UDI_ENUMERATE_LEAF 1
+#define UDI_ENUMERATE_DONE 2
+#define UDI_ENUMERATE_RESCAN 3
+#define UDI_ENUMERATE_REMOVED 4
+#define UDI_ENUMERATE_REMOVED_SELF 5
+#define UDI_ENUMERATE_RELEASED 6
+#define UDI_ENUMERATE_FAILED 255
+/**
+ * \}
+ */
+
+/**
+ * \name
+ * \{
+ */
+typedef void udi_devmgmt_req_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID);
+
+typedef void udi_devmgmt_ack_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status);
+/**
+ * \}
+ */
+typedef void udi_final_cleanup_req_op_t(udi_mgmt_cb_t *cb);
+typedef void udi_final_cleanup_ack_op_t(udi_mgmt_cb_t *cb);
+
+
+
+
+
+struct udi_mgmt_ops_s
+{
+ udi_usage_ind_op_t *usage_ind_op;
+ udi_enumerate_req_op_t *enumerate_req_op;
+ udi_devmgmt_req_op_t *devmgmt_req_op;
+ udi_final_cleanup_req_op_t *final_cleanup_req_op;
+};
+
+struct udi_mgmt_cb_s
+{
+ udi_cb_t gcb;
+};
+
+struct udi_usage_cb_s
+{
+ udi_cb_t gcb;
+ udi_trevent_t trace_mask;
+ udi_index_t meta_idx;
+};
+
+
+struct udi_filter_element_s
+{
+ char attr_name[UDI_MAX_ATTR_NAMELEN];
+ udi_ubit8_t attr_min[UDI_MAX_ATTR_SIZE];
+ udi_ubit8_t attr_min_len;
+ udi_ubit8_t attr_max[UDI_MAX_ATTR_SIZE];
+ udi_ubit8_t attr_max_len;
+ udi_instance_attr_type_t attr_type;
+ udi_ubit32_t attr_stride;
+};
+struct udi_enumerate_cb_s
+{
+ udi_cb_t gcb;
+ udi_ubit32_t child_ID;
+ void *child_data;
+ udi_instance_attr_list_t *attr_list;
+ udi_ubit8_t attr_valid_length;
+ const udi_filter_element_t *filter_list;
+ udi_ubit8_t filter_list_length;
+ udi_ubit8_t parent_ID;
+};
+/* Special parent_ID filter values */
+#define UDI_ANY_PARENT_ID 0
+
+/**
+ * \brief
+ */
+extern void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID );
+/**
+ * \brief Values for ::udi_devmgmt_req \a mgmt_op
+ */
+enum eDMGMT
+{
+ UDI_DMGMT_PREPARE_TO_SUSPEND = 1,
+ UDI_DMGMT_SUSPEND,
+ UDI_DMGMT_SHUTDOWN,
+ UDI_DMGMT_PARENT_SUSPENDED,
+ UDI_DMGMT_RESUME,
+ UDI_DMGMT_UNBIND
+};
+
+extern void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status);
+//!\brief Values for flags
+#define UDI_DMGMT_NONTRANSPARENT (1U<<0)
+//!\brief Meta-Specific Status Codes
+#define UDI_DMGMT_STAT_ROUTING_CHANGE (UDI_STAT_META_SPECIFIC|1)
+
+extern void udi_final_cleanup_req(udi_mgmt_cb_t *cb);
+extern void udi_final_cleanup_ack(udi_mgmt_cb_t *cb);
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_strmem.h
+ */
+#ifndef _UDI_STRMEM_H_
+#define _UDI_STRMEM_H_
+
+/**
+ * \brief Gets the length of a C style string
+ */
+extern udi_size_t udi_strlen(const char *s);
+
+/**
+ * \brief Appends to a string
+ */
+extern char *udi_strcat(char *s1, const char *s2);
+extern char *udi_strncat(char *s1, const char *s2, udi_size_t n);
+
+/**
+ * \brief Compares Strings/Memory
+ */
+extern udi_sbit8_t udi_strcmp(const char *s1, const char *s2);
+extern udi_sbit8_t udi_strncmp(const char *s1, const char *s2, udi_size_t n);
+extern udi_sbit8_t udi_memcmp(const void *s1, const void *s2, udi_size_t n);
+
+extern char *udi_strcpy(char *s1, const char *s2);
+extern char *udi_strncpy(char *s1, const char *s2, udi_size_t n);
+extern void *udi_memcpy(void *s1, const void *s2, udi_size_t n);
+extern void *udi_memmove(void *s1, const void *s2, udi_size_t n);
+
+extern char *udi_strncpy_rtrim(char *s1, const char *s2, udi_size_t n);
+
+extern char *udi_strchr(const char *s, char c);
+extern char *udi_strrchr(const char *s, char c);
+extern void *udi_memchr (const void *s, udi_ubit8_t c, udi_size_t n);
+
+extern void *udi_memset(void *s, udi_ubit8_t c, udi_size_t n);
+extern udi_ubit32_t udi_strtou32(const char *s, char **endptr, int base);
+
+
+extern udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...);
+
+
+
+#endif
--- /dev/null
+/**
+ * \file udi_physio.h
+ */
+#ifndef _UDI_PHYSIO_H_
+#define _UDI_PHYSIO_H_
+
+#include <udi.h>
+
+// === TYPEDEFS ===
+// DMA Core
+typedef _udi_handle_t udi_dma_handle_t;
+#define UDI_NULL_DMA_HANDLE _NULL_HANDLE
+typedef Uint64 udi_busaddr64_t; //!< \note Opaque
+typedef struct udi_scgth_element_32_s udi_scgth_element_32_t;
+typedef struct udi_scgth_element_64_s udi_scgth_element_64_t;
+typedef struct udi_scgth_s udi_scgth_t;
+typedef _udi_handle_t udi_dma_constraints_t;
+#define UDI_NULL_DMA_CONSTRAINTS _NULL_HANDLE
+/**
+ * \name DMA constraints attributes
+ * \{
+ */
+typedef udi_ubit8_t udi_dma_constraints_attr_t;
+/* DMA Convenience Attribute Codes */
+#define UDI_DMA_ADDRESSABLE_BITS 100
+#define UDI_DMA_ALIGNMENT_BITS 101
+/* DMA Constraints on the Entire Transfer */
+#define UDI_DMA_DATA_ADDRESSABLE_BITS 110
+#define UDI_DMA_NO_PARTIAL 111
+/* DMA Constraints on the Scatter/Gather List */
+#define UDI_DMA_SCGTH_MAX_ELEMENTS 120
+#define UDI_DMA_SCGTH_FORMAT 121
+#define UDI_DMA_SCGTH_ENDIANNESS 122
+#define UDI_DMA_SCGTH_ADDRESSABLE_BITS 123
+#define UDI_DMA_SCGTH_MAX_SEGMENTS 124
+/* DMA Constraints on Scatter/Gather Segments */
+#define UDI_DMA_SCGTH_ALIGNMENT_BITS 130
+#define UDI_DMA_SCGTH_MAX_EL_PER_SEG 131
+#define UDI_DMA_SCGTH_PREFIX_BYTES 132
+/* DMA Constraints on Scatter/Gather Elements */
+#define UDI_DMA_ELEMENT_ALIGNMENT_BITS 140
+#define UDI_DMA_ELEMENT_LENGTH_BITS 141
+#define UDI_DMA_ELEMENT_GRANULARITY_BITS 142
+/* DMA Constraints for Special Addressing */
+#define UDI_DMA_ADDR_FIXED_BITS 150
+#define UDI_DMA_ADDR_FIXED_TYPE 151
+#define UDI_DMA_ADDR_FIXED_VALUE_LO 152
+#define UDI_DMA_ADDR_FIXED_VALUE_HI 153
+/* DMA Constraints on DMA Access Behavior */
+#define UDI_DMA_SEQUENTIAL 160
+#define UDI_DMA_SLOP_IN_BITS 161
+#define UDI_DMA_SLOP_OUT_BITS 162
+#define UDI_DMA_SLOP_OUT_EXTRA 163
+#define UDI_DMA_SLOP_BARRIER_BITS 164
+/* Values for UDI_DMA_SCGTH_ENDIANNESS */
+#define UDI_DMA_LITTLE_ENDIAN (1U<<6)
+#define UDI_DMA_BIG_ENDIAN (1U<<5)
+/* Values for UDI_DMA_ADDR_FIXED_TYPE */
+#define UDI_DMA_FIXED_ELEMENT 1
+/**
+ * \}
+ */
+// DMA Constraints Management
+typedef struct udi_dma_constraints_attr_spec_s udi_dma_constraints_attr_spec_t;
+typedef void udi_dma_constraints_attr_set_call_t(
+ udi_cb_t *gcb, udi_dma_constraints_t new_constraints, udi_status_t status
+ );
+typedef struct udi_dma_limits_s udi_dma_limits_t;
+
+
+// === STRUCTURES ===
+// --- DMA Constraints Management ---
+struct udi_dma_constraints_attr_spec_s
+{
+ udi_dma_constraints_attr_t attr_type;
+ udi_ubit32_t attr_value;
+};
+// --- DMA Core ---
+struct udi_dma_limits_s
+{
+ udi_size_t max_legal_contig_alloc;
+ udi_size_t max_safe_contig_alloc;
+ udi_size_t cache_line_size;
+};
+struct udi_scgth_element_32_s
+{
+ udi_ubit32_t block_busaddr;
+ udi_ubit32_t block_length;
+};
+struct udi_scgth_element_64_s
+{
+ udi_busaddr64_t block_busaddr;
+ udi_ubit32_t block_length;
+ udi_ubit32_t el_reserved;
+};
+/* Extension Flag */
+#define UDI_SCGTH_EXT 0x80000000
+struct udi_scgth_s
+{
+ udi_ubit16_t scgth_num_elements;
+ udi_ubit8_t scgth_format;
+ udi_boolean_t scgth_must_swap;
+ union {
+ udi_scgth_element_32_t *el32p;
+ udi_scgth_element_64_t *el64p;
+ } scgth_elements;
+ union {
+ udi_scgth_element_32_t el32;
+ udi_scgth_element_64_t el64;
+ } scgth_first_segment;
+};
+/* Values for scgth_format */
+#define UDI_SCGTH_32 (1U<<0)
+#define UDI_SCGTH_64 (1U<<1)
+#define UDI_SCGTH_DMA_MAPPED (1U<<6)
+#define UDI_SCGTH_DRIVER_MAPPED (1U<<7)
+
+
+
+// === FUNCTIONS ===
+// --- DMA Constraints Management ---
+extern void udi_dma_constraints_attr_set(
+ udi_dma_constraints_attr_set_call_t *callback,
+ udi_cb_t *gcb,
+ udi_dma_constraints_t src_constraints,
+ const udi_dma_constraints_attr_spec_t *attr_list,
+ udi_ubit16_t list_length,
+ udi_ubit8_t flags
+ );
+/* Constraints Flags */
+#define UDI_DMA_CONSTRAINTS_COPY (1U<<0)
+
+extern void udi_dma_constraints_attr_reset(
+ udi_dma_constraints_t constraints,
+ udi_dma_constraints_attr_t attr_type
+ );
+
+extern void udi_dma_constraints_free(udi_dma_constraints_t constraints);
+
+#include <physio/meta_intr.h>
+#include <physio/meta_bus.h>
+
+
+#endif
--- /dev/null
+/**
+ * \file logging.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === PROTOTYPES ===
+
+// === CODE ===
+void udi_log_write( udi_log_write_call_t *callback, udi_cb_t *gcb,
+ udi_trevent_t trace_event, udi_ubit8_t severity, udi_index_t meta_idx,
+ udi_status_t original_status, udi_ubit32_t msgnum, ... )
+{
+ Log("UDI Log");
+}
+
+EXPORT(udi_log_write);
--- /dev/null
+/*
+ * Acess2 UDI Layer
+ */
+#define DEBUG 0
+#define VERSION ((0<<8)|1)
+#include <acess.h>
+#include <modules.h>
+#include <udi.h>
+
+// === PROTOTYPES ===
+ int UDI_Install(char **Arguments);
+ int UDI_DetectDriver(void *Base);
+ int UDI_LoadDriver(void *Base);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, UDI, UDI_Install, NULL, NULL);
+tModuleLoader gUDI_Loader = {
+ NULL, "UDI", UDI_DetectDriver, UDI_LoadDriver, NULL
+};
+
+// === CODE ===
+/**
+ * \fn int UDI_Install(char **Arguments)
+ * \brief Stub intialisation routine
+ */
+int UDI_Install(char **Arguments)
+{
+ Module_RegisterLoader( &gUDI_Loader );
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Detects if a driver should be loaded by the UDI subsystem
+ */
+int UDI_DetectDriver(void *Base)
+{
+ if( Binary_FindSymbol(Base, "udi_init_info", NULL) == 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * \fn int UDI_LoadDriver(void *Base)
+ */
+int UDI_LoadDriver(void *Base)
+{
+ udi_init_t *info;
+ char *udiprops = NULL;
+ int udiprops_size = 0;
+ int i;
+ // int j;
+
+ Log_Debug("UDI", "UDI_LoadDriver: (Base=%p)", Base);
+
+ if( Binary_FindSymbol(Base, "udi_init_info", (Uint*)&info) == 0) {
+ Binary_Unload(Base);
+ return 0;
+ }
+
+ if( Binary_FindSymbol(Base, "_udiprops", (Uint*)&udiprops) == 0 ) {
+ Log_Warning("UDI", "_udiprops is not defined, this is usually bad");
+ }
+ else {
+ Uint udiprops_end = 0;
+ int i, j, nLines;
+ char **udipropsptrs;
+
+ if( Binary_FindSymbol(Base, "_udiprops_end", (Uint*)&udiprops_end) == 0)
+ Log_Warning("UDI", "_udiprops_end is not defined");
+ Log_Debug("UDI", "udiprops_end = %p", udiprops_end);
+ udiprops_size = udiprops_end - (Uint)udiprops;
+ Log_Debug("UDI", "udiprops = %p, udiprops_size = 0x%x", udiprops, udiprops_size);
+
+ Debug_HexDump("UDI_LoadDriver", udiprops, udiprops_size);
+
+ nLines = 1;
+ for( i = 0; i < udiprops_size; i++ )
+ {
+ if( udiprops[i] == '\0' )
+ nLines ++;
+ }
+
+ Log_Debug("UDI", "nLines = %i", nLines);
+
+ udipropsptrs = malloc( sizeof(char*)*nLines );
+ udipropsptrs[0] = udiprops;
+ j = 0;
+ for( i = 0; i < udiprops_size; i++ )
+ {
+ if( udiprops[i] == '\0' ) {
+ //Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]);
+ udipropsptrs[j++] = &udiprops[i+1];
+ }
+ }
+ Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]);
+ Log_Debug("UDI", "udiprops = \"%s\"", udiprops);
+ }
+
+
+ Log("primary_init_info = %p = {", info->primary_init_info);
+ {
+ Log(" .mgmt_ops = %p = {", info->primary_init_info->mgmt_ops);
+ Log(" .usage_ind_op: %p() - 0x%02x",
+ info->primary_init_info->mgmt_ops->usage_ind_op,
+ info->primary_init_info->mgmt_op_flags[0]
+ );
+ Log(" .enumerate_req_op: %p() - 0x%02x",
+ info->primary_init_info->mgmt_ops->enumerate_req_op,
+ info->primary_init_info->mgmt_op_flags[1]
+ );
+ Log(" .devmgmt_req_op: %p() - 0x%02x",
+ info->primary_init_info->mgmt_ops->devmgmt_req_op,
+ info->primary_init_info->mgmt_op_flags[2]
+ );
+ Log(" .final_cleanup_req_op: %p() - 0x%02x",
+ info->primary_init_info->mgmt_ops->final_cleanup_req_op,
+ info->primary_init_info->mgmt_op_flags[3]
+ );
+ Log(" }");
+ Log(" .mgmt_scratch_requirement = 0x%x", info->primary_init_info->mgmt_scratch_requirement);
+ Log(" .enumeration_attr_list_length = 0x%x", info->primary_init_info->enumeration_attr_list_length);
+ Log(" .rdata_size = 0x%x", info->primary_init_info->rdata_size);
+ Log(" .child_data_size = 0x%x", info->primary_init_info->child_data_size);
+ Log(" .per_parent_paths = 0x%x", info->primary_init_info->per_parent_paths);
+ }
+ Log("}");
+ Log("secondary_init_list = %p", info->secondary_init_list);
+ Log("ops_init_list = %p", info->ops_init_list);
+
+ for( i = 0; info->ops_init_list[i].ops_idx; i++ )
+ {
+ Log("info->ops_init_list[%i] = {", i);
+ Log(" .ops_idx = 0x%x", info->ops_init_list[i].ops_idx);
+ Log(" .meta_idx = 0x%x", info->ops_init_list[i].meta_idx);
+ Log(" .meta_ops_num = 0x%x", info->ops_init_list[i].meta_ops_num);
+ Log(" .chan_context_size = 0x%x", info->ops_init_list[i].chan_context_size);
+ Log(" .ops_vector = %p", info->ops_init_list[i].ops_vector);
+// Log(" .op_flags = %p", info->ops_init_list[i].op_flags);
+ Log("}");
+ }
+
+ return 0;
+}
--- /dev/null
+/**
+ * \file mem.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === EXPORTS ===
+EXPORT(udi_mem_alloc);
+EXPORT(udi_mem_free);
+
+// === CODE ===
+void udi_mem_alloc(
+ udi_mem_alloc_call_t *callback,
+ udi_cb_t *gcb,
+ udi_size_t size,
+ udi_ubit8_t flags
+ )
+{
+ void *buf = malloc(size);
+ if(buf)
+ {
+ if( !(flags & UDI_MEM_NOZERO) )
+ memset(buf, 0, size);
+ }
+ callback(gcb, buf);
+}
+
+void udi_mem_free(void *target_mem)
+{
+ free(target_mem);
+}
--- /dev/null
+/**
+ * \file meta_gio.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === EXPORTS ===
+EXPORT(udi_gio_bind_req);
+EXPORT(udi_gio_bind_ack);
+EXPORT(udi_gio_unbind_req);
+EXPORT(udi_gio_unbind_ack);
+EXPORT(udi_gio_xfer_req);
+EXPORT(udi_gio_xfer_ack);
+EXPORT(udi_gio_xfer_nak);
+EXPORT(udi_gio_event_res);
+EXPORT(udi_gio_event_ind);
+EXPORT(udi_gio_event_res_unused);
+
+// === CODE ===
+void udi_gio_bind_req(udi_gio_bind_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_gio_bind_ack(
+ udi_gio_bind_cb_t *cb,
+ udi_ubit32_t device_size_lo,
+ udi_ubit32_t device_size_hi,
+ udi_status_t status
+ )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_gio_unbind_req(udi_gio_bind_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_gio_event_res(udi_gio_event_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_gio_event_ind(udi_gio_event_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_gio_event_res_unused(udi_gio_event_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
--- /dev/null
+/**
+ * \file meta_mgmt.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === EXPORTS ===
+EXPORT(udi_devmgmt_req);
+EXPORT(udi_devmgmt_ack);
+EXPORT(udi_final_cleanup_req);
+EXPORT(udi_final_cleanup_ack);
+EXPORT(udi_static_usage);
+EXPORT(udi_enumerate_no_children);
+
+// === CODE ===
+void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID )
+{
+ ENTER("pcb imgmt_op iparent_ID", cb, mgmt_op, parent_ID);
+ LEAVE('-');
+}
+
+void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status)
+{
+ ENTER("pcb xflags istatus", cb, flags, status);
+ LEAVE('-');
+}
+
+void udi_final_cleanup_req(udi_mgmt_cb_t *cb)
+{
+ ENTER("pcb", cb);
+ LEAVE('-');
+}
+
+void udi_final_cleanup_ack(udi_mgmt_cb_t *cb)
+{
+ ENTER("pcb", cb);
+ LEAVE('-');
+}
+
+void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
+{
+ UNIMPLEMENTED();
+}
--- /dev/null
+/**
+ * \file physio.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+#include <udi_physio.h>
+
+// === EXPORTS ===
+EXPORT(udi_dma_constraints_attr_reset);
+EXPORT(udi_dma_constraints_free);
+
+// === CODE ===
+void udi_dma_constraints_attr_reset(
+ udi_dma_constraints_t constraints,
+ udi_dma_constraints_attr_t attr_type
+ )
+{
+ UNIMPLEMENTED();
+}
+
+void udi_dma_constraints_free(udi_dma_constraints_t constraints)
+{
+ UNIMPLEMENTED();
+}
--- /dev/null
+/**
+ * \file physio/meta_bus.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+#include <udi_physio.h>
+
+// === EXPORTS ===
+EXPORT(udi_bus_unbind_req);
+EXPORT(udi_bus_unbind_ack);
+EXPORT(udi_bus_bind_req);
+EXPORT(udi_bus_bind_ack);
+
+// === CODE ===
+void udi_bus_unbind_req(udi_bus_bind_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_bus_bind_req(udi_bus_bind_cb_t *cb)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_bus_bind_ack(
+ udi_bus_bind_cb_t *cb,
+ udi_dma_constraints_t dma_constraints,
+ udi_ubit8_t preferred_endianness,
+ udi_status_t status
+ )
+{
+ UNIMPLEMENTED();
+}
--- /dev/null
+/**
+ * \file physio/meta_intr.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+#include <udi_physio.h>
+
+// === EXPORTS ===
+EXPORT(udi_intr_attach_req);
+EXPORT(udi_intr_attach_ack);
+EXPORT(udi_intr_attach_ack_unused);
+EXPORT(udi_intr_detach_req);
+EXPORT(udi_intr_detach_ack);
+EXPORT(udi_intr_detach_ack_unused);
+EXPORT(udi_intr_event_ind);
+
+// === CODE ===
+void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status)
+{
+ UNIMPLEMENTED();
+}
+void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb)
+{
+ UNIMPLEMENTED();
+}
+void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb)
+{
+ UNIMPLEMENTED();
+}
+
+void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags)
+{
+ UNIMPLEMENTED();
+}
--- /dev/null
+/**
+ * \file logging.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <common.h>
+#include <udi.h>
+#include <udi_physio.h>
+
+// === CODE ===
+void udi_bus_bind_req(udi_bus_bind_cb_t *cb)
+{
+}
+
+void udi_bus_unbind_req(udi_bus_bind_cb_t *cb)
+{
+}
--- /dev/null
+/**
+ * \file strmem.c
+ * \author John Hodge (thePowersGang)
+ */
+#include <acess.h>
+#include <udi.h>
+
+// === EXPORTS ===
+EXPORT(udi_snprintf);
+
+// === CODE ===
+udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...)
+{
+ udi_size_t ret;
+ va_list args;
+ va_start(args, format);
+
+ ret = vsnprintf(s, max_bytes, format, args);
+
+ va_end(args);
+ return ret;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - Sun RPC (RFC 1057) implementation
+ * proto.h
+ * - Protocol definition
+ */
+#ifndef _SUNRPC_PROTO_H_
+#define _SUNRPC_PROTO_H_
+
+struct sRPC_CallBody
+{
+ Uint32 RPCVers; //!< Version (2)
+ Uint32 Program; //!< Program Identifier
+ Uint32 Version; //!< Program Version
+
+};
+
+struct sRPC_Message
+{
+ tNet32 XID; //!< Transaction Identifier
+ union
+ {
+ struct sRPC_CallBody Call;
+ };
+};
+
+#endif
+
--- /dev/null
+
+# Acess2 Module/Driver Templater Makefile
+# Makefile.tpl
+
+_CPPFLAGS := $(CPPFLAGS)
+
+-include $(dir $(lastword $(MAKEFILE_LIST)))../Makefile.cfg
+
+LIBINCLUDES := $(addprefix -I$(ACESSDIR)/Modules/,$(DEPS))
+LIBINCLUDES := $(addsuffix /include,$(LIBINCLUDES))
+
+CPPFLAGS := -I$(ACESSDIR)/Kernel/include -I$(ACESSDIR)/Kernel/arch/$(ARCHDIR)/include
+CPPFLAGS += -DARCH=$(ARCH) -DARCH_is_$(ARCH) -DARCHDIR_is_$(ARCHDIR)
+CPPFLAGS += $(_CPPFLAGS)
+CPPFLAGS += $(LIBINCLUDES)
+CFLAGS := -std=gnu99 -Wall -fno-stack-protector -g -O3
+
+ifneq ($(CATEGORY),)
+ FULLNAME := $(CATEGORY)_$(NAME)
+else
+ FULLNAME := $(NAME)
+endif
+
+CPPFLAGS += -D_MODULE_NAME_=\"$(FULLNAME)\"
+
+ifneq ($(BUILDTYPE),static)
+ _SUFFIX := dyn_$(ARCH)
+ BIN := ../$(FULLNAME).kmd.$(ARCH)
+ CFLAGS += $(DYNMOD_CFLAGS) -fPIC
+else
+ _SUFFIX := st_$(ARCH)
+ CFLAGS += $(KERNEL_CFLAGS)
+ BIN := ../$(NAME).xo.$(ARCH)
+endif
+
+OBJ := $(addprefix obj-$(_SUFFIX)/,$(OBJ))
+#OBJ := $(addsuffix .$(_SUFFIX),$(OBJ))
+
+DEPFILES := $(filter %.o,$(OBJ))
+DEPFILES := $(DEPFILES:%.o=%.d)
+
+.PHONY: all clean
+
+all: $(BIN)
+
+clean:
+ $(RM) $(BIN) $(BIN).dsm $(KOBJ) $(OBJ) $(DEPFILES) $(EXTRA)
+ $(RM) -r obj-$(_SUFFIX)
+
+install: $(BIN)
+ifneq ($(BUILDTYPE),static)
+ @$(xMKDIR) $(DISTROOT)/Modules/$(ARCH); true
+ $(xCP) $(BIN) $(DISTROOT)/Modules/$(ARCH)/$(NAME).kmd
+else
+endif
+
+
+ifneq ($(BUILDTYPE),static)
+$(BIN): %.kmd.$(ARCH): $(OBJ)
+ @echo --- $(LD) -o $@
+ @$(LD) --allow-shlib-undefined -shared -nostdlib -o $@ $(OBJ) -defsym=DriverInfo=_DriverInfo_$(FULLNAME) $(LDFLAGS)
+ @$(DISASM) $(BIN) > $(BIN).dsm
+else
+$(BIN): %.xo.$(ARCH): $(OBJ)
+ @echo --- $(LD) -o $@
+ @$(LD) -r -o $@ $(OBJ) $(LDFLAGS)
+endif
+
+obj-$(_SUFFIX)/%.o: %.c Makefile $(CFGFILES)
+ @echo --- $(CC) -o $@
+ @mkdir -p $(dir $@)
+ @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+ @$(CC) -M $(CPPFLAGS) -MT $@ -o obj-$(_SUFFIX)/$*.d $<
+
+-include $(DEPFILES)
--- /dev/null
+CATEGORY = Network
+
+-include ../../Makefile.tpl
--- /dev/null
+#
+#
+
+OBJ = ne2000.o
+NAME = NE2000
+
+-include ../Makefile.tpl
--- /dev/null
+/* Acess2
+ * NE2000 Driver
+ *
+ * See: ~/Sources/bochs/bochs.../iodev/ne2k.cc
+ */
+#define DEBUG 1
+#define VERSION ((0<<8)|50)
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <drv_pci.h>
+#include <api_drv_network.h>
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define MEM_START 0x40
+#define MEM_END 0xC0
+#define RX_FIRST (MEM_START)
+#define RX_LAST (MEM_START+RX_BUF_SIZE-1)
+#define RX_BUF_SIZE 0x40
+#define TX_FIRST (MEM_START+RX_BUF_SIZE)
+#define TX_LAST (MEM_END)
+#define TX_BUF_SIZE 0x40
+#define MAX_PACKET_QUEUE 10
+
+static const struct {
+ Uint16 Vendor;
+ Uint16 Device;
+} csaCOMPAT_DEVICES[] = {
+ {0x10EC, 0x8029}, // Realtek 8029
+ {0x10EC, 0x8129} // Realtek 8129
+};
+#define NUM_COMPAT_DEVICES ((int)(sizeof(csaCOMPAT_DEVICES)/sizeof(csaCOMPAT_DEVICES[0])))
+
+enum eNe2k_Page0Read {
+ CMD = 0, //!< the master command register
+ CLDA0, //!< Current Local DMA Address 0
+ CLDA1, //!< Current Local DMA Address 1
+ BNRY, //!< Boundary Pointer (for ringbuffer)
+ TSR, //!< Transmit Status Register
+ NCR, //!< collisions counter
+ FIFO, //!< (for what purpose ??)
+ ISR, //!< Interrupt Status Register
+ CRDA0, //!< Current Remote DMA Address 0
+ CRDA1, //!< Current Remote DMA Address 1
+ RSR = 0xC //!< Receive Status Register
+};
+
+enum eNe2k_Page0Write {
+ PSTART = 1, //!< page start (init only)
+ PSTOP, //!< page stop (init only)
+ TPSR = 4, //!< transmit page start address
+ TBCR0, //!< transmit byte count (low)
+ TBCR1, //!< transmit byte count (high)
+ RSAR0 = 8, //!< remote start address (lo)
+ RSAR1, //!< remote start address (hi)
+ RBCR0, //!< remote byte count (lo)
+ RBCR1, //!< remote byte count (hi)
+ RCR, //!< receive config register
+ TCR, //!< transmit config register
+ DCR, //!< data config register (init)
+ IMR //!< interrupt mask register (init)
+};
+
+enum eNe2k_Page1Read {
+ CURR = 7 //!< current page
+};
+
+// === TYPES ===
+typedef struct sNe2k_Card {
+ Uint16 IOBase; //!< IO Port Address from PCI
+ Uint8 IRQ; //!< IRQ Assigned from PCI
+
+ tSemaphore Semaphore; //!< Semaphore for incoming packets
+ int NextRXPage; //!< Next expected RX page
+
+ int NextMemPage; //!< Next Card Memory page to use
+
+ char Name[2]; // "0"
+ tVFS_Node Node; //!< VFS Node
+ Uint8 MacAddr[6]; //!< Cached MAC address
+} tCard;
+
+// === PROTOTYPES ===
+ int Ne2k_Install(char **Arguments);
+char *Ne2k_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name);
+ int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data);
+Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+
+ int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer);
+Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length);
+void Ne2k_IRQHandler(int IntNum, void *Ptr);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, Ne2k, Ne2k_Install, NULL, NULL);
+tVFS_NodeType gNe2K_RootNodeType = {
+ .ReadDir = Ne2k_ReadDir,
+ .FindDir = Ne2k_FindDir,
+ .IOCtl = Ne2k_IOCtl
+ };
+tVFS_NodeType gNe2K_DevNodeType = {
+ .Write = Ne2k_Write,
+ .Read = Ne2k_Read,
+ .IOCtl = Ne2k_IOCtl
+ };
+tDevFS_Driver gNe2k_DriverInfo = {
+ NULL, "ne2k",
+ {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gNe2K_RootNodeType
+ }
+};
+Uint16 gNe2k_BaseAddress;
+ int giNe2k_CardCount = 0;
+tCard *gpNe2k_Cards = NULL;
+
+// === CODE ===
+/**
+ * \fn int Ne2k_Install(char **Options)
+ * \brief Installs the NE2000 Driver
+ */
+int Ne2k_Install(char **Options)
+{
+ int i, j, k;
+ int count, base;
+ tPCIDev id;
+
+ // --- Scan PCI Bus ---
+ // Count Cards
+ giNe2k_CardCount = 0;
+ for( i = 0; i < NUM_COMPAT_DEVICES; i ++ )
+ {
+ giNe2k_CardCount += PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device );
+ }
+
+ if( giNe2k_CardCount == 0 ) return MODULE_ERR_NOTNEEDED;
+
+ // Enumerate Cards
+ k = 0;
+ gpNe2k_Cards = calloc( giNe2k_CardCount, sizeof(tCard) );
+
+ for( i = 0; i < NUM_COMPAT_DEVICES; i ++ )
+ {
+ count = PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device );
+ for( j = 0; j < count; j ++,k ++ )
+ {
+ id = PCI_GetDevice( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device, j );
+ // Create Structure
+ base = PCI_GetBAR( id, 0 );
+ gpNe2k_Cards[ k ].IOBase = base;
+ gpNe2k_Cards[ k ].IRQ = PCI_GetIRQ( id );
+ gpNe2k_Cards[ k ].NextMemPage = 64;
+ gpNe2k_Cards[ k ].NextRXPage = RX_FIRST;
+
+ // Install IRQ Handler
+ IRQ_AddHandler(gpNe2k_Cards[ k ].IRQ, Ne2k_IRQHandler, &gpNe2k_Cards[k]);
+
+ // Reset Card
+ outb( base + 0x1F, inb(base + 0x1F) );
+ while( (inb( base+ISR ) & 0x80) == 0 );
+ outb( base + ISR, 0x80 );
+
+ // Initialise Card
+ outb( base + CMD, 0x40|0x21 ); // Page 1, No DMA, Stop
+ outb( base + CURR, RX_FIRST ); // Current RX page
+ outb( base + CMD, 0x21 ); // No DMA and Stop
+ outb( base + DCR, 0x49 ); // Set WORD mode
+ outb( base + IMR, 0x00 ); // Interrupt Mask Register
+ outb( base + ISR, 0xFF );
+ outb( base + RCR, 0x20 ); // Reciever to Monitor
+ outb( base + TCR, 0x02 ); // Transmitter OFF (TCR.LB = 1, Internal Loopback)
+
+ // Read MAC Address
+ outb( base + RBCR0, 6*4 ); // Remote Byte Count
+ outb( base + RBCR1, 0 );
+ outb( base + RSAR0, 0 ); // Clear Source Address
+ outb( base + RSAR1, 0 );
+ outb( base + CMD, 0x0A ); // Remote Read, Start
+ gpNe2k_Cards[ k ].MacAddr[0] = inb(base+0x10);// inb(base+0x10);
+ gpNe2k_Cards[ k ].MacAddr[1] = inb(base+0x10);// inb(base+0x10);
+ gpNe2k_Cards[ k ].MacAddr[2] = inb(base+0x10);// inb(base+0x10);
+ gpNe2k_Cards[ k ].MacAddr[3] = inb(base+0x10);// inb(base+0x10);
+ gpNe2k_Cards[ k ].MacAddr[4] = inb(base+0x10);// inb(base+0x10);
+ gpNe2k_Cards[ k ].MacAddr[5] = inb(base+0x10);// inb(base+0x10);
+
+ outb( base+PSTART, RX_FIRST); // Set Receive Start
+ outb( base+BNRY, RX_LAST-1); // Set Boundary Page
+ outb( base+PSTOP, RX_LAST); // Set Stop Page
+ outb( base+ISR, 0xFF ); // Clear all ints
+ outb( base+CMD, 0x22 ); // No DMA, Start
+ outb( base+IMR, 0x3F ); // Set Interupt Mask
+ outb( base+RCR, 0x0F ); // Set WRAP and allow all packet matches
+ outb( base+TCR, 0x00 ); // Set Normal Transmitter mode
+ outb( base+TPSR, 0x40); // Set Transmit Start
+
+ Log_Log("Ne2k", "Card %i 0x%04x IRQ%i %02x:%02x:%02x:%02x:%02x:%02x",
+ k, base, gpNe2k_Cards[ k ].IRQ,
+ gpNe2k_Cards[k].MacAddr[0], gpNe2k_Cards[k].MacAddr[1],
+ gpNe2k_Cards[k].MacAddr[2], gpNe2k_Cards[k].MacAddr[3],
+ gpNe2k_Cards[k].MacAddr[4], gpNe2k_Cards[k].MacAddr[5]
+ );
+
+ // Set VFS Node
+ gpNe2k_Cards[ k ].Name[0] = '0'+k;
+ gpNe2k_Cards[ k ].Name[1] = '\0';
+ gpNe2k_Cards[ k ].Node.ImplPtr = &gpNe2k_Cards[ k ];
+ gpNe2k_Cards[ k ].Node.NumACLs = 0; // Root Only
+ gpNe2k_Cards[ k ].Node.CTime = now();
+ gpNe2k_Cards[ k ].Node.Type = &gNe2K_DevNodeType;
+
+ // Initialise packet semaphore
+ // - Start at zero, no max
+ Semaphore_Init( &gpNe2k_Cards[k].Semaphore, 0, 0, "NE2000", gpNe2k_Cards[ k ].Name );
+ }
+ }
+
+ gNe2k_DriverInfo.RootNode.Size = giNe2k_CardCount;
+ DevFS_AddDevice( &gNe2k_DriverInfo );
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \fn char *Ne2k_ReadDir(tVFS_Node *Node, int Pos)
+ */
+char *Ne2k_ReadDir(tVFS_Node *Node, int Pos)
+{
+ char ret[2];
+ if(Pos < 0 || Pos >= giNe2k_CardCount) return NULL;
+ ret[0] = '0'+Pos;
+ ret[1] = '\0';
+ return strdup(ret);
+}
+
+/**
+ * \fn tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name)
+ */
+tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name)
+{
+ if(Name[0] == '\0' || Name[1] != '\0') return NULL;
+
+ return &gpNe2k_Cards[ Name[0]-'0' ].Node;
+}
+
+static const char *casIOCtls[] = { DRV_IOCTLNAMES, DRV_NETWORK_IOCTLNAMES, NULL };
+/**
+ * \fn int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data)
+ * \brief IOCtl calls for a network device
+ */
+int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch( ID )
+ {
+ BASE_IOCTLS(DRV_TYPE_NETWORK, "NE2000", VERSION, casIOCtls);
+ }
+
+ // If this is the root, return
+ if( Node == &gNe2k_DriverInfo.RootNode ) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Device specific settings
+ switch( ID )
+ {
+ case NET_IOCTL_GETMAC:
+ if( !CheckMem(Data, 6) ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ memcpy( Data, ((tCard*)Node->ImplPtr)->MacAddr, 6 );
+ LEAVE('i', 1);
+ return 1;
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \fn Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+ * \brief Send a packet from the network card
+ */
+Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ tCard *Card = (tCard*)Node->ImplPtr;
+ const Uint16 *buf = Buffer;
+ int rem = Length;
+ int page;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ // TODO: Lock
+
+ // Sanity Check Length
+ if(Length > TX_BUF_SIZE*256) {
+ Log_Warning(
+ "Ne2k",
+ "Ne2k_Write - Attempting to send over TX_BUF_SIZE*256 (%i) bytes (%i)",
+ TX_BUF_SIZE*256, Length
+ );
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Make sure that the card is in page 0
+ outb(Card->IOBase + CMD, 0|0x22); // Page 0, Start, NoDMA
+
+ // Clear Remote DMA Flag
+ outb(Card->IOBase + ISR, 0x40); // Bit 6
+
+ // Send Size - Transfer Byte Count Register
+ outb(Card->IOBase + TBCR0, Length & 0xFF);
+ outb(Card->IOBase + TBCR1, Length >> 8);
+
+ // Send Size - Remote Byte Count Register
+ outb(Card->IOBase + RBCR0, Length & 0xFF);
+ outb(Card->IOBase + RBCR1, Length >> 8);
+
+ // Set up transfer
+ outb(Card->IOBase + RSAR0, 0x00); // Page Offset
+ page = Ne2k_int_GetWritePage(Card, Length);
+ outb(Card->IOBase + RSAR1, page); // Page Offset
+ // Start
+ outb(Card->IOBase + CMD, 0|0x10|0x2); // Page 0, Remote Write, Start
+
+ // Send Data
+ for(rem = Length; rem > 0; rem -= 2) {
+ outw(Card->IOBase + 0x10, *buf++);
+ }
+
+ while( !(inb(Card->IOBase + ISR) & 0x40) ) // Wait for Remote DMA Complete
+ ; //Proc_Yield();
+
+ outb( Card->IOBase + ISR, 0x40 ); // ACK Interrupt
+
+ // Send Packet
+ outb(Card->IOBase + TPSR, page);
+ outb(Card->IOBase + CMD, 0|0x10|0x4|0x2);
+
+ // Complete DMA
+ //outb(Card->IOBase + CMD, 0|0x20);
+
+ LEAVE('i', Length);
+ return Length;
+}
+
+/**
+ * \fn Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Wait for and read a packet from the network card
+ */
+Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tCard *Card = (tCard*)Node->ImplPtr;
+ Uint8 page;
+ Uint8 data[256];
+ struct {
+ Uint8 Status;
+ Uint8 NextPacketPage;
+ Uint16 Length; // Little Endian
+ } *pktHdr;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ // Wait for packets
+ if( Semaphore_Wait( &Card->Semaphore, 1 ) != 1 )
+ {
+ // Error or interrupted
+ LEAVE_RET('i', 0);
+ }
+
+ outb(Card->IOBase, 0x22 | (1 << 6)); // Page 6
+ LOG("CURR : 0x%02x", inb(Card->IOBase + CURR));
+
+ // Get current read page
+ page = Card->NextRXPage;
+ LOG("page = %i", page);
+
+ Ne2k_int_ReadDMA(Card, page, 1, data);
+
+ pktHdr = (void*)data;
+
+ LOG("pktHdr->Status = 0x%x", pktHdr->Status);
+ LOG("pktHdr->NextPacketPage = %i", pktHdr->NextPacketPage);
+ LOG("pktHdr->Length = 0x%03x", pktHdr->Length);
+
+ // Have we read all the required bytes yet?
+ if(pktHdr->Length < 256 - 4)
+ {
+ if(Length > pktHdr->Length)
+ Length = pktHdr->Length;
+ memcpy(Buffer, &data[4], Length);
+ }
+ // No? oh damn, now we need to allocate a buffer
+ else
+ {
+ int pages = pktHdr->NextPacketPage - page;
+ char *buf = malloc( pages*256 );
+
+ LOG("pktHdr->Length (%i) > 256 - 4, allocated buffer %p", pktHdr->Length, buf);
+
+ if(!buf) LEAVE_RET('i', -1);
+
+ // Copy the already read data
+ memcpy(buf, data, 256);
+
+ // Read all the needed pages
+ page ++;
+ if(page == RX_LAST+1) page = RX_FIRST;
+
+ if( page + pages > RX_LAST )
+ {
+ int first_count = RX_LAST+1 - page;
+ int tmp = 0;
+ tmp += Ne2k_int_ReadDMA(Card, page, first_count, buf+256);
+ tmp += Ne2k_int_ReadDMA(Card, RX_FIRST, pages-1-first_count, buf+(first_count+1)*256);
+ LOG("composite return count = %i", tmp);
+ }
+ else
+ Ne2k_int_ReadDMA(Card, page, pages-1, buf+256);
+
+ // Wrap length to the packet length
+ if(Length > pktHdr->Length)
+ Length = pktHdr->Length;
+ else if( Length < pktHdr->Length ) {
+ Log_Warning("NE2000", "Packet truncated! (%i bytes truncated to %i)",
+ pktHdr->Length, Length);
+ }
+ memcpy(Buffer, &buf[4], Length);
+
+ free(buf);
+ }
+
+ // Write BNRY (maximum page for incoming packets)
+ if(pktHdr->NextPacketPage == RX_FIRST)
+ outb( Card->IOBase + BNRY, RX_LAST-1 );
+ else
+ outb( Card->IOBase + BNRY, pktHdr->NextPacketPage-1 );
+ // Set next RX Page and decrement the waiting list
+ Card->NextRXPage = pktHdr->NextPacketPage;
+
+ LEAVE('i', Length);
+ return Length;
+}
+
+int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer)
+{
+ int i;
+
+ // Sanity check
+ if( !(0 <= FirstPage && FirstPage < 256) ) {
+ Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - FirstPage(%i) not 8-bit", FirstPage);
+ return -1;
+ }
+ if( !(0 <= NumPages && NumPages < 256) ) {
+ Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - NumPages(%i) not 8-bit", NumPages);
+ return -1;
+ }
+
+ ENTER("pCard iFirstPage iNumPages pBuffer", Card, FirstPage, NumPages, Buffer);
+
+ // Make sure that the card is in bank 0
+ outb(Card->IOBase + CMD, 0|0x22); // Bank 0, Start, NoDMA
+ outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag
+
+ // Set up transfer
+ outb(Card->IOBase + RBCR0, 0);
+ outb(Card->IOBase + RBCR1, NumPages); // page count
+ outb(Card->IOBase + RSAR0, 0x00); // Page Offset
+ outb(Card->IOBase + RSAR1, FirstPage); // Page Number
+ outb(Card->IOBase + CMD, 0|0x08|0x2); // Bank 0, Remote Read, Start
+
+ // TODO: Less expensive
+ //while( !(inb(Card->IOBase + ISR) & 0x40) ) {
+ // HALT();
+ // LOG("inb(ISR) = 0x%02x", inb(Card->IOBase + ISR));
+ //}
+ HALT(); // Small delay?
+
+ // Read data
+ for(i = 0; i < 128*NumPages; i ++)
+ ((Uint16*)Buffer)[i] = inw(Card->IOBase + 0x10);
+
+
+ outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag
+
+ LEAVE('i', NumPages);
+ return NumPages;
+}
+
+/**
+ * \fn Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length)
+ */
+Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length)
+{
+ Uint8 ret = Card->NextMemPage;
+
+ Card->NextMemPage += (Length + 0xFF) >> 8;
+ if(Card->NextMemPage >= TX_LAST) {
+ Card->NextMemPage -= TX_BUF_SIZE;
+ }
+
+ return ret;
+}
+
+/**
+ * \fn void Ne2k_IRQHandler(int IntNum)
+ */
+void Ne2k_IRQHandler(int IntNum, void *Ptr)
+{
+ Uint8 byte;
+ tCard *card = Ptr;
+
+ if(card->IRQ != IntNum) return;
+
+ byte = inb( card->IOBase + ISR );
+
+ LOG("byte = 0x%02x", byte);
+
+
+ // Reset All (save for RDMA), that's polled
+ outb( card->IOBase + ISR, 0xFF&(~0x40) );
+
+ // 0: Packet recieved (no error)
+ if( byte & 1 )
+ {
+ //if( card->NumWaitingPackets > MAX_PACKET_QUEUE )
+ // card->NumWaitingPackets = MAX_PACKET_QUEUE;
+ if( Semaphore_Signal( &card->Semaphore, 1 ) != 1 ) {
+ // Oops?
+ }
+ }
+ // 1: Packet sent (no error)
+ // 2: Recieved with error
+ // 3: Transmission Halted (Excessive Collisions)
+ // 4: Recieve Buffer Exhausted
+ // 5:
+ // 6: Remote DMA Complete
+ // 7: Reset
+}
--- /dev/null
+#
+#
+
+OBJ = rtl8139.o
+NAME = RTL8139
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 PCnet-FAST III Driver
+ * - By John Hodge (thePowersGang)
+ */
+#define DEBUG 0
+#define VERSION ((0<<8)|10)
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <drv_pci.h>
+#include <api_drv_network.h>
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define VENDOR_ID 0x1022
+#define DEVICE_ID 0x2000
+
+enum eRegs
+{
+ REG_APROM0 = 0x00,
+ REG_APROM4 = 0x04,
+ REG_APROM8 = 0x08,
+ REG_APROMC = 0x0C,
+ REG_RDP = 0x10, // 16 bit
+ REG_RAP = 0x14, // 8-bit
+ REG_RESET = 0x18, // 16-bit
+ REG_BDP = 0x1C, // 16-bit
+};
+
+enum eCSR_Regs
+{
+ CSR_STATUS, // CSR0 - Am79C973/Am79C975 Controller Status
+ CSR_IBA0, // CSR1 - Initialization Block Address[15:0]
+ CSR_IBA1, // CSR2 - Initialization Block Address[31:16]
+ CSR_INTMASK, // CSR3 - Interrupt Masks and Deferral Control
+
+ CSR_MAC0 = 12, // CSR12 - Physical Address[15:0]
+ CSR_MAC1 = 13, // CSR13 - Physical Address[31:16]
+ CSR_MAC2 = 14, // CSR14 - Physical Address[47:32]
+ CSR_MODE = 15, // CSR15 - Mode
+
+ CSR_RXBASE0 = 24, // CSR24 - Base Address of Receive Ring Lower
+ CSR_RXBASE1 = 25, // CSR25 - Base Address of Receive Ring Upper
+ CSR_TXBASE0 = 30, // CSR26 - Base Address of Transmit Ring Lower
+ CSR_TXBASE1 = 31, // CSR27 - Base Address of Transmit Ring Upper
+
+ CSR_RXLENGTH = 76, // CSR76 - Receive Ring Length
+ CSR_TXLENGTH = 78, // CSR78 - Transmit Ring Length
+};
+
+enum eBCR_Regs
+{
+ BCR_PHYCS = 32, // BCR32 - Internal PHY Control and Status
+ BCR_PHYADDR, // BCR33 - Internal PHY Address
+ BCR_PHYMGMT, // BCR34 - Internal PHY Management Data
+};
+
+// === TYPES ===
+typedef struct sCard
+{
+ Uint16 IOBase;
+ Uint8 IRQ;
+
+ int NumWaitingPackets;
+
+ char Name[2];
+ tVFS_Node Node;
+ Uint8 MacAddr[6];
+} tCard;
+
+// === PROTOTYPES ===
+ int PCnet3_Install(char **Options);
+char *PCnet3_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename);
+ int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Arg);
+Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+ int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Arg);
+void PCnet3_IRQHandler(int Num);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, PCnet3, PCnet3_Install, NULL, NULL);
+tDevFS_Driver gPCnet3_DriverInfo = {
+ NULL, "PCnet3",
+ {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .ReadDir = PCnet3_ReadDir,
+ .FindDir = PCnet3_FindDir,
+ .IOCtl = PCnet3_RootIOCtl
+ }
+};
+ int giPCnet3_CardCount;
+tCard *gaPCnet3_Cards;
+
+// === CODE ===
+/**
+ * \brief Installs the PCnet3 Driver
+ */
+int PCnet3_Install(char **Options)
+{
+ int id = -1;
+ int i = 0;
+ Uint16 base;
+ tCard *card;
+
+ giPCnet3_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID);
+ Log_Debug("PCnet3", "%i cards", giPCnet3_CardCount);
+
+ if( giPCnet3_CardCount == 0 ) return MODULE_ERR_NOTNEEDED;
+
+ gaPCnet3_Cards = calloc( giPCnet3_CardCount, sizeof(tCard) );
+
+ while( (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1 )
+ {
+ card = &gaPCnet3_Cards[i];
+ base = PCI_GetBAR( id, 0 );
+ if( !(base & 1) ) {
+ Log_Warning("PCnet3", "Driver does not support MMIO, skipping card");
+ card->IOBase = 0;
+ card->IRQ = 0;
+ continue ;
+ }
+ base &= ~1;
+ card->IOBase = base;
+ card->IRQ = PCI_GetIRQ( id );
+
+ // Install IRQ Handler
+ IRQ_AddHandler(card->IRQ, PCnet3_IRQHandler);
+
+
+
+ Log_Log("PCnet3", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x",
+ i, card->IOBase, card->IRQ,
+ card->MacAddr[0], card->MacAddr[1], card->MacAddr[2],
+ card->MacAddr[3], card->MacAddr[4], card->MacAddr[5]
+ );
+
+ i ++;
+ }
+
+ gPCnet3_DriverInfo.RootNode.Size = giPCnet3_CardCount;
+ DevFS_AddDevice( &gPCnet3_DriverInfo );
+
+ return MODULE_ERR_OK;
+}
+
+// --- Root Functions ---
+char *PCnet3_ReadDir(tVFS_Node *Node, int Pos)
+{
+ if( Pos < 0 || Pos >= giPCnet3_CardCount ) return NULL;
+
+ return strdup( gaPCnet3_Cards[Pos].Name );
+}
+
+tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename)
+{
+ //TODO: It might be an idea to supprt >10 cards
+ if(Filename[0] == '\0' || Filename[1] != '\0') return NULL;
+ if(Filename[0] < '0' || Filename[0] > '9') return NULL;
+ return &gaPCnet3_Cards[ Filename[0]-'0' ].Node;
+}
+
+const char *csaPCnet3_RootIOCtls[] = {DRV_IOCTLNAMES, NULL};
+int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_RootIOCtls);
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+// --- File Functions ---
+Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tCard *card = Node->ImplPtr;
+ Uint16 read_ofs, pkt_length;
+ int new_read_ofs;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+retry:
+ if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 )
+ {
+ LEAVE_RET('i', 0);
+ }
+
+ Mutex_Acquire( &card->ReadMutex );
+
+ read_ofs = inw( card->IOBase + CAPR );
+ LOG("raw read_ofs = %i", read_ofs);
+ read_ofs = (read_ofs + 0x10) & 0xFFFF;
+ LOG("read_ofs = %i", read_ofs);
+
+ pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
+
+ // Calculate new read offset
+ new_read_ofs = read_ofs + pkt_length + 4;
+ new_read_ofs = (new_read_ofs + 3) & ~3; // Align
+ if(new_read_ofs > card->ReceiveBufferLength) {
+ LOG("wrapping read_ofs");
+ new_read_ofs -= card->ReceiveBufferLength;
+ }
+ new_read_ofs -= 0x10; // I dunno
+ LOG("new_read_ofs = %i", new_read_ofs);
+
+ // Check for errors
+ if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) {
+ // Update CAPR
+ outw(card->IOBase + CAPR, new_read_ofs);
+ Mutex_Release( &card->ReadMutex );
+ goto retry; // I feel evil
+ }
+
+ // Get packet
+ if( Length > pkt_length ) Length = pkt_length;
+ memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length);
+
+ // Update CAPR
+ outw(card->IOBase + CAPR, new_read_ofs);
+
+ Mutex_Release( &card->ReadMutex );
+
+ LEAVE('i', Length);
+
+ return Length;
+}
+
+Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ int td;
+ Uint32 status;
+ tCard *card = Node->ImplPtr;
+
+ if( Length > 1500 ) return 0; // MTU exceeded
+
+ ENTER("pNode XLength pBuffer", Node, Length, Buffer);
+
+ // TODO: Implement a semaphore for avaliable transmit buffers
+
+ // Find an avaliable descriptor
+ Mutex_Acquire(&card->CurTXProtector);
+ td = card->CurTXDescriptor;
+ card->CurTXDescriptor ++;
+ card->CurTXDescriptor %= 4;
+ Mutex_Release(&card->CurTXProtector);
+ // - Lock it
+ Mutex_Acquire( &card->TransmitInUse[td] );
+
+ LOG("td = %i", td);
+
+ // Transmit using descriptor `td`
+ LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]);
+ outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]);
+ LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]);
+ // Copy to buffer
+ memcpy(card->TransmitBuffers[td], Buffer, Length);
+ // Start
+ status = 0;
+ status |= Length & 0x1FFF; // 0-12: Length
+ status |= 0 << 13; // 13: OWN bit
+ status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check)
+ LOG("status = 0x%08x", status);
+ outd(card->IOBase + TSD0 + td*4, status);
+
+ LEAVE('i', (int)Length);
+
+ return Length;
+}
+
+const char *csaPCnet3_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL};
+int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tCard *card = Node->ImplPtr;
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_NodeIOCtls);
+ case NET_IOCTL_GETMAC:
+ if( !CheckMem(Data, 6) ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ memcpy( Data, card->MacAddr, 6 );
+ LEAVE('i', 1);
+ return 1;
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+void PCnet3_IRQHandler(int Num)
+{
+ int i, j;
+ tCard *card;
+ Uint16 status;
+
+ LOG("Num = %i", Num);
+
+ for( i = 0; i < giPCnet3_CardCount; i ++ )
+ {
+ card = &gaPCnet3_Cards[i];
+ if( Num != card->IRQ ) break;
+
+ status = inw(card->IOBase + ISR);
+ LOG("status = 0x%02x", status);
+
+ // Transmit OK, a transmit descriptor is now free
+ if( status & FLAG_ISR_TOK )
+ {
+ for( j = 0; j < 4; j ++ )
+ {
+ if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK
+ Mutex_Release( &card->TransmitInUse[j] );
+ // TODO: Update semaphore once implemented
+ }
+ }
+ outw(card->IOBase + ISR, FLAG_ISR_TOK);
+ }
+
+ // Recieve OK, inform read
+ if( status & FLAG_ISR_ROK )
+ {
+ int read_ofs, end_ofs;
+ int packet_count = 0;
+ int len;
+
+ // Scan recieve buffer for packets
+ end_ofs = inw(card->IOBase + CBA);
+ read_ofs = card->SeenOfs;
+ LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs);
+ if( read_ofs > end_ofs )
+ {
+ while( read_ofs < card->ReceiveBufferLength )
+ {
+ packet_count ++;
+ len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
+ LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
+ packet_count, read_ofs,
+ *(Uint16*)&card->ReceiveBuffer[read_ofs],
+ len
+ );
+ if(len > 2000) {
+ Log_Warning("PCnet3", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len);
+ }
+ read_ofs += len + 4;
+ read_ofs = (read_ofs + 3) & ~3; // Align
+ }
+ read_ofs -= card->ReceiveBufferLength;
+ LOG("wrapped read_ofs");
+ }
+ while( read_ofs < end_ofs )
+ {
+ packet_count ++;
+ LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
+ packet_count, read_ofs,
+ *(Uint16*)&card->ReceiveBuffer[read_ofs],
+ *(Uint16*)&card->ReceiveBuffer[read_ofs+2]
+ );
+ read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4;
+ read_ofs = (read_ofs + 3) & ~3; // Align
+ }
+ if( read_ofs != end_ofs ) {
+ Log_Warning("PCnet3", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs);
+ read_ofs = end_ofs;
+ }
+ card->SeenOfs = read_ofs;
+
+ LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs);
+
+ if( packet_count )
+ {
+ if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) {
+ // Oops?
+ }
+ VFS_MarkAvaliable( &card->Node, 1 );
+ }
+
+ outw(card->IOBase + ISR, FLAG_ISR_ROK);
+ }
+ }
+}
--- /dev/null
+#
+#
+
+OBJ = rtl8139.o
+NAME = RTL8139
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 RTL8139 Driver
+ * - By John Hodge (thePowersGang)
+ */
+#define DEBUG 0
+#define VERSION ((0<<8)|20)
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <drv_pci.h>
+#include <api_drv_network.h>
+#include <semaphore.h>
+
+// === CONSTANTS ===
+#define VENDOR_ID 0x10EC
+#define DEVICE_ID 0x8139
+
+enum eRTL8139_Regs
+{
+ // MAC Address
+ MAC0, MAC1, MAC2,
+ MAC3, MAC4, MAC5,
+
+ // Multicast Registers
+ MAR0 = 0x08, MAR1, MAR2, MAR3,
+ MAR4, MAR5, MAR6, MAR7,
+
+ // Transmit status of descriptors 0 - 3
+ TSD0 = 0x10, TSD1 = 0x14,
+ TSD2 = 0x18, TSD3 = 0x1C,
+ // Transmit start addresses
+ TSAD0 = 0x20, TSAD1 = 0x24,
+ TSAD2 = 0x28, TSAD3 = 0x2C,
+
+ RBSTART = 0x30, //!< Recieve Buffer Start (DWord)
+ // Early Recieve Byte Count
+ ERBCR = 0x34, // 16-bits
+ // Early RX Status Register
+ ERSR = 0x36,
+
+ // ??, ??, ??, RST, RE, TE, ??, ??
+ CMD = 0x37,
+
+ CAPR = 0x38, // Current address of packet read
+ CBA = 0x3A, // Current Buffer Address - Total byte count in RX buffer
+
+ IMR = 0x3C, // Interrupt mask register
+ ISR = 0x3E, // Interrupt status register
+
+ TCR = 0x40, // Transmit Configuration Register
+ RCR = 0x44, // Recieve Configuration Register
+ TCTR = 0x48, // 32-bit timer (count)
+ MPC = 0x4C, // Missed packet count (due to RX overflow)
+
+ CR_9346 = 0x50,
+ CONFIG0 = 0x51,
+ CONFIG1 = 0x52,
+ // 0x53 resvd
+ TIMERINT = 0x54, // Fires a timeout when TCTR equals this value
+
+};
+
+#define FLAG_ISR_TOK 0x04
+#define FLAG_ISR_ROK 0x01
+
+// === TYPES ===
+typedef struct sCard
+{
+ Uint16 IOBase;
+ Uint8 IRQ;
+
+ int NumWaitingPackets;
+
+ char *ReceiveBuffer;
+ tPAddr PhysReceiveBuffer;
+ int ReceiveBufferLength;
+ int SeenOfs; //!< End of the most recently seen packet (by IRQ)
+ tMutex ReadMutex;
+ tSemaphore ReadSemaphore;
+
+ char *TransmitBuffers[4];
+ tPAddr PhysTransmitBuffers[4];
+ tMutex TransmitInUse[4];
+ tMutex CurTXProtector; //!< Protects \a .CurTXDescriptor
+ int CurTXDescriptor;
+
+ char Name[2];
+ tVFS_Node Node;
+ Uint8 MacAddr[6];
+} tCard;
+
+// === PROTOTYPES ===
+ int RTL8139_Install(char **Options);
+char *RTL8139_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename);
+ int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Arg);
+Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Arg);
+void RTL8139_IRQHandler(int Num, void *Ptr);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, RTL8139, RTL8139_Install, NULL, NULL);
+tVFS_NodeType gRTL8139_RootNodeType = {
+ .ReadDir = RTL8139_ReadDir,
+ .FindDir = RTL8139_FindDir,
+ .IOCtl = RTL8139_IOCtl
+ };
+tVFS_NodeType gRTL8139_DevNodeType = {
+ .Write = RTL8139_Write,
+ .Read = RTL8139_Read,
+ .IOCtl = RTL8139_IOCtl
+ };
+tDevFS_Driver gRTL8139_DriverInfo = {
+ NULL, "RTL8139",
+ {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gRTL8139_RootNodeType
+ }
+};
+ int giRTL8139_CardCount;
+tCard *gaRTL8139_Cards;
+
+// === CODE ===
+/**
+ * \brief Installs the RTL8139 Driver
+ */
+int RTL8139_Install(char **Options)
+{
+ int id = -1;
+ int i = 0;
+ Uint16 base;
+ tCard *card;
+
+ giRTL8139_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID);
+
+ if( giRTL8139_CardCount == 0 ) return MODULE_ERR_NOTNEEDED;
+
+ Log_Debug("RTL8139", "%i cards", giRTL8139_CardCount);
+ gaRTL8139_Cards = calloc( giRTL8139_CardCount, sizeof(tCard) );
+
+ for( i = 0 ; (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1; i ++ )
+ {
+ card = &gaRTL8139_Cards[i];
+ base = PCI_GetBAR( id, 0 );
+ if( !(base & 1) ) {
+ Log_Warning("RTL8139", "Driver does not support MMIO, skipping card (addr %x)",
+ base);
+ card->IOBase = 0;
+ card->IRQ = 0;
+ continue ;
+ }
+ base &= ~1;
+ card->IOBase = base;
+ card->IRQ = PCI_GetIRQ( id );
+
+ // Install IRQ Handler
+ IRQ_AddHandler(card->IRQ, RTL8139_IRQHandler, card);
+
+ // Power on
+ outb( base + CONFIG1, 0x00 );
+
+ // Reset (0x10 to CMD)
+ outb( base + CMD, 0x10 );
+ while( inb(base + CMD) & 0x10 ) ;
+
+ // Set up recieve buffer
+ // - Allocate 3 pages below 4GiB for the recieve buffer (Allows 8k+16+1500)
+ card->ReceiveBuffer = (void*)MM_AllocDMA( 3, 32, &card->PhysReceiveBuffer );
+ card->ReceiveBufferLength = 8*1024;
+ outd(base + RBSTART, (Uint32)card->PhysReceiveBuffer);
+ outd(base + CBA, 0);
+ outd(base + CAPR, 0);
+ // Set IMR to Transmit OK and Receive OK
+ outw(base + IMR, 0x5);
+
+ // Set up transmit buffers
+ // - 2 non-contiguous pages (each page can fit 2 1500 byte packets)
+ card->TransmitBuffers[0] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[0] );
+ card->TransmitBuffers[1] = card->TransmitBuffers[0] + 0x800;
+ card->PhysTransmitBuffers[1] = card->PhysTransmitBuffers[0] + 0x800;
+
+ card->TransmitBuffers[2] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[2] );
+ card->TransmitBuffers[3] = card->TransmitBuffers[2] + 0x800;
+ card->PhysTransmitBuffers[3] = card->PhysTransmitBuffers[2] + 0x800;
+
+ outd(base + TSAD0, card->PhysTransmitBuffers[0]);
+ outd(base + TSAD1, card->PhysTransmitBuffers[1]);
+ outd(base + TSAD2, card->PhysTransmitBuffers[2]);
+ outd(base + TSAD3, card->PhysTransmitBuffers[3]);
+
+ // Set recieve buffer size and recieve mask
+ // - Bit 7 being set tells the card to overflow the recieve buffer if needed
+ // (i.e. when the packet starts at the end of the bufffer, it overflows up
+ // to 1500 bytes)
+ outd(base + RCR, 0x8F);
+
+ // Recive Enable and Transmit Enable
+ outb(base + CMD, 0x0C);
+
+ // Get the card's MAC address
+ card->MacAddr[0] = inb(base+MAC0);
+ card->MacAddr[1] = inb(base+MAC1);
+ card->MacAddr[2] = inb(base+MAC2);
+ card->MacAddr[3] = inb(base+MAC3);
+ card->MacAddr[4] = inb(base+MAC4);
+ card->MacAddr[5] = inb(base+MAC5);
+
+ // Set VFS Node
+ card->Name[0] = '0'+i;
+ card->Name[1] = '\0';
+ card->Node.ImplPtr = card;
+ card->Node.NumACLs = 0;
+ card->Node.CTime = now();
+ card->Node.Type = &gRTL8139_DevNodeType;
+
+ Log_Log("RTL8139", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x",
+ i, card->IOBase, card->IRQ,
+ card->MacAddr[0], card->MacAddr[1], card->MacAddr[2],
+ card->MacAddr[3], card->MacAddr[4], card->MacAddr[5]
+ );
+ }
+
+ gRTL8139_DriverInfo.RootNode.Size = giRTL8139_CardCount;
+ DevFS_AddDevice( &gRTL8139_DriverInfo );
+
+ return MODULE_ERR_OK;
+}
+
+// --- Root Functions ---
+char *RTL8139_ReadDir(tVFS_Node *Node, int Pos)
+{
+ if( Pos < 0 || Pos >= giRTL8139_CardCount ) return NULL;
+
+ return strdup( gaRTL8139_Cards[Pos].Name );
+}
+
+tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename)
+{
+ //TODO: It might be an idea to supprt >10 cards
+ if(Filename[0] == '\0' || Filename[1] != '\0') return NULL;
+ if(Filename[0] < '0' || Filename[0] > '9') return NULL;
+ return &gaRTL8139_Cards[ Filename[0]-'0' ].Node;
+}
+
+const char *csaRTL8139_RootIOCtls[] = {DRV_IOCTLNAMES, NULL};
+int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_RootIOCtls);
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+// --- File Functions ---
+Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tCard *card = Node->ImplPtr;
+ Uint16 read_ofs, pkt_length;
+ int new_read_ofs;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+retry:
+ if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 )
+ {
+ LEAVE_RET('i', 0);
+ }
+
+ Mutex_Acquire( &card->ReadMutex );
+
+ read_ofs = inw( card->IOBase + CAPR );
+ LOG("raw read_ofs = %i", read_ofs);
+ read_ofs = (read_ofs + 0x10) & 0xFFFF;
+ LOG("read_ofs = %i", read_ofs);
+
+ pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
+
+ // Calculate new read offset
+ new_read_ofs = read_ofs + pkt_length + 4;
+ new_read_ofs = (new_read_ofs + 3) & ~3; // Align
+ if(new_read_ofs > card->ReceiveBufferLength) {
+ LOG("wrapping read_ofs");
+ new_read_ofs -= card->ReceiveBufferLength;
+ }
+ new_read_ofs -= 0x10; // I dunno
+ LOG("new_read_ofs = %i", new_read_ofs);
+
+ // Check for errors
+ if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) {
+ // Update CAPR
+ outw(card->IOBase + CAPR, new_read_ofs);
+ Mutex_Release( &card->ReadMutex );
+ goto retry; // I feel evil
+ }
+
+ // Get packet
+ if( Length > pkt_length ) Length = pkt_length;
+ memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length);
+
+ // Update CAPR
+ outw(card->IOBase + CAPR, new_read_ofs);
+
+ Mutex_Release( &card->ReadMutex );
+
+ LEAVE('i', Length);
+
+ return Length;
+}
+
+Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ int td;
+ Uint32 status;
+ tCard *card = Node->ImplPtr;
+
+ if( Length > 1500 ) return 0; // MTU exceeded
+
+ ENTER("pNode XLength pBuffer", Node, Length, Buffer);
+
+ // TODO: Implement a semaphore for avaliable transmit buffers
+
+ // Find an avaliable descriptor
+ Mutex_Acquire(&card->CurTXProtector);
+ td = card->CurTXDescriptor;
+ card->CurTXDescriptor ++;
+ card->CurTXDescriptor %= 4;
+ Mutex_Release(&card->CurTXProtector);
+ // - Lock it
+ Mutex_Acquire( &card->TransmitInUse[td] );
+
+ LOG("td = %i", td);
+
+ // Transmit using descriptor `td`
+ LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]);
+ outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]);
+ LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]);
+ // Copy to buffer
+ memcpy(card->TransmitBuffers[td], Buffer, Length);
+ // Start
+ status = 0;
+ status |= Length & 0x1FFF; // 0-12: Length
+ status |= 0 << 13; // 13: OWN bit
+ status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check)
+ LOG("status = 0x%08x", status);
+ outd(card->IOBase + TSD0 + td*4, status);
+
+ LEAVE('i', (int)Length);
+
+ return Length;
+}
+
+const char *csaRTL8139_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL};
+int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tCard *card = Node->ImplPtr;
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_NodeIOCtls);
+ case NET_IOCTL_GETMAC:
+ if( !CheckMem(Data, 6) ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ memcpy( Data, card->MacAddr, 6 );
+ LEAVE('i', 1);
+ return 1;
+ }
+ LEAVE('i', 0);
+ return 0;
+}
+
+void RTL8139_IRQHandler(int Num, void *Ptr)
+{
+ int j;
+ tCard *card = Ptr;
+ Uint16 status;
+
+ LOG("Num = %i", Num);
+
+ if( Num != card->IRQ ) return;
+
+ status = inw(card->IOBase + ISR);
+ LOG("status = 0x%02x", status);
+
+ // Transmit OK, a transmit descriptor is now free
+ if( status & FLAG_ISR_TOK )
+ {
+ for( j = 0; j < 4; j ++ )
+ {
+ if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK
+ Mutex_Release( &card->TransmitInUse[j] );
+ // TODO: Update semaphore once implemented
+ }
+ }
+ outw(card->IOBase + ISR, FLAG_ISR_TOK);
+ }
+
+ // Recieve OK, inform read
+ if( status & FLAG_ISR_ROK )
+ {
+ int read_ofs, end_ofs;
+ int packet_count = 0;
+ int len;
+
+ // Scan recieve buffer for packets
+ end_ofs = inw(card->IOBase + CBA);
+ read_ofs = card->SeenOfs;
+ LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs);
+ if( read_ofs > end_ofs )
+ {
+ while( read_ofs < card->ReceiveBufferLength )
+ {
+ packet_count ++;
+ len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
+ LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
+ packet_count, read_ofs,
+ *(Uint16*)&card->ReceiveBuffer[read_ofs],
+ len
+ );
+ if(len > 2000) {
+ Log_Warning("RTL8139", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len);
+ }
+ read_ofs += len + 4;
+ read_ofs = (read_ofs + 3) & ~3; // Align
+ }
+ read_ofs -= card->ReceiveBufferLength;
+ LOG("wrapped read_ofs");
+ }
+ while( read_ofs < end_ofs )
+ {
+ packet_count ++;
+ LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
+ packet_count, read_ofs,
+ *(Uint16*)&card->ReceiveBuffer[read_ofs],
+ *(Uint16*)&card->ReceiveBuffer[read_ofs+2]
+ );
+ read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4;
+ read_ofs = (read_ofs + 3) & ~3; // Align
+ }
+ if( read_ofs != end_ofs ) {
+ Log_Warning("RTL8139", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs);
+ read_ofs = end_ofs;
+ }
+ card->SeenOfs = read_ofs;
+
+ LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs);
+
+ if( packet_count )
+ {
+ if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) {
+ // Oops?
+ }
+ VFS_MarkAvaliable( &card->Node, 1 );
+ }
+
+ outw(card->IOBase + ISR, FLAG_ISR_ROK);
+ }
+}
--- /dev/null
+#
+#
+
+OBJ = main.o
+NAME = SoundBlaster16
+
+-include ../Makefile.tpl
--- /dev/null
+/*\r
+ * Acess2 SoundBlaster16 Driver\r
+ */\r
+#define DEBUG 0\r
+#include <acess.h>\r
+#include <errno.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+#include <api_drv_sound.h>\r
+\r
+#define INT\r
+\r
+// === TYPES ===\r
+typedef struct sSB16\r
+{\r
+ Uint16 Base;\r
+} tSB16;\r
+\r
+// === CONSTANTS ===\r
+enum {\r
+ SB16_PORT_RESET = 0x6,\r
+ SB16_PORT_READ = 0xA,\r
+ SB16_PORT_WRITE = 0xC,\r
+ SB16_PORT_AVAIL = 0xE\r
+};\r
+#define SB16_BASE_PORT 0x200\r
+enum {\r
+ SB16_CMD_RAW8 = 0x10, // 8-bit DAC value follows\r
+ SB16_CMD_DMAFREQ = 0x40, // followed by TIME_CONSTANT = 256 - 1000000 / frequency\r
+ SB16_CMD_DMASTOP = 0xD0,\r
+ SB16_CMD_SPKRON = 0xD1,\r
+ SB16_CMD_SPKROFF = 0xD3,\r
+ SB16_CMD_DMACONT = 0xD4,\r
+ \r
+ // DMA Types (uses channel 1)\r
+ // - Followed by 16-bit length (well, length - 1)\r
+ SB16_CMD_DMA_8BIT = 0x14,\r
+};\r
+\r
+\r
+// === PROTOTYPES ===\r
+// Driver\r
+ int SB16_Install(char **Arguments);\r
+void SB16_Uninstall();\r
+// Filesystem\r
+Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
+ int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, 0x0032, SoundBlaster16, SB16_Install, SB16_Uninstall, "PCI", NULL);\r
+tDevFS_Driver gBGA_DriverStruct = {\r
+ NULL, "SoundBlaster16",\r
+ {\r
+ .Write = SB16_Write,\r
+ .IOCtl = SB16_IOCtl\r
+ }\r
+};\r
+\r
+// === CODE ===\r
+int SB16_Install(char **Arguments)\r
+{\r
+ int jumper_port_setting = 1; // 1-6 incl\r
+ \r
+ // Reset\r
+ outb(card->Base+SB16_PORT_RESET, 1);\r
+ // - Wait 3us\r
+ outb(card->Base+SB16_PORT_RESET, 0);\r
+ \r
+ SB16_ReadDSP(card);\r
+ \r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+void SB16_Uninstall()\r
+{\r
+}\r
+\r
+/**\r
+ * \fn Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+ * \brief Write to the framebuffer\r
+ */\r
+Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+{ \r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+ * \brief Handle messages to the device\r
+ */\r
+int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+{\r
+ return -1;\r
+}\r
+\r
+//\r
+int SB16_WriteDSP(tSB16 *Card, Uint8 Value)\r
+{\r
+ // Wait for the card to be ready\r
+ while( inb(Card->Base+SB16_PORT_WRITE) & 0x80 )\r
+ ;\r
+ \r
+ outb(Card->Base+SB16_PORT_WRITE, Value);\r
+ \r
+ return 0;\r
+}\r
+\r
+Uint8 SB16_ReadDSP(tSB16 *Card)\r
+{\r
+ // Wait for bit 7 of AVAIL\r
+ while( !(inb(card->Base+SB16_PORT_AVAIL) & 0x80) )\r
+ ;\r
+ return inb(card->Base+SB16_PORT_READ);\r
+}\r
--- /dev/null
+
+ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
+ ³ Programming the SoundBlaster DSP ³
+ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+ Written for the PC-GPE by Mark Feldman
+
+ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
+ ³ THIS FILE MAY NOT BE DISTRIBUTED ³
+ ³ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. ³
+ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Disclaimer ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+I assume no responsibility whatsoever for any effect that this file, the
+information contained therein or the use thereof has on you, your sanity,
+computer, spouse, children, pets or anything else related to you or your
+existance. No warranty is provided nor implied with this information.
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Introduction ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+The SoundBlaster is capable of both FM and digitised sounds. The FM wave
+is fully Adlib compatible, so check the ADLIB.TXT file for info
+on how to program it. This file will concentrate on recording and playback
+of digital samples through the SoundBlaster CT-DSP 1321 chip.
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ The SoundBlaster DSP I/O Ports ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+The DSP (Digital Sound Processor) chip is programmed through 4 ports which
+are determined by the SoundBlaster base address jumper setting:
+
+ RESET 2x6h
+
+ READ DATA 2xAh
+
+WRITE COMMAND/DATA output
+WRITE BUFFER STATUS input 2xCh
+
+
+ DATA AVAILABLE 2xEh
+
+where x = 1 for base address jumper setting 210h
+ x = 2 for base address jumper setting 220h
+ .
+ .
+ x = 6 for base address jumper setting 260h
+
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Resetting the DSP ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+You have to reset the DSP before you program it. This is done with the
+following procedure :
+
+1) Write a 1 to the SoundBlaster RESET port (2x6h)
+2) Wait for 3 micro-seconds
+3) Write a 0 to the SoundBlaster RESET port (2x6h)
+4) Read the byte from the DATA AVAILABLE (2xEh) port until bit 7 = 1
+5) Poll for a ready byte (AAh) from the READ DATA port (2xAh). Before
+ reading the READ DATA port it is avdvisable.
+
+The DSP usually takes somewhere around 100 micro-seconds to reset itself.
+If it fails to do within a reasonable time (say 200 micro-seconds) then
+an error has occurred, possibly an incorrect I/O address is being used.
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Writing to the DSP ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+A value can be written to the DSP with the following procedure :
+
+1) Read the DSP's WRITE BUFFER STATUS port (2xCh) until bit 7 = 0
+2) Write the value to the WRITE COMMAND/DATA port (2xCh)
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Reading the DSP ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+A value can be read from the DSP with the following procedure :
+
+1) Read the DSP's DATA AVAILABLE port (2xEh) until bit 7 = 1
+2) Read the data from the READ DATA port (2xAh)
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Turning the speaker on and controlling DMA ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+Speaker and DMA control are handled by writing one of the following bytes
+to the DSP:
+
+ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
+ ³ Value Description ³
+ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
+ ³ D0h DMA Stop ³
+ ³ D1h Turn speaker on ³
+ ³ D3h Turn speaker off ³
+ ³ D4h DMA Continue ³
+ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+DMA is discussed below. The DMA commands shown here can be used to pause
+the sample during DMA playback playback.
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Writing to the DAC ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+The DAC (Digital to Analog Converter) is the part of the card which converts
+a sample number (ie 0 -> 255) to a sound level. To generate a square sound
+wave at maximum volume (for example) you could alternate writing 0's and
+255's to the DAC.
+
+Programming the DAC in direct mode involves the main program setting the
+DAC to a desired value. Only 8 bit DAC is available in direct mode. To set
+the DAC level you write the value 10h to the DSP followed by the sample
+number (0 -> 255). Note that no sound will be heard unless the speaker has
+been turned on. In direct mode the main program is responsible for the
+timing between samples, the DAC can output sound samples as fast as the
+calling program can change it. Typically the timer interrupt is reprogrammed
+and used to generate the timing required for a sample playback. Info on
+programming the PIT chip can be found in the PIT.TXT file.
+
+The DAC can also be programmed to accept values sent to it via the DMA
+chip. Draeden has written an excellent article on programming the DMA chip
+(see DMA_VLA.TXT) so only a brief example of it's use will be given here.
+The important thing to remember is that the DMA chip cannot transfer data
+which crosses between page breaks. If the data does cross page breaks then
+it will have to be split up into several transfers, with one page per
+transfer.
+
+Setting the playback frequency for the DMA transfer is done by writing
+the value 40h to the DSP followed by TIME_CONSTANT, where
+TIME_CONSTANT = 256 - 1000000 / frequency
+
+There are several types of DMA transfers available. The following table
+lists them:
+
+ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
+ ³DMA_TYPE_VALUE Description Frequency Range ³
+ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
+ ³ 14h 8 bit 4KHz -> 23 KHz ³
+ ³ 74h 4 bit ADPCM 4KHz -> 12 KHz ³
+ ³ 75h 4 bit ADPCM with 4KHz -> 12 KHz ³
+ ³ reference byte ³
+ ³ 76h 2.6 bit ADPCM 4KHz -> 13 KHz ³
+ ³ 77h 2.6 bit ADPCM with 4KHz -> 13 KHz ³
+ ³ reference byte ³
+ ³ 16h 2 bit ADPCM 4KHz -> 11 KHz ³
+ ³ 17h 2 bit ADPCM with 4KHz -> 11 KHz ³
+ ³ reference byte ³
+ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+ADPCM stands for Adaptive Pulse Code Modulation, a sound compression
+technique where the difference between successive samples is stored rather
+than their actual values. In the modes with reference bytes, the first
+byte is the actual starting value. Having modes with and without reference
+bytes means you can output successive blocks without the need for a
+reference byte at the start of each one.
+
+The procedure for doing a DMA transfer is as follows:
+
+1) Load the sound data into memory
+2) Set up the DMA chip for the tranfer
+3) Set the DSP TIME_CONSTANT to the sampling rate
+4) Write DMA_TYPE_VALUE value to the DSP
+5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where
+ DATA_LENGTH = number of bytes to send - 1
+
+Note that the DMA chip must be programmed before the BSP.
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Reading from the ADC ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+Reading samples from the ADC (Analog to Digital Converter) can also be
+done in either direct or DMA mode.
+
+To read a sample in direct mode write the value 20h to the DSP and then
+read the value from the DSP. Simple as that!
+
+To set up the DSP for a DMA transfer, follow this procedure :
+
+1) Get a memory buffer ready to hold the sample
+2) Set up the DMA chip for the transfer
+3) Set the DSP TIME_CONSTANT to the sampling rate
+4) Write the value 24h to the DSP
+5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where
+ DATA_LENGTH = number of bytes to read - 1
+
+Note that the DMA chip must be programmed before the BSP.
+
+DMA reads only support 8 bit mode, compressed modes are done by software and
+stored in the voc file. I haven't tried to figure out how the compression is
+done. If someone does figure it out I'd like to know about it!
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Programming the DMA Chip ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+As mentioned before, Draeden has written a very good article on the dma
+chip, but here is a brief run down on what you would need to do to program
+the DMA channel 1 for the DSP in real mode:
+
+1) Calculate the 20 bit address of the memory buffer you are using
+ where Base Address = Segment * 16 + Offset
+ eg 1234h:5678h = 179B8h
+2) Send the value 05h to port 0Ah (mask off channel 1)
+3) Send the value 00h to port 0Ch (clear the internal DMA flip/flop)
+4) Send the value 49h to port 0Bh (for playback) or
+ 45h to port 0Bh (for recording)
+5) Write the LSB (bits 0 -> 7) of the 20 bit memory address to port 02h
+6) Write the MSB (bits 8 -> 15) of the 20 bit memory address to ort 02h
+7) Write the Page (bits 16 -> 19) of the 20 bit memory address to port 83h
+8) Send the LSB of DATA_LENGTH to port 03h
+9) Send the MSB of DATA_LENGTH to port 03h
+10) Send the value 01h to port 0Ah (enable channel 1)
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ End of DMA Interrupt ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+When a DMA transfer is complete an interrupt is generated. The actual
+interrupt number depends on the SoundBlaster card's IRQ jumper setting:
+
+ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
+ ³ IRQ Jumper ³
+ ³ Setting Interrupt ³
+ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
+ ³ 2 0Ah ³
+ ³ 3 0Bh ³
+ ³ 5 0Dh ³
+ ³ 7 0Fh ³
+ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+To service one of these interrupts you must perform these 3 tasks:
+
+1) Acknowledge the DSP interrupt by reading the DATA AVAILABLE port (2xEh)
+ once.
+2) If there are more blocks to transfer then set them up
+3) Output value 20h (EOI) to the interrupt controller port 20h
+
+Of course, as with any hardware interrupt you must also leave the
+state of the system (registers etc..) the way it was when the interrupt
+was called.
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ A Simple DSP Pascal Unit ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+{
+
+ DSP.PAS - A demo SoundBlaster DSP unit for real mode
+
+ By Mark Feldman
+}
+
+Unit DSP;
+
+Interface
+
+{ ResetDSP returns true if reset was successful
+ base should be 1 for base address 210h, 2 for 220h etc... }
+function ResetDSP(base : word) : boolean;
+
+{ Write DAC sets the speaker output level }
+procedure WriteDAC(level : byte);
+
+{ ReadDAC reads the microphone input level }
+function ReadDAC : byte;
+
+{ SpeakerOn connects the DAC to the speaker }
+function SpeakerOn: byte;
+
+{ SpeakerOff disconnects the DAC from the speaker,
+ but does not affect the DAC operation }
+function SpeakerOff: byte;
+
+{ Functions to pause DMA playback }
+procedure DMAStop;
+procedure DMAContinue;
+
+{ Playback plays a sample of a given size back at a given frequency using
+ DMA channel 1. The sample must not cross a page boundry }
+procedure Playback(sound : Pointer; size : word; frequency : word);
+
+Implementation
+
+Uses Crt;
+
+var DSP_RESET : word;
+ DSP_READ_DATA : word;
+ DSP_WRITE_DATA : word;
+ DSP_WRITE_STATUS : word;
+ DSP_DATA_AVAIL : word;
+
+function ResetDSP(base : word) : boolean;
+begin
+
+ base := base * $10;
+
+ { Calculate the port addresses }
+ DSP_RESET := base + $206;
+ DSP_READ_DATA := base + $20A;
+ DSP_WRITE_DATA := base + $20C;
+ DSP_WRITE_STATUS := base + $20C;
+ DSP_DATA_AVAIL := base + $20E;
+
+ { Reset the DSP, and give some nice long delays just to be safe }
+ Port[DSP_RESET] := 1;
+ Delay(10);
+ Port[DSP_RESET] := 0;
+ Delay(10);
+ if (Port[DSP_DATA_AVAIL] And $80 = $80) And
+ (Port[DSP_READ_DATA] = $AA) then
+ ResetDSP := true
+ else
+ ResetDSP := false;
+end;
+
+procedure WriteDSP(value : byte);
+begin
+ while Port[DSP_WRITE_STATUS] And $80 <> 0 do;
+ Port[DSP_WRITE_DATA] := value;
+end;
+
+function ReadDSP : byte;
+begin
+ while Port[DSP_DATA_AVAIL] and $80 = 0 do;
+ ReadDSP := Port[DSP_READ_DATA];
+end;
+
+procedure WriteDAC(level : byte);
+begin
+ WriteDSP($10);
+ WriteDSP(level);
+end;
+
+function ReadDAC : byte;
+begin
+ WriteDSP($20);
+ ReadDAC := ReadDSP;
+end;
+
+function SpeakerOn: byte;
+begin
+ WriteDSP($D1);
+end;
+
+function SpeakerOff: byte;
+begin
+ WriteDSP($D3);
+end;
+
+procedure DMAContinue;
+begin
+ WriteDSP($D4);
+end;
+
+procedure DMAStop;
+begin
+ WriteDSP($D0);
+end;
+
+procedure Playback(sound : Pointer; size : word; frequency : word);
+var time_constant : word;
+ page, offset : word;
+begin
+
+ SpeakerOn;
+
+ size := size - 1;
+
+ { Set up the DMA chip }
+ offset := Seg(sound^) Shl 4 + Ofs(sound^);
+ page := (Seg(sound^) + Ofs(sound^) shr 4) shr 12;
+ Port[$0A] := 5;
+ Port[$0C] := 0;
+ Port[$0B] := $49;
+ Port[$02] := Lo(offset);
+ Port[$02] := Hi(offset);
+ Port[$83] := page;
+ Port[$03] := Lo(size);
+ Port[$03] := Hi(size);
+ Port[$0A] := 1;
+
+ { Set the playback frequency }
+ time_constant := 256 - 1000000 div frequency;
+ WriteDSP($40);
+ WriteDSP(time_constant);
+
+ { Set the playback type (8-bit) }
+ WriteDSP($14);
+ WriteDSP(Lo(size));
+ WriteDSP(Hi(size));
+end;
+
+end.
+
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ References ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+Title : The SoundBlaster Developpers Kit
+Publishers : Creative Labs Inc
+ Creative Technology PTE LTD
+
+Title : Sound Blaster - The Official Book
+Authors : Richard Heimlich, David M. Golden, Ivan Luk, Peter M. Ridge
+Publishers : Osborne/McGraw Hill
+ISBN : 0-07-881907-5
+
+Some of the information in this file was either obtained from or verified
+by the source code in a public domain library called SOUNDX by Peter
+Sprenger. I haven't tried using his library yet (I don't have a C compiler
+at the moment) but it looks very well done and contains numerous sound card
+detection routines. Says Peter : "It would be nice, that when you make
+something commercial with my routines, that you send me a copy of your
+project or send me some bucks, just enough for pizza and coke to support my
+night programming sessions. If you send me nothing, ok. But USE the stuff,
+if you can need it!". Heh...a REAL programmer!
+
+ftpsite: ftp.uwp.edu
+directory: /pub/msdos/demos/programming/game-dev/source
+filename: soundx.zip
+
+ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+³ Sound Familiar? ³
+ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
+
+What the...why is there a faint glimmer of sunlight outside? HOLY $#!^!! It's
+5:30am! I'm goin' to bed!
+
--- /dev/null
+#
+#
+
+OBJ = main.o io.o mbr.o
+NAME = ATA
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 IDE Harddisk Driver
+ * - main.c
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <acess.h>
+#include <vfs.h>
+
+// === CONSTANTS ===
+#define MAX_ATA_DISKS 4
+#define SECTOR_SIZE 512
+#define ATA_TIMEOUT 2000 // 2s timeout
+// Needed out of io.c because it's the max for Read/WriteDMA
+#define MAX_DMA_SECTORS (0x1000 / SECTOR_SIZE)
+
+// === STRUCTURES ===
+typedef struct
+{
+ Uint8 BootCode[0x1BE];
+ struct {
+ Uint8 Boot;
+ Uint8 Unused1; // Also CHS Start
+ Uint16 StartHi; // Also CHS Start
+ Uint8 SystemID;
+ Uint8 Unused2; // Also CHS Length
+ Uint16 LengthHi; // Also CHS Length
+ Uint32 LBAStart;
+ Uint32 LBALength;
+ } __attribute__ ((packed)) Parts[4];
+ Uint16 BootFlag; // = 0xAA 55
+} __attribute__ ((packed)) tMBR;
+
+typedef struct
+{
+ Uint64 Start;
+ Uint64 Length;
+ char Name[4];
+ tVFS_Node Node;
+} tATA_Partition;
+
+typedef struct
+{
+ Uint64 Sectors;
+ char Name[2];
+ tVFS_Node Node;
+ int NumPartitions;
+ tATA_Partition *Partitions;
+} tATA_Disk;
+
+// === GLOBALS ===
+extern tATA_Disk gATA_Disks[];
+
+// === FUNCTIONS ===
+// --- Common ---
+extern void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length);
+
+// --- MBR Parsing ---
+extern void ATA_ParseMBR(int Disk, tMBR *MBR);
+
+// --- IO Functions ---
+extern int ATA_SetupIO(void);
+extern Uint64 ATA_GetDiskSize(int Disk);
+extern int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer);
+extern int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer);
+
+#endif
--- /dev/null
+/*
+ * Acess2 IDE Harddisk Driver
+ * - io.c
+ *
+ * Disk Input/Output control
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <modules.h> // Needed for error codes
+#include <drv_pci.h>
+#include "common.h"
+
+// === MACROS ===
+#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
+
+// === Constants ===
+#define IDE_PRI_BASE 0x1F0
+#define IDE_PRI_CTRL 0x3F6
+#define IDE_SEC_BASE 0x170
+#define IDE_SEC_CTRL 0x376
+
+#define IDE_PRDT_LAST 0x8000
+/**
+ \enum HddControls
+ \brief Commands to be sent to HDD_CMD
+*/
+enum HddControls {
+ HDD_PIO_R28 = 0x20,
+ HDD_PIO_R48 = 0x24,
+ HDD_DMA_R48 = 0x25,
+ HDD_PIO_W28 = 0x30,
+ HDD_PIO_W48 = 0x34,
+ HDD_DMA_W48 = 0x35,
+ HDD_DMA_R28 = 0xC8,
+ HDD_DMA_W28 = 0xCA,
+ HDD_IDENTIFY = 0xEC
+};
+
+// === TYPES ===
+/**
+ * \brief PRDT Entry
+ */
+typedef struct
+{
+ Uint32 PBufAddr; // Physical Buffer Address
+ Uint16 Bytes; // Size of transfer entry
+ Uint16 Flags; // Flags
+} __attribute__ ((packed)) tPRDT_Ent;
+
+/**
+ * \brief Structure returned by the ATA IDENTIFY command
+ */
+typedef struct
+{
+ Uint16 Flags; // 1
+ Uint16 Usused1[9]; // 10
+ char SerialNum[20]; // 20
+ Uint16 Usused2[3]; // 23
+ char FirmwareVer[8]; // 27
+ char ModelNumber[40]; // 47
+ Uint16 SectPerInt; // 48 - Low byte only
+ Uint16 Unused3; // 49
+ Uint16 Capabilities[2]; // 51
+ Uint16 Unused4[2]; // 53
+ Uint16 ValidExtData; // 54
+ Uint16 Unused5[5]; // 59
+ Uint16 SizeOfRWMultiple; // 60
+ Uint32 Sectors28; // LBA 28 Sector Count
+ Uint16 Unused6[100-62];
+ Uint64 Sectors48; // LBA 48 Sector Count
+ Uint16 Unused7[256-104];
+} __attribute__ ((packed)) tIdentify;
+
+// === PROTOTYPES ===
+ int ATA_SetupIO(void);
+Uint64 ATA_GetDiskSize(int Disk);
+Uint16 ATA_GetBasePort(int Disk);
+// Read/Write DMA
+ int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer);
+ int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer);
+// IRQs
+void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr));
+void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr));
+// Controller IO
+Uint8 ATA_int_BusMasterReadByte(int Ofs);
+Uint32 ATA_int_BusMasterReadDWord(int Ofs);
+void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value);
+void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value);
+
+// === GLOBALS ===
+// - BusMaster IO Addresses
+Uint32 gATA_BusMasterBase; //!< True Address (IO/MMIO)
+Uint8 *gATA_BusMasterBasePtr; //!< Paging Mapped MMIO (If needed)
+// - IRQs
+ int gATA_IRQPri = 14;
+ int gATA_IRQSec = 15;
+volatile int gaATA_IRQs[2] = {0};
+// - Locks to avoid tripping
+tMutex glaATA_ControllerLock[2];
+// - Buffers!
+Uint8 gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata")));
+// - PRDTs
+tPRDT_Ent gATA_PRDTs[2] = {
+ {0, 512, IDE_PRDT_LAST},
+ {0, 512, IDE_PRDT_LAST}
+};
+tPAddr gaATA_PRDT_PAddrs[2];
+
+// === CODE ===
+/**
+ * \brief Sets up the ATA controller's DMA mode
+ */
+int ATA_SetupIO(void)
+{
+ int ent;
+
+ ENTER("");
+
+ // Get IDE Controller's PCI Entry
+ ent = PCI_GetDeviceByClass(0x010100, 0xFFFF00, -1);
+ LOG("ent = %i", ent);
+ gATA_BusMasterBase = PCI_GetBAR(ent, 4);
+ if( gATA_BusMasterBase == 0 ) {
+ Log_Warning("ATA", "It seems that there is no Bus Master Controller on this machine. Get one");
+ // TODO: Use PIO mode instead
+ LEAVE('i', MODULE_ERR_NOTNEEDED);
+ return MODULE_ERR_NOTNEEDED;
+ }
+
+ LOG("BAR5 = 0x%x", PCI_GetBAR(ent, 5));
+ LOG("IRQ = %i", PCI_GetIRQ(ent));
+
+ // Map memory
+ if( !(gATA_BusMasterBase & 1) )
+ {
+ if( gATA_BusMasterBase < 0x100000 )
+ gATA_BusMasterBasePtr = (void*)(KERNEL_BASE | (tVAddr)gATA_BusMasterBase);
+ else
+ gATA_BusMasterBasePtr = (void*)( MM_MapHWPages( gATA_BusMasterBase, 1 ) + (gATA_BusMasterBase&0xFFF) );
+ LOG("gATA_BusMasterBasePtr = %p", gATA_BusMasterBasePtr);
+ }
+ else {
+ // Bit 0 is left set as a flag to other functions
+ LOG("gATA_BusMasterBase = IO 0x%x", gATA_BusMasterBase & ~1);
+ }
+
+ // Register IRQs and get Buffers
+ IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri, NULL );
+ IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec, NULL );
+
+ gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[0] );
+ gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[1] );
+
+ LOG("gATA_PRDTs = {PBufAddr: 0x%x, PBufAddr: 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr);
+
+ gaATA_PRDT_PAddrs[0] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[0] );
+ LOG("gaATA_PRDT_PAddrs[0] = 0x%x", gaATA_PRDT_PAddrs[0]);
+ ATA_int_BusMasterWriteDWord(4, gaATA_PRDT_PAddrs[0]);
+
+ gaATA_PRDT_PAddrs[1] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[1] );
+ LOG("gaATA_PRDT_PAddrs[1] = 0x%x", gaATA_PRDT_PAddrs[1]);
+ ATA_int_BusMasterWriteDWord(12, gaATA_PRDT_PAddrs[1]);
+
+ // Enable controllers
+ outb(IDE_PRI_BASE+1, 1);
+ outb(IDE_SEC_BASE+1, 1);
+ outb(IDE_PRI_CTRL, 0);
+ outb(IDE_SEC_CTRL, 0);
+
+ // Make sure interrupts are ACKed
+ ATA_int_BusMasterWriteByte(2, 0x4);
+ ATA_int_BusMasterWriteByte(10, 0x4);
+
+ // return
+ LEAVE('i', MODULE_ERR_OK);
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Get the size (in sectors) of a disk
+ * \param Disk Disk to get size of
+ * \return Number of sectors reported
+ *
+ * Does an ATA IDENTIFY
+ */
+Uint64 ATA_GetDiskSize(int Disk)
+{
+ union {
+ Uint16 buf[256];
+ tIdentify identify;
+ } data;
+ Uint16 base;
+ Uint8 val;
+ int i;
+ ENTER("iDisk", Disk);
+
+ base = ATA_GetBasePort( Disk );
+
+ // Send Disk Selector
+ if(Disk & 1) // Slave
+ outb(base+6, 0xB0);
+ else // Master
+ outb(base+6, 0xA0);
+ IO_DELAY();
+
+ // Check for a floating bus
+ if( 0xFF == inb(base+7) ) {
+ LOG("Floating bus");
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Check for the controller
+ // - Write to two RW ports and attempt to read back
+ outb(base+0x02, 0x66);
+ outb(base+0x03, 0xFF);
+ if(inb(base+0x02) != 0x66 || inb(base+0x03) != 0xFF) {
+ LOG("No controller");
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Send ATA IDENTIFY
+ outb(base+7, HDD_IDENTIFY);
+ IO_DELAY();
+ val = inb(base+7); // Read status
+ LOG("val = 0x%02x", val);
+ if(val == 0) {
+ LEAVE('i', 0);
+ return 0; // Disk does not exist
+ }
+
+ // Poll until BSY clears or ERR is set
+ // TODO: Timeout?
+ while( (val & 0x80) && !(val & 1) )
+ val = inb(base+7);
+ LOG("BSY unset (0x%x)", val);
+ // and, wait for DRQ to set
+ while( !(val & 0x08) && !(val & 1))
+ val = inb(base+7);
+ LOG("DRQ set (0x%x)", val);
+
+ // Check for an error
+ if(val & 1) {
+ LEAVE('i', 0);
+ return 0; // Error occured, so return false
+ }
+
+ // Read Data
+ for( i = 0; i < 256; i++ )
+ data.buf[i] = inw(base);
+
+ // Return the disk size
+ if(data.identify.Sectors48 != 0) {
+ LEAVE('X', data.identify.Sectors48);
+ return data.identify.Sectors48;
+ }
+ else {
+ LEAVE('x', data.identify.Sectors28);
+ return data.identify.Sectors28;
+ }
+}
+
+/**
+ * \fn Uint16 ATA_GetPortBase(int Disk)
+ * \brief Returns the base port for a given disk
+ */
+Uint16 ATA_GetBasePort(int Disk)
+{
+ switch(Disk)
+ {
+ case 0: case 1: return IDE_PRI_BASE;
+ case 2: case 3: return IDE_SEC_BASE;
+ }
+ return 0;
+}
+
+/**
+ * \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
+ * \return Boolean Failure
+ */
+int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
+{
+ int cont = (Disk>>1)&1; // Controller ID
+ int disk = Disk & 1;
+ Uint16 base;
+ Sint64 timeoutTime;
+
+ ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer);
+
+ // Check if the count is small enough
+ if(Count > MAX_DMA_SECTORS) {
+ Log_Warning("ATA", "Passed too many sectors for a bulk DMA read (%i > %i)",
+ Count, MAX_DMA_SECTORS);
+ LEAVE('i');
+ return 1;
+ }
+
+ // Hack to make debug hexdump noticable
+ #if 1
+ memset(Buffer, 0xFF, Count*SECTOR_SIZE);
+ #endif
+
+ // Get exclusive access to the disk controller
+ Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
+
+ // Set Size
+ gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
+
+ // Get Port Base
+ base = ATA_GetBasePort(Disk);
+
+ // Reset IRQ Flag
+ gaATA_IRQs[cont] = 0;
+
+ #if 1
+ if( cont == 0 ) {
+ outb(IDE_PRI_CTRL, 4);
+ IO_DELAY();
+ outb(IDE_PRI_CTRL, 0);
+ }
+ else {
+ outb(IDE_SEC_CTRL, 4);
+ IO_DELAY();
+ outb(IDE_SEC_CTRL, 0);
+ }
+ #endif
+
+ // Set up transfer
+ if( Address > 0x0FFFFFFF ) // Use LBA48
+ {
+ outb(base+0x6, 0x40 | (disk << 4));
+ IO_DELAY();
+ outb(base+0x2, 0 >> 8); // Upper Sector Count
+ outb(base+0x3, Address >> 24); // Low 2 Addr
+ outb(base+0x4, Address >> 28); // Mid 2 Addr
+ outb(base+0x5, Address >> 32); // High 2 Addr
+ }
+ else
+ {
+ // Magic, Disk, High Address nibble
+ outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
+ //outb(base+0x06, 0xA0 | (disk << 4) | ((Address >> 24) & 0x0F));
+ IO_DELAY();
+ }
+
+ //outb(base+0x01, 0x01); //?
+ outb(base+0x02, Count & 0xFF); // Sector Count
+ outb(base+0x03, Address & 0xFF); // Low Addr
+ outb(base+0x04, (Address >> 8) & 0xFF); // Middle Addr
+ outb(base+0x05, (Address >> 16) & 0xFF); // High Addr
+
+ LOG("Starting Transfer");
+
+ // HACK: Ensure the PRDT is reset
+ ATA_int_BusMasterWriteDWord(cont*8+4, gaATA_PRDT_PAddrs[cont]);
+ ATA_int_BusMasterWriteByte(cont*8, 4); // Reset IRQ
+
+ LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes);
+ if( Address > 0x0FFFFFFF )
+ outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48)
+ else
+ outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28)
+
+ // Start transfer
+ ATA_int_BusMasterWriteByte( cont * 8, 9 ); // Read and start
+
+ // Wait for transfer to complete
+ timeoutTime = now() + ATA_TIMEOUT;
+ while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
+ {
+ HALT();
+ }
+
+ // Complete Transfer
+ ATA_int_BusMasterWriteByte( cont * 8, 8 ); // Read and stop
+
+ #if DEBUG
+ {
+ Uint8 val = inb(base+0x7);
+ LOG("Status byte = 0x%02x, Controller Status = 0x%02x",
+ val, ATA_int_BusMasterReadByte(cont * 8 + 2));
+ }
+ #else
+ inb(base+0x7);
+ #endif
+
+ if( gaATA_IRQs[cont] == 0 )
+ {
+ if( ATA_int_BusMasterReadByte(cont * 8 + 2) & 0x4 ) {
+ Log_Error("ATA", "BM Status reports an interrupt, but none recieved");
+ ATA_int_BusMasterWriteByte(cont*8 + 2, 4); // Clear interrupt
+ memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ #if 1
+ Debug_HexDump("ATA", Buffer, 512);
+ #endif
+
+ // Release controller lock
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ Log_Warning("ATA",
+ "Read timeout on disk %i (Reading sector 0x%llx)",
+ Disk, Address);
+ // Return error
+ LEAVE('i', 1);
+ return 1;
+ }
+ else {
+ LOG("Transfer Completed & Acknowledged");
+ // Copy to destination buffer
+ memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
+ // Release controller lock
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+
+ LEAVE('i', 0);
+ return 0;
+ }
+}
+
+/**
+ * \fn int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
+ * \brief Write up to \a MAX_DMA_SECTORS to a disk
+ * \param Disk Disk ID to write to
+ * \param Address LBA of first sector
+ * \param Count Number of sectors to write (must be >= \a MAX_DMA_SECTORS)
+ * \param Buffer Source buffer for data
+ * \return Boolean Failure
+ */
+int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
+{
+ int cont = (Disk>>1)&1; // Controller ID
+ int disk = Disk & 1;
+ Uint16 base;
+ Sint64 timeoutTime;
+
+ // Check if the count is small enough
+ if(Count > MAX_DMA_SECTORS) return 1;
+
+ // Get exclusive access to the disk controller
+ Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
+
+ // Set Size
+ gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
+
+ // Get Port Base
+ base = ATA_GetBasePort(Disk);
+
+ // Reset IRQ Flag
+ gaATA_IRQs[cont] = 0;
+
+ // Set up transfer
+ outb(base+0x01, 0x00);
+ if( Address > 0x0FFFFFFF ) // Use LBA48
+ {
+ outb(base+0x6, 0x40 | (disk << 4));
+ outb(base+0x2, 0 >> 8); // Upper Sector Count
+ outb(base+0x3, Address >> 24); // Low 2 Addr
+ outb(base+0x3, Address >> 28); // Mid 2 Addr
+ outb(base+0x3, Address >> 32); // High 2 Addr
+ }
+ else
+ {
+ // Magic, Disk, High Address nibble
+ outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
+ }
+
+ outb(base+0x02, (Uint8) Count); // Sector Count
+ outb(base+0x03, (Uint8) Address); // Low Addr
+ outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr
+ outb(base+0x05, (Uint8) (Address >> 16)); // High Addr
+ if( Address > 0x0FFFFFFF )
+ outb(base+0x07, HDD_DMA_W48); // Write Command (LBA48)
+ else
+ outb(base+0x07, HDD_DMA_W28); // Write Command (LBA28)
+
+ // Copy to output buffer
+ memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE );
+
+ // Start transfer
+ ATA_int_BusMasterWriteByte( cont << 3, 1 ); // Write and start
+
+ // Wait for transfer to complete
+ timeoutTime = now() + ATA_TIMEOUT;
+ while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
+ {
+ HALT();
+ }
+
+ // Complete Transfer
+ ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop
+
+ // If the IRQ is unset, return error
+ if( gaATA_IRQs[cont] == 0 ) {
+ // Release controller lock
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ return 1; // Error
+ }
+ else {
+ Mutex_Release( &glaATA_ControllerLock[ cont ] );
+ return 0;
+ }
+}
+
+/**
+ * \brief Primary ATA Channel IRQ handler
+ */
+void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr))
+{
+ Uint8 val;
+
+ // IRQ bit set for Primary Controller
+ val = ATA_int_BusMasterReadByte( 0x2 );
+ LOG("IRQ val = 0x%x", val);
+ if(val & 4) {
+ LOG("IRQ hit (val = 0x%x)", val);
+ ATA_int_BusMasterWriteByte( 0x2, 4 );
+ gaATA_IRQs[0] = 1;
+ return ;
+ }
+}
+
+/**
+ * \brief Second ATA Channel IRQ handler
+ */
+void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr))
+{
+ Uint8 val;
+ // IRQ bit set for Secondary Controller
+ val = ATA_int_BusMasterReadByte( 0xA );
+ LOG("IRQ val = 0x%x", val);
+ if(val & 4) {
+ LOG("IRQ hit (val = 0x%x)", val);
+ ATA_int_BusMasterWriteByte( 0xA, 4 );
+ gaATA_IRQs[1] = 1;
+ return ;
+ }
+}
+
+/**
+ * \brief Read an 8-bit value from a Bus Master register
+ * \param Ofs Register offset
+ */
+Uint8 ATA_int_BusMasterReadByte(int Ofs)
+{
+ if( gATA_BusMasterBase & 1 )
+ return inb( (gATA_BusMasterBase & ~1) + Ofs );
+ else
+ return *(Uint8*)(gATA_BusMasterBasePtr + Ofs);
+}
+
+/**
+ * \brief Read an 32-bit value from a Bus Master register
+ * \param Ofs Register offset
+ */
+Uint32 ATA_int_BusMasterReadDWord(int Ofs)
+{
+ if( gATA_BusMasterBase & 1 )
+ return ind( (gATA_BusMasterBase & ~1) + Ofs );
+ else
+ return *(Uint32*)(gATA_BusMasterBasePtr + Ofs);
+}
+
+/**
+ * \brief Writes a byte to a Bus Master Register
+ * \param Ofs Register Offset
+ * \param Value Value to write
+ */
+void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value)
+{
+ if( gATA_BusMasterBase & 1 )
+ outb( (gATA_BusMasterBase & ~1) + Ofs, Value );
+ else
+ *(Uint8*)(gATA_BusMasterBasePtr + Ofs) = Value;
+}
+
+/**
+ * \brief Writes a 32-bit value to a Bus Master Register
+ * \param Ofs Register offset
+ * \param Value Value to write
+ */
+void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value)
+{
+ if( gATA_BusMasterBase & 1 )
+ outd( (gATA_BusMasterBase & ~1) + Ofs, Value );
+ else
+ *(Uint32*)(gATA_BusMasterBasePtr + Ofs) = Value;
+}
--- /dev/null
+/*
+ * Acess2 IDE Harddisk Driver
+ * - main.c
+ */
+#define DEBUG 0
+#define VERSION 0x0032
+#include <acess.h>
+#include <modules.h>
+#include <vfs.h>
+#include <fs_devfs.h>
+#include <api_drv_common.h>
+#include <api_drv_disk.h>
+#include "common.h"
+
+// === MACROS ===
+#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
+
+// === PROTOTYPES ===
+ int ATA_Install(char **Arguments);
+void ATA_SetupPartitions(void);
+void ATA_SetupVFS(void);
+ int ATA_ScanDisk(int Disk);
+void ATA_ParseGPT(int Disk);
+void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length);
+Uint16 ATA_GetBasePort(int Disk);
+// Filesystem Interface
+char *ATA_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name);
+Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data);
+// Read/Write Interface/Quantiser
+Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk);
+Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, i386ATA, ATA_Install, NULL, "PCI", NULL);
+tVFS_NodeType gATA_RootNodeType = {
+ .TypeName = "ATA Root Node",
+ .ReadDir = ATA_ReadDir,
+ .FindDir = ATA_FindDir
+ };
+tVFS_NodeType gATA_DiskNodeType = {
+ .TypeName = "ATA Volume",
+ .Read = ATA_ReadFS,
+ .Write = ATA_WriteFS,
+ .IOCtl = ATA_IOCtl
+ };
+tDevFS_Driver gATA_DriverInfo = {
+ NULL, "ata",
+ {
+ .NumACLs = 1,
+ .Size = -1,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Type = &gATA_RootNodeType
+ }
+};
+tATA_Disk gATA_Disks[MAX_ATA_DISKS];
+ int giATA_NumNodes;
+tVFS_Node **gATA_Nodes;
+
+// === CODE ===
+/**
+ * \brief Initialise the ATA driver
+ */
+int ATA_Install(char **Arguments)
+{
+ int ret;
+
+ ret = ATA_SetupIO();
+ if(ret) return ret;
+
+ ATA_SetupPartitions();
+
+ ATA_SetupVFS();
+
+ if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 )
+ return MODULE_ERR_MISC;
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Scan all disks, looking for partitions
+ */
+void ATA_SetupPartitions(void)
+{
+ int i;
+ for( i = 0; i < MAX_ATA_DISKS; i ++ )
+ {
+ if( !ATA_ScanDisk(i) ) {
+ gATA_Disks[i].Name[0] = '\0'; // Mark as unused
+ continue;
+ }
+ }
+}
+
+/**
+ * \brief Sets up the ATA drivers VFS information and registers with DevFS
+ */
+void ATA_SetupVFS(void)
+{
+ int i, j, k;
+
+ // Count number of nodes needed
+ giATA_NumNodes = 0;
+ for( i = 0; i < MAX_ATA_DISKS; i++ )
+ {
+ if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore
+ giATA_NumNodes ++;
+ giATA_NumNodes += gATA_Disks[i].NumPartitions;
+ }
+
+ // Allocate Node space
+ gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) );
+
+ // Set nodes
+ k = 0;
+ for( i = 0; i < MAX_ATA_DISKS; i++ )
+ {
+ if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore
+ gATA_Nodes[ k++ ] = &gATA_Disks[i].Node;
+ for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ )
+ gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node;
+ }
+
+ gATA_DriverInfo.RootNode.Size = giATA_NumNodes;
+}
+
+/**
+ * \brief Scan a disk, getting the size and any paritions
+ * \param Disk Disk ID to scan
+ */
+int ATA_ScanDisk(int Disk)
+{
+ tVFS_Node *node;
+ tMBR mbr;
+
+ ENTER("iDisk", Disk);
+
+ // Get the disk size
+ gATA_Disks[ Disk ].Sectors = ATA_GetDiskSize(Disk);
+ if(gATA_Disks[ Disk ].Sectors == 0)
+ {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ LOG("gATA_Disks[ %i ].Sectors = 0x%x", Disk, gATA_Disks[ Disk ].Sectors);
+
+ // Create Name
+ gATA_Disks[ Disk ].Name[0] = 'A'+Disk;
+ gATA_Disks[ Disk ].Name[1] = '\0';
+
+ #if 1
+ {
+ Uint64 val = gATA_Disks[ Disk ].Sectors / 2;
+ char *units = "KiB";
+ if( val > 4*1024 ) {
+ val /= 1024;
+ units = "MiB";
+ }
+ else if( val > 4*1024 ) {
+ val /= 1024;
+ units = "GiB";
+ }
+ else if( val > 4*1024 ) {
+ val /= 1024;
+ units = "TiB";
+ }
+ Log_Notice("ATA", "Disk %s: 0x%llx Sectors (%lli %s)",
+ gATA_Disks[ Disk ].Name, gATA_Disks[ Disk ].Sectors, val, units);
+ }
+ #endif
+
+ // Get pointer to vfs node and populate it
+ node = &gATA_Disks[ Disk ].Node;
+ node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE;
+ node->NumACLs = 0; // Means Superuser only can access it
+ node->Inode = (Disk << 8) | 0xFF;
+ node->ImplPtr = gATA_Disks[ Disk ].Name;
+
+ node->ATime = node->MTime = node->CTime = now();
+
+ node->Type = &gATA_DiskNodeType;
+
+ // --- Scan Partitions ---
+ LOG("Reading MBR");
+ // Read Boot Sector
+ if( ATA_ReadDMA( Disk, 0, 1, &mbr ) != 0 ) {
+ Log_Warning("ATA", "Error in reading MBR on %i", Disk);
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Check for a GPT table
+ if(mbr.Parts[0].SystemID == 0xEE)
+ ATA_ParseGPT(Disk);
+ else // No? Just parse the MBR
+ ATA_ParseMBR(Disk, &mbr);
+
+ #if DEBUG >= 2
+ ATA_ReadDMA( Disk, 1, 1, &mbr );
+ Debug_HexDump("ATA_ScanDisk", &mbr, 512);
+ #endif
+
+ LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
+ * \brief Fills a parition's information structure
+ */
+void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
+{
+ ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length);
+ Part->Start = Start;
+ Part->Length = Length;
+ Part->Name[0] = 'A'+Disk;
+ if(Num >= 10) {
+ Part->Name[1] = '1'+Num/10;
+ Part->Name[2] = '1'+Num%10;
+ Part->Name[3] = '\0';
+ } else {
+ Part->Name[1] = '1'+Num;
+ Part->Name[2] = '\0';
+ }
+ Part->Node.NumACLs = 0; // Only root can read/write raw block devices
+ Part->Node.Inode = (Disk << 8) | Num;
+ Part->Node.ImplPtr = Part->Name;
+
+ Part->Node.Type = &gATA_DiskNodeType;
+ Log_Notice("ATA", "Partition %s at 0x%llx+0x%llx", Part->Name, Part->Start, Part->Length);
+ LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node);
+ LEAVE('-');
+}
+
+/**
+ * \fn void ATA_ParseGPT(int Disk)
+ * \brief Parses the GUID Partition Table
+ */
+void ATA_ParseGPT(int Disk)
+{
+ ///\todo Support GPT Disks
+ Warning("GPT Disks are currently unsupported (Disk %i)", Disk);
+}
+
+/**
+ * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos)
+ */
+char *ATA_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
+{
+ if(Pos >= giATA_NumNodes || Pos < 0) return NULL;
+ return strdup( gATA_Nodes[Pos]->ImplPtr );
+}
+
+/**
+ * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name)
+ */
+tVFS_Node *ATA_FindDir(tVFS_Node *UNUSED(Node), const char *Name)
+{
+ int part;
+ tATA_Disk *disk;
+
+ // Check first character
+ if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS)
+ return NULL;
+ disk = &gATA_Disks[Name[0]-'A'];
+ // Raw Disk
+ if(Name[1] == '\0') {
+ if( disk->Sectors == 0 && disk->Name[0] == '\0')
+ return NULL;
+ return &disk->Node;
+ }
+
+ // Partitions
+ if(Name[1] < '0' || '9' < Name[1]) return NULL;
+ if(Name[2] == '\0') { // <= 9
+ part = Name[1] - '0';
+ part --;
+ return &disk->Partitions[part].Node;
+ }
+ // > 9
+ if('0' > Name[2] || '9' < Name[2]) return NULL;
+ if(Name[3] != '\0') return NULL;
+
+ part = (Name[1] - '0') * 10;
+ part += Name[2] - '0';
+ part --;
+ return &disk->Partitions[part].Node;
+
+}
+
+/**
+ * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ */
+Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ int disk = Node->Inode >> 8;
+ int part = Node->Inode & 0xFF;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ // Raw Disk Access
+ if(part == 0xFF)
+ {
+ if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
+ Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
+ }
+ // Partition
+ else
+ {
+ if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
+ Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
+ Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
+ }
+
+ {
+ int ret = DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk);
+ //Log("ATA_ReadFS: disk=%i, Offset=%lli, Length=%lli", disk, Offset, Length);
+ //Debug_HexDump("ATA_ReadFS", Buffer, Length);
+ LEAVE('i', ret);
+ return ret;
+ }
+}
+
+/**
+ * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+ */
+Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ int disk = Node->Inode >> 8;
+ int part = Node->Inode & 0xFF;
+
+ // Raw Disk Access
+ if(part == 0xFF)
+ {
+ if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE )
+ return 0;
+ if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
+ Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
+ }
+ // Partition
+ else
+ {
+ if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
+ return 0;
+ if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
+ Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
+ Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
+ }
+
+ Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer);
+ Debug_HexDump("ATA_WriteFS", Buffer, Length);
+ return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk);
+}
+
+const char *csaATA_IOCtls[] = {DRV_IOCTLNAMES, DRV_DISK_IOCTLNAMES, NULL};
+/**
+ * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief IO Control Funtion
+ */
+int ATA_IOCtl(tVFS_Node *UNUSED(Node), int Id, void *Data)
+{
+ switch(Id)
+ {
+ BASE_IOCTLS(DRV_TYPE_DISK, "i386ATA", VERSION, csaATA_IOCtls);
+
+ case DISK_IOCTL_GETBLOCKSIZE:
+ return 512;
+
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+// --- Disk Access ---
+/**
+ * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
+ */
+Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
+{
+ int ret;
+ Uint offset;
+ Uint done = 0;
+
+ // Pass straight on to ATA_ReadDMAPage if we can
+ if(Count <= MAX_DMA_SECTORS)
+ {
+ ret = ATA_ReadDMA(Disk, Address, Count, Buffer);
+ if(ret == 0) return 0;
+ return Count;
+ }
+
+ // Else we will have to break up the transfer
+ offset = 0;
+ while(Count > MAX_DMA_SECTORS)
+ {
+ ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
+ // Check for errors
+ if(ret != 1) return done;
+ // Change Position
+ done += MAX_DMA_SECTORS;
+ Count -= MAX_DMA_SECTORS;
+ offset += MAX_DMA_SECTORS*SECTOR_SIZE;
+ }
+
+ ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset);
+ if(ret != 1) return 0;
+ return done+Count;
+}
+
+/**
+ * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk)
+ */
+Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk)
+{
+ int ret;
+ Uint offset;
+ Uint done = 0;
+
+ // Pass straight on to ATA_WriteDMA, if we can
+ if(Count <= MAX_DMA_SECTORS)
+ {
+ ret = ATA_WriteDMA(Disk, Address, Count, Buffer);
+ if(ret == 0) return 0;
+ return Count;
+ }
+
+ // Else we will have to break up the transfer
+ offset = 0;
+ while(Count > MAX_DMA_SECTORS)
+ {
+ ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
+ // Check for errors
+ if(ret != 1) return done;
+ // Change Position
+ done += MAX_DMA_SECTORS;
+ Count -= MAX_DMA_SECTORS;
+ offset += MAX_DMA_SECTORS*SECTOR_SIZE;
+ }
+
+ ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset);
+ if(ret != 1) return 0;
+ return done+Count;
+}
--- /dev/null
+/*
+ * Acess2 IDE Harddisk Driver
+ * - MBR Parsing Code
+ * mbr.c
+ */
+#define DEBUG 0
+#include <acess.h>
+#include "common.h"
+
+// === PROTOTYPES ===
+void ATA_ParseMBR(int Disk, tMBR *MBR);
+Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length);
+
+// === GLOBALS ===
+
+// === CODE ===
+/**
+ * \fn void ATA_ParseMBR(int Disk, tMBR *MBR)
+ */
+void ATA_ParseMBR(int Disk, tMBR *MBR)
+{
+ int i, j = 0, k = 4;
+ Uint64 extendedLBA;
+ Uint64 base, len;
+
+ ENTER("iDisk", Disk);
+
+ // Count Partitions
+ gATA_Disks[Disk].NumPartitions = 0;
+ extendedLBA = 0;
+ for( i = 0; i < 4; i ++ )
+ {
+ if( MBR->Parts[i].SystemID == 0 ) continue;
+ if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 // LBA 28
+ || MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 // LBA 48
+ )
+ {
+ if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) {
+ LOG("Extended Partition at 0x%llx", MBR->Parts[i].LBAStart);
+ if(extendedLBA != 0) {
+ Warning("Disk %i has multiple extended partitions, ignoring rest", Disk);
+ continue;
+ }
+ extendedLBA = MBR->Parts[i].LBAStart;
+ continue;
+ }
+ LOG("Primary Partition at 0x%llx", MBR->Parts[i].LBAStart);
+
+ gATA_Disks[Disk].NumPartitions ++;
+ continue;
+ }
+ // Invalid Partition, so don't count it
+ }
+ while(extendedLBA != 0)
+ {
+ extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len);
+ if( extendedLBA == -1 ) break;
+ gATA_Disks[Disk].NumPartitions ++;
+ }
+ LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions);
+
+ // Create patition array
+ gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) );
+
+ // --- Fill Partition Info ---
+ extendedLBA = 0;
+ for( j = 0, i = 0; i < 4; i ++ )
+ {
+ LOG("MBR->Parts[%i].SystemID = 0x%02x", i, MBR->Parts[i].SystemID);
+ if( MBR->Parts[i].SystemID == 0 ) continue;
+ if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 ) // LBA 28
+ {
+ base = MBR->Parts[i].LBAStart;
+ len = MBR->Parts[i].LBALength;
+ }
+ else if( MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 ) // LBA 58
+ {
+ base = (MBR->Parts[i].StartHi << 16) | MBR->Parts[i].LBAStart;
+ len = (MBR->Parts[i].LengthHi << 16) | MBR->Parts[i].LBALength;
+ }
+ else
+ continue;
+
+ if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) {
+ if(extendedLBA != 0) {
+ Log_Warning("ATA", "Disk %i has multiple extended partitions, ignoring rest", Disk);
+ continue;
+ }
+ extendedLBA = base;
+ continue;
+ }
+ // Create Partition
+ ATA_int_MakePartition(
+ &gATA_Disks[Disk].Partitions[j], Disk, j,
+ base, len
+ );
+ j ++;
+
+ }
+ // Scan extended partitions
+ while(extendedLBA != 0)
+ {
+ extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len);
+ if(extendedLBA == -1) break;
+ ATA_int_MakePartition(
+ &gATA_Disks[Disk].Partitions[j], Disk, k, base, len
+ );
+ }
+
+ LEAVE('-');
+}
+
+/**
+ * \brief Reads an extended partition
+ * \return LBA of next Extended, -1 on error, 0 for last
+ */
+Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length)
+{
+ Uint64 link = 0;
+ int bFoundPart = 0;;
+ int i;
+ tMBR mbr;
+ Uint64 base, len;
+
+ if( ATA_ReadDMA( Disk, Addr, 1, &mbr ) != 0 )
+ return -1; // Stop on Errors
+
+ for( i = 0; i < 4; i ++ )
+ {
+ if( mbr.Parts[i].SystemID == 0 ) continue;
+
+ // LBA 24
+ if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) {
+ base = mbr.Parts[i].LBAStart;
+ len = mbr.Parts[i].LBALength;
+ }
+ // LBA 48
+ else if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) {
+ base = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart;
+ len = (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength;
+ }
+ else {
+ Log_Warning("ATA MBR",
+ "Unknown partition type 0x%x, Disk %i Ext 0x%llx Part %i",
+ mbr.Parts[i].Boot, Disk, Addr, i
+ );
+ return -1;
+ }
+
+ switch(mbr.Parts[i].SystemID)
+ {
+ case 0xF:
+ case 0x5:
+ if(link != 0) {
+ Log_Warning("ATA MBR",
+ "Disk %i has two forward links in the extended partition",
+ Disk
+ );
+ return -1;
+ }
+ link = base;
+ break;
+ default:
+ if(bFoundPart) {
+ Warning("ATA MBR",
+ "Disk %i has more than one partition in the extended partition at 0x%llx",
+ Disk, Addr
+ );
+ return -1;
+ }
+ bFoundPart = 1;
+ *Base = base;
+ *Length = len;
+ break;
+ }
+ }
+
+ if(!bFoundPart) {
+ Log_Warning("ATA MBR",
+ "No partition in extended partiton, Disk %i 0x%llx",
+ Disk, Addr);
+ return -1;
+ }
+
+ return link;
+}
--- /dev/null
+#
+#
+
+OBJ = fdd.o
+NAME = FDD
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * AcessOS 0.1
+ * Floppy Disk Access Code
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <api_drv_disk.h>
+#include <dma.h>
+#include <iocache.h>
+
+#define WARN 0
+
+// === CONSTANTS ===
+// --- Current Version
+#define FDD_VERSION ((0<<8)|(75))
+
+// --- Options
+#define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation
+#define MOTOR_ON_DELAY 500 // Miliseconds
+#define MOTOR_OFF_DELAY 2000 // Miliseconds
+#define FDD_MAX_READWRITE_ATTEMPTS 16
+
+// === TYPEDEFS ===
+/**
+ * \brief Representation of a floppy drive
+ */
+typedef struct sFloppyDrive
+{
+ int type;
+ volatile int motorState; //2 - On, 1 - Spinup, 0 - Off
+ int track[2];
+ int timer;
+ tVFS_Node Node;
+ #if !USE_CACHE
+ tIOCache *CacheHandle;
+ #endif
+} t_floppyDevice;
+
+/**
+ * \brief Cached Sector
+ */
+typedef struct {
+ Uint64 timestamp;
+ Uint16 disk;
+ Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD)
+ Uint8 data[512];
+} t_floppySector;
+
+// === CONSTANTS ===
+static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" };
+static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 };
+static const short cPORTBASE[] = { 0x3F0, 0x370 };
+#if DEBUG
+static const char *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"};
+#endif
+
+enum FloppyPorts {
+ PORT_STATUSA = 0x0,
+ PORT_STATUSB = 0x1,
+ PORT_DIGOUTPUT = 0x2,
+ PORT_MAINSTATUS = 0x4,
+ PORT_DATARATE = 0x4,
+ PORT_DATA = 0x5,
+ PORT_DIGINPUT = 0x7,
+ PORT_CONFIGCTRL = 0x7
+};
+
+#define CMD_FLAG_MULTI_TRACK 0x80 //!< Multitrack
+#define CMD_FLAG_MFM_ENCODING 0x40 //!< MFM Encoding Mode (Always set for read/write/format/verify)
+#define CMD_FLAG_SKIP_MODE 0x20 //!< Skip Mode (don't use)
+enum FloppyCommands {
+ CMD_READ_TRACK = 0x02,
+ CMD_SPECIFY = 0x03,
+ CMD_SENSE_STATUS = 0x04,
+ CMD_WRITE_DATA = 0x05,
+ CMD_READ_DATA = 0x06,
+ CMD_RECALIBRATE = 0x07,
+ CMD_SENSE_INTERRUPT = 0x08,
+ CMD_WRITE_DEL_DATA = 0x09,
+ CMD_READ_SECTOR_ID = 0x0A,
+ // 0x0B - ?
+ CMD_READ_DEL_DATA = 0x0C,
+ CMD_FORMAT_TRACK = 0x0D,
+ // 0x0E - ?
+ CMD_SEEK_TRACK = 0x0F,
+ CMD_VERSION = 0x10,
+
+ CMD_LOCK = 0x14, //!< Save controller parameters
+
+ CMD_CONFIGURE = 0x13
+};
+
+// === PROTOTYPES ===
+// --- Filesystem
+ int FDD_Install(char **Arguments);
+void FDD_UnloadModule();
+// --- VFS Methods
+char *FDD_ReadDir(tVFS_Node *Node, int pos);
+tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name);
+ int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
+Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
+// --- Functions for IOCache/DrvUtil
+Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk);
+// --- Raw Disk Access
+ int FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer);
+ int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer);
+// --- Helpers
+ int FDD_WaitIRQ();
+void FDD_IRQHandler(int Num, void *Ptr);
+void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl);
+
+ int FDD_Reset(int id);
+void FDD_Recalibrate(int disk);
+ int FDD_Reconfigure(int ID);
+
+ int FDD_int_SeekTrack(int disk, int head, int track);
+void FDD_int_TimerCallback(void *Arg);
+void FDD_int_StopMotor(int Disk);
+void FDD_int_StopMotorCallback(void *Arg);
+void FDD_int_StartMotor(int Disk);
+ int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt);
+
+ int FDD_int_SendByte(int base, Uint8 Byte);
+ int FDD_int_GetByte(int base, Uint8 *Byte);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "x86_ISADMA", NULL);
+t_floppyDevice gFDD_Devices[2];
+tMutex glFDD;
+volatile int gbFDD_IrqFired = 0;
+tDevFS_Driver gFDD_DriverInfo = {
+ NULL, "fdd",
+ {
+ .Size = -1,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .ReadDir = FDD_ReadDir,
+ .FindDir = FDD_FindDir,
+ .IOCtl = FDD_IOCtl
+ }
+};
+
+// === CODE ===
+/**
+ * \fn int FDD_Install(char **Arguments)
+ * \brief Installs floppy driver
+ */
+int FDD_Install(char **Arguments)
+{
+ Uint8 data;
+ char **args = Arguments;
+
+ // Determine Floppy Types (From CMOS)
+ outb(0x70, 0x10);
+ data = inb(0x71);
+ gFDD_Devices[0].type = data >> 4;
+ gFDD_Devices[1].type = data & 0xF;
+ gFDD_Devices[0].track[0] = -1;
+ gFDD_Devices[1].track[1] = -1;
+
+ Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
+
+ if( data == 0 ) {
+ return MODULE_ERR_NOTNEEDED;
+ }
+
+ // Handle arguments
+ if(args) {
+ for(;*args;args++)
+ {
+ if(strcmp(*args, "disable")==0)
+ return MODULE_ERR_NOTNEEDED;
+ }
+ }
+
+ // Install IRQ6 Handler
+ IRQ_AddHandler(6, FDD_IRQHandler, NULL);
+
+ // Ensure the FDD version is 0x90
+ {
+ Uint8 tmp = 0;
+ FDD_int_SendByte(cPORTBASE[0], CMD_VERSION);
+ FDD_int_GetByte(cPORTBASE[0], &tmp);
+ if( tmp != 0x90 ) {
+ Log_Error("FDD", "Version(0x%2x) != 0x90", tmp);
+ return MODULE_ERR_NOTNEEDED;
+ }
+ }
+
+ // Configure
+ FDD_Reconfigure(0);
+
+ // Reset Primary FDD Controller
+ if( FDD_Reset(0) != 0 ) {
+ return MODULE_ERR_MISC;
+ }
+
+ #if 0
+ {
+ int retries;
+ // Recalibrate disks
+ LOG("Recalibrate disks (16x seek)");
+ retries = 16;
+ while(FDD_int_SeekTrack(0, 0, 1) == 0 && retries --)
+ Threads_Yield(); // set track
+ if(retries < 0) LEAVE_RET('i', -1);
+
+ retries = 16;
+ while(FDD_int_SeekTrack(0, 1, 1) == 0 && retries --)
+ Threads_Yield(); // set track
+ if(retries < 0) LEAVE_RET('i', -1);
+ }
+ #endif
+
+
+ // Initialise Root Node
+ gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
+ = gFDD_DriverInfo.RootNode.ATime = now();
+
+ // Initialise Child Nodes
+ gFDD_Devices[0].Node.Inode = 0;
+ gFDD_Devices[0].Node.Flags = 0;
+ gFDD_Devices[0].Node.NumACLs = 0;
+ gFDD_Devices[0].Node.Read = FDD_ReadFS;
+ gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS;
+ memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node));
+
+ gFDD_Devices[1].Node.Inode = 1;
+
+ // Set Lengths
+ gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];
+ gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];
+
+ // Create Sector Cache
+ if( cFDD_SIZES[data >> 4] )
+ {
+ gFDD_Devices[0].CacheHandle = IOCache_Create(
+ FDD_WriteSector, 0, 512,
+ gFDD_Devices[0].Node.Size / (512*4)
+ ); // Cache is 1/4 the size of the disk
+ }
+ if( cFDD_SIZES[data & 15] )
+ {
+ gFDD_Devices[1].CacheHandle = IOCache_Create(
+ FDD_WriteSector, 0, 512,
+ gFDD_Devices[1].Node.Size / (512*4)
+ ); // Cache is 1/4 the size of the disk
+ }
+
+ // Register with devfs
+ DevFS_AddDevice(&gFDD_DriverInfo);
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Prepare the module for removal
+ */
+void FDD_UnloadModule()
+{
+ int i;
+ DevFS_DelDevice( &gFDD_DriverInfo );
+ Mutex_Acquire(&glFDD);
+ for(i=0;i<4;i++) {
+ Time_RemoveTimer(gFDD_Devices[i].timer);
+ FDD_int_StopMotor(i);
+ }
+ Mutex_Release(&glFDD);
+ //IRQ_Clear(6);
+}
+
+/**
+ * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
+ * \brief Read Directory
+ */
+char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
+{
+ char name[2] = "0\0";
+
+ if(Pos >= 2 || Pos < 0) return NULL;
+
+ if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;
+
+ name[0] += Pos;
+
+ return strdup(name);
+}
+
+/**
+ * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename);
+ * \brief Find File Routine (for vfs_node)
+ */
+tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename)
+{
+ int i;
+
+ ENTER("sFilename", Filename);
+
+ // Sanity check string
+ if(Filename == NULL) {
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Check string length (should be 1)
+ if(Filename[0] == '\0' || Filename[1] != '\0') {
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Get First character
+ i = Filename[0] - '0';
+
+ // Check for 1st disk and if it is present return
+ if(i == 0 && gFDD_Devices[0].type != 0) {
+ LEAVE('p', &gFDD_Devices[0].Node);
+ return &gFDD_Devices[0].Node;
+ }
+
+ // Check for 2nd disk and if it is present return
+ if(i == 1 && gFDD_Devices[1].type != 0) {
+ LEAVE('p', &gFDD_Devices[1].Node);
+ return &gFDD_Devices[1].Node;
+ }
+
+ // Else return null
+ LEAVE('n');
+ return NULL;
+}
+
+static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
+/**
+ * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)
+ * \brief Stub ioctl function
+ */
+int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data)
+{
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS);
+
+ case DISK_IOCTL_GETBLOCKSIZE: return 512;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read Data from a disk
+*/
+Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ int ret;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ if(Node == NULL) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ if(Node->Inode != 0 && Node->Inode != 1) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);
+ LEAVE('i', ret);
+ return ret;
+}
+
+/**
+ * \brief Reads \a Count contiguous sectors from a disk
+ * \param SectorAddr Address of the first sector
+ * \param Count Number of sectors to read
+ * \param Buffer Destination Buffer
+ * \param Disk Disk Number
+ * \return Number of sectors read
+ * \note Used as a ::DrvUtil_ReadBlock helper
+ */
+Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)
+{
+ Uint ret = 0;
+ while(Count --)
+ {
+ if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )
+ return ret;
+
+ Buffer = (void*)( (tVAddr)Buffer + 512 );
+ SectorAddr ++;
+ ret ++;
+ }
+ return ret;
+}
+
+int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)
+{
+ int cyl, head, sec;
+ int spt, base;
+ int i;
+ int lba = SectorAddr;
+ Uint8 st0=0, st1=0, st2=0, bps=0; // Status Values
+
+ ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
+
+ base = cPORTBASE[Disk >> 1];
+
+ LOG("Calculating Disk Dimensions");
+ // Get CHS position
+ if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)
+ {
+ LEAVE('i', -1);
+ return -1;
+ }
+ LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);
+
+ // Start the motor
+ Mutex_Acquire(&glFDD); // Lock to stop the motor stopping on us
+ Time_RemoveTimer(gFDD_Devices[Disk].timer); // Remove Old Timer
+ // Start motor if needed
+ if(gFDD_Devices[Disk].motorState != 2) FDD_int_StartMotor(Disk);
+ Mutex_Release(&glFDD);
+
+ // Wait for spinup
+ LOG("Wait for the motor to spin up");
+ while(gFDD_Devices[Disk].motorState == 1) Threads_Yield();
+
+ LOG("Acquire Spinlock");
+ Mutex_Acquire(&glFDD);
+
+ // Read Data from DMA
+ LOG("Setting DMA for read");
+ DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2
+
+ LOG("Sending command");
+
+ #define SENDB(__data) if(FDD_int_SendByte(base, __data)) { FDD_Reset(Disk >> 1); continue; }
+
+ for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
+ {
+ FDD_int_SeekTrack(Disk, head, cyl);
+ if( Write ) {
+ SENDB(CMD_WRITE_DATA|CMD_FLAG_MFM_ENCODING);
+ }
+ else {
+ SENDB(CMD_READ_DATA|CMD_FLAG_MFM_ENCODING);
+ }
+ SENDB( (head << 2) | (Disk&1) );
+ SENDB(cyl & 0xFF);
+ SENDB(head & 0xFF);
+ SENDB(sec & 0xFF);
+ SENDB(0x02); // Bytes Per Sector (Real BPS=128*2^{val})
+ SENDB(spt); // SPT
+ SENDB(0x1B); // Gap Length (27 is default)
+ SENDB(0xFF); // Data Length
+
+ // Wait for IRQ
+ if( Write ) {
+ LOG("Writing Data");
+ DMA_WriteData(2, 512, Buffer);
+ LOG("Waiting for Data to be written");
+ if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; }
+ }
+ else {
+ LOG("Waiting for data to be read");
+ if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; }
+ LOG("Reading Data");
+ DMA_ReadData(2, 512, Buffer);
+ }
+
+ // Clear Input Buffer
+ LOG("Clearing Input Buffer");
+ // Status Values
+ FDD_int_GetByte(base, &st0);
+ FDD_int_GetByte(base, &st1);
+ FDD_int_GetByte(base, &st2);
+
+ // Cylinder, Head and Sector (mutilated in some way)
+ FDD_int_GetByte(base, NULL); // Cylinder
+ FDD_int_GetByte(base, NULL); // Head
+ FDD_int_GetByte(base, NULL); // Sector
+ // Should be the BPS set above (0x02)
+ FDD_int_GetByte(base, &bps);
+
+ // Check Status
+ // - Error Code
+ if(st0 & 0xC0) {
+ LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);
+ continue;
+ }
+ // - Status Flags
+ if(st0 & 0x08) { LOG("Drive not ready"); continue; }
+ if(st1 & 0x80) { LOG("End of Cylinder"); continue; }
+ if(st1 & 0x20) { LOG("CRC Error"); continue; }
+ if(st1 & 0x10) { LOG("Controller Timeout"); continue; }
+ if(st1 & 0x04) { LOG("No Data Found"); continue; }
+ if(st1 & 0x01 || st2 & 0x01) {
+ LOG("No Address mark found");
+ continue;
+ }
+ if(st2 & 0x40) { LOG("Deleted address mark"); continue; }
+ if(st2 & 0x20) { LOG("CRC error in data"); continue; }
+ if(st2 & 0x10) { LOG("Wrong Cylinder"); continue; }
+ if(st2 & 0x04) { LOG("uPD765 sector not found"); continue; }
+ if(st2 & 0x02) { LOG("Bad Cylinder"); continue; }
+
+ if(bps != 0x2) {
+ LOG("Returned BPS = 0x%02x, not 0x02", bps);
+ continue;
+ }
+
+ if(st1 & 0x02) {
+ LOG("Floppy not writable");
+ // Return error without triggering the attempt count check
+ i = FDD_MAX_READWRITE_ATTEMPTS+1;
+ break;
+ }
+
+ // Success!
+ break;
+ }
+ #undef SENDB
+
+ // Release Spinlock
+ LOG("Realeasing Spinlock and setting motor to stop");
+ Mutex_Release(&glFDD);
+
+ if(i == FDD_MAX_READWRITE_ATTEMPTS) {
+ Log_Warning("FDD", "Exceeded %i attempts in %s the disk",
+ FDD_MAX_READWRITE_ATTEMPTS,
+ (Write ? "writing to" : "reading from")
+ );
+ }
+
+ // Don't turn the motor off now, wait for a while
+ FDD_int_StopMotor(Disk);
+
+ // Error check
+ if( i < FDD_MAX_READWRITE_ATTEMPTS ) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ else {
+ LEAVE('i', 1);
+ return 1;
+ }
+}
+
+/**
+ * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
+ * \brief Read a sector from disk
+ * \todo Make real-hardware safe (account for read errors)
+ */
+int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
+{
+ int ret;
+
+ ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
+
+ if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ // Pass to general function
+ ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer);
+
+ if( ret == 0 ) {
+ IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer );
+ LEAVE('i', 1);
+ return 1;
+ }
+ else {
+ LOG("Reading failed");
+ LEAVE('i', 0);
+ return 0;
+ }
+}
+
+/**
+ * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
+ * \brief Write a sector to the floppy disk
+ * \note Not Implemented
+ */
+int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
+{
+ Log_Warning("FDD", "Read Only at the moment");
+ return -1;
+}
+
+/**
+ * \brief Seek disk to selected track
+ */
+int FDD_int_SeekTrack(int disk, int head, int track)
+{
+ Uint8 sr0=0, cyl=0;
+ int base, i, bUnclean;
+
+ base = cPORTBASE[disk>>1];
+
+ // Check if seeking is needed
+ if(gFDD_Devices[disk].track[head] == track)
+ return 1;
+
+ // - Seek Head 0
+ for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
+ {
+ if(i && bUnclean) FDD_Reset(disk >> 1);
+ bUnclean = 1;
+ if(FDD_int_SendByte(base, CMD_SEEK_TRACK)) continue;
+ if(FDD_int_SendByte(base, (head<<2)|(disk&1))) continue;
+ if(FDD_int_SendByte(base, track)) continue;
+ FDD_WaitIRQ();
+ FDD_SenseInt(base, &sr0, &cyl); // Wait for IRQ
+
+ bUnclean = 0;
+ if( cyl != track )
+ continue; // Try again
+ if( (sr0 & 0xF0) != 0x20 ) {
+ LOG("sr0 = 0x%x", sr0);
+ continue ;
+ }
+
+ break;
+ }
+
+ if( i == FDD_MAX_READWRITE_ATTEMPTS ) {
+ Log_Warning("FDD", "Unable to seek to track %i on disk %i",
+ track, disk);
+ return 0;
+ }
+
+ // Set Track in structure
+ gFDD_Devices[disk].track[head] = track;
+
+ LOG("Time_Delay(100)");
+ // Wait for Head to settle
+ Time_Delay(100);
+
+ return 1;
+}
+
+/**
+ * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
+ * \brief Get Dimensions of a disk
+ */
+int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
+{
+ switch(type) {
+ case 0:
+ return 0;
+
+ // 360Kb 5.25"
+ case 1:
+ *spt = 9;
+ *s = (lba % 9) + 1;
+ *c = lba / 18;
+ *h = (lba / 9) & 1;
+ break;
+
+ // 1220Kb 5.25"
+ case 2:
+ *spt = 15;
+ *s = (lba % 15) + 1;
+ *c = lba / 30;
+ *h = (lba / 15) & 1;
+ break;
+
+ // 720Kb 3.5"
+ case 3:
+ *spt = 9;
+ *s = (lba % 9) + 1;
+ *c = lba / 18;
+ *h = (lba / 9) & 1;
+ break;
+
+ // 1440Kb 3.5"
+ case 4:
+ *spt = 18;
+ *s = (lba % 18) + 1;
+ *c = lba / 36;
+ *h = (lba / 18) & 1;
+ //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);
+ break;
+
+ // 2880Kb 3.5"
+ case 5:
+ *spt = 36;
+ *s = (lba % 36) + 1;
+ *c = lba / 72;
+ *h = (lba / 32) & 1;
+ break;
+
+ default:
+ return -2;
+ }
+ return 1;
+}
+
+/**
+ * \fn void FDD_IRQHandler(int Num)
+ * \brief Handles IRQ6
+ */
+void FDD_IRQHandler(int Num, void *Ptr)
+{
+ gbFDD_IrqFired = 1;
+}
+
+/**
+ * \brief Wait for the FDD IRQ to fire
+ * \return Boolean failure (1 for timeout)
+ */
+inline int FDD_WaitIRQ()
+{
+ tTime end = now() + 2000;
+
+ // Wait for IRQ
+ while(!gbFDD_IrqFired && now() < end)
+ Threads_Yield();
+
+ if( !gbFDD_IrqFired ) {
+ Log_Warning("FDD", "FDD_WaitIRQ - Timeout");
+ return 1;
+ }
+
+ gbFDD_IrqFired = 0;
+ return 0;
+}
+
+void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl)
+{
+ FDD_int_SendByte(base, CMD_SENSE_INTERRUPT);
+ FDD_int_GetByte(base, sr0);
+ FDD_int_GetByte(base, cyl);
+}
+
+/**
+ * void FDD_int_SendByte(int base, char byte)
+ * \brief Sends a command to the controller
+ */
+int FDD_int_SendByte(int base, Uint8 byte)
+{
+ tTime end = now() + 1000; // 1s
+
+ while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end )
+ Threads_Yield(); //Delay
+// Time_Delay(10); //Delay
+
+ if( inb(base + PORT_MAINSTATUS) & 0x40 ) {
+ Log_Warning("FDD", "FDD_int_SendByte: DIO set, is this ok?");
+ return -2;
+ }
+
+ if( now() > end )
+ {
+ Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x", byte, base);
+ return 1;
+ }
+ outb(base + PORT_DATA, byte);
+// Log_Debug("FDD", "FDD_int_SendByte: Sent 0x%02x to 0x%x", byte, base);
+ return 0;
+}
+
+/**
+ * int FDD_int_GetByte(int base, char byte)
+ * \brief Receive data from fdd controller
+ */
+int FDD_int_GetByte(int base, Uint8 *value)
+{
+ tTime end = now() + 1000; // 1s
+
+ while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end )
+ Time_Delay(10);
+
+ if( !(inb(base + PORT_MAINSTATUS) & 0x40) ) {
+ Log_Warning("FDD", "FDD_int_GetByte: DIO unset, is this ok?");
+ return -2;
+ }
+
+ if( now() > end )
+ {
+ Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x", base);
+ return -1;
+ }
+
+ if(value)
+ *value = inb(base + PORT_DATA);
+ else
+ inb(base + PORT_DATA);
+ return 0;
+}
+
+/**
+ * \brief Recalibrate the specified disk
+ */
+void FDD_Recalibrate(int disk)
+{
+ ENTER("idisk", disk);
+
+ LOG("Starting Motor");
+ FDD_int_StartMotor(disk);
+ // Wait for Spinup
+ while(gFDD_Devices[disk].motorState <= 1) Threads_Yield();
+
+ LOG("Sending Calibrate Command");
+ FDD_int_SendByte(cPORTBASE[disk>>1], CMD_RECALIBRATE);
+ FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);
+
+ LOG("Waiting for IRQ");
+ FDD_WaitIRQ();
+ FDD_SenseInt(cPORTBASE[disk>>1], NULL, NULL);
+
+ LOG("Stopping Motor");
+ FDD_int_StopMotor(disk);
+ LEAVE('-');
+}
+
+/**
+ * \brief Reconfigure the controller
+ */
+int FDD_Reconfigure(int ID)
+{
+ Uint16 base = cPORTBASE[ID];
+
+ ENTER("iID", ID);
+
+ FDD_int_SendByte(base, CMD_CONFIGURE);
+ FDD_int_SendByte(base, 0);
+ // Implied seek enabled, FIFO Enabled, Drive Polling Disabled, data buffer threshold 8 bytes
+ FDD_int_SendByte(base, (1 << 6) | (0 << 5) | (0 << 4) | 7);
+ FDD_int_SendByte(base, 0); // Precompensation - use default
+
+ // Commit
+ FDD_int_SendByte(base, CMD_LOCK|CMD_FLAG_MULTI_TRACK);
+ FDD_int_GetByte(base, NULL);
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \brief Reset the specified FDD controller
+ */
+int FDD_Reset(int id)
+{
+ Uint16 base = cPORTBASE[id];
+ Uint8 motor_state;
+
+ ENTER("iID", id);
+
+ // Reset the card
+ motor_state = inb(base + PORT_DIGOUTPUT) & 0xF0;
+ outb(base + PORT_DIGOUTPUT, motor_state|0); // Disable FDC
+ Time_Delay(1);
+ outb(base + PORT_DIGOUTPUT, motor_state|8|4); // Re-enable FDC (DMA and Enable)
+
+ // Set the data rate
+ outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s
+
+ // Wait for IRQ
+ LOG("Awaiting IRQ");
+
+ FDD_WaitIRQ();
+
+ FDD_SenseInt(base, NULL, NULL);
+
+ // Specify
+ FDD_int_SendByte(base, CMD_SPECIFY); // Step and Head Load Times
+ FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
+ FDD_int_SendByte(base, 0x02); // Head Load Time >> 1
+
+ LOG("Recalibrating Disk");
+ FDD_Recalibrate((id<<1)|0);
+ FDD_Recalibrate((id<<1)|1);
+
+ LEAVE_RET('i', 0);
+}
+
+/**
+ * \fn void FDD_int_TimerCallback()
+ * \brief Called by timer
+ */
+void FDD_int_TimerCallback(void *Arg)
+{
+ int disk = (Uint)Arg;
+ ENTER("iarg", disk);
+ if(gFDD_Devices[disk].motorState == 1)
+ gFDD_Devices[disk].motorState = 2;
+ Time_RemoveTimer(gFDD_Devices[disk].timer);
+ gFDD_Devices[disk].timer = -1;
+ LEAVE('-');
+}
+
+/**
+ * \fn void FDD_int_StartMotor(char disk)
+ * \brief Starts FDD Motor
+ */
+void FDD_int_StartMotor(int disk)
+{
+ Uint8 state;
+ if( gFDD_Devices[disk].motorState != 0 ) return ;
+ // Set motor ON bit
+ state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
+ state |= 1 << (4+disk);
+ outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
+ // Mark as spinning up
+ gFDD_Devices[disk].motorState = 1;
+ // Schedule a timer for when it's up to speed
+ gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk);
+}
+
+/**
+ * \brief Schedule the drive motor to stop
+ * \param Disk Disk number to stop
+ */
+void FDD_int_StopMotor(int Disk)
+{
+ // Ignore if the motor is aready off
+ if( gFDD_Devices[Disk].motorState == 0 ) return ;
+
+ // Don't double-schedule timer
+ if( gFDD_Devices[Disk].timer != -1 )
+ {
+ Time_RemoveTimer( gFDD_Devices[Disk].timer );
+ }
+
+ gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(Uint)Disk);
+}
+
+/**
+ * \brief Stops FDD Motor
+ */
+void FDD_int_StopMotorCallback(void *Arg)
+{
+ Uint8 state, disk = (Uint)Arg;
+
+ // Mutex is only locked if disk is in use
+ if( Mutex_IsLocked(&glFDD) ) return ;
+
+ ENTER("iDisk", disk);
+
+ // Clear motor on bit
+ state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
+ state &= ~( 1 << (4+disk) );
+ outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
+
+ // Mark as off
+ gFDD_Devices[disk].motorState = 0;
+
+ LEAVE('-');
+}
+
--- /dev/null
+#
+#
+
+DEPS = x86/ISADMA
+OBJ = main.o fdc.o
+NAME = FDDv2
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 82077AA FDC
+ * - By John Hodge (thePowersGang)
+ *
+ * common.h
+ * - Common definitions
+ */
+#ifndef _FDC_COMMON_H_
+#define _FDC_COMMON_H_
+
+#include <mutex.h>
+#include <timers.h>
+
+// === CONSTANTS ===
+#define MAX_DISKS 8 // 4 per controller, 2 controllers
+#define TRACKS_PER_DISK (1440*2/18)
+#define BYTES_PER_TRACK (18*512)
+
+// === TYPEDEFS ===
+typedef struct sFDD_Drive tDrive;
+
+// === STRUCTURES ===
+struct sFDD_Drive
+{
+ int bValid;
+ int bInserted;
+ int MotorState;
+ tTimer *Timer;
+
+ tMutex Mutex;
+
+ void *TrackData[TRACKS_PER_DISK]; // Whole tracks are read
+};
+
+// === FUNCTIONS ===
+extern int FDD_SetupIO(void);
+extern int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
+
+// === GLOBALS ===
+extern tDrive gaFDD_Disks[MAX_DISKS];
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 82077AA FDC
+ * - By John Hodge (thePowersGang)
+ *
+ * fdc.c
+ * - FDC IO Functions
+ */
+#define DEBUG 0
+#include <acess.h>
+#include "common.h"
+#include <dma.h>
+#include <timers.h>
+
+// === CONSTANTS ===
+#define MOTOR_ON_DELAY 500
+#define MOTOR_OFF_DELAY 2000
+
+enum eMotorState
+{
+ MOTOR_OFF,
+ MOTOR_ATSPEED,
+};
+
+enum eFDC_Registers
+{
+ FDC_DOR = 0x02, // Digital Output Register
+ FDC_MSR = 0x04, // Master Status Register (Read Only)
+ FDC_FIFO = 0x05, // FIFO port
+ FDC_CCR = 0x07 // Configuration Control Register (write only)
+};
+
+enum eFDC_Commands
+{
+ CMD_SPECIFY = 3, // Specify parameters
+ CMD_WRITE_DATA = 5, // Write Data
+ CMD_READ_DATA = 6, // Read Data
+ CMD_RECALIBRATE = 7, // Recalibrate a drive
+ CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt
+ CMD_SEEK = 15, // Seek to a track
+};
+
+// === PROTOTYPES ===
+ int FDD_SetupIO(void);
+ int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
+ int FDD_int_SeekToTrack(int Disk, int Track);
+ int FDD_int_Calibrate(int Disk);
+ int FDD_int_Reset(int Disk);
+// --- FIFO
+ int FDD_int_WriteData(Uint16 Base, Uint8 Data);
+ int FDD_int_ReadData(Uint16 Base, Uint8 *Data);
+void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl);
+// --- Motor Control
+ int FDD_int_StartMotor(int Disk);
+ int FDD_int_StopMotor(int Disk);
+void FDD_int_StopMotorCallback(void *Ptr);
+// --- Helpers
+ int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0);
+Uint16 FDD_int_GetBase(int Disk, int *Drive);
+// --- Interrupt
+void FDD_int_ClearIRQ(void);
+ int FDD_int_WaitIRQ(void);
+void FDD_int_IRQHandler(int IRQ, void *Ptr);
+
+// === GLOBALS ===
+/**
+ * \brief Marker for IRQ6
+ * \todo Convert into a semaphore?
+ */
+ int gbFDD_IRQ6Fired;
+/**
+ * \brief Protector for DMA and IRQ6
+ */
+tMutex gFDD_IOMutex;
+
+// === CODE ===
+/**
+ * \brief Set up FDC IO
+ * \return Boolean failure
+ *
+ * Registers the IRQ handler and resets the controller
+ */
+int FDD_SetupIO(void)
+{
+ // Install IRQ6 Handler
+ IRQ_AddHandler(6, FDD_int_IRQHandler, NULL);
+
+ // Reset controller
+ FDD_int_Reset(0);
+ // TODO: All controllers
+
+ return 0;
+}
+
+/**
+ * \brief Read/Write data from/to a disk
+ * \param Disk Global disk number
+ * \param Track Track number (Cyl*2+Head)
+ * \param bWrite Toggle write mode
+ * \param Buffer Destination/Source buffer
+ * \return Boolean failure
+ */
+int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer)
+{
+ Uint8 cmd;
+ int i, _disk;
+ Uint16 base = FDD_int_GetBase(Disk, &_disk);
+ int cyl = Track >> 1, head = Track & 1;
+
+ ENTER("iDisk iTrack ibWrite pBuffer", Disk, Track, bWrite, Buffer);
+
+ Mutex_Acquire( &gFDD_IOMutex );
+
+ // Initialise DMA for read/write
+ // TODO: Support non 1.44MiB FDs
+ DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite);
+
+ // Select command
+ if( bWrite )
+ cmd = CMD_WRITE_DATA | 0xC0;
+ else
+ cmd = CMD_READ_DATA | 0xC0;
+
+ LOG("cmd = 0x%x", cmd);
+
+ // Seek
+ if( FDD_int_SeekToTrack(Disk, Track) ) {
+ Mutex_Release( &gFDD_IOMutex );
+ LEAVE('i', -1);
+ return -1;
+ }
+ LOG("Track seek done");
+
+ for( i = 0; i < 20; i ++ )
+ {
+ LOG("Starting motor");
+ FDD_int_StartMotor(Disk);
+
+ // Write data
+ if( bWrite )
+ DMA_WriteData(2, BYTES_PER_TRACK, Buffer);
+
+ LOG("Sending command stream");
+ FDD_int_WriteData(base, cmd);
+ FDD_int_WriteData(base, (head << 2) | _disk);
+ FDD_int_WriteData(base, cyl);
+ FDD_int_WriteData(base, head);
+ FDD_int_WriteData(base, 1); // First Sector
+ FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n)
+ FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44
+ FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again
+ FDD_int_WriteData(base, 0xFF); // Data length - ?
+
+ LOG("Waiting for IRQ");
+ FDD_int_WaitIRQ();
+
+ // No Sense Interrupt
+
+ LOG("Reading result");
+ Uint8 st0=0, st1=0, st2=0, bps=0;
+ FDD_int_ReadData(base, &st0);
+ FDD_int_ReadData(base, &st1); // st1
+ FDD_int_ReadData(base, &st2); // st2
+ FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl
+ FDD_int_ReadData(base, NULL); // rhe - Mutilated Head
+ FDD_int_ReadData(base, NULL); // rse - Mutilated sector
+ FDD_int_ReadData(base, &bps); // bps - Should be the same as above
+
+ if( st0 & 0xc0 ) {
+ FDD_int_HandleST0Error(__func__, Disk, st0);
+ continue ;
+ }
+
+ if( st2 & 0x02 ) {
+ Log_Debug("FDD", "Disk %i is not writable", Disk);
+ Mutex_Release( &gFDD_IOMutex );
+ LEAVE('i', 2);
+ return 2;
+ }
+
+ if( st0 & 0x08 ) {
+ Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready");
+ continue ;
+ }
+
+
+ if( st1 & 0x80 ) {
+ Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder");
+ continue ;
+ }
+
+ if( st1 & (0x20|0x10|0x04|0x01) ) {
+ Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1);
+ continue;
+ }
+
+ if( st2 & (0x40|0x20|0x10|0x04|0x01) ) {
+ Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2);
+ continue ;
+ }
+
+ if( bps != 0x2 ) {
+ Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps);
+ continue ;
+ }
+
+ // Read back data
+ if( !bWrite )
+ DMA_ReadData(2, BYTES_PER_TRACK, Buffer);
+
+ LOG("All data done");
+ FDD_int_StopMotor(Disk);
+ Mutex_Release( &gFDD_IOMutex );
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ Log_Debug("FDD", "%i retries exhausted", i);
+ FDD_int_StopMotor(Disk);
+ Mutex_Release( &gFDD_IOMutex );
+ LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \brief Seek to a specific track
+ * \param Disk Global disk number
+ * \param Track Track number (Cyl*2+Head)
+ * \return Boolean failure
+ */
+int FDD_int_SeekToTrack(int Disk, int Track)
+{
+ Uint8 st0=0, res_cyl=0;
+ int cyl, head;
+ int _disk;
+ Uint16 base = FDD_int_GetBase(Disk, &_disk);;
+
+ ENTER("iDisk iTrack", Disk, Track);
+
+ cyl = Track / 2;
+ head = Track % 2;
+
+ LOG("cyl = %i, head = %i", cyl, head);
+
+ FDD_int_StartMotor(Disk);
+
+ for( int i = 0; i < 10; i ++ )
+ {
+ LOG("Sending command");
+ FDD_int_ClearIRQ();
+ FDD_int_WriteData(base, CMD_SEEK);
+ FDD_int_WriteData(base, (head << 2) + _disk);
+ FDD_int_WriteData(base, cyl);
+
+ LOG("Waiting for IRQ");
+ FDD_int_WaitIRQ();
+ FDD_int_SenseInterrupt(base, &st0, &res_cyl);
+
+ if( st0 & 0xC0 )
+ {
+ FDD_int_HandleST0Error(__func__, Disk, st0);
+ continue ;
+ }
+
+ if( res_cyl == cyl ) {
+ FDD_int_StopMotor(Disk);
+ LEAVE('i', 0);
+ return 0;
+ }
+ }
+
+ Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n");
+ FDD_int_StopMotor(Disk);
+ LEAVE('i', 1);
+ return 1;
+}
+
+/**
+ * \brief Calibrate a drive
+ * \param Disk Global disk number
+ */
+int FDD_int_Calibrate(int Disk)
+{
+ int _disk;
+ Uint16 base = FDD_int_GetBase(Disk, &_disk);
+ FDD_int_StartMotor(Disk);
+
+ for( int i = 0; i < 10; i ++ )
+ {
+ Uint8 st0=0, cyl = -1;
+
+ FDD_int_ClearIRQ();
+ FDD_int_WriteData(base, CMD_RECALIBRATE);
+ FDD_int_WriteData(base, _disk);
+
+ FDD_int_WaitIRQ();
+
+ FDD_int_SenseInterrupt(base, &st0, &cyl);
+
+ if( st0 & 0xC0 ) {
+ FDD_int_HandleST0Error(__func__, Disk, st0);
+ continue ;
+ }
+
+ if( cyl == 0 )
+ {
+ FDD_int_StopMotor(Disk);
+ return 0;
+ }
+ }
+
+ Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted");
+
+ return 1;
+}
+
+/**
+ * \brief Reset a controller
+ * \param Base Controller base address
+ */
+int FDD_int_Reset(int Disk)
+{
+ Uint8 tmp;
+ int _disk;
+ Uint16 base = FDD_int_GetBase(Disk, &_disk);
+
+ tmp = inb(base + FDC_DOR) & 0xF0;
+ outb( base + FDC_DOR, 0x00 );
+ Time_Delay(1);
+ outb( base + FDC_DOR, tmp | 0x0C );
+
+ FDD_int_SenseInterrupt(base, NULL, NULL);
+
+ outb(base + FDC_CCR, 0x00); // 500KB/s
+
+ FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times
+ FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
+ FDD_int_WriteData(base, 0x02); // Head Load Time >> 1
+
+ // TODO: Recalibrate all present disks
+ FDD_int_Calibrate(Disk);
+ return 0;
+}
+
+/**
+ * \brief Write a byte to the FIFO
+ */
+int FDD_int_WriteData(Uint16 Base, Uint8 Data)
+{
+ for( int i = 0; i < 100; i ++ )
+ {
+ if( inb(Base + FDC_MSR) & 0x80 )
+ {
+ outb(Base + FDC_FIFO, Data);
+ return 0;
+ }
+ Time_Delay(10);
+ }
+ Log_Error("FDD", "Write timeout");
+ return 1;
+}
+
+/**
+ * \brief Read a byte from the FIFO
+ */
+int FDD_int_ReadData(Uint16 Base, Uint8 *Data)
+{
+ for( int i = 0; i < 100; i ++ )
+ {
+ if( inb(Base + FDC_MSR) & 0x80 )
+ {
+ Uint8 tmp = inb(Base + FDC_FIFO);
+ if(Data) *Data = tmp;
+ return 0;
+ }
+ Time_Delay(10);
+ }
+ Log_Error("FDD", "Read timeout");
+ return 1;
+}
+
+/**
+ * \brief Acknowledge an interrupt
+ * \param Base Controller base address
+ * \param ST0 Location to store the ST0 value
+ * \param Cyl Current cylinder
+ */
+void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl)
+{
+ FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT);
+ FDD_int_ReadData(Base, ST0);
+ FDD_int_ReadData(Base, Cyl);
+}
+
+/**
+ * \brief Start the motor on a disk
+ */
+int FDD_int_StartMotor(int Disk)
+{
+ int _disk;
+ Uint16 base = FDD_int_GetBase(Disk, &_disk);
+
+ // Clear the motor off timer
+ Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
+ gaFDD_Disks[Disk].Timer = NULL;
+
+ // Check if the motor is already on
+ if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
+ return 0;
+
+ // Turn motor on
+ outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4)));
+
+ // Wait for it to reach speed
+ Time_Delay(MOTOR_ON_DELAY);
+
+ gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED;
+
+ return 0;
+}
+
+/**
+ * \brief Schedule the motor to stop
+ */
+int FDD_int_StopMotor(int Disk)
+{
+ if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
+ return 0;
+ if( gaFDD_Disks[Disk].Timer != NULL )
+ return 0;
+
+ gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk);
+
+ return 0;
+}
+
+/**
+ * \brief Actually stop the motor
+ * \param Ptr Actaully the global disk number
+ */
+void FDD_int_StopMotorCallback(void *Ptr)
+{
+ int Disk = (tVAddr)Ptr;
+ int _disk;
+ Uint16 base = FDD_int_GetBase(Disk, &_disk);
+
+ gaFDD_Disks[Disk].Timer = NULL;
+ gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
+
+ outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
+
+ return ;
+}
+
+/**
+ * \brief Converts a global disk number into a controller and drive
+ * \param Disk Global disk number
+ * \param Drive Destination for controller disk number
+ * \return Controller base address
+ */
+Uint16 FDD_int_GetBase(int Disk, int *Drive)
+{
+ if(Drive) *Drive = Disk & 3;
+ switch(Disk >> 2)
+ {
+ case 0: return 0x3F0;
+ case 1: return 0x370;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * \brief Convert a ST0 error value into a message
+ * \param Fcn Calling function name
+ * \parma Disk Global disk number
+ * \param ST0 ST0 Value
+ * \return Boolean failure
+ */
+int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0)
+{
+ static const char *status_type[] = {
+ 0, "Error", "Invalid", "Drive Error"
+ };
+
+ Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)",
+ Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0
+ );
+ return 0;
+}
+
+/**
+ * \brief Clear the IRQ fired flag
+ */
+void FDD_int_ClearIRQ(void)
+{
+ gbFDD_IRQ6Fired = 0;
+}
+
+/**
+ * \brief Wait for an IRQ to fire
+ */
+int FDD_int_WaitIRQ(void)
+{
+ while(gbFDD_IRQ6Fired == 0)
+ Threads_Yield();
+ return 0;
+}
+
+/**
+ * \brief IRQ Handler
+ * \param IRQ IRQ Number (unused)
+ * \param Ptr Data Pointer (unused)
+ */
+void FDD_int_IRQHandler(int IRQ, void *Ptr)
+{
+ gbFDD_IRQ6Fired = 1;
+}
+
--- /dev/null
+/*
+ * Acess2 82077AA FDC
+ * - By John Hodge (thePowersGang)
+ *
+ * fdc.c
+ * - Core file
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include "common.h"
+#include <api_drv_disk.h>
+
+// === CONSTANTS ===
+#define FDD_VERSION VER2(1,10)
+
+// === STRUCTURES ===
+
+// === PROTOTYPES ===
+ int FDD_Install(char **Arguments);
+ int FDD_RegisterFS(void);
+// --- VFS
+char *FDD_ReadDir(tVFS_Node *Node, int pos);
+tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name);
+ int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
+Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
+// --- Helpers
+ int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, FDD_VERSION, Storage_FDDv2, FDD_Install, NULL, "x86_ISADMA", NULL);
+tDrive gaFDD_Disks[MAX_DISKS];
+tVFS_Node gaFDD_DiskNodes[MAX_DISKS];
+tVFS_NodeType gFDD_RootNodeType = {
+ .TypeName = "FDD Root Node",
+ .ReadDir = FDD_ReadDir,
+ .FindDir = FDD_FindDir,
+ .IOCtl = FDD_IOCtl
+ };
+tVFS_NodeType gFDD_DevNodeType = {
+ .TypeName = "FDD Device",
+ .Read = FDD_ReadFS,
+ .Write = NULL // FDD_WriteFS?
+ };
+tDevFS_Driver gFDD_DriverInfo = {
+ NULL, "fdd",
+ {
+ .Size = -1,
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gFDD_RootNodeType
+ }
+ };
+
+// === CODE ===
+int FDD_Install(char **Arguments)
+{
+ // Query CMOS memory
+ {
+ Uint8 data;
+ outb(0x70, 0x10);
+ data = inb(0x71);
+
+ // NOTE: CMOS only reports 2 disks
+ if( (data & 0xF0) == 0x40 )
+ gaFDD_Disks[0].bValid = gaFDD_Disks[0].bInserted = 1;
+ if( (data & 0x0F) == 0x04 )
+ gaFDD_Disks[1].bValid = gaFDD_Disks[1].bInserted = 1;
+
+ if( gaFDD_Disks[0].bValid == 0 && gaFDD_Disks[1].bValid == 0 )
+ return MODULE_ERR_NOTNEEDED;
+ }
+
+ // Initialise controller
+ FDD_SetupIO();
+
+ FDD_RegisterFS();
+
+ return 0;
+}
+
+/**
+ * \brief Register the FDD driver with DevFS
+ */
+int FDD_RegisterFS(void)
+{
+ gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
+ = gFDD_DriverInfo.RootNode.ATime = now();
+
+ for( int i = 0; i < MAX_DISKS; i ++ )
+ {
+ if( !gaFDD_Disks[i].bValid ) continue ;
+
+ // Initialise Child Nodes
+ gaFDD_DiskNodes[i].Inode = i;
+ gaFDD_DiskNodes[i].Flags = 0;
+ gaFDD_DiskNodes[i].NumACLs = 0;
+ gaFDD_DiskNodes[i].Type = &gFDD_DevNodeType;
+ gaFDD_DiskNodes[i].Size = 1440*1024; // TODO: Non 1.44 disks
+ }
+
+ DevFS_AddDevice( &gFDD_DriverInfo );
+ return 0;
+}
+
+/**
+ * \brief Get the name of the \a Pos th item in the driver root
+ * \param Node Root node (unused)
+ * \param Pos Position
+ * \return Heap string of node name
+ */
+char *FDD_ReadDir(tVFS_Node *Node, int Pos)
+{
+ char ret_tpl[2];
+ if(Pos < 0 || Pos > MAX_DISKS )
+ return NULL;
+ if(gaFDD_Disks[Pos].bValid)
+ return VFS_SKIP;
+
+ ret_tpl[0] = '0' + Pos;
+ ret_tpl[1] = '\0';
+ return strdup(ret_tpl);
+}
+
+/**
+ * \brief Get a node by name
+ * \param Node Root node (unused)
+ * \param Name Drive name
+ * \return Pointer to node structure
+ */
+tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *Name)
+{
+ int pos;
+ if( '0' > Name[0] || Name[0] > '9' ) return NULL;
+ if( Name[1] != '\0' ) return NULL;
+
+ pos = Name[0] - '0';
+
+ return &gaFDD_DiskNodes[pos];
+}
+
+static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
+/**
+ * \brief Driver root IOCtl Handler
+ * \param Node Root node (unused)
+ * \param ID IOCtl ID
+ * \param Data IOCtl specific data pointer
+ */
+int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_DISK, "FDDv2", FDD_VERSION, casIOCTLS);
+
+ case DISK_IOCTL_GETBLOCKSIZE: return 512;
+
+ default:
+ return -1;
+ }
+}
+
+/**
+ * \brief Read from a disk
+ * \param Node Disk node
+ * \param Offset Byte offset in disk
+ * \param Length Number of bytes to read
+ * \param Buffer Destination buffer
+ * \return Number of bytes read
+ */
+Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ int disk = Node->Inode;
+ int track;
+ int rem_len;
+ char *dest = Buffer;
+
+ ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ if( Offset > Node->Size ) LEAVE_RET('i', 0);
+ if( Length > Node->Size ) Length = Node->Size;
+ if( Offset + Length > Node->Size )
+ Length = Node->Size - Offset;
+ if( Length == 0 ) return 0;
+
+ rem_len = Length;
+
+ track = Offset / BYTES_PER_TRACK;
+ Offset %= BYTES_PER_TRACK;
+
+ if( Offset )
+ {
+ int len;
+ if(rem_len > BYTES_PER_TRACK - Offset)
+ len = BYTES_PER_TRACK - Offset;
+ else
+ len = rem_len;
+ FDD_int_ReadWriteWithinTrack(disk, track, 0, Offset, len, dest);
+ dest += len;
+ rem_len -= len;
+ track ++;
+ }
+
+ if( rem_len > 0)
+ {
+ while( rem_len > BYTES_PER_TRACK )
+ {
+ FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, BYTES_PER_TRACK, dest);
+ dest += BYTES_PER_TRACK;
+ rem_len -= BYTES_PER_TRACK;
+ track ++;
+ }
+
+ FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, rem_len, dest);
+ }
+
+ LEAVE('X', Length);
+ return Length;
+}
+
+/**
+ * \brief Read from a track
+ */
+int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer)
+{
+ if( Offset > BYTES_PER_TRACK || Length > BYTES_PER_TRACK )
+ return 1;
+ if( Offset + Length > BYTES_PER_TRACK )
+ return 1;
+
+ if( Length == 0 )
+ return 0;
+
+ ENTER("iDisk iTrack bbWrite xOffset xLength pBuffer",
+ Disk, Track, bWrite, Offset, Length, Buffer);
+
+ Mutex_Acquire( &gaFDD_Disks[Disk].Mutex );
+
+ // If the cache doesn't exist, create it
+ if( !gaFDD_Disks[Disk].TrackData[Track] )
+ {
+ gaFDD_Disks[Disk].TrackData[Track] = malloc( BYTES_PER_TRACK );
+ // Don't bother reading if this is a whole track write
+ if( !(bWrite && Offset == 0 && Length == BYTES_PER_TRACK) )
+ {
+ LOG("Reading track");
+ FDD_int_ReadWriteTrack(Disk, Track, 0, gaFDD_Disks[Disk].TrackData[Track]);
+ }
+ }
+
+ // Read/Write
+ if( bWrite )
+ {
+ // Write to cache then commit cache to disk
+ char *dest = gaFDD_Disks[Disk].TrackData[Track];
+ LOG("Write to cache");
+ memcpy( dest + Offset, Buffer, Length );
+ FDD_int_ReadWriteTrack(Disk, Track, 1, gaFDD_Disks[Disk].TrackData[Track]);
+ }
+ else
+ {
+ // Read from cache
+ char *src = gaFDD_Disks[Disk].TrackData[Track];
+ LOG("Read from cache");
+ memcpy(Buffer, src + Offset, Length);
+ }
+
+ Mutex_Release( &gaFDD_Disks[Disk].Mutex );
+
+ LEAVE('i', 0);
+ return 0;
+}
--- /dev/null
+#
+# Acess2 Logical Volume Manager
+# - Handles MBR Partitions (and eventually others)
+#
+
+OBJ = main.o mbr.o
+NAME = LVM
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 Logical Volume Manager
+ * - By John Hodge (thePowersGang)
+ *
+ * lvm_int.h
+ * - Internal definitions
+ */
+
--- /dev/null
+/*
+ * Acess2 IDE Harddisk Driver
+ * - MBR Parsing Code
+ * mbr.c
+ */
+#define DEBUG 0
+#include <acess.h>
+#include "lvm_int.h"
+
+// === PROTOTYPES ===
+void LVM_MBR_Initialise(int Disk, tMBR *MBR);
+Uint64 LVM_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length);
+
+// === GLOBALS ===
+
+// === CODE ===
+/**
+ * \fn void ATA_ParseMBR(int Disk, tMBR *MBR)
+ */
+void ATA_ParseMBR(int Disk, tMBR *MBR)
+{
+ int i, j = 0, k = 4;
+ Uint64 extendedLBA;
+ Uint64 base, len;
+
+ ENTER("iDisk", Disk);
+
+ // Count Partitions
+ gATA_Disks[Disk].NumPartitions = 0;
+ extendedLBA = 0;
+ for( i = 0; i < 4; i ++ )
+ {
+ if( MBR->Parts[i].SystemID == 0 ) continue;
+ if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 // LBA 28
+ || MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 // LBA 48
+ )
+ {
+ if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) {
+ LOG("Extended Partition at 0x%llx", MBR->Parts[i].LBAStart);
+ if(extendedLBA != 0) {
+ Warning("Disk %i has multiple extended partitions, ignoring rest", Disk);
+ continue;
+ }
+ extendedLBA = MBR->Parts[i].LBAStart;
+ continue;
+ }
+ LOG("Primary Partition at 0x%llx", MBR->Parts[i].LBAStart);
+
+ gATA_Disks[Disk].NumPartitions ++;
+ continue;
+ }
+ // Invalid Partition, so don't count it
+ }
+ while(extendedLBA != 0)
+ {
+ extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len);
+ if( extendedLBA == -1 ) break;
+ gATA_Disks[Disk].NumPartitions ++;
+ }
+ LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions);
+
+ // Create patition array
+ gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) );
+
+ // --- Fill Partition Info ---
+ extendedLBA = 0;
+ for( j = 0, i = 0; i < 4; i ++ )
+ {
+ LOG("MBR->Parts[%i].SystemID = 0x%02x", i, MBR->Parts[i].SystemID);
+ if( MBR->Parts[i].SystemID == 0 ) continue;
+ if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 ) // LBA 28
+ {
+ base = MBR->Parts[i].LBAStart;
+ len = MBR->Parts[i].LBALength;
+ }
+ else if( MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 ) // LBA 58
+ {
+ base = (MBR->Parts[i].StartHi << 16) | MBR->Parts[i].LBAStart;
+ len = (MBR->Parts[i].LengthHi << 16) | MBR->Parts[i].LBALength;
+ }
+ else
+ continue;
+
+ if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) {
+ if(extendedLBA != 0) {
+ Log_Warning("ATA", "Disk %i has multiple extended partitions, ignoring rest", Disk);
+ continue;
+ }
+ extendedLBA = base;
+ continue;
+ }
+ // Create Partition
+ ATA_int_MakePartition(
+ &gATA_Disks[Disk].Partitions[j], Disk, j,
+ base, len
+ );
+ j ++;
+
+ }
+ // Scan extended partitions
+ while(extendedLBA != 0)
+ {
+ extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len);
+ if(extendedLBA == -1) break;
+ ATA_int_MakePartition(
+ &gATA_Disks[Disk].Partitions[j], Disk, k, base, len
+ );
+ }
+
+ LEAVE('-');
+}
+
+/**
+ * \brief Reads an extended partition
+ * \return LBA of next Extended, -1 on error, 0 for last
+ */
+Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length)
+{
+ Uint64 link = 0;
+ int bFoundPart = 0;;
+ int i;
+ tMBR mbr;
+ Uint64 base, len;
+
+ if( ATA_ReadDMA( Disk, Addr, 1, &mbr ) != 0 )
+ return -1; // Stop on Errors
+
+ for( i = 0; i < 4; i ++ )
+ {
+ if( mbr.Parts[i].SystemID == 0 ) continue;
+
+ // LBA 24
+ if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) {
+ base = mbr.Parts[i].LBAStart;
+ len = mbr.Parts[i].LBALength;
+ }
+ // LBA 48
+ else if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) {
+ base = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart;
+ len = (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength;
+ }
+ else {
+ Log_Warning("ATA MBR",
+ "Unknown partition type 0x%x, Disk %i Ext 0x%llx Part %i",
+ mbr.Parts[i].Boot, Disk, Addr, i
+ );
+ return -1;
+ }
+
+ switch(mbr.Parts[i].SystemID)
+ {
+ case 0xF:
+ case 0x5:
+ if(link != 0) {
+ Log_Warning("ATA MBR",
+ "Disk %i has two forward links in the extended partition",
+ Disk
+ );
+ return -1;
+ }
+ link = base;
+ break;
+ default:
+ if(bFoundPart) {
+ Warning("ATA MBR",
+ "Disk %i has more than one partition in the extended partition at 0x%llx",
+ Disk, Addr
+ );
+ return -1;
+ }
+ bFoundPart = 1;
+ *Base = base;
+ *Length = len;
+ break;
+ }
+ }
+
+ if(!bFoundPart) {
+ Log_Warning("ATA MBR",
+ "No partition in extended partiton, Disk %i 0x%llx",
+ Disk, Addr);
+ return -1;
+ }
+
+ return link;
+}
--- /dev/null
+CATEGORY = Storage
+
+-include ../../Makefile.tpl
--- /dev/null
+#
+#
+
+OBJ = main.o
+OBJ += usb.o usb_lowlevel.o usb_devinit.o usb_io.o usb_poll.o
+OBJ += hub.o
+CPPFLAGS = -Iinclude
+NAME = Core
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * hub.c
+ * - Basic hub driver
+ */
+#define DEBUG 1
+#include <usb_hub.h>
+
+#define MAX_PORTS 32 // Not actually a max, but used for DeviceRemovable
+
+
+#define GET_STATUS 0
+#define CLEAR_FEATURE 1
+// resvd
+#define SET_FEATURE 3
+
+#define PORT_CONNECTION 0
+#define PORT_ENABLE 1
+#define PORT_SUSPEND 2
+#define PORT_OVER_CURRENT 3
+#define PORT_RESET 4
+#define PORT_POWER 8
+#define PORT_LOW_SPEED 9
+#define C_PORT_CONNECTION 16
+#define C_PORT_ENABLE 17
+#define C_PORT_SUSPEND 18
+#define C_PORT_OVER_CURRENT 19
+#define C_PORT_RESET 20
+#define PORT_TEST 21
+#define PORT_INDICATOR 21
+
+struct sHubDescriptor
+{
+ Uint8 DescLength;
+ Uint8 DescType; // = 0x29
+ Uint8 NbrPorts;
+ Uint16 HubCharacteristics;
+ Uint8 PwrOn2PwrGood; // 2 ms intervals
+ Uint8 HubContrCurrent; // Max internal current (mA)
+ Uint8 DeviceRemovable[MAX_PORTS];
+};
+
+struct sHubInfo
+{
+ tUSBHub *HubPtr;
+ int PowerOnDelay; // in ms
+ int nPorts;
+ Uint8 DeviceRemovable[];
+};
+
+// === PROTOTYPES ===
+void Hub_Connected(tUSBInterface *Dev);
+void Hub_Disconnected(tUSBInterface *Dev);
+void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
+void Hub_int_HandleChange(tUSBInterface *Dev, int Port);
+
+// === GLOBALS ===
+tUSBDriver gUSBHub_Driver = {
+ .Name = "Hub",
+ .Match = {.Class = {0x090000, 0xFF0000}},
+ .Connected = Hub_Connected,
+ .Disconnected = Hub_Disconnected,
+ .MaxEndpoints = 1,
+ .Endpoints = {
+ {0x83, Hub_PortStatusChange}
+ }
+};
+
+// === CODE ===
+#if 0
+int Hub_DriverInitialise(char **Arguments)
+{
+ USB_RegisterDriver( &gUSBHub_Driver );
+ return 0;
+}
+#endif
+
+void Hub_Connected(tUSBInterface *Dev)
+{
+ struct sHubDescriptor hub_desc;
+ struct sHubInfo *info;
+
+ // Read hub descriptor (Class descriptor 0x29)
+ USB_ReadDescriptor(Dev, 0x129, 0, sizeof(hub_desc), &hub_desc);
+
+ LOG("%i Ports", hub_desc.NbrPorts);
+ LOG("Takes %i ms for power to stabilise", hub_desc.PwrOn2PwrGood*2);
+
+ // Allocate infomation structure
+ info = malloc(sizeof(*info) + (hub_desc.NbrPorts+7)/8);
+ if(!info) {
+ Log_Error("USBHub", "malloc() failed");
+ return ;
+ }
+ USB_SetDeviceDataPtr(Dev, info);
+
+ // Fill data
+ info->nPorts = hub_desc.NbrPorts;
+ info->PowerOnDelay = hub_desc.PwrOn2PwrGood * 2;
+ memcpy(info->DeviceRemovable, hub_desc.DeviceRemovable, (hub_desc.NbrPorts+7)/8);
+ // Register
+ info->HubPtr = USB_RegisterHub(Dev, info->nPorts);
+
+ // Register poll on endpoint
+ USB_StartPollingEndpoint(Dev, 1);
+}
+
+void Hub_Disconnected(tUSBInterface *Dev)
+{
+ struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
+ USB_RemoveHub(info->HubPtr);
+ free(info);
+}
+
+void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
+{
+ Uint8 *status = Data;
+ struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
+ int i;
+ for( i = 0; i < info->nPorts; i += 8, status ++ )
+ {
+ if( i/8 >= Length ) break;
+ if( *status == 0 ) continue;
+
+ for( int j = 0; j < 8; j ++ )
+ if( *status & (1 << j) )
+ Hub_int_HandleChange(Dev, i+j);
+ }
+}
+
+void Hub_int_HandleChange(tUSBInterface *Dev, int Port)
+{
+ struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
+ Uint16 status[2]; // Status, Change
+
+ // Get port status
+ USB_Request(Dev, 0, 0xA3, GET_STATUS, 0, Port, 4, status);
+
+ LOG("Port %i: status = {0b%b, 0b%b}", Port, status[0], status[1]);
+
+ // Handle connections / disconnections
+ if( status[1] & 0x0001 )
+ {
+ if( status[0] & 0x0001 ) {
+ // Connected
+ // - Power on port
+ USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_POWER, Port, 0, NULL);
+ Time_Delay(info->PowerOnDelay);
+ // - Reset
+ USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_RESET, Port, 0, NULL);
+ Time_Delay(20); // Spec says 10ms after reset, but how long is reset?
+ // - Enable
+ USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_ENABLE, Port, 0, NULL);
+ // - Poke USB Stack
+ USB_DeviceConnected(info->HubPtr, Port);
+ }
+ else {
+ // Disconnected
+ USB_DeviceDisconnected(info->HubPtr, Port);
+ }
+
+ USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, Port, 0, NULL);
+ }
+
+ // Reset change
+ if( status[1] & 0x0010 )
+ {
+ if( status[0] & 0x0010 ) {
+ // Reset complete
+ }
+ else {
+ // Useful?
+ }
+ // ACK
+ USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_RESET, Port, 0, NULL);
+ }
+}
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_core.h
+ * - Core USB IO Header
+ */
+#ifndef _USB_CORE_H_
+#define _USB_CORE_H_
+
+#include <acess.h>
+
+typedef struct sUSBInterface tUSBInterface;
+typedef struct sUSBDriver tUSBDriver;
+
+typedef void (*tUSB_DataCallback)(tUSBInterface *Dev, int EndPt, int Length, void *Data);
+
+/**
+ */
+struct sUSBDriver
+{
+ tUSBDriver *Next;
+
+ const char *Name;
+
+ int MatchType; // 0: Interface, 1: Device, 2: Vendor
+ union {
+ struct {
+ // 23:16 - Interface Class
+ // 15:8 - Interface Sub Class
+ // 7:0 - Interface Protocol
+ Uint32 ClassCode;
+ Uint32 ClassMask;
+ } Class;
+ struct {
+ Uint16 VendorID;
+ Uint16 DeviceID;
+ } VendorDev;
+ } Match;
+
+ void (*Connected)(tUSBInterface *Dev);
+ void (*Disconnected)(tUSBInterface *Dev);
+
+ int MaxEndpoints;
+ struct {
+ // USB Attrbute byte
+ // NOTE: Top bit indicates the direction (1=Input)
+ Uint8 Attributes;
+ // Data availiable Callback
+ tUSB_DataCallback DataAvail;
+ } Endpoints[];
+};
+
+extern void *USB_GetDeviceDataPtr(tUSBInterface *Dev);
+extern void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr);
+
+extern void USB_StartPollingEndpoint(tUSBInterface *Dev, int Endpoint);
+extern void USB_ReadDescriptor(tUSBInterface *Dev, int Type, int Index, int Length, void *Data);
+extern void USB_Request(tUSBInterface *Dev, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data);
+// TODO: Async
+extern void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
+extern void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
+extern void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_host.h
+ * - USB Host Controller Interface
+ */
+#ifndef _USB_HOST_H_
+#define _USB_HOST_H_
+
+#include "usb_core.h"
+#include "usb_hub.h"
+
+typedef struct sUSBHostDef tUSBHostDef;
+
+typedef void (*tUSBHostCb)(void *DataPtr, void *Data, int Length);
+
+typedef void *(*tUSBHostOp)(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length);
+
+/**
+ * \brief Defines a USB Host Controller type
+ */
+struct sUSBHostDef
+{
+ tUSBHostOp SendIN;
+ tUSBHostOp SendOUT;
+ tUSBHostOp SendSETUP;
+
+ int (*IsOpComplete)(void *Ptr, void *OpPtr);
+
+ void (*CheckPorts)(void *Ptr);
+};
+
+extern tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_hub.h
+ * - Core Hub Definitions
+ */
+#ifndef _USB_HUB_H_
+#define _USB_HUB_H_
+
+#include "usb_core.h"
+
+typedef struct sUSBHub tUSBHub;
+
+/**
+ * \brief Register a device as a hub
+ *
+ * Used by the hub class initialisation routine.
+ */
+extern tUSBHub *USB_RegisterHub(tUSBInterface *Device, int nPorts);
+extern void USB_RemoveHub(tUSBHub *Hub);
+
+extern void USB_DeviceConnected(tUSBHub *Hub, int Port);
+extern void USB_DeviceDisconnected(tUSBHub *Hub, int Port);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2
+ * USB Stack
+ */
+#define VERSION ( (0<<8)| 5 )
+#define DEBUG 1
+#include <acess.h>
+#include <vfs.h>
+#include <fs_devfs.h>
+#include <modules.h>
+#include "usb.h"
+
+// === IMPORTS ===
+extern void USB_PollThread(void *unused);
+extern void USB_AsyncThread(void *Unused);
+
+// === PROTOTYPES ===
+ int USB_Install(char **Arguments);
+void USB_Cleanup(void);
+char *USB_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name);
+ int USB_IOCtl(tVFS_Node *Node, int Id, void *Data);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, USB_Core, USB_Install, NULL, NULL);
+tVFS_NodeType gUSB_RootNodeType = {
+ .ReadDir = USB_ReadDir,
+ .FindDir = USB_FindDir,
+ .IOCtl = USB_IOCtl
+};
+tDevFS_Driver gUSB_DrvInfo = {
+ NULL, "usb", {
+ .NumACLs = 1,
+ .ACLs = &gVFS_ACL_EveryoneRX,
+ .Flags = VFS_FFLAG_DIRECTORY,
+ .Type = &gUSB_RootNodeType
+ }
+};
+tUSBHost *gUSB_Hosts = NULL;
+
+// === CODE ===
+/**
+ * \brief Called once module is loaded
+ */
+int USB_Install(char **Arguments)
+{
+ Log_Warning("USB", "Not Complete - Devel Only");
+
+ Proc_SpawnWorker(USB_PollThread, NULL);
+ Proc_SpawnWorker(USB_AsyncThread, NULL);
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Called just before module is unloaded
+ */
+void USB_Cleanup()
+{
+}
+
+/**
+ * \fn char *USB_ReadDir(tVFS_Node *Node, int Pos)
+ * \brief Read from the USB root
+ */
+char *USB_ReadDir(tVFS_Node *Node, int Pos)
+{
+ return NULL;
+}
+
+/**
+ * \fn tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name)
+ * \brief Locate an entry in the USB root
+ */
+tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name)
+{
+ return NULL;
+}
+
+/**
+ * \brief Handles IOCtl Calls to the USB driver
+ */
+int USB_IOCtl(tVFS_Node *Node, int Id, void *Data)
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb.c
+ * - USB Structure
+ */
+#define DEBUG 1
+#include <acess.h>
+#include <vfs.h>
+#include <drv_pci.h>
+#include "usb.h"
+
+// === IMPORTS ===
+extern tUSBHost *gUSB_Hosts;
+extern tUSBDriver gUSBHub_Driver;
+
+// === STRUCTURES ===
+
+// === PROTOTYPES ===
+tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);
+
+// === GLOBALS ===
+tUSBDriver *gpUSB_InterfaceDrivers = &gUSBHub_Driver;
+
+// === CODE ===
+tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts)
+{
+ tUSBHost *host;
+
+ host = malloc(sizeof(tUSBHost) + nPorts*sizeof(void*));
+ if(!host) {
+ // Oh, bugger.
+ return NULL;
+ }
+ host->HostDef = HostDef;
+ host->Ptr = ControllerPtr;
+ memset(host->AddressBitmap, 0, sizeof(host->AddressBitmap));
+
+ host->RootHubDev.ParentHub = NULL;
+ host->RootHubDev.Host = host;
+ host->RootHubDev.Address = 0;
+
+// host->RootHubIf.Next = NULL;
+ host->RootHubIf.Dev = &host->RootHubDev;
+ host->RootHubIf.Driver = NULL;
+ host->RootHubIf.Data = NULL;
+ host->RootHubIf.nEndpoints = 0;
+
+ host->RootHub.Interface = &host->RootHubIf;
+ host->RootHub.nPorts = nPorts;
+ memset(host->RootHub.Devices, 0, sizeof(void*)*nPorts);
+
+ // TODO: Lock
+ host->Next = gUSB_Hosts;
+ gUSB_Hosts = host;
+
+ return &host->RootHub;
+}
+
+// --- Drivers ---
+void USB_RegisterDriver(tUSBDriver *Driver)
+{
+ Log_Warning("USB", "TODO: Implement USB_RegisterDriver");
+}
+
+tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode)
+{
+ ENTER("xClassCode", ClassCode);
+ for( tUSBDriver *ret = gpUSB_InterfaceDrivers; ret; ret = ret->Next )
+ {
+ LOG(" 0x%x & 0x%x == 0x%x?", ClassCode, ret->Match.Class.ClassMask, ret->Match.Class.ClassCode);
+ if( (ClassCode & ret->Match.Class.ClassMask) == ret->Match.Class.ClassCode )
+ {
+ LOG("Found '%s'", ret->Name);
+ LEAVE('p', ret);
+ return ret;
+ }
+ }
+ LEAVE('n');
+ return NULL;
+}
+
+// --- Hub Registration ---
+// NOTE: Doesn't do much nowdays
+tUSBHub *USB_RegisterHub(tUSBInterface *Device, int PortCount)
+{
+ tUSBHub *ret;
+
+ ret = malloc(sizeof(tUSBHub) + sizeof(ret->Devices[0])*PortCount);
+ ret->Interface = Device;
+ ret->nPorts = PortCount;
+ memset(ret->Devices, 0, sizeof(ret->Devices[0])*PortCount);
+ return ret;
+}
+
+void USB_RemoveHub(tUSBHub *Hub)
+{
+ for( int i = 0; i < Hub->nPorts; i ++ )
+ {
+ if( Hub->Devices[i] )
+ {
+ USB_DeviceDisconnected( Hub, i );
+ }
+ }
+ free(Hub);
+}
+
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb.h
+ * - USB Internal definitions
+ */
+#ifndef _USB_H_
+#define _USB_H_
+
+#include <usb_core.h>
+#include <usb_hub.h>
+#include <usb_host.h>
+
+typedef struct sUSBHost tUSBHost;
+typedef struct sUSBDevice tUSBDevice;
+typedef struct sUSBEndpoint tUSBEndpoint;
+
+// === STRUCTURES ===
+/**
+ * \brief USB Hub data
+ */
+struct sUSBHub
+{
+ tUSBInterface *Interface;
+
+ int nPorts;
+ tUSBDevice *Devices[];
+};
+
+struct sUSBEndpoint
+{
+ tUSBEndpoint *Next; // (usb_poll.c) Clock list
+ tUSBInterface *Interface;
+ int EndpointIdx; // Interface endpoint index
+ int EndpointNum; // Device endpoint num
+
+ int PollingPeriod; // In 1ms intervals
+ int MaxPacketSize; // In bytes
+ Uint8 Type; // Same as sUSBDriver.Endpoints.Type
+
+ int PollingAtoms; // (usb_poll.c) Period in clock list
+ void *InputData;
+};
+
+/**
+ * \brief Structure for a device's interface
+ */
+struct sUSBInterface
+{
+// tUSBInterface *Next;
+ tUSBDevice *Dev;
+
+ tUSBDriver *Driver;
+ void *Data;
+
+ int nEndpoints;
+ tUSBEndpoint Endpoints[];
+};
+
+/**
+ * \brief Defines a single device on the USB Bus
+ */
+struct sUSBDevice
+{
+ tUSBHub *ParentHub;
+
+ /**
+ * \brief Host controller used
+ */
+ tUSBHost *Host;
+ int Address;
+
+ int nInterfaces;
+ tUSBInterface *Interfaces[];
+};
+
+struct sUSBHost
+{
+ struct sUSBHost *Next;
+
+ tUSBHostDef *HostDef;
+ void *Ptr;
+
+ Uint8 AddressBitmap[128/8];
+
+ tUSBDevice RootHubDev;
+ tUSBInterface RootHubIf;
+ tUSBHub RootHub;
+};
+
+extern tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode);
+
+#endif
--- /dev/null
+/*
+ * Acess 2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_devinit.c
+ * - USB Device Initialisation
+ */
+#define DEBUG 1
+
+#include <acess.h>
+#include <vfs.h>
+#include <drv_pci.h>
+#include "usb.h"
+#include "usb_proto.h"
+#include "usb_lowlevel.h"
+
+// === PROTOTYPES ===
+void USB_DeviceConnected(tUSBHub *Hub, int Port);
+void USB_DeviceDisconnected(tUSBHub *Hub, int Port);
+void *USB_GetDeviceDataPtr(tUSBInterface *Dev);
+void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr);
+ int USB_int_AllocateAddress(tUSBHost *Host);
+
+// === CODE ===
+void USB_DeviceConnected(tUSBHub *Hub, int Port)
+{
+ tUSBDevice tmpdev;
+ tUSBDevice *dev = &tmpdev;
+ if( Port >= Hub->nPorts ) return ;
+ if( Hub->Devices[Port] ) return ;
+
+ ENTER("pHub iPort", Hub, Port);
+
+ // Device should be in 'Default' state
+
+ // Create structure
+ dev->ParentHub = Hub;
+ dev->Host = Hub->Interface->Dev->Host;
+ dev->Address = 0;
+
+ // 1. Assign an address
+ dev->Address = USB_int_AllocateAddress(dev->Host);
+ if(dev->Address == 0) {
+ Log_Error("USB", "No addresses avaliable on host %p", dev->Host);
+ free(dev);
+ LEAVE('-');
+ return ;
+ }
+ USB_int_SendSetupSetAddress(dev->Host, dev->Address);
+ LOG("Assigned address %i", dev->Address);
+
+ // 2. Get device information
+ {
+ struct sDescriptor_Device desc;
+ LOG("Getting device descriptor");
+ // Endpoint 0, Desc Type 1, Index 0
+ USB_int_ReadDescriptor(dev, 0, 1, 0, sizeof(desc), &desc);
+
+ LOG("Device Descriptor = {");
+ LOG(" .Length = %i", desc.Length);
+ LOG(" .Type = %i", desc.Type);
+ LOG(" .USBVersion = 0x%04x", desc.USBVersion);
+ LOG(" .DeviceClass = 0x%02x", desc.DeviceClass);
+ LOG(" .DeviceSubClass = 0x%02x", desc.DeviceSubClass);
+ LOG(" .DeviceProtocol = 0x%02x", desc.DeviceProtocol);
+ LOG(" .MaxPacketSize = 0x%02x", desc.MaxPacketSize);
+ LOG(" .VendorID = 0x%04x", desc.VendorID);
+ LOG(" .ProductID = 0x%04x", desc.ProductID);
+ LOG(" .DeviceID = 0x%04x", desc.DeviceID);
+ LOG(" .ManufacturerStr = Str %i", desc.ManufacturerStr);
+ LOG(" .ProductStr = Str %i", desc.ProductStr);
+ LOG(" .SerialNumberStr = Str %i", desc.SerialNumberStr);
+ LOG(" .NumConfigurations = %i", desc.SerialNumberStr);
+ LOG("}");
+
+ if( desc.ManufacturerStr )
+ {
+ char *tmp = USB_int_GetDeviceString(dev, 0, desc.ManufacturerStr);
+ LOG("ManufacturerStr = '%s'", tmp);
+ free(tmp);
+ }
+ if( desc.ProductStr )
+ {
+ char *tmp = USB_int_GetDeviceString(dev, 0, desc.ProductStr);
+ LOG("ProductStr = '%s'", tmp);
+ free(tmp);
+ }
+ if( desc.SerialNumberStr )
+ {
+ char *tmp = USB_int_GetDeviceString(dev, 0, desc.SerialNumberStr);
+ LOG("SerialNumbertStr = '%s'", tmp);
+ free(tmp);
+ }
+ }
+
+ // TODO: Support alternate configurations
+
+ // 3. Get configurations
+ for( int i = 0; i < 1; i ++ )
+ {
+ struct sDescriptor_Configuration desc;
+ void *full_buf;
+ char *cur_ptr;
+
+ USB_int_ReadDescriptor(dev, 0, 2, i, sizeof(desc), &desc);
+ LOG("Configuration Descriptor %i = {", i);
+ LOG(" .Length = %i", desc.Length);
+ LOG(" .Type = %i", desc.Type);
+ LOG(" .TotalLength = 0x%x", LittleEndian16(desc.TotalLength));
+ LOG(" .NumInterfaces = %i", desc.NumInterfaces);
+ LOG(" .ConfigurationValue = %i", desc.ConfigurationValue);
+ LOG(" .ConfigurationStr = %i", desc.ConfigurationStr);
+ LOG(" .AttributesBmp = 0b%b", desc.AttributesBmp);
+ LOG(" .MaxPower = %i (*2mA)", desc.MaxPower);
+ LOG("}");
+ if( desc.ConfigurationStr ) {
+ char *tmp = USB_int_GetDeviceString(dev, 0, desc.ConfigurationStr);
+ LOG("ConfigurationStr = '%s'", tmp);
+ free(tmp);
+ }
+
+ // TODO: Split here and allow some method of selection
+
+ // Allocate device now that we have the configuration
+ dev = malloc(sizeof(tUSBDevice) + desc.NumInterfaces * sizeof(void*));
+ memcpy(dev, &tmpdev, sizeof(tUSBDevice));
+ dev->nInterfaces = desc.NumInterfaces;
+
+ // Allocate a temp buffer for config info
+ cur_ptr = full_buf = malloc( LittleEndian16(desc.TotalLength) );
+ USB_int_ReadDescriptor(dev, 0, 2, i, desc.TotalLength, full_buf);
+
+ cur_ptr += desc.Length;
+
+ // TODO: Interfaces
+ for( int j = 0; j < desc.NumInterfaces; j ++ )
+ {
+ struct sDescriptor_Interface *iface;
+ tUSBInterface *dev_if;
+ iface = (void*)cur_ptr;
+ // TODO: Sanity check with remaining space
+ cur_ptr += sizeof(*iface);
+
+ LOG("Interface %i/%i = {", i, j);
+ LOG(" .InterfaceNum = %i", iface->InterfaceNum);
+ LOG(" .NumEndpoints = %i", iface->NumEndpoints);
+ LOG(" .InterfaceClass = 0x%x", iface->InterfaceClass);
+ LOG(" .InterfaceSubClass = 0x%x", iface->InterfaceSubClass);
+ LOG(" .InterfaceProcol = 0x%x", iface->InterfaceProtocol);
+
+ if( iface->InterfaceStr ) {
+ char *tmp = USB_int_GetDeviceString(dev, 0, iface->InterfaceStr);
+ LOG(" .InterfaceStr = %i '%s'", iface->InterfaceStr, tmp);
+ free(tmp);
+ }
+ LOG("}");
+
+ dev_if = malloc(sizeof(tUSBInterface) + iface->NumEndpoints*sizeof(dev_if->Endpoints[0]));
+ dev_if->Dev = dev;
+ dev_if->Driver = NULL;
+ dev_if->Data = NULL;
+ dev_if->nEndpoints = iface->NumEndpoints;
+ dev->Interfaces[j] = dev_if;
+
+ // Copy interface data
+ for( int k = 0; k < iface->NumEndpoints; k ++ )
+ {
+ struct sDescriptor_Endpoint *endpt;
+ endpt = (void*)cur_ptr;
+ // TODO: Sanity check with remaining space
+ cur_ptr += sizeof(*endpt);
+
+ LOG("Endpoint %i/%i/%i = {", i, j, k);
+ LOG(" .Address = 0x%2x", endpt->Address);
+ LOG(" .Attributes = 0b%8b", endpt->Attributes);
+ LOG(" .MaxPacketSize = %i", LittleEndian16(endpt->MaxPacketSize));
+ LOG(" .PollingInterval = %i", endpt->PollingInterval);
+ LOG("}");
+
+ dev_if->Endpoints[k].Next = NULL;
+ dev_if->Endpoints[k].Interface = dev_if;
+ dev_if->Endpoints[k].EndpointIdx = k;
+ dev_if->Endpoints[k].EndpointNum = endpt->Address & 0x7F;
+ dev_if->Endpoints[k].PollingPeriod = endpt->PollingInterval;
+ dev_if->Endpoints[k].MaxPacketSize = LittleEndian16(endpt->MaxPacketSize);
+ dev_if->Endpoints[k].Type = endpt->Attributes | (endpt->Address & 0x80);
+ dev_if->Endpoints[k].PollingAtoms = 0;
+ dev_if->Endpoints[k].InputData = NULL;
+ }
+
+ // Initialise driver
+ dev_if->Driver = USB_int_FindDriverByClass(
+ ((int)iface->InterfaceClass << 16)
+ |((int)iface->InterfaceSubClass << 8)
+ |((int)iface->InterfaceProtocol << 0)
+ );
+ if(!dev_if->Driver) {
+ Log_Notice("USB", "No driver for Class %02x:%02x:%02x",
+ iface->InterfaceClass, iface->InterfaceSubClass, iface->InterfaceProtocol
+ );
+ }
+ else {
+ dev_if->Driver->Connected( dev_if );
+ }
+ }
+
+ free(full_buf);
+ }
+
+ // Done.
+ LEAVE('-');
+}
+
+void USB_DeviceDisconnected(tUSBHub *Hub, int Port)
+{
+
+}
+
+void *USB_GetDeviceDataPtr(tUSBInterface *Dev) { return Dev->Data; }
+void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr) { Dev->Data = Ptr; }
+
+int USB_int_AllocateAddress(tUSBHost *Host)
+{
+ int i;
+ for( i = 1; i < 128; i ++ )
+ {
+ if(Host->AddressBitmap[i/8] & (1 << (i%8)))
+ continue ;
+ Host->AddressBitmap[i/8] |= 1 << (i%8);
+ return i;
+ }
+ return 0;
+}
+
+void USB_int_DeallocateAddress(tUSBHost *Host, int Address)
+{
+ Host->AddressBitmap[Address/8] &= ~(1 << (Address%8));
+}
+
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_io.c
+ * - High-level IO
+ */
+#define DEBUG 0
+
+#include <usb_core.h>
+#include "usb.h"
+#include "usb_lowlevel.h"
+#include <workqueue.h>
+
+typedef struct sAsyncOp tAsyncOp;
+
+struct sAsyncOp
+{
+ tAsyncOp *Next;
+ tUSBEndpoint *Endpt;
+ int Length;
+ void *Data;
+};
+
+// === PROTOTYPES ===
+void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data);
+void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data);
+void USB_AsyncCallback(void *Ptr, void *Buf, int Length);
+void USB_AsyncThread(void *unused);
+
+// === GLOBALS ===
+tWorkqueue gUSB_AsyncQueue;
+
+// === CODE ===
+void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data)
+{
+ USB_int_ReadDescriptor(Iface->Dev, 0, Type, Index, Length, Data);
+}
+
+void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data)
+{
+ int endpt;
+
+ // Sanity check
+ if(Endpoint < 0 || Endpoint >= Iface->nEndpoints)
+ return ;
+
+ // Get endpoint number
+ if(Endpoint)
+ endpt = Iface->Endpoints[Endpoint-1].EndpointNum;
+ else
+ endpt = 0;
+
+ USB_int_Request(Iface->Dev->Host, Iface->Dev->Address, endpt, Type, Req, Value, Index, Len, Data);
+}
+
+
+void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
+{
+ Log_Warning("USB", "TODO: Implement USB_SendData");
+}
+
+void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
+{
+ Log_Warning("USB", "TODO: Implement USB_RecvData");
+}
+
+void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback)
+{
+ tAsyncOp *op;
+ tUSBHost *host;
+
+ ENTER("pDev iEndpoint iLength pDataBuf", Dev, Endpoint, Length, DataBuf);
+
+ op = malloc(sizeof(*op));
+ op->Next = NULL;
+ op->Endpt = &Dev->Endpoints[Endpoint-1];
+ op->Length = Length;
+ op->Data = DataBuf;
+
+ // TODO: Handle transfers that are larger than one packet
+
+ host = Dev->Dev->Host;
+ LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum);
+ host->HostDef->SendIN(
+ host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum,
+ 0, USB_AsyncCallback, op,
+ DataBuf, Length
+ );
+
+ LEAVE('-');
+
+// Log_Warning("USB", "TODO: Implement USB_RecvDataA");
+}
+
+void USB_AsyncCallback(void *Ptr, void *Buf, int Length)
+{
+ tAsyncOp *op = Ptr;
+ op->Length = Length;
+ LOG("adding %p to work queue", op);
+ Workqueue_AddWork(&gUSB_AsyncQueue, op);
+}
+
+void USB_AsyncThread(void *Unused)
+{
+ Threads_SetName("USB Async IO Thread");
+ for(;;)
+ {
+ tAsyncOp *op = Workqueue_GetWork(&gUSB_AsyncQueue);
+ tUSBInterface *iface = op->Endpt->Interface;
+
+ LOG("op = %p", op);
+
+ iface->Driver->Endpoints[op->Endpt->EndpointIdx].DataAvail(
+ iface, op->Endpt->EndpointIdx,
+ op->Length, op->Data);
+ }
+}
+
--- /dev/null
+/*
+ * Acess 2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_lowlevel.c
+ * - Low Level IO
+ */
+#define DEBUG 1
+#include <acess.h>
+#include "usb.h"
+#include "usb_proto.h"
+#include "usb_lowlevel.h"
+
+// === PROTOTYPES ===
+void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data);
+ int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address);
+ int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest);
+char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index);
+ int _UTF16to8(Uint16 *Input, int InputLen, char *Dest);
+
+// === CODE ===
+void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data)
+{
+ void *hdl;
+ // TODO: Sanity check (and check that Type is valid)
+ struct sDeviceRequest req;
+ req.ReqType = Type;
+ req.Request = Req;
+ req.Value = LittleEndian16( Val );
+ req.Index = LittleEndian16( Indx );
+ req.Length = LittleEndian16( Len );
+
+ hdl = Host->HostDef->SendSETUP(Host->Ptr, Addr, EndPt, 0, NULL, NULL, &req, sizeof(req));
+
+ // TODO: Data toggle?
+ // TODO: Multi-packet transfers
+ if( Type & 0x80 )
+ {
+ void *hdl2;
+
+ hdl = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len);
+
+ hdl2 = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, NULL, 0);
+ while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 )
+ Time_Delay(1);
+ }
+ else
+ {
+ void *hdl2;
+
+ if( Len > 0 )
+ hdl = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len);
+ else
+ hdl = NULL;
+
+ // Status phase (DataToggle=1)
+ hdl2 = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 1, NULL, NULL, NULL, 0);
+ while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 )
+ Time_Delay(1);
+ }
+ return hdl;
+}
+
+int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address)
+{
+ USB_int_Request(Host, 0, 0, 0x00, 5, Address & 0x7F, 0, 0, NULL);
+ return 0;
+}
+
+int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest)
+{
+ const int ciMaxPacketSize = 0x400;
+ struct sDeviceRequest req;
+ int bToggle = 0;
+ void *final;
+
+ req.ReqType = 0x80;
+ switch( Type & 0xF00 )
+ {
+ case 0x000: req.ReqType |= (0 << 5); break; // Standard
+ case 0x100: req.ReqType |= (1 << 5); break; // Class
+ case 0x200: req.ReqType |= (2 << 5); break; // Vendor
+ }
+
+ req.Request = 6; // GET_DESCRIPTOR
+ req.Value = LittleEndian16( ((Type & 0xFF) << 8) | (Index & 0xFF) );
+ req.Index = LittleEndian16( 0 ); // TODO: Language ID
+ req.Length = LittleEndian16( Length );
+
+ Dev->Host->HostDef->SendSETUP(
+ Dev->Host->Ptr, Dev->Address, Endpoint,
+ 0, NULL, NULL,
+ &req, sizeof(req)
+ );
+
+ bToggle = 1;
+ while( Length > ciMaxPacketSize )
+ {
+ Dev->Host->HostDef->SendIN(
+ Dev->Host->Ptr, Dev->Address, Endpoint,
+ bToggle, NULL, NULL,
+ Dest, ciMaxPacketSize
+ );
+ bToggle = !bToggle;
+ Length -= ciMaxPacketSize;
+ }
+
+ final = Dev->Host->HostDef->SendIN(
+ Dev->Host->Ptr, Dev->Address, Endpoint,
+ bToggle, INVLPTR, NULL,
+ Dest, Length
+ );
+
+ while( Dev->Host->HostDef->IsOpComplete(Dev->Host->Ptr, final) == 0 )
+ Time_Delay(1);
+
+ return 0;
+}
+
+char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index)
+{
+ struct sDescriptor_String str;
+ int src_len, new_len;
+ char *ret;
+
+ if(Index == 0) return strdup("");
+
+ USB_int_ReadDescriptor(Dev, Endpoint, 3, Index, sizeof(str), &str);
+ if(str.Length < 2) {
+ Log_Error("USB", "String %p:%i:%i:%i descriptor is undersized (%i)",
+ Dev->Host, Dev->Address, Endpoint, Index, str.Length);
+ return NULL;
+ }
+// if(str.Length > sizeof(str)) {
+// // IMPOSSIBLE!
+// Log_Error("USB", "String is %i bytes, which is over prealloc size (%i)",
+// str.Length, sizeof(str)
+// );
+// }
+ src_len = (str.Length - 2) / sizeof(str.Data[0]);
+
+ LOG("&str = %p, src_len = %i", &str, src_len);
+
+ new_len = _UTF16to8(str.Data, src_len, NULL);
+ ret = malloc( new_len + 1 );
+ _UTF16to8(str.Data, src_len, ret);
+ ret[new_len] = 0;
+ return ret;
+}
+
+int _UTF16to8(Uint16 *Input, int InputLen, char *Dest)
+{
+ int str_len, cp_len;
+ Uint32 saved_bits = 0;
+ str_len = 0;
+ for( int i = 0; i < InputLen; i ++)
+ {
+ Uint32 cp;
+ Uint16 val = Input[i];
+ if( val >= 0xD800 && val <= 0xDBFF )
+ {
+ // Multibyte - Leading
+ if(i + 1 > InputLen) {
+ cp = '?';
+ }
+ else {
+ saved_bits = (val - 0xD800) << 10;
+ saved_bits += 0x10000;
+ continue ;
+ }
+ }
+ else if( val >= 0xDC00 && val <= 0xDFFF )
+ {
+ if( !saved_bits ) {
+ cp = '?';
+ }
+ else {
+ saved_bits |= (val - 0xDC00);
+ cp = saved_bits;
+ }
+ }
+ else
+ cp = val;
+
+ cp_len = WriteUTF8((Uint8*)Dest, cp);
+ if(Dest)
+ Dest += cp_len;
+ str_len += cp_len;
+
+ saved_bits = 0;
+ }
+
+ return str_len;
+}
+
--- /dev/null
+/**
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_lowlevel.h
+ * - Low-Level USB IO Functions
+ */
+#ifndef _USB_LOWLEVEL_H_
+#define _USB_LOWLEVEL_H_
+
+extern void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data);
+extern int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address);
+extern int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest);
+extern char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index);
+
+#endif
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_poll.c
+ * - Endpoint polling
+ */
+#define DEBUG 1
+#include <usb_core.h>
+#include "usb.h"
+
+#define POLL_ATOM 25 // 25ms atom
+#define POLL_MAX 256 // Max period that can be nominated
+#define POLL_SLOTS ((int)(POLL_MAX/POLL_ATOM))
+
+// === IMPORTS ===
+extern tUSBHost *gUSB_Hosts;
+
+// === PROTOTYPES ===
+void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint);
+
+// === GLOBALS ===
+tUSBEndpoint *gUSB_PollQueues[POLL_MAX/POLL_ATOM];
+ int giUSB_PollPosition; // Index into gUSB_PollQueues
+
+// === CODE ===
+void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint)
+{
+ tUSBEndpoint *endpt;
+
+ // Some sanity checks
+ if(Endpoint <= 0 || Endpoint > Iface->nEndpoints) return ;
+ endpt = &Iface->Endpoints[Endpoint-1];
+ if(endpt->PollingPeriod > POLL_MAX || endpt->PollingPeriod <= 0)
+ return ;
+
+ // TODO: Check that this endpoint isn't already on the queue
+
+ endpt->InputData = malloc(endpt->MaxPacketSize);
+
+ // Determine polling period in atoms
+ endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM;
+ if(endpt->PollingAtoms > POLL_SLOTS) endpt->PollingAtoms = POLL_SLOTS;
+ // Add to poll queue
+ // TODO: Locking
+ {
+ int idx = giUSB_PollPosition + 1;
+ if(idx >= POLL_SLOTS) idx -= POLL_SLOTS;
+ endpt->Next = gUSB_PollQueues[idx];
+ gUSB_PollQueues[idx] = endpt;
+ }
+}
+
+/**
+ * \brief USB polling thread
+ */
+int USB_PollThread(void *unused)
+{
+ Threads_SetName("USB Polling Thread");
+ for(;;)
+ {
+ tUSBEndpoint *ep, *prev;
+
+ if(giUSB_PollPosition == 0)
+ {
+ // Check hosts
+ for( tUSBHost *host = gUSB_Hosts; host; host = host->Next )
+ {
+ host->HostDef->CheckPorts(host->Ptr);
+ }
+ }
+
+// Log_Debug("USBPoll", "giUSB_PollPosition = %i", giUSB_PollPosition);
+
+ // A little evil for neater code
+ prev = (void*)( (tVAddr)&gUSB_PollQueues[giUSB_PollPosition] - offsetof(tUSBEndpoint, Next) );
+
+ // Process queue
+// LOG("giUSB_PollPosition = %i", giUSB_PollPosition);
+ for( ep = gUSB_PollQueues[giUSB_PollPosition]; ep; prev = ep, ep = ep->Next )
+ {
+ int period_in_atoms = ep->PollingAtoms;
+// LOG("%i: ep = %p", giUSB_PollPosition, ep);
+
+ // Check for invalid entries
+ if(period_in_atoms < 0 || period_in_atoms > POLL_ATOM)
+ {
+ Log_Warning("USB", "Endpoint on polling queue with invalid period");
+ continue ;
+ }
+ // Check for entries to delete
+ if(period_in_atoms == 0)
+ {
+ // Remove
+ prev->Next = ep->Next;
+ ep->PollingAtoms = -1; // Mark as removed
+ ep = prev; // Make sure prev is kept valid
+ continue ;
+ }
+
+ // Read data
+ // TODO: Check the endpoint
+ // TODO: Async checking?
+ // - Send the read request on all of them then wait for the first to complete
+ USB_RecvDataA(
+ ep->Interface, ep->EndpointIdx+1,
+ ep->MaxPacketSize, ep->InputData,
+ ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail
+ );
+
+ // Call callback
+
+ // Reschedule
+ if( period_in_atoms != POLL_SLOTS )
+ {
+ int newqueue_id = (giUSB_PollPosition + period_in_atoms) % POLL_SLOTS;
+ tUSBEndpoint **newqueue = &gUSB_PollQueues[newqueue_id];
+
+ prev->Next = ep->Next;
+
+ ep->Next = *newqueue;
+ *newqueue = ep;
+ ep = prev;
+ }
+ }
+ giUSB_PollPosition ++;
+ if(giUSB_PollPosition == POLL_SLOTS)
+ giUSB_PollPosition = 0;
+ // TODO: Check for a longer delay
+ Time_Delay(POLL_ATOM);
+ }
+}
+
--- /dev/null
+/**
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_proto.h
+ * - USB Core Protocol Definitions
+ */
+#ifndef _USB_PROTO_H_
+#define _USB_PROTO_H_
+
+struct sDeviceRequest
+{
+ Uint8 ReqType;
+ Uint8 Request;
+ Uint16 Value;
+ Uint16 Index;
+ Uint16 Length;
+};
+
+/*
+ */
+struct sDescriptor_Device
+{
+ Uint8 Length;
+ Uint8 Type; // = 1
+ Uint16 USBVersion; // BCD, 0x210 = 2.10
+ Uint8 DeviceClass;
+ Uint8 DeviceSubClass;
+ Uint8 DeviceProtocol;
+ Uint8 MaxPacketSize;
+
+ Uint16 VendorID;
+ Uint16 ProductID;
+ Uint16 DeviceID; // BCD
+
+ Uint8 ManufacturerStr;
+ Uint8 ProductStr;
+ Uint8 SerialNumberStr;
+
+ Uint8 NumConfigurations;
+} PACKED;
+
+struct sDescriptor_Configuration
+{
+ Uint8 Length;
+ Uint8 Type; // = 2
+
+ Uint16 TotalLength;
+ Uint8 NumInterfaces;
+ Uint8 ConfigurationValue;
+ Uint8 ConfigurationStr;
+ Uint8 AttributesBmp;
+ Uint8 MaxPower; // in units of 2 mA
+} PACKED;
+
+struct sDescriptor_String
+{
+ Uint8 Length;
+ Uint8 Type; // = 3
+
+ Uint16 Data[128-1]; // (256 bytes - 2 bytes) / Uint16
+} PACKED;
+
+struct sDescriptor_Interface
+{
+ Uint8 Length;
+ Uint8 Type; // = 4
+
+ Uint8 InterfaceNum;
+ Uint8 AlternateSetting;
+ Uint8 NumEndpoints; // Excludes endpoint 0
+
+ Uint8 InterfaceClass; //
+ Uint8 InterfaceSubClass;
+ Uint8 InterfaceProtocol;
+
+ Uint8 InterfaceStr;
+} PACKED;
+
+struct sDescriptor_Endpoint
+{
+ Uint8 Length;
+ Uint8 Type; // = 5
+ Uint8 Address; // 3:0 Endpoint Num, 7: Direction (1=IN)
+ /**
+ * 1:0 - Transfer Type
+ * - 00 = Control
+ * - 01 = Isochronous
+ * - 10 = Bulk
+ * - 11 = Interrupt
+ * 3:2 - Synchronisation type (Isonchronous only)
+ * - 00 = No Synchronisation
+ * - 01 = Asynchronous
+ * - 10 = Adaptive
+ * - 11 = Synchronous
+ * 5:4 - Usage type (Isonchronous only)
+ * - 00 = Data endpoint
+ * - 01 = Feedback endpoint
+ */
+ Uint8 Attributes;
+
+ Uint16 MaxPacketSize;
+
+ /**
+ *
+ */
+ Uint8 PollingInterval;
+} PACKED;
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 USB Stack HID Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * hid.h
+ * - Core header
+ */
+#ifndef _HID_H_
+#define _HID_H_
+
+// Report Descriptor Types
+// - 0: Main
+// > 8: Input - Axis/Button etc
+// > 9: Output - LED/Position
+// > B: Feature -
+// > A: Collection - Group of Input/Output/Feature
+// > C: End collection
+// - 1: Global (Not restored on main)
+// - 2: Local
+// >
+// - 3: Reserved/Unused
+
+// I/O/F Data Values
+#define HID_IOF_CONSTANT 0x001 //!< Host Read-only
+#define HID_IOF_VARIABLE 0x002 //!<
+
+struct sLongItem
+{
+ Uint8 Header; // 0xFE (Tag 15, Type 3, Size 2)
+ Uint8 DataSize;
+ Uint8 Tag;
+};
+
+struct sDescriptor_HID
+{
+ Uint8 Length;
+ Uint8 Type; //
+ Uint16 Version; // 0x0111 = 1.11
+ Uint8 CountryCode;
+ Uint8 NumDescriptors; // >= 1
+ struct {
+ Uint8 DescType;
+ Uint16 DescLen;
+ } Descriptors[];
+}
+
+#endif
--- /dev/null
+/*
+ * Acess2 USB Stack HID Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * usb_keysyms.h
+ * - USB HID Keyboard Symbols
+ */
+#ifndef _USB_KEYSYMS_H_
+#define _USB_KEYSYMS_H_
+
+enum eUSB_Keysyms
+{
+ KEYSYM_NONE,
+ KEYSYM_ERRORROLLOVER,
+ KEYSYM_POSTFAIL,
+ KEYSYM_ERRORUNDEFINED,
+ // 0x04 / 4
+ KEYSYM_a, KEYSYM_b, KEYSYM_c,
+ KEYSYM_d, KEYSYM_e, KEYSYM_f,
+ KEYSYM_g, KEYSYM_h, KEYSYM_i,
+ KEYSYM_j, KEYSYM_k, KEYSYM_l,
+ KEYSYM_m, KEYSYM_n, KEYSYM_o,
+ KEYSYM_p, KEYSYM_q, KEYSYM_r,
+ KEYSYM_s, KEYSYM_t, KEYSYM_u,
+ KEYSYM_v, KEYSYM_w, KEYSYM_x,
+ KEYSYM_y, KEYSYM_z,
+
+ // 0x1E / 30
+ KEYSYM_1, KEYSYM_2,
+ KEYSYM_3, KEYSYM_4,
+ KEYSYM_5, KEYSYM_6,
+ KEYSYM_7, KEYSYM_8,
+ KEYSYM_9, KEYSYM_0,
+
+ KEYSYM_RETURN, // Enter
+ KEYSYM_ESC, // Esc.
+ KEYSYM_BACKSP, // Backspace
+ KEYSYM_TAB, // Tab
+ KEYSYM_SPACE, // Spacebar
+ KEYSYM_MINUS, // - _
+ KEYSYM_EQUALS, // = +
+ KEYSYM_SQUARE_OPEN, // [ {
+ KEYSYM_SQUARE_CLOSE, // ] }
+ KEYSYM_BACKSLASH, // \ |
+ KEYSYM_HASH_TILDE, // # ~ (Non-US)
+ KEYSYM_SEMICOLON, // ; :
+ KEYSYM_QUOTE, // ' "
+ KEYSYM_GRAVE_TILDE, // Grave Accent, Tilde
+ KEYSYM_COMMA, // , <
+ KEYSYM_PERIOD, // . >
+ KEYSYM_SLASH, // / ?
+ KEYSYM_CAPS, // Caps Lock
+ KEYSYM_F1, KEYSYM_F2,
+ KEYSYM_F3, KEYSYM_F4,
+ KEYSYM_F5, KEYSYM_F6,
+ KEYSYM_F7, KEYSYM_F8,
+ KEYSYM_F9, KEYSYM_F10,
+ KEYSYM_F11, KEYSYM_F12,
+ KEYSYM_PRINTSCREEN,
+ KEYSYM_SCROLLLOCK,
+ KEYSYM_PAUSE,
+ KEYSYM_INSERT,
+ KEYSYM_HOME,
+ KEYSYM_PGUP,
+ KEYSYM_DELETE,
+ KEYSYM_END,
+ KEYSYM_PGDN,
+ KEYSYM_RIGHTARROW,
+ KEYSYM_LEFTARROW,
+ KEYSYM_DOWNARROW,
+ KEYSYM_UPARROW,
+
+ KEYSYM_NUMLOCK,
+ KEYSYM_KPSLASH,
+ KEYSYM_KPSTAR,
+ KEYSYM_KPMINUS,
+ KEYSYM_KPPLUS,
+ KEYSYM_KPENTER,
+ KEYSYM_KP1,
+ KEYSYM_KP2,
+ KEYSYM_KP3,
+ KEYSYM_KP4,
+ KEYSYM_KP5,
+ KEYSYM_KP7,
+ KEYSYM_KP8,
+ KEYSYM_KP9
+ KEYSYM_KP0,
+ KEYSYM_KPPERIOD,
+
+ KEYSYM_NONUS_BACKSLASH,
+ KEYSYM_APPLICATION, // Windows Key
+ KEYSYM_POWER,
+ KEYSYM_KPEQUALS,
+
+ KEYSYM_F13, KEYSYM_F14,
+ KEYSYM_F15, KEYSYM_F16,
+ KEYSYM_F17, KEYSYM_F18,
+ KEYSYM_F19, KEYSYM_F20,
+ KEYSYM_F21, KEYSYM_F22,
+ KEYSYM_F23, KEYSYM_F24,
+ KEYSYM_EXECUTE,
+ KEYSYM_HELP,
+ KEYSYM_MENU,
+ KEYSYM_SELECT,
+ KEYSYM_STOP,
+ KEYSYM_AGAIN,
+ KEYSYM_UNDO,
+ KEYSYM_CUT,
+ KEYSYM_COPY,
+ KEYSYM_PASTE,
+ KEYSYM_FIND,
+ KEYSYM_MUTE,
+ KEYSYM_VOLUP,
+ KEYSYM_VOLDN,
+ KEYSYM_LOCKING_CAPS, // Physically toggles
+ KEYSYM_LOGKING_NUM,
+ KEYSYM_LOGKING_SCROLL,
+ KEYSYM_KPCOMMA
+
+ // TODO: Define the rest
+};
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 USB Stack HID Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * main.c
+ * - Driver Core
+ */
+#define DEBUG 0
+#define VERSION VER2(0,1)
+#include <acess.h>
+#include <usb_core.h>
+
+// === PROTOTYPES ===
+ int HID_Initialise(const char **Arguments);
+void HID_DeviceConnected(tUSBInterface *Dev);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL);
+tUSBDriver gHID_Driver = {
+ .Name = "HID",
+ .Match = {.Class = {0x030000, 0xFF0000}},
+ .Connected = HID_DeviceConnected,
+};
+
+// === CODE ===
+int HID_Initialise(const char **Arguments)
+{
+ USB_RegisterDriver( &gHID_Driver );
+ return 0;
+}
+
+void HID_DeviceConnected(tUSBInterface *Dev)
+{
+
+}
+
--- /dev/null
+CATEGORY = USB
+
+-include ../../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 OHCI Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * ohci.h
+ * - Core Header
+ */
+#ifndef _OHCI_H_
+#define _OHCI_H_
+
+struct sEndpointDesc
+{
+ // 0: 6 = Address
+ // 7:10 = Endpoint Num
+ // 11:12 = Direction (TD, OUT, IN, TD)
+ // 13 = Speed (Full, Low)
+ // 14 = Skip entry
+ // 15 = Format (Others, Isochronous)
+ // 16:26 = Max Packet Size
+ // 27:31 = AVAIL
+ Uint32 Flags;
+ // 0: 3 = AVAIL
+ // 4:31 = TailP
+ Uint32 TailP; // Last TD in queue
+ // 0 = Halted (Queue stopped due to error)
+ // 1 = Data toggle carry
+ // 2: 3 = ZERO
+ // 4:31 = HeadP
+ Uint32 HeadP; // First TD in queue
+ // 0: 3 = AVAIL
+ // 4:31 = NextED
+ Uint32 NextED; // Next endpoint descriptor
+};
+
+struct sGeneralTD
+{
+ // 0:17 = AVAIL
+ // 18 = Buffer Rounding (Allow an undersized packet)
+ // 19:20 = Direction (SETUP, OUT, IN, Resvd)
+ // 21:23 = Delay Interrupt (Frame count, 7 = no int)
+ // 24:25 = Data Toggle (ToggleCarry, ToggleCarry, 0, 1)
+ // 26:27 = Error Count
+ // 28:31 = Condition Code
+ Uint32 Flags;
+
+ // Base address of packet (or current when being read)
+ Uint32 CBP;
+
+ Uint32 NextTD;
+
+ // Address of final byte in buffer
+ Uint32 BE;
+};
+
+struct sIsochronousTD
+{
+ // 0:15 = Starting Frame
+ // 16:20 = AVAIL
+ // 21:23 = Delay Interrupt
+ // 24:26 = Frame Count - 1 (1, 2, 3, 4, 5, 6, 7, 8)
+ // 27 = AVAIL
+ // 28:31 = Condition Code
+ Uint32 Flags;
+
+ // 0:11 = AVAIL
+ // 12:31 = Page number of first byte in buffer
+ Uint32 BP0; // Buffer Page 0
+
+ Uint32 NextTD;
+
+ // Address of last byte in buffer
+ Uint32 BufferEnd;
+
+ // 0:11 = Page Offset
+ // 12 = Page selector (BufferPage0, BufferEnd)
+ // 13:15 = Unused?
+ Uint16 Offsets[8];
+};
+
+#endif
+
--- /dev/null
+#
+#
+
+OBJ = uhci.o
+CPPFLAGS = -I../Core/include
+NAME = UHCI
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess 2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * Universal Host Controller Interface
+ */
+#define DEBUG 0
+#define VERSION VER2(0,5)
+#include <acess.h>
+#include <vfs.h>
+#include <drv_pci.h>
+#include <modules.h>
+#include <usb_host.h>
+#include "uhci.h"
+
+// === CONSTANTS ===
+#define MAX_CONTROLLERS 4
+#define NUM_TDs 1024
+
+// === PROTOTYPES ===
+ int UHCI_Initialise(char **Arguments);
+void UHCI_Cleanup();
+tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
+void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD);
+void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+ int UHCI_IsTransferComplete(void *Ptr, void *Handle);
+ int UHCI_Int_InitHost(tUHCI_Controller *Host);
+void UHCI_CheckPortUpdate(void *Ptr);
+void UHCI_InterruptHandler(int IRQ, void *Ptr);
+//
+static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value);
+static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value);
+static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value);
+static Uint16 _InWord(tUHCI_Controller *Host, int Reg);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
+tUHCI_TD gaUHCI_TDPool[NUM_TDs];
+tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS];
+tUSBHostDef gUHCI_HostDef = {
+ .SendIN = UHCI_DataIN,
+ .SendOUT = UHCI_DataOUT,
+ .SendSETUP = UHCI_SendSetup,
+ .CheckPorts = UHCI_CheckPortUpdate,
+ .IsOpComplete = UHCI_IsTransferComplete
+ };
+
+// === CODE ===
+/**
+ * \fn int UHCI_Initialise()
+ * \brief Called to initialise the UHCI Driver
+ */
+int UHCI_Initialise(char **Arguments)
+{
+ int i=0, id=-1;
+ int ret;
+
+ ENTER("");
+
+ // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
+ while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
+ {
+ tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
+ Uint32 base_addr;
+ // NOTE: Check "protocol" from PCI?
+
+ cinfo->PciId = id;
+ base_addr = PCI_GetBAR(id, 4);
+
+ if( base_addr & 1 )
+ {
+ cinfo->IOBase = base_addr & ~1;
+ cinfo->MemIOMap = NULL;
+ }
+ else
+ {
+ cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
+ }
+ cinfo->IRQNum = PCI_GetIRQ(id);
+
+ Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
+ id, base_addr, cinfo->IRQNum);
+
+ IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
+
+ // Initialise Host
+ ret = UHCI_Int_InitHost(&gUHCI_Controllers[i]);
+ // Detect an error
+ if(ret != 0) {
+ LEAVE('i', ret);
+ return ret;
+ }
+
+ cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
+ LOG("cinfo->RootHub = %p", cinfo->RootHub);
+
+ i ++;
+ }
+
+ if(i == 0) {
+ LEAVE('i', MODULE_ERR_NOTNEEDED);
+ return MODULE_ERR_NOTNEEDED;
+ }
+
+ if(i == MAX_CONTROLLERS) {
+ Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
+ }
+ LEAVE('i', MODULE_ERR_OK);
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \fn void UHCI_Cleanup()
+ * \brief Called just before module is unloaded
+ */
+void UHCI_Cleanup()
+{
+}
+
+tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
+{
+ int i;
+ for(i = 0; i < NUM_TDs; i ++)
+ {
+ if(gaUHCI_TDPool[i].Link == 0) {
+ gaUHCI_TDPool[i].Link = 1;
+ gaUHCI_TDPool[i].Control = 1 << 23;
+ return &gaUHCI_TDPool[i];
+ }
+ // Still in use? Skip
+ if( gaUHCI_TDPool[i].Control & (1 << 23) )
+ continue ;
+ // Is there a callback on it? Skip
+ if( gaUHCI_TDPool[i]._info.Callback )
+ continue ;
+ // TODO: Garbage collect, but that means removing from the list too
+ #if 0
+ // Ok, this is actually unused
+ gaUHCI_TDPool[i].Link = 1;
+ gaUHCI_TDPool[i].Control = 1 << 23;
+ return &gaUHCI_TDPool[i];
+ #endif
+ }
+ return NULL;
+}
+
+tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr)
+{
+ // TODO: Fix this to work with a non-contiguous pool
+ static tPAddr td_pool_base;
+ const int pool_size = NUM_TDs;
+ int offset;
+ if(!td_pool_base) td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
+ offset = (PAddr - td_pool_base) / sizeof(gaUHCI_TDPool[0]);
+ if( offset < 0 || offset >= pool_size )
+ {
+ Log_Error("UHCI", "TD PAddr %P not from pool", PAddr);
+ return NULL;
+ }
+ return gaUHCI_TDPool + offset;
+}
+
+void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD)
+{
+ int next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1);
+ tUHCI_TD *prev_td;
+ Uint32 link;
+
+ // TODO: How to handle FRNUM incrementing while we are in this function?
+
+ // Empty list
+ if( Cont->FrameList[next_frame] & 1 )
+ {
+ // TODO: Ensure 32-bit paddr
+ Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD );
+ TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame
+ LOG("next_frame = %i", next_frame);
+ return;
+ }
+
+ // Find the end of the list
+ link = Cont->FrameList[next_frame];
+ do {
+ prev_td = UHCI_int_GetTDFromPhys(link);
+ link = prev_td->Link;
+ } while( !(link & 1) );
+
+ // Append
+ prev_td->Link = MM_GetPhysAddr( (tVAddr)TD );
+
+ LOG("next_frame = %i, prev_td = %p", next_frame, prev_td);
+}
+
+/**
+ * \brief Send a transaction to the USB bus
+ * \param Cont Controller pointer
+ * \param Addr Function Address * 16 + Endpoint
+ * \param bTgl Data toggle value
+ */
+void *UHCI_int_SendTransaction(
+ tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
+ tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
+{
+ tUHCI_TD *td;
+
+ if( Length > 0x400 ) return NULL; // Controller allows up to 0x500, but USB doesn't
+
+ td = UHCI_int_AllocateTD(Cont);
+
+ if( !td ) {
+ // TODO: Wait for one to free?
+ Log_Error("UHCI", "No avaliable TDs, transaction dropped");
+ return NULL;
+ }
+
+ td->Link = 1;
+ td->Control = (Length - 1) & 0x7FF;
+ td->Control |= (1 << 23);
+ td->Token = ((Length - 1) & 0x7FF) << 21;
+ td->Token |= (bTgl & 1) << 19;
+ td->Token |= (Addr & 0xF) << 15;
+ td->Token |= ((Addr/16) & 0xFF) << 8;
+ td->Token |= Type;
+
+ // TODO: Ensure 32-bit paddr
+ if( ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE ) {
+ Log_Warning("UHCI", "TODO: Support non single page transfers (%x + %x > %x)",
+ (tVAddr)Data & (PAGE_SIZE-1), Length, PAGE_SIZE
+ );
+ // TODO: Need to enable IOC to copy the data back
+// td->BufferPointer =
+ td->_info.bCopyData = 1;
+ return NULL;
+ }
+ else {
+ td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
+ td->_info.bCopyData = 0;
+ }
+
+ // Interrupt on completion
+ if( Cb ) {
+ td->Control |= (1 << 24);
+ LOG("IOC Cb=%p CbData=%p", Cb, CbData);
+ td->_info.Callback = Cb; // NOTE: if ERRPTR then the TD is kept allocated until checked
+ td->_info.CallbackPtr = CbData;
+ }
+
+ td->_info.DataPtr = Data;
+
+ UHCI_int_AppendTD(Cont, td);
+
+ return td;
+}
+
+void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+{
+ return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x69, DataTgl, Cb, CbData, Buf, Length);
+}
+
+void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+{
+ return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0xE1, DataTgl, Cb, CbData, Buf, Length);
+}
+
+void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+{
+ return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x2D, DataTgl, Cb, CbData, Buf, Length);
+}
+
+int UHCI_IsTransferComplete(void *Ptr, void *Handle)
+{
+ tUHCI_TD *td = Handle;
+ int ret;
+ ret = !(td->Control & (1 << 23));
+ if(ret) {
+ td->_info.Callback = NULL;
+ td->Link = 0;
+ }
+ return ret;
+}
+
+// === INTERNAL FUNCTIONS ===
+/**
+ * \fn int UHCI_Int_InitHost(tUCHI_Controller *Host)
+ * \brief Initialises a UHCI host controller
+ * \param Host Pointer - Host to initialise
+ */
+int UHCI_Int_InitHost(tUHCI_Controller *Host)
+{
+ ENTER("pHost", Host);
+
+ _OutWord( Host, USBCMD, 4 ); // GRESET
+ // TODO: Wait for at least 10ms
+ _OutWord( Host, USBCMD, 0 ); // GRESET
+
+ // Allocate Frame List
+ // - 1 Page, 32-bit address
+ // - 1 page = 1024 4 byte entries
+ Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
+ if( !Host->FrameList ) {
+ Log_Warning("UHCI", "Unable to allocate frame list, aborting");
+ LEAVE('i', -1);
+ return -1;
+ }
+ LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList);
+ memsetd( Host->FrameList, 1, 1024 ); // Clear List (Disabling all entries)
+
+ //! \todo Properly fill frame list
+
+ // Set frame length to 1 ms
+ _OutByte( Host, SOFMOD, 64 );
+
+ // Set Frame List
+ _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
+ _OutWord( Host, FRNUM, 0 );
+
+ // Enable Interrupts
+ _OutWord( Host, USBINTR, 0x000F );
+ PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
+
+ // Enable processing
+ _OutWord( Host, USBCMD, 0x0001 );
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+void UHCI_CheckPortUpdate(void *Ptr)
+{
+ tUHCI_Controller *Host = Ptr;
+ // Enable ports
+ for( int i = 0; i < 2; i ++ )
+ {
+ int port = PORTSC1 + i*2;
+ Uint16 status;
+
+ status = _InWord(Host, port);
+ // Check for port change
+ if( !(status & 0x0002) ) continue;
+ _OutWord(Host, port, 0x0002);
+
+ // Check if the port is connected
+ if( !(status & 1) )
+ {
+ // Tell the USB code it's gone.
+ USB_DeviceDisconnected(Host->RootHub, i);
+ continue;
+ }
+ else
+ {
+ LOG("Port %i has something", i);
+ // Reset port (set bit 9)
+ LOG("Reset");
+ _OutWord(Host, port, 0x0200);
+ Time_Delay(50); // 50ms delay
+ _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
+ // Enable port
+ LOG("Enable");
+ Time_Delay(50); // 50ms delay
+ _OutWord(Host, port, _InWord(Host, port) | 0x0004);
+ // Tell USB there's a new device
+ USB_DeviceConnected(Host->RootHub, i);
+ }
+ }
+}
+
+void UHCI_InterruptHandler(int IRQ, void *Ptr)
+{
+ tUHCI_Controller *Host = Ptr;
+ int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
+ Uint16 status = _InWord(Host, USBSTS);
+// Log_Debug("UHCI", "UHIC Interrupt, status = 0x%x, frame = %i", status, frame);
+
+ // Interrupt-on-completion
+ if( status & 1 )
+ {
+ tPAddr link;
+
+ for( int i = 0; i < 10; i ++ )
+ {
+ link = Host->FrameList[frame];
+ Host->FrameList[frame] = 1;
+ while( link && !(link & 1) )
+ {
+ tUHCI_TD *td = UHCI_int_GetTDFromPhys(link);
+ int byte_count = (td->Control&0x7FF)+1;
+ LOG("link = 0x%x, td = %p, byte_count = %i", link, td, byte_count);
+ // Handle non-page aligned destination
+ // TODO: This will break if the destination is not in global memory
+ if(td->_info.bCopyData)
+ {
+ void *ptr = (void*)MM_MapTemp(td->BufferPointer);
+ Log_Debug("UHCI", "td->_info.DataPtr = %p", td->_info.DataPtr);
+ memcpy(td->_info.DataPtr, ptr, byte_count);
+ MM_FreeTemp((tVAddr)ptr);
+ }
+ // Callback
+ if(td->_info.Callback && td->_info.Callback != INVLPTR)
+ {
+ LOG("Calling cb %p", td->_info.Callback);
+ td->_info.Callback(td->_info.CallbackPtr, td->_info.DataPtr, byte_count);
+ td->_info.Callback = NULL;
+ }
+ link = td->Link;
+ if( td->_info.Callback != INVLPTR )
+ td->Link = 0;
+ }
+
+ if(frame == 0)
+ frame = 0x3ff;
+ else
+ frame --;
+ }
+
+// Host->LastCleanedFrame = frame;
+ }
+
+ LOG("status = 0x%02x", status);
+ _OutWord(Host, USBSTS, status);
+}
+
+void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
+{
+ if( Host->MemIOMap )
+ ((Uint8*)Host->MemIOMap)[Reg] = Value;
+ else
+ outb(Host->IOBase + Reg, Value);
+}
+
+void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
+{
+ if( Host->MemIOMap )
+ Host->MemIOMap[Reg/2] = Value;
+ else
+ outw(Host->IOBase + Reg, Value);
+}
+
+void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
+{
+ if( Host->MemIOMap )
+ ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
+ else
+ outd(Host->IOBase + Reg, Value);
+}
+
+Uint16 _InWord(tUHCI_Controller *Host, int Reg)
+{
+ if( Host->MemIOMap )
+ return Host->MemIOMap[Reg/2];
+ else
+ return inw(Host->IOBase + Reg);
+}
+
--- /dev/null
+/*
+ * AcessOS Version 1
+ * USB Stack
+ * - Universal Host Controller Interface
+ */
+#ifndef _UHCI_H_
+#define _UHCI_H_
+
+// === TYPES ===
+typedef struct sUHCI_Controller tUHCI_Controller;
+typedef struct sUHCI_TD tUHCI_TD;
+typedef struct sUHCI_QH tUHCI_QH;
+
+// === STRUCTURES ===
+struct sUHCI_Controller
+{
+ /**
+ * \brief PCI Device ID
+ */
+ Uint16 PciId;
+
+ /**
+ * \brief IO Base Address
+ */
+ Uint16 IOBase;
+
+ /**
+ * \brief Memory Mapped-IO base address
+ */
+ Uint16 *MemIOMap;
+
+ /**
+ * \brief IRQ Number assigned to the device
+ */
+ int IRQNum;
+
+ /**
+ * \brief Number of the last frame to be cleaned
+ */
+ int LastCleanedFrame;
+
+ /**
+ * \brief Frame list
+ *
+ * 31:4 - Frame Pointer
+ * 3:2 - Reserved
+ * 1 - QH/TD Selector
+ * 0 - Terminate (Empty Pointer)
+ */
+ Uint32 *FrameList;
+
+ /**
+ * \brief Physical Address of the Frame List
+ */
+ tPAddr PhysFrameList;
+
+ tUSBHub *RootHub;
+};
+
+struct sUHCI_TD
+{
+ /**
+ * \brief Next Entry in list
+ *
+ * 31:4 - Address
+ * 3 - Reserved
+ * 2 - Depth/Breadth Select
+ * 1 - QH/TD Select
+ * 0 - Terminate (Last in List)
+ */
+ Uint32 Link;
+
+ /**
+ * \brief Control and Status Field
+ *
+ * 31:30 - Reserved
+ * 29 - Short Packet Detect (Input Only)
+ * 28:27 - Number of Errors Allowed
+ * 26 - Low Speed Device (Communicating with a low speed device)
+ * 25 - Isynchonious Select
+ * 24 - Interrupt on Completion (IOC)
+ * 23:16 - Status
+ * 23 - Active
+ * 22 - Stalled
+ * 21 - Data Buffer Error
+ * 20 - Babble Detected
+ * 19 - NAK Detected
+ * 18 - CRC/Timout Error
+ * 17 - Bitstuff Error
+ * 16 - Reserved
+ * 15:11 - Reserved
+ * 10:0 - Actual Length (Number of bytes transfered)
+ */
+ Uint32 Control;
+
+ /**
+ * \brief Packet Header
+ *
+ * 31:21 - Maximum Length (0=1, Max 0x4FF, 0x7FF=0)
+ * 20 - Reserved
+ * 19 - Data Toggle
+ * 18:15 - Endpoint
+ * 14:8 - Device Address
+ * 7:0 - PID (Packet Identifcation) - Only 96, E1, 2D allowed
+ *
+ * 0x96 = Data IN
+ * 0xE1 = Data Out
+ * 0x2D = Setup
+ */
+ Uint32 Token;
+
+ /**
+ * \brief Pointer to the data to send
+ */
+ Uint32 BufferPointer;
+
+ struct
+ {
+ tUSBHostCb Callback;
+ void *CallbackPtr;
+ void *DataPtr;
+ int bCopyData;
+ } _info;
+} __attribute__((aligned(16)));
+
+struct sUHCI_QH
+{
+ /**
+ * \brief Next Entry in list
+ *
+ * 31:4 - Address
+ * 3:2 - Reserved
+ * 1 - QH/TD Select
+ * 0 - Terminate (Last in List)
+ */
+ Uint32 Next;
+
+
+ /**
+ * \brief Next Entry in list
+ *
+ * 31:4 - Address
+ * 3:2 - Reserved
+ * 1 - QH/TD Select
+ * 0 - Terminate (Last in List)
+ */
+ Uint32 Child;
+};
+
+// === ENUMERATIONS ===
+enum eUHCI_IOPorts {
+ /**
+ * \brief USB Command Register
+ *
+ * 15:8 - Reserved
+ * 7 - Maximum Packet Size selector (1: 64 bytes, 0: 32 bytes)
+ * 6 - Configure Flag (No Hardware Effect)
+ * 5 - Software Debug (Don't think it will be needed)
+ * 4 - Force Global Resume
+ * 3 - Enter Global Suspend Mode
+ * 2 - Global Reset (Resets all devices on the bus)
+ * 1 - Host Controller Reset (Reset just the controller)
+ * 0 - Run/Stop
+ */
+ USBCMD = 0x00,
+ /**
+ * \brief USB Status Register
+ *
+ * 15:6 - Reserved
+ * 5 - HC Halted, set to 1 when USBCMD:RS is set to 0
+ * 4 - Host Controller Process Error (Errors related to the bus)
+ * 3 - Host System Error (Errors related to the OS/PCI Bus)
+ * 2 - Resume Detect (Set if a RESUME command is sent to the Controller)
+ * 1 - USB Error Interrupt
+ * 0 - USB Interrupts (Set if a transaction with the IOC bit set is completed)
+ */
+ USBSTS = 0x02,
+ /**
+ * \brief USB Interrupt Enable Register
+ *
+ * 15:4 - Reserved
+ * 3 - Short Packet Interrupt Enable
+ * 2 - Interrupt on Complete (IOC) Enable
+ * 1 - Resume Interrupt Enable
+ * 0 - Timout / CRC Error Interrupt Enable
+ */
+ USBINTR = 0x04,
+ /**
+ * \brief Frame Number (Index into the Frame List)
+ *
+ * 15:11 - Reserved
+ * 10:0 - Index (Incremented each approx 1ms)
+ */
+ FRNUM = 0x06,
+ /**
+ * \brief Frame List Base Address
+ *
+ * 31:12 - Pysical Address >> 12
+ * 11:0 - Reserved (Set to Zero)
+ */
+ FLBASEADD = 0x08, // 32-bit
+ /**
+ * \brief Start-of-frame Modify Register
+ * \note 8-bits only
+ *
+ * Sets the size of a frame
+ * Frequency = (11936+n)/12000 kHz
+ *
+ * 7 - Reserved
+ * 6:0 -
+ */
+ SOFMOD = 0x0C, // 8bit
+ /**
+ * \brief Port Status and Controll Register (Port 1)
+ *
+ * 15:13 - Reserved
+ * 12 - Suspend
+ * 11:10 - Reserved
+ * 9 - Port Reset
+ * 8 - Low Speed Device Attached
+ * 5:4 - Line Status
+ * 3 - Port Enable/Disable Change - Used for detecting device removal
+ * 2 - Port Enable/Disable
+ * 1 - Connect Status Change
+ * 0 - Current Connect Status
+ */
+ PORTSC1 = 0x10,
+ /**
+ * \brief Port Status and Controll Register (Port 2)
+ *
+ * See ::PORTSC1
+ */
+ PORTSC2 = 0x12
+};
+
+#endif
--- /dev/null
+#
+#
+
+OBJ := gic.o
+NAME := GIC
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * ARMv7 GIC Support
+ * - By John Hodge (thePowersGang)
+ *
+ * gic.c
+ * - GIC Core
+ */
+#define DEBUG 1
+
+#include <acess.h>
+#include <modules.h>
+#include "gic.h"
+#include <options.h>
+
+#define N_IRQS 1024
+
+// === IMPORTS ===
+extern void *gpIRQHandler;
+
+// === TYPES ===
+typedef void (*tIRQ_Handler)(int, void*);
+
+// === PROTOTYPES ===
+ int GIC_Install(char **Arguments);
+void GIC_IRQHandler(void);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x100, armv7_GIC, GIC_Install, NULL, NULL);
+Uint32 *gpGIC_DistributorBase;
+Uint32 *gpGIC_InterfaceBase;
+tPAddr gGIC_DistributorAddr;
+tPAddr gGIC_InterfaceAddr;
+tIRQ_Handler gaIRQ_Handlers[N_IRQS];
+void *gaIRQ_HandlerData[N_IRQS];
+
+// === CODE ===
+int GIC_Install(char **Arguments)
+{
+ // Realview PB
+ gGIC_InterfaceAddr = 0x1e000000;
+ gGIC_DistributorAddr = 0x1e001000;
+
+ // Initialise
+ gpGIC_InterfaceBase = (void*)MM_MapHWPages(gGIC_InterfaceAddr, 1);
+ LOG("gpGIC_InterfaceBase = %p", gpGIC_InterfaceBase);
+ gpGIC_DistributorBase = (void*)MM_MapHWPages(gGIC_DistributorAddr, 1);
+ LOG("gpGIC_DistributorBase = %p", gpGIC_DistributorBase);
+
+ gpGIC_InterfaceBase[GICC_PMR] = 0xFF;
+ gpGIC_InterfaceBase[GICC_CTLR] = 1; // Enable CPU
+ gpGIC_DistributorBase[GICD_CTLR] = 1; // Enable Distributor
+
+ gpIRQHandler = GIC_IRQHandler;
+
+ __asm__ __volatile__ ("cpsie if"); // Enable IRQs and FIQs
+
+ return MODULE_ERR_OK;
+}
+
+void GIC_IRQHandler(void)
+{
+ Uint32 num = gpGIC_InterfaceBase[GICC_IAR];
+// Log_Debug("GIC", "IRQ 0x%x", num);
+ gaIRQ_Handlers[num]( num, gaIRQ_HandlerData[num] );
+ gpGIC_InterfaceBase[GICC_EOIR] = num;
+}
+
+int IRQ_AddHandler(int IRQ, tIRQ_Handler Handler, void *Ptr)
+{
+ if( IRQ < 0 || IRQ >= N_IRQS-32 ) {
+ return 1;
+ }
+
+ LOG("IRQ = %i", IRQ);
+ IRQ += 32; // 32 internal IRQs
+ LOG("IRQ = %i (after adjust)", IRQ);
+ LOG("mask = 0x%x", 1 << (IRQ & (31-1)));
+ gpGIC_DistributorBase[GICD_ISENABLER0+IRQ/32] = 1 << (IRQ & (32-1));
+ ((Uint8*)&gpGIC_DistributorBase[GICD_ITARGETSR0])[IRQ] = 1;
+
+// Log_Warning("GIC", "TODO: Implement IRQ_AddHandler");
+
+ if( gaIRQ_Handlers[IRQ] )
+ return 2;
+
+ gaIRQ_Handlers[IRQ] = Handler;
+ gaIRQ_HandlerData[IRQ] = Ptr;
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * ARMv7 GIC Support
+ * - By John Hodge (thePowersGang)
+ *
+ * gic.h
+ * - GIC Core Definitions
+ */
+#ifndef _ARM7_GIC_H_
+#define _ARM7_GIC_H_
+
+enum eGICD_Registers
+{
+ GICD_CTLR = 0x000/4, // Distributor Control Register
+ GICD_TYPER = 0x004/4, // Interrupt Controller Type
+ GICD_IIDR = 0x008/4, // Distributor Implementer Identifcation
+
+ GICD_IGROUPR0 = 0x080/4, // Interrupt Group Register (#0)
+ GICD_ISENABLER0 = 0x100/4, // Interrupt Set-Enable Register #0 (128*8=1024)
+ GICD_ICENABLER0 = 0x180/4, // Interrupt Clear-Enable Register #0
+ GICD_ISPENDR0 = 0x200/4, // Interrupt Set-Pending Register #0
+ GICD_ICPENDR0 = 0x280/4, // Interrupt Clear-Pending Register #0
+ GICD_ISACTIVER0 = 0x300/4, // Interrupt Set-Active Register (GICv2)
+ GICD_ICACTIVER0 = 0x380/4, // Interrupt Clear-Active Register (GICv2)
+
+ GICD_IPRIORITYR0 = 0x400/4, // Interrupt priority registers (254*4 = )
+
+ GICD_ITARGETSR0 = 0x800/4, // Interrupt Processor Targets Register (8*4)
+
+ GICD_ICFGR0 = 0xC00/4, // Interrupt Configuration Register (64*4)
+ GICD_NSACR0 = 0xE00/4, // Non-secure Access Control Register (64*4)
+ GICD_SIGR = 0xF00/4, // Software Generated Interrupt Register (Write Only)
+ GICD_CPENDSGIR0 = 0xF10/4, // SGI Clear-Pending Registers (4*4)
+ GICD_SPENDSGIR0 = 0xF20/4, // SGI Set-Pending Registers (4*4)
+};
+
+enum eGICC_Registers
+{
+ GICC_CTLR = 0x000/4, // CPU Interface Control Register
+ GICC_PMR = 0x004/4, // Interrupt Priority Mask Register
+ GICC_BPR = 0x008/4, // Binary Point Register
+ GICC_IAR = 0x00C/4, // Interrupt Acknowledge Register
+ GICC_EOIR = 0x010/4, // End of Interrupt Register
+ GICC_RPR = 0x014/4, // Running Priority Register
+ GICC_HPPIR = 0x018/4, // Highest Priority Pending Interrupt Register
+ GICC_ABPR = 0x01C/4, // Aliased Binary Point Register
+ GICC_AIAR = 0x020/4, // Aliased Interrupt Acknowledge Register,
+ GICC_AEOIR = 0x024/4, // Aliased End of Interrupt Register
+ GICC_AHPPIR = 0x028/4, // Aliased Highest Priority Pending Interrupt Register
+
+ GICC_APR0 = 0x0D0/4, // Active Priorities Registers (4*4)
+ GICC_NSAPR0 = 0x0E0/4, // Non-secure Active Priorities Registers (4*4)
+
+ GICC_IIDR = 0x0FC/4, // CPU Interface Identifcation Register
+ GICC_DIR = 0x0FC/4, // Deactivate Interrupt Register (Write Only)
+};
+
+#endif
--- /dev/null
+CATEGORY = armv7
+
+-include ../../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 Kernel Modules
+ * Linker Script
+ */
+
+ENTRY(ModuleEntry)
+OUTPUT_FORMAT(elf32-i386)
+
+SECTIONS
+{
+ . = 0 + SIZEOF_HEADERS;
+
+ .text : AT(ADDR(.text)) {
+ textzero = .;
+ *(.text)
+ }
+
+ .rodata ALIGN(0x1000): AT(ADDR(.rodata)) {
+ *(.rodata)
+ *(.rdata)
+ DriverInfo = .;
+ *(KMODULES)
+ }
+
+ .data ALIGN (0x1000) : AT(ADDR(.data)) {
+ *(.data)
+ }
+
+ .bss : AT(ADDR(.bss)) {
+ _sbss = .;
+ *(COMMON)
+ *(.bss)
+ _ebss = .;
+ }
+}
--- /dev/null
+#
+#
+
+OBJ := dma.o
+NAME := ISADMA
+
+-include ../Makefile.tpl
--- /dev/null
+/*\r
+ * AcessOS 1.0\r
+ * DMA Driver\r
+ */\r
+#include <acess.h>\r
+#include <modules.h>\r
+\r
+#define DMA_SIZE (0x2400)\r
+#define DMA_ADDRESS(c) ((c)*DMA_SIZE+0x500) //Save Space for IDT and BDA\r
+\r
+#define LOWB(x) ((x)&0xFF)\r
+#define HIB(x) (((x)>>8)&0xFF)\r
+#define HIW(x) (((x)>>16)&0xFFFF)\r
+\r
+// === TYPES ===\r
+typedef struct\r
+{\r
+ int mode;\r
+ char *address;\r
+} t_dmaChannel;\r
+\r
+// === PROTOTYPES ===\r
+ int DMA_Install(char **Arguments);\r
+void DMA_SetChannel(int Channel, int length, int read);\r
+ int DMA_ReadData(int channel, int count, void *buffer);\r
+ int DMA_WriteData(int channel, int count, const void *buffer);\r
+\r
+// === CONSTANTS ===\r
+const Uint8 cMASKPORT [8] = { 0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4 };\r
+const Uint8 cMODEPORT [8] = { 0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6 };\r
+const Uint8 cCLEARPORT[8] = { 0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8 };\r
+const Uint8 cPAGEPORT [8] = { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A };\r
+const Uint8 cADDRPORT [8] = { 0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC };\r
+const Uint8 cCOUNTPORT[8] = { 0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE };\r
+\r
+// === GLOBALS ===\r
+MODULE_DEFINE(0, 0x0100, x86_ISADMA, DMA_Install, NULL, NULL);\r
+char *dma_addresses[8];\r
+t_dmaChannel dma_channels[8];\r
+\r
+// === CODE ===\r
+/**\r
+ * \brief Initialise DMA channels\r
+ * \param Arguments Arguments passed at boot time\r
+ */\r
+int DMA_Install(char **Arguments)\r
+{\r
+ Uint i;\r
+ for(i=8;i--;)\r
+ {\r
+ outb( cMASKPORT[i], 0x04 | (i & 0x3) ); // mask channel\r
+ outb( cCLEARPORT[i], 0x00 );\r
+ outb( cMODEPORT[i], 0x48 | (i & 0x3) ); //Read Flag\r
+ outb( 0xd8, 0xff); //Reset Flip-Flop\r
+ outb( cADDRPORT[i], LOWB(DMA_ADDRESS(i)) ); // send address\r
+ outb( cADDRPORT[i], HIB(DMA_ADDRESS(i)) ); // send address\r
+ outb( 0xd8, 0xff); //Reset Flip-Flop\r
+ outb( cCOUNTPORT[i], LOWB(DMA_SIZE) ); // send size\r
+ outb( cCOUNTPORT[i], HIB(DMA_SIZE) ); // send size\r
+ outb( cPAGEPORT[i], LOWB(HIW(DMA_ADDRESS(i))) ); // send page\r
+ outb( cMASKPORT[i], i & 0x3 ); // unmask channel\r
+ \r
+ dma_channels[i].mode = 0;\r
+ dma_addresses[i] = (char*)DMA_ADDRESS(i);\r
+ dma_addresses[i] += KERNEL_BASE;\r
+ }\r
+ return MODULE_ERR_OK;\r
+}\r
+\r
+/**\r
+ * \fn void DMA_SetChannel(int Channel, int length, int read)\r
+ * \brief Set DMA Channel Length and RW\r
+ */\r
+void DMA_SetChannel(int Channel, int length, int read)\r
+{\r
+ Uint chan = Channel & 7;\r
+ read = !!read;\r
+ if(length > DMA_SIZE) length = DMA_SIZE;\r
+ length --; //Adjust for DMA\r
+ outb( cMASKPORT[chan], 0x04 | (chan & 0x3) ); // mask channel\r
+ outb( cCLEARPORT[chan], 0x00 );\r
+ outb( cMODEPORT[chan], (0x44 + (!read)*4) | (chan & 0x3) );\r
+ outb( cADDRPORT[chan], LOWB(DMA_ADDRESS(chan)) ); // send address\r
+ outb( cADDRPORT[chan], HIB(DMA_ADDRESS(chan)) ); // send address\r
+ outb( cPAGEPORT[chan], HIW(DMA_ADDRESS(chan)) ); // send page\r
+ outb( cCOUNTPORT[chan], LOWB(length) ); // send size\r
+ outb( cCOUNTPORT[chan], HIB(length) ); // send size\r
+ outb( cMASKPORT[chan], chan & 0x3 ); // unmask channel\r
+ dma_addresses[chan] = (char*)DMA_ADDRESS(chan);\r
+ dma_addresses[chan] += KERNEL_BASE;\r
+}\r
+\r
+/**\r
+ * \fn void DMA_ReadData(int channel, int count, void *buffer)\r
+ * \brief Read data from a DMA buffer\r
+ */\r
+int DMA_ReadData(int channel, int count, void *buffer)\r
+{\r
+ if(channel < 0 || channel > 7)\r
+ return -1;\r
+ if(count < 0 || count > DMA_SIZE)\r
+ return -2;\r
+ //LogF("memcpy(*0x%x, dma_channels[channel].address, count)\n", buffer\r
+ memcpy(buffer, dma_addresses[channel], count);\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn void DMA_WriteData(int channel, int count, void *buffer)\r
+ * \brief Write data to a DMA buffer\r
+ */\r
+int DMA_WriteData(int channel, int count, const void *buffer)\r
+{\r
+ if(channel < 0 || channel > 7)\r
+ return -1;\r
+ if(count < 0 || count > DMA_SIZE)\r
+ return -2;\r
+ \r
+ memcpy(dma_addresses[channel], buffer, count);\r
+ \r
+ return 0;\r
+}\r
--- /dev/null
+/*
+ * Acess2 DMA Driver
+ */
+#ifndef _DMA_H_
+#define _DMA_H_
+
+extern void DMA_SetChannel(int channel, int length, int read);
+extern int DMA_ReadData(int channel, int count, void *buffer);
+extern int DMA_WriteData(int channel, int count, void *buffer);
+
+#endif
--- /dev/null
+CATEGORY = x86
+
+-include ../../Makefile.tpl
--- /dev/null
+#
+#
+
+OBJ := vga.o
+NAME := VGAText
+
+-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 VGA Controller Driver
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <fs_devfs.h>
+#include <api_drv_video.h>
+#include <modules.h>
+
+// === CONSTANTS ===
+#define VGA_WIDTH 80
+#define VGA_HEIGHT 25
+
+// === PROTOTYPES ===
+ int VGA_Install(char **Arguments);
+Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+ int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data);
+Uint8 VGA_int_GetColourNibble(Uint16 col);
+Uint16 VGA_int_GetWord(const tVT_Char *Char);
+void VGA_int_SetCursor(Sint16 x, Sint16 y);
+// --- 2D Acceleration Functions --
+void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
+void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x000A, x86_VGAText, VGA_Install, NULL, NULL);
+tVFS_NodeType gVGA_NodeType = {
+ //.Read = VGA_Read,
+ .Write = VGA_Write,
+ .IOCtl = VGA_IOCtl
+ };
+tDevFS_Driver gVGA_DevInfo = {
+ NULL, "x86_VGAText",
+ {
+ .NumACLs = 0,
+ .Size = VGA_WIDTH*VGA_HEIGHT*sizeof(tVT_Char),
+ .Type = &gVGA_NodeType
+ }
+};
+Uint16 *gVGA_Framebuffer = (void*)( KERNEL_BASE|0xB8000 );
+ int giVGA_BufferFormat = VIDEO_BUFFMT_TEXT;
+tDrvUtil_Video_2DHandlers gVGA_2DFunctions = {
+ NULL,
+ VGA_2D_Fill,
+ VGA_2D_Blit
+};
+
+// === CODE ===
+/**
+ * \fn int VGA_Install(char **Arguments)
+ */
+int VGA_Install(char **Arguments)
+{
+ Uint8 byte;
+
+ // Enable Bright Backgrounds
+ inb(0x3DA); // Reset flipflop
+ outb(0x3C0, 0x30); // Index 0x10, PAS
+ byte = inb(0x3C1);
+ byte &= ~8; // Disable Blink
+ outb(0x3C0, byte); // Write value
+
+
+ // Install DevFS
+ DevFS_AddDevice( &gVGA_DevInfo );
+
+ return MODULE_ERR_OK;
+}
+
+/**
+ * \brief Writes a string of bytes to the VGA controller
+ */
+Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+{
+ if( giVGA_BufferFormat == VIDEO_BUFFMT_TEXT )
+ {
+ int num = Length / sizeof(tVT_Char);
+ int ofs = Offset / sizeof(tVT_Char);
+ int i = 0;
+ const tVT_Char *chars = Buffer;
+ Uint16 word;
+
+ //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+
+ for( ; num--; i ++, ofs ++)
+ {
+ word = VGA_int_GetWord( &chars[i] );
+ gVGA_Framebuffer[ ofs ] = word;
+ }
+
+ //LEAVE('X', Length);
+ return Length;
+ }
+ else if( giVGA_BufferFormat == VIDEO_BUFFMT_2DSTREAM )
+ {
+ return DrvUtil_Video_2DStream(NULL, Buffer, Length, &gVGA_2DFunctions, sizeof(gVGA_2DFunctions));
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * \fn int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ * \brief IO Control Call
+ */
+int VGA_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ int rv;
+ switch(ID)
+ {
+ case DRV_IOCTL_TYPE: return DRV_TYPE_VIDEO;
+ case DRV_IOCTL_IDENT: memcpy(Data, "VGA\0", 4); return 1;
+ case DRV_IOCTL_VERSION: *(int*)Data = 50; return 1;
+ case DRV_IOCTL_LOOKUP: return 0;
+
+ case VIDEO_IOCTL_GETSETMODE: return 0; // Mode 0 only
+ case VIDEO_IOCTL_FINDMODE:
+ if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1;
+ ((tVideo_IOCtl_Mode*)Data)->id = 0; // Text Only!
+ case VIDEO_IOCTL_MODEINFO:
+ if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1;
+ if( ((tVideo_IOCtl_Mode*)Data)->id != 0) return 0;
+ ((tVideo_IOCtl_Mode*)Data)->width = VGA_WIDTH*giVT_CharWidth;
+ ((tVideo_IOCtl_Mode*)Data)->height = VGA_HEIGHT*giVT_CharHeight;
+ ((tVideo_IOCtl_Mode*)Data)->bpp = 4;
+ return 1;
+
+ case VIDEO_IOCTL_SETBUFFORMAT:
+ if( !CheckMem(Data, sizeof(int)) ) return -1;
+ switch( *(int*)Data )
+ {
+ case VIDEO_BUFFMT_TEXT:
+ case VIDEO_BUFFMT_2DSTREAM:
+ rv = giVGA_BufferFormat;
+ giVGA_BufferFormat = *(int*)Data;
+// Log_Debug("VGA", "Buffer format set to %i", giVGA_BufferFormat);
+ return rv;
+ default:
+ break;
+ }
+ return -1;
+
+ case VIDEO_IOCTL_SETCURSOR:
+ VGA_int_SetCursor( ((tVideo_IOCtl_Pos*)Data)->x, ((tVideo_IOCtl_Pos*)Data)->y );
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * \fn Uint8 VGA_int_GetColourNibble(Uint16 col)
+ * \brief Converts a 12-bit colour into a VGA 4-bit colour
+ */
+Uint8 VGA_int_GetColourNibble(Uint16 col)
+{
+ Uint8 ret = 0;
+ int bright = 0;
+
+ col = col & 0xCCC;
+ col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30);
+ bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2;
+
+ switch(col)
+ {
+ // Black
+ case 0x00: ret = 0x0; break;
+ // Dark Grey
+ case 0x15: ret = 0x8; break;
+ // Blues
+ case 0x01:
+ case 0x02: ret = 0x1; break;
+ case 0x03: ret = 0x9; break;
+ // Green
+ case 0x04:
+ case 0x08: ret = 0x2; break;
+ case 0x0C: ret = 0xA; break;
+ // Reds
+ case 0x10:
+ case 0x20: ret = 0x4; break;
+ case 0x30: ret = 0xC; break;
+ // Light Grey
+ case 0x2A: ret = 0x7; break;
+ // White
+ case 0x3F: ret = 0xF; break;
+
+ default:
+ ret |= (col & 0x03 ? 1 : 0);
+ ret |= (col & 0x0C ? 2 : 0);
+ ret |= (col & 0x30 ? 4 : 0);
+ ret |= (bright ? 8 : 0);
+ break;
+ }
+ return ret;
+}
+
+/**
+ * \fn Uint16 VGA_int_GetWord(tVT_Char *Char)
+ * \brief Convers a character structure to a VGA character word
+ */
+Uint16 VGA_int_GetWord(const tVT_Char *Char)
+{
+ Uint16 ret;
+ Uint16 col;
+
+ // Get Character
+ if(Char->Ch < 128)
+ ret = Char->Ch;
+ else {
+ switch(Char->Ch)
+ {
+ default: ret = 0; break;
+ }
+ }
+
+ col = VGA_int_GetColourNibble(Char->BGCol);
+ ret |= col << 12;
+
+ col = VGA_int_GetColourNibble(Char->FGCol);
+ ret |= col << 8;
+
+ return ret;
+}
+
+/**
+ * \fn void VGA_int_SetCursor(Sint16 x, Sint16 y)
+ * \brief Updates the cursor position
+ */
+void VGA_int_SetCursor(Sint16 x, Sint16 y)
+{
+ int pos = x+y*VGA_WIDTH;
+ if(x == -1 || y == -1)
+ pos = -1;
+ outb(0x3D4, 14);
+ outb(0x3D5, pos >> 8);
+ outb(0x3D4, 15);
+ outb(0x3D5, pos);
+}
+
+void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
+{
+ tVT_Char ch;
+ Uint16 word, *buf;
+
+ X /= giVT_CharWidth;
+ W /= giVT_CharWidth;
+ Y /= giVT_CharHeight;
+ H /= giVT_CharHeight;
+
+ ch.Ch = 0x20;
+ ch.BGCol = (Colour & 0x0F0000) >> (16-8);
+ ch.BGCol |= (Colour & 0x000F00) >> (8-4);
+ ch.BGCol |= (Colour & 0x00000F);
+ word = VGA_int_GetWord(&ch);
+
+ Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word);
+
+ if( X > VGA_WIDTH || Y > VGA_HEIGHT ) return ;
+ if( X + W > VGA_WIDTH ) W = VGA_WIDTH - X;
+ if( Y + H > VGA_HEIGHT ) H = VGA_HEIGHT - Y;
+
+ buf = gVGA_Framebuffer + Y*VGA_WIDTH + X;
+
+
+ while( H -- ) {
+ int i;
+ for( i = 0; i < W; i ++ )
+ *buf++ = word;
+ buf += VGA_WIDTH - W;
+ }
+}
+
+void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
+{
+ Uint16 *src, *dst;
+
+ DstX /= giVT_CharWidth;
+ SrcX /= giVT_CharWidth;
+ W /= giVT_CharWidth;
+
+ DstY /= giVT_CharHeight;
+ SrcY /= giVT_CharHeight;
+ H /= giVT_CharHeight;
+
+// Log("(%i,%i) from (%i,%i) %ix%i", DstX, DstY, SrcX, SrcY, W, H);
+
+ if( SrcX > VGA_WIDTH || SrcY > VGA_HEIGHT ) return ;
+ if( SrcX + W > VGA_WIDTH ) W = VGA_WIDTH - SrcX;
+ if( SrcY + H > VGA_HEIGHT ) H = VGA_HEIGHT - SrcY;
+ if( DstX > VGA_WIDTH || DstY > VGA_HEIGHT ) return ;
+ if( DstX + W > VGA_WIDTH ) W = VGA_WIDTH - DstX;
+ if( DstY + H > VGA_HEIGHT ) H = VGA_HEIGHT - DstY;
+
+
+ src = gVGA_Framebuffer + SrcY*VGA_WIDTH + SrcX;
+ dst = gVGA_Framebuffer + DstY*VGA_WIDTH + DstX;
+
+ if( src > dst )
+ {
+ // Simple copy
+ while( H-- ) {
+ memcpy(dst, src, W*2);
+ dst += VGA_WIDTH;
+ src += VGA_WIDTH;
+ }
+ }
+ else
+ {
+ dst += H*VGA_WIDTH;
+ src += H*VGA_WIDTH;
+ while( H -- ) {
+ int i;
+ dst -= VGA_WIDTH-W;
+ src -= VGA_WIDTH-W;
+ for( i = W; i --; ) *--dst = *--src;
+ }
+ }
+}
+++ /dev/null
-#
-#
-
-OBJ = bochsvbe.o
-NAME = BochsGA
-
--include ../Makefile.tpl
+++ /dev/null
-/**\r
- * Acess2 Bochs graphics adapter Driver\r
- * - By John Hodge (thePowersGang)\r
- *\r
- * bochsvbe.c\r
- * - Driver core\r
- */\r
-#define DEBUG 0\r
-#define VERSION VER2(0,10)\r
-\r
-#include <acess.h>\r
-#include <errno.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include <fs_devfs.h>\r
-#include <drv_pci.h>\r
-#include <api_drv_video.h>\r
-\r
-// === TYPES ===\r
-typedef struct sBGA_Mode {\r
- Uint16 width;\r
- Uint16 height;\r
- Uint16 bpp;\r
- Uint32 fbSize;\r
-} tBGA_Mode;\r
-\r
-// === CONSTANTS ===\r
-#define BGA_LFB_MAXSIZE (1024*768*4)\r
-#define VBE_DISPI_BANK_ADDRESS 0xA0000\r
-#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000\r
-#define VBE_DISPI_IOPORT_INDEX 0x01CE\r
-#define VBE_DISPI_IOPORT_DATA 0x01CF\r
-#define VBE_DISPI_DISABLED 0x00\r
-#define VBE_DISPI_ENABLED 0x01\r
-#define VBE_DISPI_LFB_ENABLED 0x40\r
-#define VBE_DISPI_NOCLEARMEM 0x80\r
-enum {\r
- VBE_DISPI_INDEX_ID,\r
- VBE_DISPI_INDEX_XRES,\r
- VBE_DISPI_INDEX_YRES,\r
- VBE_DISPI_INDEX_BPP,\r
- VBE_DISPI_INDEX_ENABLE,\r
- VBE_DISPI_INDEX_BANK,\r
- VBE_DISPI_INDEX_VIRT_WIDTH,\r
- VBE_DISPI_INDEX_VIRT_HEIGHT,\r
- VBE_DISPI_INDEX_X_OFFSET,\r
- VBE_DISPI_INDEX_Y_OFFSET\r
-};\r
-\r
-extern void MM_DumpTables(tVAddr Start, tVAddr End);\r
-\r
-// === PROTOTYPES ===\r
-// Driver\r
- int BGA_Install(char **Arguments);\r
-void BGA_Uninstall();\r
-// Internal\r
-void BGA_int_WriteRegister(Uint16 reg, Uint16 value);\r
-Uint16 BGA_int_ReadRegister(Uint16 reg);\r
-void BGA_int_SetBank(Uint16 bank);\r
-void BGA_int_SetMode(Uint16 width, Uint16 height);\r
- int BGA_int_UpdateMode(int id);\r
- int BGA_int_FindMode(tVideo_IOCtl_Mode *info);\r
- int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);\r
- int BGA_int_MapFB(void *Dest);\r
-// Filesystem\r
-Uint64 BGA_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer);\r
-Uint64 BGA_Write(tVFS_Node *Node, Uint64 off, Uint64 len, const void *buffer);\r
- int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, VERSION, BochsGA, BGA_Install, NULL, "PCI", NULL);\r
-tVFS_NodeType gBGA_NodeType = {\r
- .Read = BGA_Read,\r
- .Write = BGA_Write,\r
- .IOCtl = BGA_IOCtl\r
- };\r
-tDevFS_Driver gBGA_DriverStruct = {\r
- NULL, "BochsGA",\r
- {.Type = &gBGA_NodeType}\r
- };\r
- int giBGA_CurrentMode = -1;\r
-tVideo_IOCtl_Pos gBGA_CursorPos = {-1,-1};\r
-Uint *gBGA_Framebuffer;\r
-const tBGA_Mode *gpBGA_CurrentMode;\r
-const tBGA_Mode gBGA_Modes[] = {\r
- {640,480,32, 640*480*4},\r
- {800,600,32, 800*600*4},\r
- {1024,768,32, 1024*768*4}\r
-};\r
-#define BGA_MODE_COUNT (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))\r
-tDrvUtil_Video_BufInfo gBGA_DrvUtil_BufInfo;\r
-\r
-// === CODE ===\r
-/**\r
- * \fn int BGA_Install(char **Arguments)\r
- */\r
-int BGA_Install(char **Arguments)\r
-{\r
- int version = 0;\r
- tPAddr base;\r
- tPCIDev dev;\r
- \r
- // Check BGA Version\r
- version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);\r
- LOG("version = 0x%x", version);\r
- \r
- // NOTE: This driver was written for BGA versions >= 0xBOC2\r
- // NOTE: However, Qemu is braindead and doesn't return the actual version\r
- if( version != 0xB0C0 && ((version & 0xFFF0) != 0xB0C0 || version < 0xB0C2) ) {\r
- Log_Warning("BGA", "Bochs Adapter Version is not compatible (need >= 0xB0C2), instead 0x%x", version);\r
- return MODULE_ERR_NOTNEEDED;\r
- }\r
-\r
- // Get framebuffer base \r
- dev = PCI_GetDevice(0x1234, 0x1111, 0);\r
- if(dev == -1)\r
- base = VBE_DISPI_LFB_PHYSICAL_ADDRESS;\r
- else\r
- base = PCI_GetBAR(dev, 0);\r
-\r
- // Map Framebuffer to hardware address\r
- gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768); // 768 pages (3Mb)\r
-\r
- // Install Device\r
- if( DevFS_AddDevice( &gBGA_DriverStruct ) == -1 )\r
- {\r
- Log_Warning("BGA", "Unable to register with DevFS, maybe already loaded?");\r
- return MODULE_ERR_MISC;\r
- }\r
- \r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \brief Clean up driver resources before destruction\r
- */\r
-void BGA_Uninstall(void)\r
-{\r
- DevFS_DelDevice( &gBGA_DriverStruct );\r
- MM_UnmapHWPages( (tVAddr)gBGA_Framebuffer, 768 );\r
-}\r
-\r
-/**\r
- * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
- * \brief Read from the framebuffer\r
- */\r
-Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
-{\r
- // Check Mode\r
- if(giBGA_CurrentMode == -1) return -1;\r
- \r
- // Check Offset and Length against Framebuffer Size\r
- if(off+len > gpBGA_CurrentMode->fbSize)\r
- return -1;\r
- \r
- // Copy from Framebuffer\r
- memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);\r
- return len;\r
-}\r
-\r
-/**\r
- * \brief Write to the framebuffer\r
- */\r
-Uint64 BGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
-{\r
- if( giBGA_CurrentMode == -1 ) BGA_int_UpdateMode(0);\r
- return DrvUtil_Video_WriteLFB(&gBGA_DrvUtil_BufInfo, Offset, Length, Buffer);\r
-}\r
-\r
-const char *csaBGA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
-/**\r
- * \brief Handle messages to the device\r
- */\r
-int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
- int ret = -2;\r
- ENTER("pNode iId pData", Node, ID, Data);\r
- \r
- switch(ID)\r
- {\r
- BASE_IOCTLS(DRV_TYPE_VIDEO, "BochsGA", VERSION, csaBGA_IOCtls);\r
-\r
- case VIDEO_IOCTL_GETSETMODE:\r
- if( Data ) BGA_int_UpdateMode(*(int*)(Data));\r
- ret = giBGA_CurrentMode;\r
- break;\r
- \r
- case VIDEO_IOCTL_FINDMODE:\r
- ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data);\r
- break;\r
- \r
- case VIDEO_IOCTL_MODEINFO:\r
- ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
- break;\r
- \r
- case VIDEO_IOCTL_SETBUFFORMAT:\r
- DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo );\r
- ret = gBGA_DrvUtil_BufInfo.BufferFormat;\r
- if(Data)\r
- gBGA_DrvUtil_BufInfo.BufferFormat = *(int*)Data;\r
- if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_SetCursor( &gBGA_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
- break;\r
- \r
- case VIDEO_IOCTL_SETCURSOR:\r
- DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo );\r
- gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x;\r
- gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y;\r
- if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_DrawCursor(\r
- &gBGA_DrvUtil_BufInfo,\r
- gBGA_CursorPos.x*giVT_CharWidth,\r
- gBGA_CursorPos.y*giVT_CharHeight\r
- );\r
- else\r
- DrvUtil_Video_DrawCursor(\r
- &gBGA_DrvUtil_BufInfo,\r
- gBGA_CursorPos.x, gBGA_CursorPos.y\r
- );\r
- break;\r
- \r
- default:\r
- LEAVE('i', -2);\r
- return -2;\r
- }\r
- \r
- LEAVE('i', ret);\r
- return ret;\r
-}\r
-\r
-//== Internal Functions ==\r
-/**\r
- * \brief Writes to a BGA register\r
- */\r
-void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
-{\r
- outw(VBE_DISPI_IOPORT_INDEX, reg);\r
- outw(VBE_DISPI_IOPORT_DATA, value);\r
-}\r
-\r
-Uint16 BGA_int_ReadRegister(Uint16 reg)\r
-{\r
- outw(VBE_DISPI_IOPORT_INDEX, reg);\r
- return inw(VBE_DISPI_IOPORT_DATA);\r
-}\r
-\r
-/**\r
- * \brief Sets the video mode (32bpp only)\r
- */\r
-void BGA_int_SetMode(Uint16 Width, Uint16 Height)\r
-{\r
- ENTER("iWidth iHeight", Width, Height);\r
- BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);\r
- BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, Width);\r
- BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, Height);\r
- BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, 32);\r
- BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);\r
- LEAVE('-');\r
-}\r
-\r
-/**\r
- * \fn int BGA_int_UpdateMode(int id)\r
- * \brief Set current vide mode given a mode id\r
- */\r
-int BGA_int_UpdateMode(int id)\r
-{\r
- // Sanity Check\r
- if(id < 0 || id >= BGA_MODE_COUNT) return -1;\r
- \r
- BGA_int_SetMode(\r
- gBGA_Modes[id].width,\r
- gBGA_Modes[id].height);\r
- \r
- gBGA_DrvUtil_BufInfo.Framebuffer = gBGA_Framebuffer;\r
- gBGA_DrvUtil_BufInfo.Pitch = gBGA_Modes[id].width * 4;\r
- gBGA_DrvUtil_BufInfo.Width = gBGA_Modes[id].width;\r
- gBGA_DrvUtil_BufInfo.Height = gBGA_Modes[id].height;\r
- gBGA_DrvUtil_BufInfo.Depth = gBGA_Modes[id].bpp;\r
-\r
- giBGA_CurrentMode = id;\r
- gpBGA_CurrentMode = &gBGA_Modes[id];\r
- return id;\r
-}\r
-\r
-/**\r
- * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
- * \brief Find a mode matching the given options\r
- */\r
-int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
-{\r
- int i;\r
- int best = 0, bestFactor = 1000;\r
- int tmp;\r
- int rqdProduct = info->width * info->height;\r
- \r
- ENTER("pinfo", info);\r
- LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);\r
- \r
- for(i = 0; i < BGA_MODE_COUNT; i++)\r
- {\r
- #if DEBUG >= 2\r
- LOG("Mode %i (%ix%i,%ibpp), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);\r
- #endif\r
-\r
- if( gBGA_Modes[i].bpp != info->bpp )\r
- continue ; \r
- \r
- // Ooh! A perfect match\r
- if(gBGA_Modes[i].width == info->width && gBGA_Modes[i].height == info->height)\r
- {\r
- #if DEBUG >= 2\r
- LOG("Perfect");\r
- #endif\r
- best = i;\r
- break;\r
- }\r
-\r
-\r
- // If not, how close are we?\r
- tmp = gBGA_Modes[i].width * gBGA_Modes[i].height - rqdProduct;\r
- tmp = tmp < 0 ? -tmp : tmp; // tmp = ABS(tmp)\r
- \r
- #if DEBUG >= 2\r
- LOG("tmp = %i", tmp);\r
- #endif\r
- \r
- if(tmp < bestFactor)\r
- {\r
- bestFactor = tmp;\r
- best = i;\r
- }\r
- }\r
- \r
- info->id = best;\r
- info->width = gBGA_Modes[best].width;\r
- info->height = gBGA_Modes[best].height;\r
- info->bpp = gBGA_Modes[best].bpp;\r
-\r
- LEAVE('i', best); \r
- return best;\r
-}\r
-\r
-/**\r
- * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
- * \brief Get mode information\r
- */\r
-int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
-{\r
- // Sanity Check\r
- //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {\r
- // return -EINVAL;\r
- //}\r
- \r
- if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1;\r
- \r
- info->width = gBGA_Modes[info->id].width;\r
- info->height = gBGA_Modes[info->id].height;\r
- info->bpp = gBGA_Modes[info->id].bpp;\r
- \r
- return 1;\r
-}\r
-\r
+++ /dev/null
-CATEGORY = Video
-
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-OBJ = main.o
-NAME = PL110
-
--include ../Makefile.tpl
+++ /dev/null
-/**\r
- * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver\r
- * - By John Hodge (thePowersGang)\r
- *\r
- * main.c\r
- * - Driver core\r
- *\r
- *\r
- * NOTE: The PL110 is set to 24bpp, but these are stored as 32-bit words.\r
- * This corresponds to the Acess 32bpp mode, as the Acess 24bpp is packed\r
- */\r
-#define DEBUG 0\r
-#define VERSION ((0<<8)|10)\r
-#include <acess.h>\r
-#include <errno.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include <fs_devfs.h>\r
-#include <drv_pci.h>\r
-#include <api_drv_video.h>\r
-#include <lib/keyvalue.h>\r
-#include <options.h> // ARM Arch\r
-\r
-#define ABS(a) ((a)>0?(a):-(a))\r
-\r
-// === TYPEDEFS ===\r
-typedef struct sPL110 tPL110;\r
-\r
-struct sPL110\r
-{\r
- Uint32 LCDTiming0;\r
- Uint32 LCDTiming1;\r
- Uint32 LCDTiming2;\r
- Uint32 LCDTiming3;\r
- \r
- Uint32 LCDUPBase;\r
- Uint32 LCDLPBase;\r
- Uint32 LCDIMSC;\r
- Uint32 LCDControl;\r
- Uint32 LCDRIS;\r
- Uint32 LCDMIS;\r
- Uint32 LCDICR;\r
- Uint32 LCDUPCurr;\r
- Uint32 LCDLPCurr;\r
-};\r
-\r
-#ifndef PL110_BASE\r
-#define PL110_BASE 0x10020000 // Integrator\r
-#endif\r
-\r
-// === CONSTANTS ===\r
-const struct {\r
- short W, H;\r
-} caPL110_Modes[] = {\r
- {640,480},\r
- {800,600},\r
- {1024,768} // MAX\r
-};\r
-const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]);\r
-\r
-// === PROTOTYPES ===\r
-// Driver\r
- int PL110_Install(char **Arguments);\r
-void PL110_Uninstall();\r
-// Internal\r
-// Filesystem\r
-Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
-Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, const void *buffer);\r
- int PL110_IOCtl(tVFS_Node *node, int id, void *data);\r
-// -- Internals\r
- int PL110_int_SetResolution(int W, int H);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL);\r
-tVFS_NodeType gPL110_DevNodeType = {\r
- .Read = PL110_Read,\r
- .Write = PL110_Write,\r
- .IOCtl = PL110_IOCtl\r
- };\r
-tDevFS_Driver gPL110_DriverStruct = {\r
- NULL, "PL110",\r
- {.Type = &gPL110_DevNodeType}\r
-};\r
-// -- Options\r
-tPAddr gPL110_PhysBase = PL110_BASE;\r
- int gbPL110_IsVersatile = 1;\r
-// -- KeyVal parse rules\r
-const tKeyVal_ParseRules gPL110_KeyValueParser = {\r
- NULL,\r
- {\r
- {"Base", "P", &gPL110_PhysBase},\r
- {"IsVersatile", "i", &gbPL110_IsVersatile},\r
- {NULL, NULL, NULL}\r
- }\r
-};\r
-// -- Driver state\r
- int giPL110_CurrentMode = 0;\r
- int giPL110_BufferMode;\r
- int giPL110_Width = 640;\r
- int giPL110_Height = 480;\r
-size_t giPL110_FramebufferSize;\r
-tPL110 *gpPL110_IOMem;\r
-tPAddr gPL110_FramebufferPhys;\r
-void *gpPL110_Framebuffer;\r
-// -- Misc\r
-tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo;\r
-tVideo_IOCtl_Pos gPL110_CursorPos;\r
-\r
-// === CODE ===\r
-/**\r
- */\r
-int PL110_Install(char **Arguments)\r
-{\r
-// KeyVal_Parse(&gPL110_KeyValueParser, Arguments);\r
- \r
- gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1);\r
-\r
- PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H);\r
-\r
- DevFS_AddDevice( &gPL110_DriverStruct );\r
-\r
- return 0;\r
-}\r
-\r
-/**\r
- * \brief Clean up resources for driver unloading\r
- */\r
-void PL110_Uninstall()\r
-{\r
-}\r
-\r
-/**\r
- * \brief Read from the framebuffer\r
- */\r
-Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
-{\r
- return 0;\r
-}\r
-\r
-/**\r
- * \brief Write to the framebuffer\r
- */\r
-Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
-{\r
- gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode;\r
- return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer);\r
-}\r
-\r
-const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
-\r
-/**\r
- * \brief Handle messages to the device\r
- */\r
-int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
- int ret = -2;\r
- ENTER("pNode iID pData", Node, ID, Data);\r
- \r
- switch(ID)\r
- {\r
- BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls);\r
-\r
- case VIDEO_IOCTL_SETBUFFORMAT:\r
- DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );\r
- ret = giPL110_BufferMode;\r
- if(Data) giPL110_BufferMode = *(int*)Data;\r
- if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
- break;\r
- \r
- case VIDEO_IOCTL_GETSETMODE:\r
- if(Data)\r
- {\r
- int newMode;\r
- \r
- if( !CheckMem(Data, sizeof(int)) )\r
- LEAVE_RET('i', -1);\r
- \r
- newMode = *(int*)Data;\r
- \r
- if(newMode < 0 || newMode >= ciPL110_ModeCount)\r
- LEAVE_RET('i', -1);\r
-\r
- if(newMode != giPL110_CurrentMode)\r
- {\r
- giPL110_CurrentMode = newMode;\r
- PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H );\r
- }\r
- }\r
- ret = giPL110_CurrentMode;\r
- break;\r
- \r
- case VIDEO_IOCTL_FINDMODE:\r
- {\r
- tVideo_IOCtl_Mode *mode = Data;\r
- int closest, closestArea, reqArea = 0;\r
- if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
- LEAVE_RET('i', -1);\r
- if( mode->bpp != 32 )\r
- LEAVE_RET('i', 0);\r
- if( mode->flags != 0 )\r
- LEAVE_RET('i', 0);\r
-\r
- ret = 0;\r
-\r
- for( int i = 0; i < ciPL110_ModeCount; i ++ )\r
- {\r
- int area;\r
- if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) {\r
- mode->id = i;\r
- ret = 1;\r
- break;\r
- }\r
- \r
- area = caPL110_Modes[i].W * caPL110_Modes[i].H;\r
- if(!reqArea) {\r
- reqArea = mode->width * mode->height;\r
- closest = i;\r
- closestArea = area;\r
- }\r
- else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {\r
- closest = i;\r
- closestArea = area;\r
- }\r
- }\r
- \r
- if( ret == 0 )\r
- {\r
- mode->id = closest;\r
- ret = 1;\r
- }\r
- mode->width = caPL110_Modes[mode->id].W;\r
- mode->height = caPL110_Modes[mode->id].H;\r
- break;\r
- }\r
- \r
- case VIDEO_IOCTL_MODEINFO:\r
- {\r
- tVideo_IOCtl_Mode *mode = Data;\r
- if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
- LEAVE_RET('i', -1);\r
- if(mode->id < 0 || mode->id >= ciPL110_ModeCount)\r
- LEAVE_RET('i', 0);\r
- \r
-\r
- mode->bpp = 32;\r
- mode->flags = 0;\r
- mode->width = caPL110_Modes[mode->id].W;\r
- mode->height = caPL110_Modes[mode->id].H;\r
-\r
- ret = 1;\r
- break;\r
- }\r
- \r
- case VIDEO_IOCTL_SETCURSOR:\r
- if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
- LEAVE_RET('i', -1);\r
-\r
- DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );\r
- \r
- gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data;\r
- if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_DrawCursor(\r
- &gPL110_DrvUtil_BufInfo,\r
- gPL110_CursorPos.x*giVT_CharWidth,\r
- gPL110_CursorPos.y*giVT_CharHeight\r
- );\r
- else\r
- DrvUtil_Video_DrawCursor(\r
- &gPL110_DrvUtil_BufInfo,\r
- gPL110_CursorPos.x,\r
- gPL110_CursorPos.y\r
- );\r
- break;\r
- \r
- default:\r
- LEAVE('i', -2);\r
- return -2;\r
- }\r
- \r
- LEAVE('i', ret);\r
- return ret;\r
-}\r
-\r
-//\r
-//\r
-//\r
-\r
-/**\r
- * \brief Set the LCD controller resolution\r
- * \param W Width (aligned to 16 pixels, cipped to 1024)\r
- * \param H Height (clipped to 768)\r
- * \return Boolean failure\r
- */\r
-int PL110_int_SetResolution(int W, int H)\r
-{\r
- W = (W + 15) & ~0xF;\r
- if(W <= 0 || H <= 0) {\r
- Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H);\r
- return 1;\r
- }\r
- if(W > 1024) W = 1024;\r
- if(H > 768) H = 768;\r
-\r
- gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2;\r
- gpPL110_IOMem->LCDTiming1 = H-1;\r
- gpPL110_IOMem->LCDTiming2 = (14 << 27);\r
- gpPL110_IOMem->LCDTiming3 = 0;\r
-\r
- if( gpPL110_Framebuffer ) {\r
- MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12);\r
- }\r
- giPL110_FramebufferSize = W*H*4;\r
-\r
- gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys );\r
- gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys;\r
- gpPL110_IOMem->LCDLPBase = 0;\r
-\r
- // Power on, BGR mode, ???, ???, enabled\r
- Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1;\r
- // According to qemu, the Versatile version has these two the wrong\r
- // way around\r
- if( gbPL110_IsVersatile )\r
- {\r
- gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl\r
- gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC\r
- }\r
- else\r
- {\r
- gpPL110_IOMem->LCDIMSC = 0;\r
- gpPL110_IOMem->LCDControl = controlWord;\r
- }\r
-\r
- giPL110_Width = W;\r
- giPL110_Height = H;\r
-\r
- // Update the DrvUtil buffer info\r
- gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer;\r
- gPL110_DrvUtil_BufInfo.Pitch = W * 4;\r
- gPL110_DrvUtil_BufInfo.Width = W;\r
- gPL110_DrvUtil_BufInfo.Height = H;\r
- gPL110_DrvUtil_BufInfo.Depth = 32;\r
- \r
- return 0;\r
-}\r
+++ /dev/null
-#
-#
-
-OBJ = main.o
-NAME = Tegra2Vid
-
--include ../Makefile.tpl
+++ /dev/null
-/**\r
- * main.c\r
- * - Driver core\r
- */\r
-#define DEBUG 0\r
-#define VERSION ((0<<8)|10)\r
-#include <acess.h>\r
-#include <errno.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include <fs_devfs.h>\r
-#include <drv_pci.h>\r
-#include <api_drv_video.h>\r
-#include <lib/keyvalue.h>\r
-#include <options.h> // ARM Arch\r
-#include "tegra2.h"\r
-\r
-#define ABS(a) ((a)>0?(a):-(a))\r
-\r
-// === PROTOTYPES ===\r
-// Driver\r
- int Tegra2Vid_Install(char **Arguments);\r
-void Tegra2Vid_Uninstall();\r
-// Internal\r
-// Filesystem\r
-Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
-Uint64 Tegra2Vid_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
- int Tegra2Vid_IOCtl(tVFS_Node *node, int id, void *data);\r
-// -- Internals\r
- int Tegra2Vid_int_SetMode(int Mode);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, VERSION, Tegra2Vid, Tegra2Vid_Install, NULL, NULL);\r
-tDevFS_Driver gTegra2Vid_DriverStruct = {\r
- NULL, "Tegra2Vid",\r
- {\r
- .Read = Tegra2Vid_Read,\r
- .Write = Tegra2Vid_Write,\r
- .IOCtl = Tegra2Vid_IOCtl\r
- }\r
-};\r
-// -- Options\r
-tPAddr gTegra2Vid_PhysBase = TEGRA2VID_BASE;\r
- int gbTegra2Vid_IsVersatile = 1;\r
-// -- KeyVal parse rules\r
-const tKeyVal_ParseRules gTegra2Vid_KeyValueParser = {\r
- NULL,\r
- {\r
- {"Base", "P", &gTegra2Vid_PhysBase},\r
- {NULL, NULL, NULL}\r
- }\r
-};\r
-// -- Driver state\r
- int giTegra2Vid_CurrentMode = 0;\r
- int giTegra2Vid_BufferMode;\r
-size_t giTegra2Vid_FramebufferSize;\r
-Uint32 *gpTegra2Vid_IOMem;\r
-tPAddr gTegra2Vid_FramebufferPhys;\r
-void *gpTegra2Vid_Framebuffer;\r
-// -- Misc\r
-tDrvUtil_Video_BufInfo gTegra2Vid_DrvUtil_BufInfo;\r
-tVideo_IOCtl_Pos gTegra2Vid_CursorPos;\r
-\r
-// === CODE ===\r
-/**\r
- */\r
-int Tegra2Vid_Install(char **Arguments)\r
-{\r
-// KeyVal_Parse(&gTegra2Vid_KeyValueParser, Arguments);\r
-\r
- gpTegra2Vid_IOMem = (void*)MM_MapHWPages(gTegra2Vid_PhysBase, 256/4);\r
- {\r
- Log_Debug("Tegra2Vid", "Display CMD Registers");\r
- for( int i = 0x000; i <= 0x01A; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- for( int i = 0x028; i <= 0x043; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- Log_Debug("Tegra2Vid", "Display COM Registers");\r
- for( int i = 0x300; i <= 0x329; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- Log_Debug("Tegra2Vid", "Display DISP Registers");\r
- for( int i = 0x400; i <= 0x446; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- for( int i = 0x480; i <= 0x484; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- for( int i = 0x4C0; i <= 0x4C1; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
-\r
- Log_Debug("Tegra2Vid", "WINC_A Registers");\r
- for( int i = 0x700; i <= 0x714; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- Log_Debug("Tegra2Vid", "WINBUF_A");\r
- for( int i = 0x800; i <= 0x80A; i ++ )\r
- Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
- }\r
-// return 1;\r
- \r
- giTegra2Vid_FramebufferSize =\r
- (gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]&0xFFFF)\r
- *(gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]>>16)*4;\r
-\r
- Log_Debug("Tegra2Vid", "giTegra2Vid_FramebufferSize = 0x%x", giTegra2Vid_FramebufferSize);\r
- gpTegra2Vid_Framebuffer = MM_MapHWPages(\r
- gpTegra2Vid_IOMem[DC_WINBUF_A_START_ADDR_0],\r
- (giTegra2Vid_FramebufferSize+PAGE_SIZE-1)/PAGE_SIZE\r
- );\r
- memset(gpTegra2Vid_Framebuffer, 0x1F, 0x1000);\r
-\r
-\r
-// Tegra2Vid_int_SetMode(4);\r
-\r
- DevFS_AddDevice( &gTegra2Vid_DriverStruct );\r
-\r
- return 0;\r
-}\r
-\r
-/**\r
- * \brief Clean up resources for driver unloading\r
- */\r
-void Tegra2Vid_Uninstall()\r
-{\r
-}\r
-\r
-/**\r
- * \brief Read from the framebuffer\r
- */\r
-Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
-{\r
- return 0;\r
-}\r
-\r
-/**\r
- * \brief Write to the framebuffer\r
- */\r
-Uint64 Tegra2Vid_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
-{\r
- gTegra2Vid_DrvUtil_BufInfo.BufferFormat = giTegra2Vid_BufferMode;\r
- return DrvUtil_Video_WriteLFB(&gTegra2Vid_DrvUtil_BufInfo, Offset, Length, Buffer);\r
-}\r
-\r
-const char *csaTegra2Vid_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
-\r
-/**\r
- * \brief Handle messages to the device\r
- */\r
-int Tegra2Vid_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
- int ret = -2;\r
- ENTER("pNode iID pData", Node, ID, Data);\r
- \r
- switch(ID)\r
- {\r
- BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls);\r
-\r
- case VIDEO_IOCTL_SETBUFFORMAT:\r
- DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );\r
- ret = giTegra2Vid_BufferMode;\r
- if(Data) giTegra2Vid_BufferMode = *(int*)Data;\r
- if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_SetCursor( &gTegra2Vid_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
- break;\r
- \r
- case VIDEO_IOCTL_GETSETMODE:\r
- if(Data)\r
- {\r
- int newMode;\r
- \r
- if( !CheckMem(Data, sizeof(int)) )\r
- LEAVE_RET('i', -1);\r
- \r
- newMode = *(int*)Data;\r
- \r
- if(newMode < 0 || newMode >= ciTegra2Vid_ModeCount)\r
- LEAVE_RET('i', -1);\r
-\r
- if(newMode != giTegra2Vid_CurrentMode)\r
- {\r
- giTegra2Vid_CurrentMode = newMode;\r
- Tegra2Vid_int_SetMode( newMode );\r
- }\r
- }\r
- ret = giTegra2Vid_CurrentMode;\r
- break;\r
- \r
- case VIDEO_IOCTL_FINDMODE:\r
- {\r
- tVideo_IOCtl_Mode *mode = Data;\r
- int closest, closestArea, reqArea = 0;\r
- if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
- LEAVE_RET('i', -1);\r
- if( mode->bpp != 32 )\r
- LEAVE_RET('i', 0);\r
- if( mode->flags != 0 )\r
- LEAVE_RET('i', 0);\r
-\r
- ret = 0;\r
-\r
- for( int i = 0; i < ciTegra2Vid_ModeCount; i ++ )\r
- {\r
- int area;\r
- if(mode->width == caTegra2Vid_Modes[i].W && mode->height == caTegra2Vid_Modes[i].H) {\r
- mode->id = i;\r
- ret = 1;\r
- break;\r
- }\r
- \r
- area = caTegra2Vid_Modes[i].W * caTegra2Vid_Modes[i].H;\r
- if(!reqArea) {\r
- reqArea = mode->width * mode->height;\r
- closest = i;\r
- closestArea = area;\r
- }\r
- else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {\r
- closest = i;\r
- closestArea = area;\r
- }\r
- }\r
- \r
- if( ret == 0 )\r
- {\r
- mode->id = closest;\r
- ret = 1;\r
- }\r
- mode->width = caTegra2Vid_Modes[mode->id].W;\r
- mode->height = caTegra2Vid_Modes[mode->id].H;\r
- break;\r
- }\r
- \r
- case VIDEO_IOCTL_MODEINFO:\r
- {\r
- tVideo_IOCtl_Mode *mode = Data;\r
- if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
- LEAVE_RET('i', -1);\r
- if(mode->id < 0 || mode->id >= ciTegra2Vid_ModeCount)\r
- LEAVE_RET('i', 0);\r
- \r
-\r
- mode->bpp = 32;\r
- mode->flags = 0;\r
- mode->width = caTegra2Vid_Modes[mode->id].W;\r
- mode->height = caTegra2Vid_Modes[mode->id].H;\r
-\r
- ret = 1;\r
- break;\r
- }\r
- \r
- case VIDEO_IOCTL_SETCURSOR:\r
- if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
- LEAVE_RET('i', -1);\r
-\r
- DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );\r
- \r
- gTegra2Vid_CursorPos = *(tVideo_IOCtl_Pos*)Data;\r
- if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_DrawCursor(\r
- &gTegra2Vid_DrvUtil_BufInfo,\r
- gTegra2Vid_CursorPos.x*giVT_CharWidth,\r
- gTegra2Vid_CursorPos.y*giVT_CharHeight\r
- );\r
- else\r
- DrvUtil_Video_DrawCursor(\r
- &gTegra2Vid_DrvUtil_BufInfo,\r
- gTegra2Vid_CursorPos.x,\r
- gTegra2Vid_CursorPos.y\r
- );\r
- break;\r
- \r
- default:\r
- LEAVE('i', -2);\r
- return -2;\r
- }\r
- \r
- LEAVE('i', ret);\r
- return ret;\r
-}\r
-\r
-//\r
-//\r
-//\r
-\r
-int Tegra2Vid_int_SetMode(int Mode)\r
-{\r
- const struct sTegra2_Disp_Mode *mode = &caTegra2Vid_Modes[Mode];\r
- int w = mode->W, h = mode->H; // Horizontal/Vertical Active\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_FRONT_PORCH_0) = (mode->VFP << 16) | mode->HFP; \r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_SYNC_WIDTH_0) = (mode->HS << 16) | mode->HS;\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_BACK_PORCH_0) = (mode->VBP << 16) | mode->HBP;\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_ACTIVE_0) = (mode->H << 16) | mode->W;\r
-\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_POSITION_0) = 0;\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_SIZE_0) = (mode->H << 16) | mode->W;\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_COLOR_CONTROL_0) = 0x8; // BASE888\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_COLOR_DEPTH_0) = 12; // Could be 13 (BGR/RGB)\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_PRESCALED_SIZE_0) = (mode->H << 16) | mode->W;\r
-\r
- Log_Debug("Tegra2Vid", "Mode %i (%ix%i) selected", Mode, w, h);\r
-\r
- if( !gpTegra2Vid_Framebuffer || w*h*4 != giTegra2Vid_FramebufferSize )\r
- {\r
- if( gpTegra2Vid_Framebuffer )\r
- {\r
- // TODO: Free framebuffer for reallocation\r
- }\r
-\r
- giTegra2Vid_FramebufferSize = w*h*4; \r
-\r
- gpTegra2Vid_Framebuffer = (void*)MM_AllocDMA(\r
- (giTegra2Vid_FramebufferSize + PAGE_SIZE-1) / PAGE_SIZE,\r
- 32,\r
- &gTegra2Vid_FramebufferPhys\r
- );\r
- // TODO: Catch allocation failures\r
- Log_Debug("Tegra2Vid", "0x%x byte framebuffer at %p (%P phys)",\r
- giTegra2Vid_FramebufferSize,\r
- gpTegra2Vid_Framebuffer,\r
- gTegra2Vid_FramebufferPhys\r
- );\r
- \r
- // Tell hardware\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_START_ADDR_0) = gTegra2Vid_FramebufferPhys;\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_V_OFFSET_0) = 0; // Y offset\r
- *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_H_OFFSET_0) = 0; // X offset\r
- }\r
-\r
- return 0;\r
-}\r
+++ /dev/null
-/*
- * Acess2 NVidia Tegra2 Display Driver
- * - By John Hodge (thePowersGang)
- *
- * tegra2.h
- * - Driver definitions
- */
-#ifndef _TEGRA2_DISP_H_
-#define _TEGRA2_DISP_H_
-
-#define TEGRA2VID_BASE 0x54200000 // 0x40000 Large (256 KB)
-
-const struct sTegra2_Disp_Mode
-{
- Uint16 W, H;
- Uint16 HFP, VFP;
- Uint16 HS, VS;
- Uint16 HBP, VBP;
-} caTegra2Vid_Modes[] = {
- // TODO: VESA timings
- {720, 487, 16,33, 63, 33, 59, 133}, // NTSC 2
- {720, 576, 12,33, 63, 33, 69, 193}, // PAL 2 (VFP shown as 2/33, used 33)
- {720, 483, 16, 6, 63, 6, 59, 30}, // 480p
- {1280, 720, 70, 5, 804, 6, 220, 20}, // 720p
- {1920,1080, 44, 4, 884, 5, 148, 36}, // 1080p
- // TODO: Can all but HA/VA be constant and those select the resolution?
-};
-const int ciTegra2Vid_ModeCount = sizeof(caTegra2Vid_Modes)/sizeof(caTegra2Vid_Modes[0]);
-
-enum eTegra2_Disp_Regs
-{
- DC_DISP_DISP_SIGNAL_OPTIONS0_0 = 0x400,
- DC_DISP_DISP_SIGNAL_OPTIONS1_0, // 401
- DC_DISP_DISP_WIN_OPTIONS_0, // 402
- DC_DISP_MEM_HIGH_PRIORITY_0, // 403
- DC_DISP_MEM_HIGH_PRIORITY_TIMER_0, // 404
- DC_DISP_DISP_TIMING_OPTIONS_0, // 405
- DC_DISP_REF_TO_SYNC_0, // 406 (TrimSlice 0x0001 000B)
- DC_DISP_SYNC_WIDTH_0, // 407 (TrimSlice 0x0004 003A)
- DC_DISP_BACK_PORCH_0, // 408 (TrimSlice 0x0004 003A)
- DC_DISP_DISP_ACTIVE_0, // 409 (TrimSlice 0x0300 0400)
- DC_DISP_FRONT_PORCH_0, // 40A (TrimSlice 0x0004 003A)
-
- DC_DISP_H_PULSE0_CONTROL_0, // 40B
-
- DC_DISP_DISP_COLOR_CONTROL_0 = 0x430,
-
- DC_WINC_A_COLOR_PALETTE_0 = 0x500,
- DC_WINC_A_PALETTE_COLOR_EXT_0 = 0x600,
- DC_WIN_A_WIN_OPTIONS_0 = 0x700,
- DC_WIN_A_BYTE_SWAP_0, // 701
- DC_WIN_A_BUFFER_CONTROL_0, // 702
- DC_WIN_A_COLOR_DEPTH_0, // 703
- DC_WIN_A_POSITION_0, // 704
- DC_WIN_A_SIZE_0, // 705 (TrimSlice 0x0300 0400)
- DC_WIN_A_PRESCALED_SIZE_0,
- DC_WIN_A_H_INITIAL_DDA_0,
- DC_WIN_A_V_INITIAL_DDA_0,
- DC_WIN_A_DDA_INCREMENT_0,
- DC_WIN_A_LINE_STRIDE_0,
- DC_WIN_A_BUF_STRIDE_0,
- DC_WIN_A_BUFFER_ADDR_MODE_0,
- DC_WIN_A_DV_CONTROL_0,
- DC_WIN_A_BLEND_NOKEY_0,
-
- DC_WINBUF_A_START_ADDR_0 = 0x800,
- DC_WINBUF_A_START_ADDR_NS_0,
- DC_WINBUF_A_ADDR_H_OFFSET_0,
- DC_WINBUF_A_ADDR_H_OFFSET_NS_0,
- DC_WINBUF_A_ADDR_V_OFFSET_0,
- DC_WINBUF_A_ADDR_V_OFFSET_NS_0,
-};
-
-#endif
-
+++ /dev/null
-#
-#
-
-OBJ = main.o
-NAME = VESA
-
--include ../Makefile.tpl
+++ /dev/null
-/**
- */
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-// === TYPES ===
-typedef struct sFarPtr
-{
- Uint16 ofs;
- Uint16 seg;
-} tFarPtr;
-
-typedef struct sVesa_Mode
-{
- Uint16 code;
- Uint16 width, height;
- Uint16 pitch, bpp;
- Uint16 flags;
- Uint32 fbSize;
- Uint32 framebuffer;
-} tVesa_Mode;
-
-typedef struct sVesa_CallModeInfo
-{
- Uint16 attributes;
- Uint8 winA,winB;
- Uint16 granularity;
- Uint16 winsize;
- Uint16 segmentA, segmentB;
- tFarPtr realFctPtr;
- Uint16 pitch; // Bytes per scanline
-
- Uint16 Xres, Yres;
- Uint8 Wchar, Ychar, planes, bpp, banks;
- Uint8 memory_model, bank_size, image_pages;
- Uint8 reserved0;
-
- Uint8 red_mask, red_position;
- Uint8 green_mask, green_position;
- Uint8 blue_mask, blue_position;
- Uint8 rsv_mask, rsv_position;
- Uint8 directcolor_attributes;
-
- Uint32 physbase; // Your LFB address ;)
- Uint32 reserved1;
- Sint16 reserved2;
-} tVesa_CallModeInfo;
-
-typedef struct sVesa_CallInfo
-{
- char signature[4]; // == "VESA"
- Uint16 Version; // == 0x0300 for Vesa 3.0
- tFarPtr OEMString; // isa vbeFarPtr
- Uint8 Capabilities[4];
- tFarPtr VideoModes; // isa vbeParPtr
- Uint16 TotalMemory; // as # of 64KB blocks
-} tVesa_CallInfo;
-
-#endif
+++ /dev/null
-/*\r
- * AcessOS 1\r
- * Video BIOS Extensions (Vesa) Driver\r
- */\r
-#define DEBUG 0\r
-#define VERSION 0x100\r
-\r
-#include <acess.h>\r
-#include <vfs.h>\r
-#include <api_drv_video.h>\r
-#include <fs_devfs.h>\r
-#include <modules.h>\r
-#include <vm8086.h>\r
-#include "common.h"\r
-\r
-// === CONSTANTS ===\r
-#define FLAG_LFB 0x1\r
-#define VESA_DEFAULT_FRAMEBUFFER (KERNEL_BASE|0xA0000)\r
-#define BLINKING_CURSOR 1\r
-#if BLINKING_CURSOR\r
-# define VESA_CURSOR_PERIOD 1000\r
-#endif\r
-\r
-// === PROTOTYPES ===\r
- int Vesa_Install(char **Arguments);\r
-Uint64 Vesa_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
-Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);\r
- int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
- int Vesa_Int_SetMode(int Mode);\r
- int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data);\r
- int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data);\r
-void Vesa_int_HideCursor(void);\r
-void Vesa_int_ShowCursor(void);\r
-void Vesa_FlipCursor(void *Arg);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL);\r
-tVFS_NodeType gVesa_NodeType = {\r
- .Read = Vesa_Read,\r
- .Write = Vesa_Write,\r
- .IOCtl = Vesa_IOCtl\r
- };\r
-tDevFS_Driver gVesa_DriverStruct = {\r
- NULL, "Vesa",\r
- {.Type = &gVesa_NodeType}\r
- };\r
-tMutex glVesa_Lock;\r
-tVM8086 *gpVesa_BiosState;\r
- int giVesaDriverId = -1;\r
-// --- Video Modes ---\r
- int giVesaCurrentMode = 0;\r
-tVesa_Mode *gVesa_Modes;\r
-tVesa_Mode *gpVesaCurMode;\r
- int giVesaModeCount = 0;\r
- int gbVesaModesChecked;\r
-// --- Framebuffer ---\r
-char *gpVesa_Framebuffer = (void*)VESA_DEFAULT_FRAMEBUFFER;\r
- int giVesaPageCount = 0; //!< Framebuffer size in pages\r
-// --- Cursor Control ---\r
- int giVesaCursorX = -1;\r
- int giVesaCursorY = -1;\r
- int giVesaCursorTimer = -1; // Invalid timer\r
- int gbVesa_CursorVisible = 0;\r
-// --- 2D Video Stream Handlers ---\r
-tDrvUtil_Video_BufInfo gVesa_BufInfo;\r
-\r
-// === CODE ===\r
-int Vesa_Install(char **Arguments)\r
-{\r
- tVesa_CallInfo *info;\r
- tFarPtr infoPtr;\r
- Uint16 *modes;\r
- int i;\r
- \r
- // Allocate Info Block\r
- gpVesa_BiosState = VM8086_Init();\r
- info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs);\r
- // Set Requested Version\r
- memcpy(info->signature, "VBE2", 4);\r
- // Set Registers\r
- gpVesa_BiosState->AX = 0x4F00;\r
- gpVesa_BiosState->ES = infoPtr.seg; gpVesa_BiosState->DI = infoPtr.ofs;\r
- // Call Interrupt\r
- VM8086_Int(gpVesa_BiosState, 0x10);\r
- if(gpVesa_BiosState->AX != 0x004F) {\r
- Log_Warning("VESA", "Vesa_Install - VESA/VBE Unsupported (AX = 0x%x)", gpVesa_BiosState->AX);\r
- return MODULE_ERR_NOTNEEDED;\r
- }\r
- \r
- //Log_Debug("VESA", "info->VideoModes = %04x:%04x", info->VideoModes.seg, info->VideoModes.ofs);\r
- modes = (Uint16 *) VM8086_GetPointer(gpVesa_BiosState, info->VideoModes.seg, info->VideoModes.ofs);\r
- \r
- // Read Modes\r
- for( giVesaModeCount = 0; modes[giVesaModeCount] != 0xFFFF; giVesaModeCount++ );\r
- gVesa_Modes = (tVesa_Mode *)malloc( giVesaModeCount * sizeof(tVesa_Mode) );\r
- \r
- Log_Debug("VESA", "%i Modes", giVesaModeCount);\r
- \r
- // Insert Text Mode\r
- gVesa_Modes[0].width = 80;\r
- gVesa_Modes[0].height = 25;\r
- gVesa_Modes[0].bpp = 12;\r
- gVesa_Modes[0].code = 0x3;\r
- gVesa_Modes[0].flags = 1;\r
- gVesa_Modes[0].fbSize = 80*25*2;\r
- gVesa_Modes[0].framebuffer = 0xB8000;\r
- \r
- for( i = 1; i < giVesaModeCount; i++ )\r
- {\r
- gVesa_Modes[i].code = modes[i];\r
- }\r
-\r
-// VM8086_Deallocate( info );\r
- \r
- // Install Device\r
- giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct );\r
- if(giVesaDriverId == -1) return MODULE_ERR_MISC;\r
- \r
- return MODULE_ERR_OK;\r
-}\r
-\r
-void Vesa_int_FillModeList(void)\r
-{\r
- if( !gbVesaModesChecked )\r
- {\r
- int i;\r
- tVesa_CallModeInfo *modeinfo;\r
- tFarPtr modeinfoPtr;\r
- \r
- modeinfo = VM8086_Allocate(gpVesa_BiosState, 512, &modeinfoPtr.seg, &modeinfoPtr.ofs);\r
- for( i = 1; i < giVesaModeCount; i ++ )\r
- {\r
- // Get Mode info\r
- gpVesa_BiosState->AX = 0x4F01;\r
- gpVesa_BiosState->CX = gVesa_Modes[i].code;\r
- gpVesa_BiosState->ES = modeinfoPtr.seg;\r
- gpVesa_BiosState->DI = modeinfoPtr.ofs;\r
- VM8086_Int(gpVesa_BiosState, 0x10);\r
- \r
- // Parse Info\r
- gVesa_Modes[i].flags = 0;\r
- if ( (modeinfo->attributes & 0x90) == 0x90 )\r
- {\r
- gVesa_Modes[i].flags |= FLAG_LFB;\r
- gVesa_Modes[i].framebuffer = modeinfo->physbase;\r
- gVesa_Modes[i].fbSize = modeinfo->Yres*modeinfo->pitch;\r
- } else {\r
- gVesa_Modes[i].framebuffer = 0;\r
- gVesa_Modes[i].fbSize = 0;\r
- }\r
- \r
- gVesa_Modes[i].pitch = modeinfo->pitch;\r
- gVesa_Modes[i].width = modeinfo->Xres;\r
- gVesa_Modes[i].height = modeinfo->Yres;\r
- gVesa_Modes[i].bpp = modeinfo->bpp;\r
- \r
- #if DEBUG\r
- Log_Log("VESA", "0x%x - %ix%ix%i",\r
- gVesa_Modes[i].code, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp);\r
- #endif\r
- }\r
- \r
-// VM8086_Deallocate( modeinfo );\r
- \r
- gbVesaModesChecked = 1;\r
- }\r
-}\r
-\r
-/* Read from the framebuffer\r
- */\r
-Uint64 Vesa_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer)\r
-{\r
- #if DEBUG >= 2\r
- Log("Vesa_Read: () - NULL\n");\r
- #endif\r
- return 0;\r
-}\r
-\r
-/**\r
- * \brief Write to the framebuffer\r
- */\r
-Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
-{\r
- if( gVesa_Modes[giVesaCurrentMode].framebuffer == 0 ) {\r
- Log_Warning("VESA", "Vesa_Write - Non-LFB Modes not yet supported.");\r
- return 0;\r
- }\r
-\r
- return DrvUtil_Video_WriteLFB(&gVesa_BufInfo, Offset, Length, Buffer);\r
-}\r
-\r
-const char *csaVESA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
-/**\r
- * \brief Handle messages to the device\r
- */\r
-int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
- int ret;\r
- //Log_Debug("VESA", "Vesa_Ioctl: (Node=%p, ID=%i, Data=%p)", Node, ID, Data);\r
- switch(ID)\r
- {\r
- BASE_IOCTLS(DRV_TYPE_VIDEO, "VESA", VERSION, csaVESA_IOCtls);\r
-\r
- case VIDEO_IOCTL_GETSETMODE:\r
- if( !Data ) return giVesaCurrentMode;\r
- return Vesa_Int_SetMode( *(int*)Data );\r
- \r
- case VIDEO_IOCTL_FINDMODE:\r
- return Vesa_Int_FindMode((tVideo_IOCtl_Mode*)Data);\r
- case VIDEO_IOCTL_MODEINFO:\r
- return Vesa_Int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
- \r
- case VIDEO_IOCTL_SETBUFFORMAT:\r
- Vesa_int_HideCursor();\r
- ret = gVesa_BufInfo.BufferFormat;\r
- if(Data) gVesa_BufInfo.BufferFormat = *(int*)Data;\r
- if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor );\r
- Vesa_int_ShowCursor();\r
- return ret;\r
- \r
- case VIDEO_IOCTL_SETCURSOR: // Set cursor position\r
- Vesa_int_HideCursor();\r
- giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x;\r
- giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y;\r
- Vesa_int_ShowCursor();\r
- return 0;\r
- \r
- case VIDEO_IOCTL_SETCURSORBITMAP:\r
- DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data );\r
- return 0;\r
- }\r
- return 0;\r
-}\r
-\r
-/**\r
- * \brief Updates the video mode\r
- */\r
-int Vesa_Int_SetMode(int mode)\r
-{ \r
- // Sanity Check values\r
- if(mode < 0 || mode > giVesaModeCount) return -1;\r
-\r
- // Check for fast return\r
- if(mode == giVesaCurrentMode) return 1;\r
- \r
- Vesa_int_FillModeList();\r
-\r
- Time_RemoveTimer(giVesaCursorTimer);\r
- giVesaCursorTimer = -1;\r
- \r
- Mutex_Acquire( &glVesa_Lock );\r
- \r
- gpVesa_BiosState->AX = 0x4F02;\r
- gpVesa_BiosState->BX = gVesa_Modes[mode].code;\r
- if(gVesa_Modes[mode].flags & FLAG_LFB) {\r
- gpVesa_BiosState->BX |= 0x4000; // Bit 14 - Use LFB\r
- }\r
- \r
- // Set Mode\r
- VM8086_Int(gpVesa_BiosState, 0x10);\r
- \r
- // Map Framebuffer\r
- if( (tVAddr)gpVesa_Framebuffer != VESA_DEFAULT_FRAMEBUFFER )\r
- MM_UnmapHWPages((tVAddr)gpVesa_Framebuffer, giVesaPageCount);\r
- giVesaPageCount = (gVesa_Modes[mode].fbSize + 0xFFF) >> 12;\r
- gpVesa_Framebuffer = (void*)MM_MapHWPages(gVesa_Modes[mode].framebuffer, giVesaPageCount);\r
- \r
- Log_Log("VESA", "Setting mode to %i (%ix%i %ibpp) %p[0x%x] maps %P",\r
- mode,\r
- gVesa_Modes[mode].width, gVesa_Modes[mode].height,\r
- gVesa_Modes[mode].bpp,\r
- gpVesa_Framebuffer, giVesaPageCount << 12, gVesa_Modes[mode].framebuffer\r
- );\r
- \r
- // Record Mode Set\r
- giVesaCurrentMode = mode;\r
- gpVesaCurMode = &gVesa_Modes[giVesaCurrentMode];\r
- \r
- Mutex_Release( &glVesa_Lock );\r
-\r
- gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer;\r
- gVesa_BufInfo.Pitch = gVesa_Modes[mode].pitch;\r
- gVesa_BufInfo.Width = gVesa_Modes[mode].width;\r
- gVesa_BufInfo.Height = gVesa_Modes[mode].height;\r
- gVesa_BufInfo.Depth = gVesa_Modes[mode].bpp; \r
-\r
- return 1;\r
-}\r
-\r
-int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data)\r
-{\r
- int i;\r
- int best = -1, bestFactor = 1000;\r
- int factor, tmp;\r
- \r
- ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp);\r
-\r
- Vesa_int_FillModeList();\r
- \r
- for(i=0;i<giVesaModeCount;i++)\r
- {\r
- LOG("Mode %i (%ix%ix%i)", i, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp);\r
- \r
- if(gVesa_Modes[i].width == data->width && gVesa_Modes[i].height == data->height)\r
- {\r
- //if( (data->bpp == 32 || data->bpp == 24)\r
- // && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) )\r
- if( data->bpp == gVesa_Modes[i].bpp )\r
- {\r
- LOG("Perfect!");\r
- best = i;\r
- break;\r
- }\r
- }\r
- \r
- tmp = gVesa_Modes[i].width * gVesa_Modes[i].height;\r
- tmp -= data->width * data->height;\r
- tmp = tmp < 0 ? -tmp : tmp;\r
- factor = tmp * 1000 / (data->width * data->height);\r
- \r
- if( data->bpp == 8 && gVesa_Modes[i].bpp != 8 ) continue;\r
- if( data->bpp == 16 && gVesa_Modes[i].bpp != 16 ) continue;\r
- \r
- if( (data->bpp == 32 || data->bpp == 24)\r
- && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) )\r
- {\r
- if( data->bpp == gVesa_Modes[i].bpp )\r
- factor /= 2;\r
- }\r
- else {\r
- if( data->bpp != gVesa_Modes[i].bpp )\r
- continue ;\r
- }\r
- \r
- LOG("factor = %i", factor);\r
- \r
- if(factor < bestFactor)\r
- {\r
- bestFactor = factor;\r
- best = i;\r
- }\r
- }\r
- data->id = best;\r
- data->width = gVesa_Modes[best].width;\r
- data->height = gVesa_Modes[best].height;\r
- data->bpp = gVesa_Modes[best].bpp;\r
- LEAVE('i', best);\r
- return best;\r
-}\r
-\r
-int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data)\r
-{\r
- if(data->id < 0 || data->id > giVesaModeCount) return -1;\r
-\r
- Vesa_int_FillModeList();\r
-\r
- data->width = gVesa_Modes[data->id].width;\r
- data->height = gVesa_Modes[data->id].height;\r
- data->bpp = gVesa_Modes[data->id].bpp;\r
- return 1;\r
-}\r
-\r
-void Vesa_int_HideCursor(void)\r
-{\r
- DrvUtil_Video_RemoveCursor( &gVesa_BufInfo );\r
- #if BLINKING_CURSOR\r
- if(giVesaCursorTimer != -1) {\r
- Time_RemoveTimer(giVesaCursorTimer);\r
- giVesaCursorTimer = -1;\r
- }\r
- #endif\r
-}\r
-\r
-void Vesa_int_ShowCursor(void)\r
-{\r
- gbVesa_CursorVisible = (giVesaCursorX >= 0);\r
- if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- {\r
- DrvUtil_Video_DrawCursor(\r
- &gVesa_BufInfo,\r
- giVesaCursorX*giVT_CharWidth,\r
- giVesaCursorY*giVT_CharHeight\r
- );\r
- #if BLINKING_CURSOR\r
- giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, NULL);\r
- #endif\r
- }\r
- else\r
- DrvUtil_Video_DrawCursor(\r
- &gVesa_BufInfo,\r
- giVesaCursorX,\r
- giVesaCursorY\r
- );\r
-}\r
-\r
-/**\r
- * \brief Swaps the text cursor on/off\r
- */\r
-void Vesa_FlipCursor(void *Arg)\r
-{\r
- if( gVesa_BufInfo.BufferFormat != VIDEO_BUFFMT_TEXT )\r
- return ;\r
-\r
- if( gbVesa_CursorVisible )\r
- DrvUtil_Video_RemoveCursor(&gVesa_BufInfo);\r
- else\r
- DrvUtil_Video_DrawCursor(&gVesa_BufInfo,\r
- giVesaCursorX*giVT_CharWidth,\r
- giVesaCursorY*giVT_CharHeight\r
- );\r
- gbVesa_CursorVisible = !gbVesa_CursorVisible;\r
- \r
- #if BLINKING_CURSOR\r
- giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, Arg);\r
- #endif\r
-}\r
-\r
+++ /dev/null
-#
-#
-
-OBJ = ext2.o read.o dir.o write.o
-NAME = Ext2
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess OS
- * Ext2 Driver Version 1
- */
-/**
- * \file dir.c
- * \brief Second Extended Filesystem Driver
- * \todo Implement file full write support
- */
-#define DEBUG 1
-#define VERBOSE 0
-#include "ext2_common.h"
-
-// === MACROS ===
-#define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)])
-
-// === PROTOTYPES ===
-char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
- int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
- int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
- int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name);
-// --- Helpers ---
-tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId);
-
-// === GLOBALS ===
-tVFS_NodeType gExt2_DirType = {
- .TypeName = "ext2-dir",
- .ReadDir = Ext2_ReadDir,
- .FindDir = Ext2_FindDir,
- .MkNod = Ext2_MkNod,
- .Relink = Ext2_Relink,
- .Link = Ext2_Link,
- .Close = Ext2_CloseFile
- };
-tVFS_NodeType gExt2_FileType = {
- .TypeName = "ext2-file",
- .Read = Ext2_Read,
- .Write = Ext2_Write,
- .Close = Ext2_CloseFile
- };
-
-// === CODE ===
-/**
- * \brief Reads a directory entry
- * \param Node Directory node
- * \param Pos Position of desired element
- */
-char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
-{
- tExt2_Inode inode;
- tExt2_DirEnt dirent;
- Uint64 Base; // Block's Base Address
- int block = 0;
- Uint ofs = 0;
- int entNum = 0;
- tExt2_Disk *disk = Node->ImplPtr;
- Uint size;
-
- ENTER("pNode iPos", Node, Pos);
-
- // Read directory's inode
- Ext2_int_ReadInode(disk, Node->Inode, &inode);
- size = inode.i_size;
-
- LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
-
- // Find Entry
- // Get First Block
- // - Do this ourselves as it is a simple operation
- Base = inode.i_block[0] * disk->BlockSize;
- // Scan directory
- while(Pos -- && size > 0)
- {
- VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
- ofs += dirent.rec_len;
- size -= dirent.rec_len;
- entNum ++;
-
- if(ofs >= disk->BlockSize) {
- block ++;
- if( ofs > disk->BlockSize ) {
- Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
- entNum-1, Node->Inode);
- }
- ofs = 0;
- Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
- }
- }
-
- // Check for the end of the list
- if(size <= 0) {
- LEAVE('n');
- return NULL;
- }
-
- // Read Entry
- VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
- //LOG("dirent.inode = %i", dirent.inode);
- //LOG("dirent.rec_len = %i", dirent.rec_len);
- //LOG("dirent.name_len = %i", dirent.name_len);
- dirent.name[ dirent.name_len ] = '\0'; // Cap off string
-
-
- // Ignore . and .. (these are done in the VFS)
- if( (dirent.name[0] == '.' && dirent.name[1] == '\0')
- || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) {
- LEAVE('p', VFS_SKIP);
- return VFS_SKIP; // Skip
- }
-
- LEAVE('s', dirent.name);
- // Create new node
- return strdup(dirent.name);
-}
-
-/**
- * \brief Gets information about a file
- * \param Node Parent Node
- * \param Filename Name of wanted file
- * \return VFS Node of file
- */
-tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename)
-{
- tExt2_Disk *disk = Node->ImplPtr;
- tExt2_Inode inode;
- tExt2_DirEnt dirent;
- Uint64 Base; // Block's Base Address
- int block = 0;
- Uint ofs = 0;
- int entNum = 0;
- Uint size;
- int filenameLen = strlen(Filename);
-
- // Read directory's inode
- Ext2_int_ReadInode(disk, Node->Inode, &inode);
- size = inode.i_size;
-
- // Get First Block
- // - Do this ourselves as it is a simple operation
- Base = inode.i_block[0] * disk->BlockSize;
- // Find File
- while(size > 0)
- {
- VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
- dirent.name[ dirent.name_len ] = '\0'; // Cap off string
- // If it matches, create a node and return it
- if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0)
- return Ext2_int_CreateNode( disk, dirent.inode );
- // Increment pointers
- ofs += dirent.rec_len;
- size -= dirent.rec_len;
- entNum ++;
-
- // Check for end of block
- if(ofs >= disk->BlockSize) {
- block ++;
- if( ofs > disk->BlockSize ) {
- Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring",
- entNum-1, Node->Inode);
- }
- ofs = 0;
- Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
- }
- }
-
- return NULL;
-}
-
-/**
- * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
- * \brief Create a new node
- */
-int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags)
-{
- #if 0
- tVFS_Node *child;
- Uint64 inodeNum;
- tExt2_Inode inode;
- inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
-
- memset(&inode, 0, sizeof(tExt2_Inode));
-
- // File type
- inode.i_mode = 0664;
- if( Flags & VFS_FFLAG_READONLY )
- inode.i_mode &= ~0222;
- if( Flags & VFS_FFLAG_SYMLINK )
- inode.i_mode |= EXT2_S_IFLNK;
- else if( Flags & VFS_FFLAG_DIRECTORY )
- inode.i_mode |= EXT2_S_IFDIR | 0111;
-
- inode.i_uid = Threads_GetUID();
- inode.i_gid = Threads_GetGID();
- inode.i_ctime =
- inode.i_mtime =
- inode.i_atime = now() / 1000;
-
- child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
- return Ext2_Link(Parent, child, Name);
- #else
- return 1;
- #endif
-}
-
-/**
- * \brief Rename a file
- * \param Node This (directory) node
- * \param OldName Old name of file
- * \param NewName New name for file
- * \return Boolean Failure - See ::tVFS_Node.Relink for info
- */
-int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
-{
- return 1;
-}
-
-/**
- * \brief Links an existing node to a new name
- * \param Parent Parent (directory) node
- * \param Node Node to link
- * \param Name New name for the node
- * \return Boolean Failure - See ::tVFS_Node.Link for info
- */
-int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name)
-{
- #if 0
- tExt2_Disk *disk = Node->ImplPtr;
- tExt2_Inode inode;
- tExt2_DirEnt dirent;
- tExt2_DirEnt newEntry;
- Uint64 Base; // Block's Base Address
- int block = 0, ofs = 0;
- Uint size;
- void *blockData;
- int bestMatch = -1, bestSize, bestBlock, bestOfs;
- int nEntries;
-
- blockData = malloc(disk->BlockSize);
-
- // Read child inode (get's the file type)
- Ext2_int_ReadInode(disk, Child->Inode, &inode);
-
- // Create a stub entry
- newEntry.inode = Child->Inode;
- newEntry.name_len = strlen(Name);
- newEntry.rec_len = (newEntry.name_len+3+8)&~3;
- newEntry.type = inode.i_mode >> 12;
- memcpy(newEntry.name, Name, newEntry.name_len);
-
- // Read directory's inode
- Ext2_int_ReadInode(disk, Node->Inode, &inode);
- size = inode.i_size;
-
- // Get a lock on the inode
- Ext2_int_LockInode(disk, Node->Inode);
-
- // Get First Block
- // - Do this ourselves as it is a simple operation
- base = inode.i_block[0] * disk->BlockSize;
- VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
- block = 0;
- // Find File
- while(size > 0)
- {
- dirent = blockData + ofs;
- // Sanity Check the entry
- if(ofs + dirent->rec_len > disk->BlockSize) {
- Log_Warning("EXT2",
- "Directory entry %i of inode 0x%x extends over a block boundary",
- nEntries, (Uint)Node->Inode);
- }
- else {
-
- // Free entry
- if(dirent->type == 0) {
- if( dirent->rec_len >= newEntry.rec_len
- && (bestMatch == -1 || bestSize > dirent->rec_len) )
- {
- bestMatch = nEntries;
- bestSize = dirent->rec_len;
- bestBlock = block;
- bestOfs = ofs;
- }
- }
- // Non free - check name to avoid duplicates
- else {
- if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
- Ext2_int_UnlockInode(disk, Node->Inode);
- return 1; // ERR_???
- }
- }
- }
-
- // Increment the pointer
- nEntries ++;
- ofs += dirent->rec_len;
- if( ofs >= disk->BlockSize ) {
- // Read the next block if needed
- BLOCK_DIR_OFS(Node->Data, block) = nEntries;
- block ++;
- ofs = 0;
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
- VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
- }
- }
-
- // Check if a free slot was found
- if( bestMatch >= 0 ) {
- // Read-Modify-Write
- bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
- if( block > 0 )
- bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
- VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
- dirent = blockData + bestOfs;
- memcpy(dirent, newEntry, newEntry.rec_len);
- VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
- }
- else {
- // Allocate block, Write
- block = Ext2_int_AllocateBlock(Disk, block);
- Log_Warning("EXT2", "");
- }
-
- Ext2_int_UnlockInode(disk, Node->Inode);
- return 0;
- #else
- return 1;
- #endif
-}
-
-// ---- INTERNAL FUNCTIONS ----
-/**
- * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
- * \brief Create a new VFS Node
- */
-tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID)
-{
- tExt2_Inode inode;
- tVFS_Node retNode;
- tVFS_Node *tmpNode;
-
- if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
- return NULL;
-
- if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
- return tmpNode;
-
-
- // Set identifiers
- retNode.Inode = InodeID;
- retNode.ImplPtr = Disk;
-
- // Set file length
- retNode.Size = inode.i_size;
- retNode.Data = NULL;
-
- // Set Access Permissions
- retNode.UID = inode.i_uid;
- retNode.GID = inode.i_gid;
- retNode.NumACLs = 3;
- retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
-
- // Set Function Pointers
- retNode.Type = &gExt2_FileType;
-
- switch(inode.i_mode & EXT2_S_IFMT)
- {
- // Symbolic Link
- case EXT2_S_IFLNK:
- retNode.Flags = VFS_FFLAG_SYMLINK;
- break;
- // Regular File
- case EXT2_S_IFREG:
- retNode.Flags = 0;
- retNode.Size |= (Uint64)inode.i_dir_acl << 32;
- break;
- // Directory
- case EXT2_S_IFDIR:
- retNode.Type = &gExt2_DirType;
- retNode.Flags = VFS_FFLAG_DIRECTORY;
- retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) );
- break;
- // Unknown, Write protect it to be safe
- default:
- retNode.Flags = VFS_FFLAG_READONLY;
- break;
- }
-
- // Set Timestamps
- retNode.ATime = inode.i_atime * 1000;
- retNode.MTime = inode.i_mtime * 1000;
- retNode.CTime = inode.i_ctime * 1000;
-
- // Save in node cache and return saved node
- return Inode_CacheNode(Disk->CacheID, &retNode);
-}
+++ /dev/null
-/*\r
- * Acess OS\r
- * Ext2 Driver Version 1\r
- */\r
-/**\r
- * \file fs/ext2.c\r
- * \brief Second Extended Filesystem Driver\r
- * \todo Implement file full write support\r
- */\r
-#define DEBUG 1\r
-#define VERBOSE 0\r
-#include "ext2_common.h"\r
-#include <modules.h>\r
-\r
-// === IMPORTS ===\r
-extern tVFS_NodeType gExt2_DirType;\r
-\r
-// === PROTOTYPES ===\r
- int Ext2_Install(char **Arguments);\r
-// Interface Functions\r
-tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options);\r
-void Ext2_Unmount(tVFS_Node *Node);\r
-void Ext2_CloseFile(tVFS_Node *Node);\r
-// Internal Helpers\r
- int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);\r
-Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);\r
-Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);\r
-void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);\r
-\r
-// === SEMI-GLOBALS ===\r
-MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL);\r
-tExt2_Disk gExt2_disks[6];\r
- int giExt2_count = 0;\r
-tVFS_Driver gExt2_FSInfo = {\r
- "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL\r
- };\r
-\r
-// === CODE ===\r
-/**\r
- * \fn int Ext2_Install(char **Arguments)\r
- * \brief Install the Ext2 Filesystem Driver\r
- */\r
-int Ext2_Install(char **Arguments)\r
-{\r
- VFS_AddDriver( &gExt2_FSInfo );\r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- \brief Initializes a device to be read by by the driver\r
- \param Device String - Device to read from\r
- \param Options NULL Terminated array of option strings\r
- \return Root Node\r
-*/\r
-tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options)\r
-{\r
- tExt2_Disk *disk;\r
- int fd;\r
- int groupCount;\r
- tExt2_SuperBlock sb;\r
- tExt2_Inode inode;\r
- \r
- ENTER("sDevice pOptions", Device, Options);\r
- \r
- // Open Disk\r
- fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); //Open Device\r
- if(fd == -1) {\r
- Log_Warning("EXT2", "Unable to open '%s'", Device);\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- // Read Superblock at offset 1024\r
- VFS_ReadAt(fd, 1024, 1024, &sb); // Read Superblock\r
- \r
- // Sanity Check Magic value\r
- if(sb.s_magic != 0xEF53) {\r
- Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)",\r
- Device, sb.s_magic);\r
- VFS_Close(fd);\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- // Get Group count\r
- groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
- LOG("groupCount = %i", groupCount);\r
- \r
- // Allocate Disk Information\r
- disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);\r
- if(!disk) {\r
- Log_Warning("EXT2", "Unable to allocate disk structure");\r
- VFS_Close(fd);\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- disk->FD = fd;\r
- memcpy(&disk->SuperBlock, &sb, 1024);\r
- disk->GroupCount = groupCount;\r
- \r
- // Get an inode cache handle\r
- disk->CacheID = Inode_GetHandle();\r
- \r
- // Get Block Size\r
- LOG("s_log_block_size = 0x%x", sb.s_log_block_size);\r
- disk->BlockSize = 1024 << sb.s_log_block_size;\r
- \r
- // Read Group Information\r
- VFS_ReadAt(\r
- disk->FD,\r
- sb.s_first_data_block * disk->BlockSize + 1024,\r
- sizeof(tExt2_Group)*groupCount,\r
- disk->Groups\r
- );\r
- \r
- #if VERBOSE\r
- LOG("Block Group 0");\r
- LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);\r
- LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);\r
- LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);\r
- LOG("Block Group 1");\r
- LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);\r
- LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);\r
- LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);\r
- #endif\r
- \r
- // Get root Inode\r
- Ext2_int_ReadInode(disk, 2, &inode);\r
- \r
- // Create Root Node\r
- memset(&disk->RootNode, 0, sizeof(tVFS_Node));\r
- disk->RootNode.Inode = 2; // Root inode ID\r
- disk->RootNode.ImplPtr = disk; // Save disk pointer\r
- disk->RootNode.Size = -1; // Fill in later (on readdir)\r
- disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;\r
-\r
- disk->RootNode.Type = &gExt2_DirType;\r
- \r
- // Complete root node\r
- disk->RootNode.UID = inode.i_uid;\r
- disk->RootNode.GID = inode.i_gid;\r
- disk->RootNode.NumACLs = 1;\r
- disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;\r
- \r
- #if DEBUG\r
- LOG("inode.i_size = 0x%x", inode.i_size);\r
- LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);\r
- #endif\r
- \r
- LEAVE('p', &disk->RootNode);\r
- return &disk->RootNode;\r
-}\r
-\r
-/**\r
- * \fn void Ext2_Unmount(tVFS_Node *Node)\r
- * \brief Close a mounted device\r
- */\r
-void Ext2_Unmount(tVFS_Node *Node)\r
-{\r
- tExt2_Disk *disk = Node->ImplPtr;\r
- \r
- VFS_Close( disk->FD );\r
- Inode_ClearCache( disk->CacheID );\r
- memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));\r
- free(disk);\r
-}\r
-\r
-/**\r
- * \fn void Ext2_CloseFile(tVFS_Node *Node)\r
- * \brief Close a file (Remove it from the cache)\r
- */\r
-void Ext2_CloseFile(tVFS_Node *Node)\r
-{\r
- tExt2_Disk *disk = Node->ImplPtr;\r
- Inode_UncacheNode(disk->CacheID, Node->Inode);\r
- return ;\r
-}\r
-\r
-//==================================\r
-//= INTERNAL FUNCTIONS =\r
-//==================================\r
-/**\r
- * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)\r
- * \brief Read an inode into memory\r
- */\r
-int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
-{\r
- int group, subId;\r
- \r
- ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
- \r
- if(InodeId == 0) return 0;\r
- \r
- InodeId --; // Inodes are numbered starting at 1\r
- \r
- group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
- subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
- \r
- LOG("group=%i, subId = %i", group, subId);\r
- \r
- // Read Inode\r
- VFS_ReadAt(Disk->FD,\r
- Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
- sizeof(tExt2_Inode),\r
- Inode);\r
- \r
- LEAVE('i', 1);\r
- return 1;\r
-}\r
-\r
-/**\r
- * \brief Write a modified inode out to disk\r
- */\r
-int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode)\r
-{\r
- int group, subId;\r
- ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);\r
- \r
- if(InodeId == 0) {\r
- LEAVE('i', 0);\r
- return 0;\r
- }\r
- \r
- InodeId --; // Inodes are numbered starting at 1\r
- \r
- group = InodeId / Disk->SuperBlock.s_inodes_per_group;\r
- subId = InodeId % Disk->SuperBlock.s_inodes_per_group;\r
- \r
- LOG("group=%i, subId = %i", group, subId);\r
- \r
- // Write Inode\r
- VFS_WriteAt(Disk->FD,\r
- Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,\r
- sizeof(tExt2_Inode),\r
- Inode\r
- );\r
- \r
- LEAVE('i', 1);\r
- return 1;\r
-}\r
-\r
-/**\r
- * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
- * \brief Get the address of a block from an inode's list\r
- * \param Disk Disk information structure\r
- * \param Blocks Pointer to an inode's block list\r
- * \param BlockNum Block index in list\r
- */\r
-Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)\r
-{\r
- Uint32 *iBlocks;\r
- int dwPerBlock = Disk->BlockSize / 4;\r
- \r
- // Direct Blocks\r
- if(BlockNum < 12)\r
- return (Uint64)Blocks[BlockNum] * Disk->BlockSize;\r
- \r
- // Single Indirect Blocks\r
- iBlocks = malloc( Disk->BlockSize );\r
- VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
- \r
- BlockNum -= 12;\r
- if(BlockNum < dwPerBlock)\r
- {\r
- BlockNum = iBlocks[BlockNum];\r
- free(iBlocks);\r
- return (Uint64)BlockNum * Disk->BlockSize;\r
- }\r
- \r
- BlockNum -= dwPerBlock;\r
- // Double Indirect Blocks\r
- if(BlockNum < dwPerBlock*dwPerBlock)\r
- {\r
- VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
- VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
- BlockNum = iBlocks[BlockNum%dwPerBlock];\r
- free(iBlocks);\r
- return (Uint64)BlockNum * Disk->BlockSize;\r
- }\r
- \r
- BlockNum -= dwPerBlock*dwPerBlock;\r
- // Triple Indirect Blocks\r
- VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
- VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
- VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks);\r
- BlockNum = iBlocks[BlockNum%dwPerBlock];\r
- free(iBlocks);\r
- return (Uint64)BlockNum * Disk->BlockSize;\r
-}\r
-\r
-/**\r
- * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
- * \brief Allocate an inode (from the current group preferably)\r
- * \param Disk EXT2 Disk Information Structure\r
- * \param Parent Inode ID of the parent (used to locate the child nearby)\r
- */\r
-Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
-{\r
-// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
- Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
- return 0;\r
-}\r
-\r
-/**\r
- * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
- * \brief Updates the superblock\r
- */\r
-void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)\r
-{\r
- int bpg = Disk->SuperBlock.s_blocks_per_group;\r
- int ngrp = Disk->SuperBlock.s_blocks_count / bpg;\r
- int i;\r
- \r
- // Update Primary\r
- VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);\r
- \r
- // Secondaries\r
- // at Block Group 1, 3^n, 5^n, 7^n\r
- \r
- // 1\r
- if(ngrp <= 1) return;\r
- VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
- \r
- #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1)\r
- \r
- // Powers of 3\r
- for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 )\r
- VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
- \r
- // Powers of 5\r
- for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 )\r
- VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
- \r
- // Powers of 7\r
- for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 )\r
- VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);\r
-}\r
+++ /dev/null
-/*
- * Acess OS
- * Ext2 Driver Version 1
- */
-/**
- * \file ext2_common.h
- * \brief Second Extended Filesystem Driver
- */
-#ifndef _EXT2_COMMON_H
-#define _EXT2_COMMON_H
-#include <acess.h>
-#include <vfs.h>
-#include "ext2fs.h"
-
-#define EXT2_UPDATE_WRITEBACK 1
-
-// === STRUCTURES ===
-typedef struct {
- int FD;
- int CacheID;
- tVFS_Node RootNode;
-
- tExt2_SuperBlock SuperBlock;
- Uint BlockSize;
-
- int GroupCount;
- tExt2_Group Groups[];
-} tExt2_Disk;
-
-// === FUNCTIONS ===
-// --- Common ---
-extern void Ext2_CloseFile(tVFS_Node *Node);
-extern Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
-extern void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);
-extern int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode);
-extern int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode);
-// --- Dir ---
-extern char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
-extern tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName);
-extern int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
-extern int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name);
-// --- Read ---
-extern Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
-// --- Write ---
-extern Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, const void *buffer);
-
-#endif
+++ /dev/null
-/**\r
- * Acess2\r
- * \file ext2fs.h\r
- * \brief EXT2 Filesystem Driver\r
- */\r
-#ifndef _EXT2FS_H_\r
-#define _EXT2FS_H_\r
-\r
-/**\r
- \name Inode Flag Values\r
- \{\r
-*/\r
-#define EXT2_S_IFMT 0xF000 //!< Format Mask\r
-#define EXT2_S_IFSOCK 0xC000 //!< Socket\r
-#define EXT2_S_IFLNK 0xA000 //!< Symbolic Link\r
-#define EXT2_S_IFREG 0x8000 //!< Regular File\r
-#define EXT2_S_IFBLK 0x6000 //!< Block Device\r
-#define EXT2_S_IFDIR 0x4000 //!< Directory\r
-#define EXT2_S_IFCHR 0x2000 //!< Character Device\r
-#define EXT2_S_IFIFO 0x1000 //!< FIFO\r
-#define EXT2_S_ISUID 0x0800 //!< SUID\r
-#define EXT2_S_ISGID 0x0400 //!< SGID\r
-#define EXT2_S_ISVTX 0x0200 //!< sticky bit\r
-#define EXT2_S_IRWXU 0700 //!< user access rights mask\r
-#define EXT2_S_IRUSR 0400 //!< Owner Read\r
-#define EXT2_S_IWUSR 0200 //!< Owner Write\r
-#define EXT2_S_IXUSR 0100 //!< Owner Execute\r
-#define EXT2_S_IRWXG 0070 //!< Group Access rights mask\r
-#define EXT2_S_IRGRP 0040 //!< Group Read\r
-#define EXT2_S_IWGRP 0020 //!< Group Write\r
-#define EXT2_S_IXGRP 0010 //!< Group Execute\r
-#define EXT2_S_IRWXO 0007 //!< Global Access rights mask\r
-#define EXT2_S_IROTH 0004 //!< Global Read\r
-#define EXT2_S_IWOTH 0002 //!< Global Write\r
-#define EXT2_S_IXOTH 0001 //!< Global Execute\r
-//! \}\r
-\r
-#define EXT2_NAME_LEN 255 //!< Maximum Name Length\r
-\r
-// === TYPEDEFS ===\r
-typedef struct ext2_inode_s tExt2_Inode; //!< Inode Type\r
-typedef struct ext2_super_block_s tExt2_SuperBlock; //!< Superblock Type\r
-typedef struct ext2_group_desc_s tExt2_Group; //!< Group Descriptor Type\r
-typedef struct ext2_dir_entry_s tExt2_DirEnt; //!< Directory Entry Type\r
-\r
-// === STRUCTURES ===\r
-/**\r
- * \brief EXT2 Superblock Structure\r
- */\r
-struct ext2_super_block_s {\r
- Uint32 s_inodes_count; //!< Inodes count\r
- Uint32 s_blocks_count; //!< Blocks count\r
- Uint32 s_r_blocks_count; //!< Reserved blocks count\r
- Uint32 s_free_blocks_count; //!< Free blocks count\r
- \r
- Uint32 s_free_inodes_count; //!< Free inodes count\r
- Uint32 s_first_data_block; //!< First Data Block\r
- Uint32 s_log_block_size; //!< Block size\r
- Sint32 s_log_frag_size; //!< Fragment size\r
- \r
- Uint32 s_blocks_per_group; //!< Number Blocks per group\r
- Uint32 s_frags_per_group; //!< Number Fragments per group\r
- Uint32 s_inodes_per_group; //!< Number Inodes per group\r
- Uint32 s_mtime; //!< Mount time\r
- \r
- Uint32 s_wtime; //!< Write time\r
- Uint16 s_mnt_count; //!< Mount count\r
- Sint16 s_max_mnt_count; //!< Maximal mount count\r
- Uint16 s_magic; //!< Magic signature\r
- Uint16 s_state; //!< File system state\r
- Uint16 s_errors; //!< Behaviour when detecting errors\r
- Uint16 s_pad; //!< Padding\r
- \r
- Uint32 s_lastcheck; //!< time of last check\r
- Uint32 s_checkinterval; //!< max. time between checks\r
- Uint32 s_creator_os; //!< Formatting OS\r
- Uint32 s_rev_level; //!< Revision level\r
- \r
- Uint16 s_def_resuid; //!< Default uid for reserved blocks\r
- Uint16 s_def_resgid; //!< Default gid for reserved blocks\r
- Uint32 s_reserved[235]; //!< Padding to the end of the block\r
-};\r
-\r
-/**\r
- * \struct ext2_inode_s\r
- * \brief EXT2 Inode Definition\r
- */\r
-struct ext2_inode_s {\r
- Uint16 i_mode; //!< File mode\r
- Uint16 i_uid; //!< Owner Uid\r
- Uint32 i_size; //!< Size in bytes\r
- Uint32 i_atime; //!< Access time\r
- Uint32 i_ctime; //!< Creation time\r
- Uint32 i_mtime; //!< Modification time\r
- Uint32 i_dtime; //!< Deletion Time\r
- Uint16 i_gid; //!< Group Id\r
- Uint16 i_links_count; //!< Links count\r
- Uint32 i_blocks; //!< Number of blocks allocated for the file\r
- Uint32 i_flags; //!< File flags\r
- union {\r
- Uint32 linux_reserved1; //!< Linux: Reserved\r
- Uint32 hurd_translator; //!< HURD: Translator\r
- Uint32 masix_reserved1; //!< Masix: Reserved\r
- } osd1; //!< OS dependent 1\r
- Uint32 i_block[15]; //!< Pointers to blocks\r
- Uint32 i_version; //!< File version (for NFS)\r
- Uint32 i_file_acl; //!< File ACL\r
- Uint32 i_dir_acl; //!< Directory ACL / Extended File Size\r
- Uint32 i_faddr; //!< Fragment address\r
- union {\r
- struct {\r
- Uint8 l_i_frag; //!< Fragment number\r
- Uint8 l_i_fsize; //!< Fragment size\r
- Uint16 i_pad1; //!< Padding\r
- Uint32 l_i_reserved2[2]; //!< Reserved\r
- } linux2;\r
- struct {\r
- Uint8 h_i_frag; //!< Fragment number\r
- Uint8 h_i_fsize; //!< Fragment size\r
- Uint16 h_i_mode_high; //!< Mode High Bits\r
- Uint16 h_i_uid_high; //!< UID High Bits\r
- Uint16 h_i_gid_high; //!< GID High Bits\r
- Uint32 h_i_author; //!< Creator ID\r
- } hurd2;\r
- struct {\r
- Uint8 m_i_frag; //!< Fragment number\r
- Uint8 m_i_fsize; //!< Fragment size\r
- Uint16 m_pad1; //!< Padding\r
- Uint32 m_i_reserved2[2]; //!< reserved\r
- } masix2;\r
- } osd2; //!< OS dependent 2\r
-};\r
-\r
-/**\r
- * \struct ext2_group_desc_s\r
- * \brief EXT2 Group Descriptor\r
- */\r
-struct ext2_group_desc_s {\r
- Uint32 bg_block_bitmap; //!< Blocks bitmap block\r
- Uint32 bg_inode_bitmap; //!< Inodes bitmap block\r
- Uint32 bg_inode_table; //!< Inodes table block\r
- Uint16 bg_free_blocks_count; //!< Free blocks count\r
- Uint16 bg_free_inodes_count; //!< Free inodes count\r
- Uint16 bg_used_dirs_count; //!< Directories count\r
- Uint16 bg_pad; //!< Padding\r
- Uint32 bg_reserved[3]; //!< Reserved\r
-};\r
-\r
-/**\r
- * \brief EXT2 Directory Entry\r
- * \note The name may take up less than 255 characters\r
- */\r
-struct ext2_dir_entry_s {\r
- Uint32 inode; //!< Inode number\r
- Uint16 rec_len; //!< Directory entry length\r
- Uint8 name_len; //!< Short Name Length\r
- Uint8 type; //!< File Type (Duplicate of ext2_inode_s.i_mode)\r
- char name[EXT2_NAME_LEN+1]; //!< File name\r
-};\r
-#define EXT2_DIRENT_SIZE (sizeof(struct ext2_dir_entry_s)-EXT2_NAME_LEN+1)\r
-\r
-#endif\r
+++ /dev/null
-/*
- * Acess OS
- * Ext2 Driver Version 1
- */
-/**
- * \file read.c
- * \brief Second Extended Filesystem Driver
- * \todo Implement file full write support
- */
-#define DEBUG 1
-#define VERBOSE 0
-#include "ext2_common.h"
-
-// === PROTOTYPES ===
-Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
-
-// === CODE ===
-/**
- * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from a file
- */
-Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tExt2_Disk *disk = Node->ImplPtr;
- tExt2_Inode inode;
- Uint64 base;
- Uint block;
- Uint64 remLen;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- // Get Inode
- Ext2_int_ReadInode(disk, Node->Inode, &inode);
-
- // Sanity Checks
- if(Offset >= inode.i_size) {
- LEAVE('i', 0);
- return 0;
- }
- if(Offset + Length > inode.i_size)
- Length = inode.i_size - Offset;
-
- block = Offset / disk->BlockSize;
- Offset = Offset / disk->BlockSize;
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
- if(base == 0) {
- Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
- LEAVE('i', 0);
- return 0;
- }
-
- // Read only block
- if(Length <= disk->BlockSize - Offset)
- {
- VFS_ReadAt( disk->FD, base+Offset, Length, Buffer);
- LEAVE('X', Length);
- return Length;
- }
-
- // Read first block
- remLen = Length;
- VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer);
- remLen -= disk->BlockSize - Offset;
- Buffer += disk->BlockSize - Offset;
- block ++;
-
- // Read middle blocks
- while(remLen > disk->BlockSize)
- {
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
- if(base == 0) {
- Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
- LEAVE('i', 0);
- return 0;
- }
- VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer);
- Buffer += disk->BlockSize;
- remLen -= disk->BlockSize;
- block ++;
- }
-
- // Read last block
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
- VFS_ReadAt( disk->FD, base, remLen, Buffer);
-
- LEAVE('X', Length);
- return Length;
-}
+++ /dev/null
-/*
- * Acess OS
- * Ext2 Driver Version 1
- */
-/**
- * \file write.c
- * \brief Second Extended Filesystem Driver
- * \todo Implement file full write support
- */
-#define DEBUG 1
-#define VERBOSE 0
-#include "ext2_common.h"
-
-// === PROTOYPES ===
-Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);
-void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block);
- int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block);
-
-// === CODE ===
-/**
- * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to a file
- */
-Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tExt2_Disk *disk = Node->ImplPtr;
- tExt2_Inode inode;
- Uint64 base;
- Uint64 retLen;
- Uint block;
- Uint64 allocSize;
- int bNewBlocks = 0;
-
- Debug_HexDump("Ext2_Write", Buffer, Length);
-
- Ext2_int_ReadInode(disk, Node->Inode, &inode);
-
- // Get the ammount of space already allocated
- // - Round size up to block size
- // - block size is a power of two, so this will work
- allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
-
- // Are we writing to inside the allocated space?
- if( Offset > allocSize ) return 0;
-
- if( Offset < allocSize )
- {
- // Will we go out of it?
- if(Offset + Length > allocSize) {
- bNewBlocks = 1;
- retLen = allocSize - Offset;
- } else
- retLen = Length;
-
- // Within the allocated space
- block = Offset / disk->BlockSize;
- Offset %= disk->BlockSize;
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-
- // Write only block (if only one)
- if(Offset + retLen <= disk->BlockSize) {
- VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
- if(!bNewBlocks) return Length;
- goto addBlocks; // Ugh! A goto, but it seems unavoidable
- }
-
- // Write First Block
- VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
- Buffer += disk->BlockSize-Offset;
- retLen -= disk->BlockSize-Offset;
- block ++;
-
- // Write middle blocks
- while(retLen > disk->BlockSize)
- {
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
- VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
- Buffer += disk->BlockSize;
- retLen -= disk->BlockSize;
- block ++;
- }
-
- // Write last block
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
- VFS_WriteAt(disk->FD, base, retLen, Buffer);
- if(!bNewBlocks) return Length; // Writing in only allocated space
- }
- else
- base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
-
-addBlocks:
- Log_Notice("EXT2", "File extending is untested");
-
- // Allocate blocks and copy data to them
- retLen = Length - (allocSize-Offset);
- while( retLen > disk->BlockSize )
- {
- // Allocate a block
- block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
- if(!block) return Length - retLen;
- // Add it to this inode
- if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
- Ext2_int_DeallocateBlock(disk, block);
- goto ret;
- }
- // Copy data to the node
- base = block * disk->BlockSize;
- VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
- // Update pointer and size remaining
- inode.i_size += disk->BlockSize;
- Buffer += disk->BlockSize;
- retLen -= disk->BlockSize;
- }
- // Last block :D
- block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
- if(!block) goto ret;
- if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
- Ext2_int_DeallocateBlock(disk, block);
- goto ret;
- }
- base = block * disk->BlockSize;
- VFS_WriteAt(disk->FD, base, retLen, Buffer);
- inode.i_size += retLen;
- retLen = 0;
-
-ret: // Makes sure the changes to the inode are committed
- Ext2_int_WriteInode(disk, Node->Inode, &inode);
- return Length - retLen;
-}
-
-/**
- * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
- * \brief Allocate a block from the best possible location
- * \param Disk EXT2 Disk Information Structure
- * \param PrevBlock Previous block ID in the file
- */
-Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
-{
- int bpg = Disk->SuperBlock.s_blocks_per_group;
- Uint blockgroup = PrevBlock / bpg;
- Uint bitmap[Disk->BlockSize/sizeof(Uint)];
- Uint bitsperblock = 8*Disk->BlockSize;
- int i, j = 0;
- Uint block;
-
- // Are there any free blocks?
- if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
-
- if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
- {
- // Search block group's bitmap
- for(i = 0; i < bpg; i++)
- {
- // Get the block in the bitmap block
- j = i & (bitsperblock-1);
-
- // Read in if needed
- if(j == 0) {
- VFS_ReadAt(
- Disk->FD,
- (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
- Disk->BlockSize,
- bitmap
- );
- }
-
- // Fast Check
- if( bitmap[j/32] == 0xFFFFFFFF ) {
- j = (j + 31) & ~31;
- continue;
- }
-
- // Is the bit set?
- if( bitmap[j/32] & (1 << (j%32)) )
- continue;
-
- // Ooh! We found one
- break;
- }
- if( i < bpg ) {
- Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
- goto checkAll; // Search the entire filesystem for a free block
- // Goto needed for neatness
- }
-
- // Mark as used
- bitmap[j/32] |= (1 << (j%32));
- VFS_WriteAt(
- Disk->FD,
- (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
- Disk->BlockSize,
- bitmap
- );
- block = i;
- Disk->Groups[blockgroup].bg_free_blocks_count --;
- #if EXT2_UPDATE_WRITEBACK
- //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
- #endif
- }
- else
- {
- checkAll:
- Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
- return 0;
- }
-
- // Reduce global count
- Disk->SuperBlock.s_free_blocks_count --;
- #if EXT2_UPDATE_WRITEBACK
- Ext2_int_UpdateSuperblock(Disk);
- #endif
-
- return block;
-}
-
-/**
- * \brief Deallocates a block
- */
-void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
-{
-}
-
-/**
- * \brief Append a block to an inode
- */
-int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
-{
- int nBlocks;
- int dwPerBlock = Disk->BlockSize / 4;
- Uint32 *blocks;
- Uint32 id1, id2;
-
- nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
-
- // Direct Blocks
- if( nBlocks < 12 ) {
- Inode->i_block[nBlocks] = Block;
- return 0;
- }
-
- blocks = malloc( Disk->BlockSize );
- if(!blocks) return 1;
-
- nBlocks -= 12;
- // Single Indirect
- if( nBlocks < dwPerBlock)
- {
- // Allocate/Get Indirect block
- if( nBlocks == 0 ) {
- Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
- if( !Inode->i_block[12] ) {
- free(blocks);
- return 1;
- }
- memset(blocks, 0, Disk->BlockSize);
- }
- else
- VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
-
- blocks[nBlocks] = Block;
-
- VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
- free(blocks);
- return 0;
- }
-
- nBlocks += dwPerBlock;
- // Double Indirect
- if( nBlocks < dwPerBlock*dwPerBlock )
- {
- // Allocate/Get Indirect block
- if( nBlocks == 0 ) {
- Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
- if( !Inode->i_block[13] ) {
- free(blocks);
- return 1;
- }
- memset(blocks, 0, Disk->BlockSize);
- }
- else
- VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
-
- // Allocate / Get Indirect lvl2 Block
- if( nBlocks % dwPerBlock == 0 ) {
- id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
- if( !id1 ) {
- free(blocks);
- return 1;
- }
- blocks[nBlocks/dwPerBlock] = id1;
- // Write back indirect 1 block
- VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
- memset(blocks, 0, Disk->BlockSize);
- }
- else {
- id1 = blocks[nBlocks / dwPerBlock];
- VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
- }
-
- blocks[nBlocks % dwPerBlock] = Block;
-
- VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
- free(blocks);
- return 0;
- }
-
- nBlocks -= dwPerBlock*dwPerBlock;
- // Triple Indirect
- if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
- {
- // Allocate/Get Indirect block
- if( nBlocks == 0 ) {
- Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
- if( !Inode->i_block[14] ) {
- free(blocks);
- return 1;
- }
- memset(blocks, 0, Disk->BlockSize);
- }
- else
- VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
-
- // Allocate / Get Indirect lvl2 Block
- if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
- {
- id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
- if( !id1 ) {
- free(blocks);
- return 1;
- }
- blocks[nBlocks/dwPerBlock] = id1;
- // Write back indirect 1 block
- VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
- memset(blocks, 0, Disk->BlockSize);
- }
- else {
- id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
- VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
- }
-
- // Allocate / Get Indirect Level 3 Block
- if( nBlocks % dwPerBlock == 0 ) {
- id2 = Ext2_int_AllocateBlock(Disk, id1);
- if( !id2 ) {
- free(blocks);
- return 1;
- }
- blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
- // Write back indirect 1 block
- VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
- memset(blocks, 0, Disk->BlockSize);
- }
- else {
- id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
- VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
- }
-
- blocks[nBlocks % dwPerBlock] = Block;
-
- VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
- free(blocks);
- return 0;
- }
-
- Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");
- free(blocks);
- return 1;
-}
+++ /dev/null
-#
-#
-
-OBJ = fat.o
-NAME = FAT
-
--include ../Makefile.tpl
+++ /dev/null
-/*\r
- * Acess 2\r
- * FAT12/16/32 Driver Version (Incl LFN)\r
- * \r
- * NOTE: This driver will only support _reading_ long file names, not\r
- * writing. I don't even know why I'm adding write-support. FAT sucks.\r
- * \r
- * Known Bugs:\r
- * - LFN Is buggy in FAT_ReadDir\r
- * \r
- * Notes:\r
- * - There's hard-coded 512 byte sectors everywhere, that needs to be\r
- * cleaned.\r
- * - Thread safety is out the window with the write and LFN code\r
- */\r
-/**\r
- * \todo Implement changing of the parent directory when a file is written to\r
- * \todo Implement file creation / deletion\r
- */\r
-#define DEBUG 0\r
-#define VERBOSE 1\r
-\r
-#define CACHE_FAT 0 //!< Caches the FAT in memory\r
-#define USE_LFN 1 //!< Enables the use of Long File Names\r
-#define SUPPORT_WRITE 0 //!< Enables write support\r
-\r
-#include <acess.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include "fs_fat.h"\r
-\r
-#define FAT_FLAG_DIRTY 0x10000\r
-#define FAT_FLAG_DELETE 0x20000\r
-\r
-// === TYPES ===\r
-#if USE_LFN\r
-/**\r
- * \brief Long-Filename cache entry\r
- */\r
-typedef struct sFAT_LFNCacheEnt\r
-{\r
- int ID;\r
- // TODO: Handle UTF16 names correctly\r
- char Data[256];\r
-} tFAT_LFNCacheEnt;\r
-/**\r
- * \brief Long-Filename cache\r
- */\r
-typedef struct sFAT_LFNCache\r
-{\r
- int NumEntries;\r
- tFAT_LFNCacheEnt Entries[];\r
-} tFAT_LFNCache;\r
-#endif\r
-\r
-// === PROTOTYPES ===\r
-// --- Driver Core\r
- int FAT_Install(char **Arguments);\r
-tVFS_Node *FAT_InitDevice(const char *device, const char **options);\r
-void FAT_Unmount(tVFS_Node *Node);\r
-// --- Helpers\r
- int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);\r
-Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);\r
-#if SUPPORT_WRITE\r
-Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);\r
-Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);\r
-#endif\r
-void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);\r
-// --- File IO\r
-Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
-#if SUPPORT_WRITE\r
-void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer);\r
-Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
-#endif\r
-// --- Directory IO\r
-char *FAT_ReadDir(tVFS_Node *Node, int ID);\r
-tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);\r
-tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);\r
-#if SUPPORT_WRITE\r
- int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);\r
- int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);\r
-#endif\r
-void FAT_CloseFile(tVFS_Node *node);\r
-\r
-// === Options ===\r
- int giFAT_MaxCachedClusters = 1024*512/4;\r
-\r
-// === SEMI-GLOBALS ===\r
-MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL);\r
-tFAT_VolInfo gFAT_Disks[8];\r
- int giFAT_PartCount = 0;\r
-tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL};\r
-tVFS_NodeType gFAT_DirType = {\r
- .TypeName = "FAT-Dir",\r
- .ReadDir = FAT_ReadDir,\r
- .FindDir = FAT_FindDir,\r
- #if SUPPORT_WRITE\r
- .MkNod = FAT_Mknod,\r
- .Relink = FAT_Relink,\r
- #endif\r
- .Close = FAT_CloseFile\r
- };\r
-tVFS_NodeType gFAT_FileType = {\r
- .TypeName = "FAT-File",\r
- .Read = FAT_Read,\r
- #if SUPPORT_WRITE\r
- .Write = FAT_Write,\r
- #endif\r
- .Close = FAT_CloseFile\r
- };\r
-\r
-// === CODE ===\r
-/**\r
- * \fn int FAT_Install(char **Arguments)\r
- * \brief Install the FAT Driver\r
- */\r
-int FAT_Install(char **Arguments)\r
-{\r
- VFS_AddDriver( &gFAT_FSInfo );\r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \brief Reads the boot sector of a disk and prepares the structures for it\r
- */\r
-tVFS_Node *FAT_InitDevice(const char *Device, const char **Options)\r
-{\r
- fat_bootsect *bs;\r
- int i;\r
- Uint32 FATSz, RootDirSectors, TotSec;\r
- tVFS_Node *node = NULL;\r
- tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];\r
- \r
- // Temporary Pointer\r
- bs = &diskInfo->bootsect;\r
- \r
- // Open device and read boot sector\r
- diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);\r
- if(diskInfo->fileHandle == -1) {\r
- Log_Notice("FAT", "Unable to open device '%s'", Device);\r
- return NULL;\r
- }\r
- \r
- VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);\r
- \r
- if(bs->bps == 0 || bs->spc == 0) {\r
- Log_Notice("FAT", "Error in FAT Boot Sector");\r
- return NULL;\r
- }\r
- \r
- // FAT Type Determining\r
- // - From Microsoft FAT Specifcation\r
- RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;\r
- \r
- if(bs->fatSz16 != 0)\r
- FATSz = bs->fatSz16;\r
- else\r
- FATSz = bs->spec.fat32.fatSz32;\r
- \r
- if(bs->totalSect16 != 0)\r
- TotSec = bs->totalSect16;\r
- else\r
- TotSec = bs->totalSect32;\r
- \r
- diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;\r
- \r
- if(diskInfo->ClusterCount < 4085)\r
- diskInfo->type = FAT12;\r
- else if(diskInfo->ClusterCount < 65525)\r
- diskInfo->type = FAT16;\r
- else\r
- diskInfo->type = FAT32;\r
- \r
- #if VERBOSE\r
- {\r
- char *sFatType, *sSize;\r
- Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024;\r
- \r
- switch(diskInfo->type)\r
- {\r
- case FAT12: sFatType = "FAT12"; break;\r
- case FAT16: sFatType = "FAT16"; break;\r
- case FAT32: sFatType = "FAT32"; break;\r
- default: sFatType = "UNKNOWN"; break;\r
- }\r
- if(iSize <= 2*1024) {\r
- sSize = "KiB";\r
- }\r
- else if(iSize <= 2*1024*1024) {\r
- sSize = "MiB";\r
- iSize >>= 10;\r
- }\r
- else {\r
- sSize = "GiB";\r
- iSize >>= 20;\r
- }\r
- Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize);\r
- }\r
- #endif\r
- \r
- // Get Name\r
- if(diskInfo->type == FAT32) {\r
- for(i=0;i<11;i++)\r
- diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);\r
- }\r
- else {\r
- for(i=0;i<11;i++)\r
- diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);\r
- }\r
- diskInfo->name[11] = '\0';\r
- \r
- // Compute Root directory offset\r
- if(diskInfo->type == FAT32)\r
- diskInfo->rootOffset = bs->spec.fat32.rootClust;\r
- else\r
- diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;\r
- \r
- diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;\r
- \r
- //Allow for Caching the FAT\r
- #if CACHE_FAT\r
- if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- Uint32 Ofs;\r
- diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);\r
- if(diskInfo->FATCache == NULL) {\r
- Log_Warning("FAT", "Heap Exhausted");\r
- return NULL;\r
- }\r
- Ofs = bs->resvSectCount*512;\r
- if(diskInfo->type == FAT12)\r
- {\r
- Uint32 val;\r
- int j;\r
- char buf[1536];\r
- for(i = 0; i < diskInfo->ClusterCount/2; i++) {\r
- j = i & 511; //%512\r
- if( j == 0 ) {\r
- VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);\r
- Ofs += 3*512;\r
- }\r
- val = *((int*)(buf+j*3));\r
- diskInfo->FATCache[i*2] = val & 0xFFF;\r
- diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF;\r
- }\r
- }\r
- else if(diskInfo->type == FAT16)\r
- {\r
- Uint16 buf[256];\r
- for(i=0;i<diskInfo->ClusterCount;i++) {\r
- if( (i & 255) == 0 ) {\r
- VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);\r
- Ofs += 512;\r
- }\r
- diskInfo->FATCache[i] = buf[i&255];\r
- }\r
- }\r
- else if(diskInfo->type == FAT32)\r
- {\r
- Uint32 buf[128];\r
- for(i=0;i<diskInfo->ClusterCount;i++) {\r
- if( (i & 127) == 0 ) {\r
- VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);\r
- Ofs += 512;\r
- }\r
- diskInfo->FATCache[i] = buf[i&127];\r
- }\r
- }\r
- LOG("FAT Fully Cached");\r
- }\r
- #endif /*CACHE_FAT*/\r
- \r
- diskInfo->BytesPerCluster = bs->spc * bs->bps;\r
- \r
- // Initalise inode cache for filesystem\r
- diskInfo->inodeHandle = Inode_GetHandle();\r
- LOG("Inode Cache handle is %i", diskInfo->inodeHandle);\r
- \r
- // == VFS Interface\r
- node = &diskInfo->rootNode;\r
- //node->Size = bs->files_in_root;\r
- node->Size = -1;\r
- node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster\r
- node->ImplPtr = diskInfo; // Disk info pointer\r
- node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag\r
- \r
- node->ReferenceCount = 1;\r
- \r
- node->UID = 0; node->GID = 0;\r
- node->NumACLs = 1;\r
- node->ACLs = &gVFS_ACL_EveryoneRWX;\r
- node->Flags = VFS_FFLAG_DIRECTORY;\r
- node->CTime = node->MTime = node->ATime = now();\r
-\r
- node->Type = &gFAT_DirType; \r
- \r
- giFAT_PartCount ++;\r
- return node;\r
-}\r
-\r
-/**\r
- * \brief Closes a mount and marks it as free\r
- * \param Node Mount Root\r
- * \r
- * \todo Remove FAT Cache\r
- * \todo Clear LFN Cache\r
- * \todo Check that all files are closed and flushed\r
- */\r
-void FAT_Unmount(tVFS_Node *Node)\r
-{\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- \r
- // Close Disk Handle\r
- VFS_Close( disk->fileHandle );\r
- // Clear Node Cache\r
- Inode_ClearCache(disk->inodeHandle);\r
- // Mark as unused\r
- disk->fileHandle = -2;\r
- return;\r
-}\r
-\r
-/**\r
- * \brief Converts an offset in a file into a disk address\r
- * \param Node File (or directory) node\r
- * \param Offset Offset in the file\r
- * \param Addr Return Address\r
- * \param Cluster Set to the current cluster (or the last one if \a Offset\r
- * is past EOC) - Not touched if the node is the root\r
- * directory.\r
- * \return Zero on success, non-zero on error\r
- */\r
-int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)\r
-{\r
- Uint32 cluster;\r
- Uint64 addr;\r
- int skip;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- \r
- ENTER("pNode XOffset", Node, Offset);\r
- \r
- cluster = Node->Inode & 0xFFFFFFF; // Cluster ID\r
- LOG("cluster = 0x%07x", cluster);\r
- \r
- // Do Cluster Skip\r
- // - Pre FAT32 had a reserved area for the root.\r
- if( disk->type == FAT32 || cluster != disk->rootOffset )\r
- {\r
- skip = Offset / disk->BytesPerCluster;\r
- LOG("skip = %i", skip);\r
- // Skip previous clusters\r
- for(; skip-- ; )\r
- {\r
- if(Cluster) *Cluster = cluster;\r
- cluster = FAT_int_GetFatValue(disk, cluster);\r
- // Check for end of cluster chain\r
- if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;}\r
- }\r
- if(Cluster) *Cluster = cluster;\r
- }\r
- else {\r
- // Increment by clusters in offset\r
- cluster += Offset / disk->BytesPerCluster;\r
- }\r
- \r
- LOG("cluster = %08x", cluster);\r
- \r
- // Bounds Checking (Used to spot corruption)\r
- if(cluster > disk->ClusterCount + 2)\r
- {\r
- Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)",\r
- cluster, disk->ClusterCount+2);\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- \r
- // Compute Offsets\r
- // - Pre FAT32 cluster base (in sectors)\r
- if( cluster == disk->rootOffset && disk->type != FAT32 ) {\r
- addr = disk->bootsect.resvSectCount * disk->bootsect.bps;\r
- addr += cluster * disk->BytesPerCluster;\r
- }\r
- else {\r
- addr = disk->firstDataSect * disk->bootsect.bps;\r
- addr += (cluster - 2) * disk->BytesPerCluster;\r
- }\r
- // In-cluster offset\r
- addr += Offset % disk->BytesPerCluster;\r
- \r
- LOG("addr = 0x%08x", addr);\r
- *Addr = addr;\r
- LEAVE('i', 0);\r
- return 0;\r
-}\r
-\r
-/*\r
- * ====================\r
- * FAT Manipulation\r
- * ====================\r
- */\r
-/**\r
- * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
- * \brief Fetches a value from the FAT\r
- */\r
-Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
-{\r
- Uint32 val = 0;\r
- Uint32 ofs;\r
- ENTER("pDisk xCluster", Disk, cluster);\r
- Mutex_Acquire( &Disk->lFAT );\r
- #if CACHE_FAT\r
- if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- val = Disk->FATCache[cluster];\r
- if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;\r
- if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;\r
- if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;\r
- }\r
- else\r
- {\r
- #endif\r
- ofs = Disk->bootsect.resvSectCount*512;\r
- if(Disk->type == FAT12) {\r
- VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);\r
- val = (cluster & 1 ? val>>12 : val & 0xFFF);\r
- if(val == EOC_FAT12) val = -1;\r
- } else if(Disk->type == FAT16) {\r
- VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);\r
- if(val == EOC_FAT16) val = -1;\r
- } else {\r
- VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);\r
- if(val == EOC_FAT32) val = -1;\r
- }\r
- #if CACHE_FAT\r
- }\r
- #endif /*CACHE_FAT*/\r
- Mutex_Release( &Disk->lFAT );\r
- LEAVE('x', val);\r
- return val;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Allocate a new cluster\r
- */\r
-Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)\r
-{\r
- Uint32 ret = Previous;\r
- #if CACHE_FAT\r
- if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- Uint32 eoc;\r
- \r
- LOCK(Disk->lFAT);\r
- for(ret = Previous; ret < Disk->ClusterCount; ret++)\r
- {\r
- if(Disk->FATCache[ret] == 0)\r
- goto append;\r
- }\r
- for(ret = 0; ret < Previous; ret++)\r
- {\r
- if(Disk->FATCache[ret] == 0)\r
- goto append;\r
- }\r
- \r
- RELEASE(Disk->lFAT);\r
- return 0;\r
- \r
- append:\r
- switch(Disk->type)\r
- {\r
- case FAT12: eoc = EOC_FAT12; break;\r
- case FAT16: eoc = EOC_FAT16; break;\r
- case FAT32: eoc = EOC_FAT32; break;\r
- default: return 0;\r
- }\r
- \r
- Disk->FATCache[ret] = eoc;\r
- Disk->FATCache[Previous] = ret;\r
- \r
- RELEASE(Disk->lFAT);\r
- return ret;\r
- }\r
- else\r
- {\r
- #endif\r
- Uint32 val;\r
- Uint32 ofs = Disk->bootsect.resvSectCount*512;\r
- Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT");\r
- return 0;\r
- \r
- switch(Disk->type)\r
- {\r
- case FAT12:\r
- VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
- if( Previous & 1 ) {\r
- val &= 0xFFF000;\r
- val |= ret;\r
- }\r
- else {\r
- val &= 0xFFF;\r
- val |= ret<<12;\r
- }\r
- VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
- \r
- VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
- if( Cluster & 1 ) {\r
- val &= 0xFFF000;\r
- val |= eoc;\r
- }\r
- else {\r
- val &= 0x000FFF;\r
- val |= eoc<<12;\r
- }\r
- VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
- break;\r
- case FAT16:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
- VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);\r
- break;\r
- case FAT32:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
- VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);\r
- break;\r
- }\r
- return ret;\r
- #if CACHE_FAT\r
- }\r
- #endif\r
-}\r
-\r
-/**\r
- * \brief Free's a cluster\r
- * \return The original contents of the cluster\r
- */\r
-Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)\r
-{\r
- Uint32 ret;\r
- #if CACHE_FAT\r
- if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- LOCK(Disk->lFAT);\r
- \r
- ret = Disk->FATCache[Cluster];\r
- Disk->FATCache[Cluster] = 0;\r
- \r
- RELEASE(Disk->lFAT);\r
- }\r
- else\r
- {\r
- #endif\r
- Uint32 val;\r
- Uint32 ofs = Disk->bootsect.resvSectCount*512;\r
- LOCK(Disk->lFAT);\r
- switch(Disk->type)\r
- {\r
- case FAT12:\r
- VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);\r
- if( Cluster & 1 ) {\r
- ret = val & 0xFFF0000;\r
- val &= 0xFFF;\r
- }\r
- else {\r
- ret = val & 0xFFF;\r
- val &= 0xFFF000;\r
- }\r
- VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
- break;\r
- case FAT16:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
- val = 0;\r
- VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
- break;\r
- case FAT32:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
- val = 0;\r
- VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
- break;\r
- }\r
- RELEASE(Disk->lFAT);\r
- #if CACHE_FAT\r
- }\r
- #endif\r
- if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;\r
- if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;\r
- if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;\r
- return ret;\r
-}\r
-#endif\r
-\r
-/*\r
- * ====================\r
- * Cluster IO\r
- * ====================\r
- */\r
-/**\r
- * \brief Read a cluster\r
- * \param Disk Disk (Volume) to read from\r
- * \param Length Length to read\r
- * \param Buffer Destination for read data\r
- */\r
-void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)\r
-{\r
- ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);\r
- VFS_ReadAt(\r
- Disk->fileHandle,\r
- (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
- * Disk->bootsect.bps,\r
- Length,\r
- Buffer\r
- );\r
- LEAVE('-');\r
-}\r
-\r
-/* ====================\r
- * File IO\r
- * ====================\r
- */\r
-/**\r
- * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)\r
- * \brief Reads data from a specified file\r
- */\r
-Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
-{\r
- int preSkip, count;\r
- Uint64 final_bytes;\r
- int i, cluster, pos;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- char tmpBuf[disk->BytesPerCluster];\r
- int bpc = disk->BytesPerCluster;\r
- \r
- ENTER("pNode Xoffset Xlength pbuffer", Node, Offset, Length, Buffer);\r
- \r
- // Sanity Check offset\r
- if(Offset > Node->Size) {\r
- LOG("Seek past EOF (%i > %i)", Offset, Node->Size);\r
- LEAVE('i', 0);\r
- return 0;\r
- }\r
- \r
- // Cluster is stored in the low 32-bits of the Inode field\r
- cluster = Node->Inode & 0xFFFFFFFF;\r
- \r
- // Clamp Size\r
- if(Offset + Length > Node->Size) {\r
- LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli",\r
- Offset, Length, Node->Size, Node->Size - Offset);\r
- Length = Node->Size - Offset;\r
- }\r
- \r
- // Skip previous clusters\r
- preSkip = Offset / bpc;\r
- Offset %= bpc;\r
- LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset);\r
- for(i = preSkip; i--; )\r
- {\r
- cluster = FAT_int_GetFatValue(disk, cluster);\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "Offset is past end of cluster chain mark");\r
- LEAVE('i', 0);\r
- return 0;\r
- }\r
- }\r
-\r
- // Reading from within one cluster\r
- if((int)Offset + (int)Length <= bpc)\r
- {\r
- LOG("single cluster only");\r
- FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);\r
- memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length );\r
- LEAVE('X', Length);\r
- return Length;\r
- }\r
- \r
- // Align read to a cluster\r
- if( Offset > 0 )\r
- {\r
- pos = bpc - Offset;\r
- FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf);\r
- memcpy( Buffer, (void*)( tmpBuf + Offset ), pos );\r
- LOG("pos = %i, Reading the rest of the clusters");\r
- // Get next cluster in the chain\r
- cluster = FAT_int_GetFatValue(disk, cluster);\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "Read past End of Cluster Chain (Align)");\r
- LEAVE('X', pos);\r
- return pos;\r
- }\r
- }\r
- else\r
- pos = 0;\r
-\r
- // Get Count of Clusters to read\r
-// count = DivMod64U(Length - pos, bpc, &final_bytes);\r
- count = (Length - pos) / bpc;\r
- final_bytes = (Length - pos) % bpc;\r
- LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes);\r
- \r
- // Read the rest of the cluster data\r
- for( ; count; count -- )\r
- {\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)");\r
- LEAVE('X', pos);\r
- return pos;\r
- }\r
- // Read cluster\r
- FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos));\r
- pos += bpc;\r
- // Get next cluster in the chain\r
- cluster = FAT_int_GetFatValue(disk, cluster);\r
- }\r
-\r
- if( final_bytes > 0 )\r
- {\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "Read past End of Cluster Chain (Final)");\r
- LEAVE('X', pos);\r
- return pos;\r
- }\r
- // Read final cluster\r
- FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf );\r
- memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos );\r
- }\r
- \r
- #if DEBUG\r
- //Debug_HexDump("FAT_Read", Buffer, Length);\r
- #endif\r
- \r
- LEAVE('X', Length);\r
- return Length;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Write a cluster to disk\r
- */\r
-void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)\r
-{\r
- ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);\r
- VFS_ReadAt(\r
- Disk->fileHandle,\r
- (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
- * Disk->bootsect.bps,\r
- Disk->BytesPerCluster,\r
- Buffer\r
- );\r
- LEAVE('-');\r
-}\r
-\r
-/**\r
- * \brief Write to a file\r
- * \param Node File Node\r
- * \param Offset Offset within file\r
- * \param Length Size of data to write\r
- * \param Buffer Data source\r
- */\r
-Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
-{\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- char tmpBuf[disk->BytesPerCluster];\r
- int remLength = Length;\r
- Uint32 cluster, tmpCluster;\r
- int bNewCluster = 0;\r
- \r
- if(Offset > Node->Size) return 0;\r
- \r
- // Seek Clusters\r
- cluster = Node->Inode & 0xFFFFFFFF;\r
- while( Offset > disk->BytesPerCluster )\r
- {\r
- cluster = FAT_int_GetFatValue( disk, cluster );\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "EOC Unexpectedly Reached");\r
- return 0;\r
- }\r
- Offset -= disk->BytesPerCluster;\r
- }\r
- if( Offset == disk->BytesPerCluster )\r
- {\r
- Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);\r
- if(!tmp) return 0;\r
- cluster = tmp;\r
- Offset -= disk->BytesPerCluster;\r
- }\r
- \r
- if( Offset + Length < disk->BytesPerCluster )\r
- {\r
- char tmpBuf[disk->BytesPerCluster];\r
- \r
- // Read-Modify-Write\r
- FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
- memcpy( tmpBuf + Offset, Buffer, Length );\r
- FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
- \r
- return Length;\r
- }\r
- \r
- // Clean up changes within a cluster\r
- if( Offset )\r
- { \r
- // Read-Modify-Write\r
- FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
- memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );\r
- FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
- \r
- remLength -= disk->BytesPerCluster - Offset;\r
- Buffer += disk->BytesPerCluster - Offset;\r
- \r
- // Get next cluster (allocating if needed)\r
- tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
- if(tmpCluster == -1) {\r
- tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
- if( tmpCluster == 0 ) {\r
- return Length - remLength;\r
- }\r
- }\r
- cluster = tmpCluster;\r
- }\r
- \r
- while( remLength > disk->BytesPerCluster )\r
- {\r
- FAT_int_WriteCluster( disk, cluster, Buffer );\r
- Buffer += disk->BytesPerCluster;\r
- \r
- // Get next cluster (allocating if needed)\r
- tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
- if(tmpCluster == -1) {\r
- bNewCluster = 1;\r
- tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
- if( tmpCluster == 0 ) {\r
- return Length - remLength;\r
- }\r
- }\r
- cluster = tmpCluster;\r
- }\r
- \r
- // Finish off\r
- tmpBuf = malloc( disk->BytesPerCluster );\r
- if( bNewCluster )\r
- memset(tmpBuf, 0, disk->BytesPerCluster);\r
- else\r
- FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
- memcpy( tmpBuf, Buffer, remLength );\r
- FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
- free( tmpBuf );\r
- \r
- return Length;\r
-}\r
-#endif\r
-\r
-/* ====================\r
- * File Names & Nodes\r
- * ====================\r
- */\r
-/**\r
- * \brief Converts a FAT directory entry name into a proper filename\r
- * \param dest Destination array (must be at least 13 bytes in size)\r
- * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT')\r
- */\r
-void FAT_int_ProperFilename(char *dest, const char *src)\r
-{\r
- int inpos, outpos;\r
- \r
- // Name\r
- outpos = 0;\r
- for( inpos = 0; inpos < 8; inpos++ ) {\r
- if(src[inpos] == ' ') break;\r
- dest[outpos++] = src[inpos];\r
- }\r
- inpos = 8;\r
- // Check for empty extensions\r
- if(src[8] != ' ')\r
- {\r
- dest[outpos++] = '.';\r
- for( ; inpos < 11; inpos++) {\r
- if(src[inpos] == ' ') break;\r
- dest[outpos++] = src[inpos];\r
- }\r
- }\r
- dest[outpos++] = '\0';\r
- \r
- //LOG("dest='%s'", dest);\r
-}\r
-\r
-/**\r
- * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
- * \brief Converts either a LFN or a 8.3 Name into a proper name\r
- * \param ft Pointer to the file's entry in the parent directory\r
- * \param LongFileName Long file name pointer\r
- * \return Filename as a heap string\r
- */\r
-char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
-{\r
- char *ret;\r
- ENTER("pft sLongFileName", ft, LongFileName);\r
- //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);\r
- #if USE_LFN\r
- if(LongFileName && LongFileName[0] != '\0')\r
- { \r
- ret = strdup(LongFileName);\r
- }\r
- else\r
- {\r
- #endif\r
- ret = (char*) malloc(13);\r
- if( !ret ) {\r
- Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");\r
- return NULL;\r
- }\r
- FAT_int_ProperFilename(ret, ft->name);\r
- #if USE_LFN\r
- }\r
- #endif\r
- LEAVE('s', ret);\r
- return ret;\r
-}\r
-\r
-/**\r
- * \brief Creates a tVFS_Node structure for a given file entry\r
- * \param Parent Parent directory VFS node\r
- * \param Entry File table entry for the new node\r
- * \param Pos Position in the parent of the new node\r
- */\r
-tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos)\r
-{\r
- tVFS_Node node;\r
- tVFS_Node *ret;\r
- tFAT_VolInfo *disk = Parent->ImplPtr;\r
- \r
- ENTER("pParent pFT", Parent, Entry);\r
- LOG("disk = %p", disk);\r
- \r
- memset(&node, 0, sizeof(tVFS_Node));\r
- \r
- // Set Other Data\r
- // 0-27: Cluster, 32-59: Parent Cluster\r
- node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);\r
- LOG("node.Inode = %llx", node.Inode);\r
- // Position in parent directory\r
- node.ImplInt = Pos & 0xFFFF;\r
- // Disk Pointer\r
- node.ImplPtr = disk;\r
- node.Size = Entry->size;\r
- LOG("Entry->size = %i", Entry->size);\r
- // root:root\r
- node.UID = 0; node.GID = 0;\r
- node.NumACLs = 1;\r
- \r
- node.Flags = 0;\r
- if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;\r
- if(Entry->attrib & ATTR_READONLY) {\r
- node.Flags |= VFS_FFLAG_READONLY;\r
- node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X\r
- }\r
- else {\r
- node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX\r
- }\r
- \r
- // Create timestamps\r
- node.ATime = timestamp(0,0,0,\r
- ((Entry->adate&0x1F) - 1), // Days\r
- ((Entry->adate&0x1E0) - 1), // Months\r
- 1980+((Entry->adate&0xFF00)>>8) // Years\r
- );\r
- \r
- node.CTime = Entry->ctimems * 10; // Miliseconds\r
- node.CTime += timestamp(\r
- ((Entry->ctime&0x1F)<<1), // Seconds\r
- ((Entry->ctime&0x3F0)>>5), // Minutes\r
- ((Entry->ctime&0xF800)>>11), // Hours\r
- ((Entry->cdate&0x1F)-1), // Days\r
- ((Entry->cdate&0x1E0)-1), // Months\r
- 1980+((Entry->cdate&0xFF00)>>8) // Years\r
- );\r
- \r
- node.MTime = timestamp(\r
- ((Entry->mtime&0x1F)<<1), // Seconds\r
- ((Entry->mtime&0x3F0)>>5), // Minutes\r
- ((Entry->mtime&0xF800)>>11), // Hours\r
- ((Entry->mdate&0x1F)-1), // Days\r
- ((Entry->mdate&0x1E0)-1), // Months\r
- 1980+((Entry->mdate&0xFF00)>>8) // Years\r
- );\r
- \r
- // Set pointers\r
- if(node.Flags & VFS_FFLAG_DIRECTORY) {\r
- //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);\r
- node.Type = &gFAT_DirType; \r
- node.Size = -1;\r
- }\r
- else {\r
- node.Type = &gFAT_FileType;\r
- }\r
- \r
- ret = Inode_CacheNode(disk->inodeHandle, &node);\r
- LEAVE('p', ret);\r
- return ret;\r
-}\r
-\r
-/* \r
- * ====================\r
- * Directory IO\r
- * ====================\r
- */\r
-\r
-/**\r
- * \brief Reads a sector from the disk\r
- * \param Node Directory node to read\r
- * \param Sector Sector number in the directory to read\r
- * \param Buffer Destination buffer for the read data\r
- */\r
-int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)\r
-{\r
- Uint64 addr;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- \r
- ENTER("pNode iSector pEntry", Node, Sector, Buffer);\r
- \r
- // Parse address\r
- if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))\r
- {\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- \r
- LOG("addr = 0x%llx", addr);\r
- // Read Sector\r
- if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)\r
- {\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- \r
- LEAVE('i', 0);\r
- return 0;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Writes an entry to the disk\r
- * \todo Support expanding a directory\r
- * \param Node Directory node\r
- * \param ID ID of entry to update\r
- * \param Entry Entry data\r
- * \return Zero on success, non-zero on error\r
- */\r
-int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)\r
-{\r
- Uint64 addr = 0;\r
- int tmp;\r
- Uint32 cluster = 0;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- \r
- ENTER("pNode iID pEntry", Node, ID, Entry);\r
- \r
- tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
- if( tmp )\r
- {\r
- //TODO: Allocate a cluster\r
- cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
- }\r
- \r
-\r
- LOG("addr = 0x%llx", addr);\r
- \r
- // Read Sector\r
- VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data\r
- \r
- LEAVE('i', 0);\r
- return 0;\r
-}\r
-#endif\r
-\r
-#if USE_LFN \r
-/**\r
- * \fn char *FAT_int_GetLFN(tVFS_Node *node)\r
- * \brief Return pointer to LFN cache entry\r
- * \param Node Directory node\r
- * \param ID ID of the short name\r
- * \return Pointer to the LFN cache entry\r
- */\r
-char *FAT_int_GetLFN(tVFS_Node *Node, int ID)\r
-{\r
- tFAT_LFNCache *cache;\r
- int i, firstFree;\r
- \r
- Mutex_Acquire( &Node->Lock );\r
- \r
- // TODO: Thread Safety (Lock things)\r
- cache = Node->Data;\r
- \r
- // Create a cache if it isn't there\r
- if(!cache) {\r
- cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );\r
- cache->NumEntries = 1;\r
- cache->Entries[0].ID = ID;\r
- cache->Entries[0].Data[0] = '\0';\r
- Mutex_Release( &Node->Lock );\r
- //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);\r
- return cache->Entries[0].Data;\r
- }\r
- \r
- // Scan for this entry\r
- firstFree = -1;\r
- for( i = 0; i < cache->NumEntries; i++ )\r
- {\r
- if( cache->Entries[i].ID == ID ) {\r
- Mutex_Release( &Node->Lock );\r
- //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);\r
- return cache->Entries[i].Data;\r
- }\r
- if( cache->Entries[i].ID == -1 && firstFree == -1 )\r
- firstFree = i;\r
- }\r
- \r
- if(firstFree == -1) {\r
- // Use `i` for temp length\r
- i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);\r
- Node->Data = realloc( Node->Data, i );\r
- if( !Node->Data ) {\r
- Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);\r
- Mutex_Release( &Node->Lock );\r
- return NULL;\r
- }\r
- //Log_Debug("FAT", "Realloc (%i)\n", i);\r
- cache = Node->Data;\r
- i = cache->NumEntries;\r
- cache->NumEntries ++;\r
- }\r
- else {\r
- i = firstFree;\r
- }\r
- \r
- // Create new entry\r
- cache->Entries[ i ].ID = ID;\r
- cache->Entries[ i ].Data[0] = '\0';\r
- \r
- Mutex_Release( &Node->Lock );\r
- //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);\r
- return cache->Entries[ i ].Data;\r
-}\r
-\r
-/**\r
- * \fn void FAT_int_DelLFN(tVFS_Node *node)\r
- * \brief Delete a LFN cache entry\r
- * \param Node Directory node\r
- * \param ID File Entry ID\r
- */\r
-void FAT_int_DelLFN(tVFS_Node *Node, int ID)\r
-{\r
- tFAT_LFNCache *cache = Node->Data;\r
- int i;\r
- \r
- // Fast return\r
- if(!cache) return;\r
- \r
- // Scan for a current entry\r
- for( i = 0; i < cache->NumEntries; i++ )\r
- {\r
- if( cache->Entries[i].ID == ID )\r
- cache->Entries[i].ID = -1;\r
- }\r
- return ;\r
-}\r
-#endif\r
-\r
-/**\r
- * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
- * \param Node Node structure of directory\r
- * \param ID Directory position\r
- * \return Filename as a heap string, NULL or VFS_SKIP\r
- */\r
-char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
-{\r
- fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector\r
- int a = 0;\r
- char *ret;\r
- #if USE_LFN\r
- char *lfn = NULL;\r
- #endif\r
- \r
- ENTER("pNode iID", Node, ID);\r
- \r
- if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))\r
- {\r
- LOG("End of chain, end of dir");\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- // Offset in sector\r
- a = ID % 16;\r
-\r
- LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);\r
- \r
- // Check if this is the last entry\r
- if( fileinfo[a].name[0] == '\0' ) {\r
- Node->Size = ID;\r
- LOG("End of list");\r
- LEAVE('n');\r
- return NULL; // break\r
- }\r
- \r
- // Check for empty entry\r
- if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {\r
- LOG("Empty Entry");\r
- #if 0 // Stop on empty entry?\r
- LEAVE('n');\r
- return NULL; // Stop\r
- #else\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP; // Skip\r
- #endif\r
- }\r
- \r
- #if USE_LFN\r
- // Get Long File Name Cache\r
- if(fileinfo[a].attrib == ATTR_LFN)\r
- {\r
- fat_longfilename *lfnInfo;\r
- \r
- lfnInfo = (fat_longfilename *) &fileinfo[a];\r
- \r
- // Get cache for corresponding file\r
- // > ID + Index gets the corresponding short node\r
- lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );\r
- \r
- // Bit 6 indicates the start of an entry\r
- if(lfnInfo->id & 0x40) memset(lfn, 0, 256);\r
- \r
- a = ((lfnInfo->id & 0x3F) - 1) * 13;\r
- //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a);\r
- \r
- // Sanity Check (FAT implementations should not allow >255 character names)\r
- if(a > 255) return VFS_SKIP;\r
- \r
- // Append new bytes\r
- lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1];\r
- lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3];\r
- lfn[a+ 4] = lfnInfo->name1[4]; \r
- lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1];\r
- lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3];\r
- lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5];\r
- lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1];\r
- LOG("lfn = '%s'", lfn);\r
- //Log_Debug("FAT", "lfn = '%s'", lfn);\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- #endif\r
- \r
- // Check if it is a volume entry\r
- if(fileinfo[a].attrib & 0x08) {\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- // Ignore .\r
- if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- // and ..\r
- if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- \r
- LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",\r
- fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],\r
- fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],\r
- fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );\r
- \r
- #if USE_LFN\r
- lfn = FAT_int_GetLFN(Node, ID);\r
- //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);\r
- ret = FAT_int_CreateName(&fileinfo[a], lfn);\r
- #else\r
- ret = FAT_int_CreateName(&fileinfo[a], NULL);\r
- #endif\r
- \r
- LEAVE('s', ret);\r
- return ret;\r
-}\r
-\r
-/**\r
- * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)\r
- * \brief Finds an entry in the current directory\r
- */\r
-tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)\r
-{\r
- fat_filetable fileinfo[16];\r
- char tmpName[13];\r
- #if USE_LFN\r
- fat_longfilename *lfnInfo;\r
- char lfn[256];\r
- int lfnPos=255, lfnId = -1;\r
- #endif\r
- int i;\r
- tVFS_Node *tmpNode;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- Uint32 cluster;\r
- \r
- ENTER("pNode sname", Node, Name); \r
-\r
- // Fast Returns\r
- if(!Name || Name[0] == '\0') {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- for( i = 0; ; i++ )\r
- {\r
- if((i & 0xF) == 0) {\r
- if(FAT_int_ReadDirSector(Node, i/16, fileinfo))\r
- {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- }\r
- \r
- //Check if the files are free\r
- if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker\r
- if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry\r
- \r
- \r
- #if USE_LFN\r
- // Long File Name Entry\r
- if(fileinfo[i & 0xF].attrib == ATTR_LFN)\r
- {\r
- lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];\r
- if(lfnInfo->id & 0x40) {\r
- memset(lfn, 0, 256);\r
- lfnPos = (lfnInfo->id & 0x3F) * 13 - 1;\r
- }\r
- // Sanity check the position so we don't overflow\r
- if( lfnPos < 12 )\r
- continue ;\r
- lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0];\r
- lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4];\r
- lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2];\r
- lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0];\r
- lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3];\r
- lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1];\r
- lfn[lfnPos--] = lfnInfo->name1[0];\r
- if((lfnInfo->id&0x3F) == 1)\r
- {\r
- lfnId = i+1;\r
- }\r
- }\r
- else\r
- {\r
- // Remove LFN if it does not apply\r
- if(lfnId != i) lfn[0] = '\0';\r
- #else\r
- if(fileinfo[i&0xF].attrib == ATTR_LFN) continue;\r
- #endif\r
- // Get Real Filename\r
- FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);\r
- LOG("tmpName = '%s'", tmpName);\r
- \r
- // Only the long name is case sensitive, 8.3 is not\r
- #if USE_LFN\r
- if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0)\r
- #else\r
- if(strucmp(tmpName, Name) == 0)\r
- #endif\r
- {\r
- cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);\r
- tmpNode = Inode_GetCache(disk->inodeHandle, cluster);\r
- if(tmpNode == NULL) // Node is not cached\r
- {\r
- tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i);\r
- }\r
- LEAVE('p', tmpNode);\r
- return tmpNode;\r
- }\r
- #if USE_LFN\r
- }\r
- #endif\r
- }\r
- \r
- LEAVE('n');\r
- return NULL;\r
-}\r
-\r
-tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)\r
-{\r
- tFAT_VolInfo *disk = Root->ImplPtr;\r
- int ents_per_sector = 512 / sizeof(fat_filetable); \r
- fat_filetable fileinfo[ents_per_sector];\r
- int sector = 0, i;\r
- tVFS_Node stub_node;\r
-\r
- ENTER("pRoot XInode", Root, Inode);\r
-\r
- stub_node.ImplPtr = disk;\r
- stub_node.Size = -1;\r
- stub_node.Inode = Inode >> 32;\r
-\r
- for( i = 0; ; i ++ )\r
- {\r
- if( i == 0 || i == ents_per_sector )\r
- {\r
- if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo))\r
- {\r
- LOG("ReadDirSector failed");\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- i = 0;\r
- sector ++;\r
- }\r
- \r
- // Check for free/end of list\r
- if(fileinfo[i].name[0] == '\0') break; // End of List marker\r
- if(fileinfo[i].name[0] == '\xE5') continue; // Free entry\r
- \r
- if(fileinfo[i].attrib == ATTR_LFN) continue;\r
-\r
- LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster);\r
- #if DEBUG\r
- {\r
- char tmpName[13];\r
- FAT_int_ProperFilename(tmpName, fileinfo[i].name);\r
- LOG("tmpName = '%s'", tmpName);\r
- }\r
- #endif\r
- \r
- \r
- if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue;\r
- if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue;\r
-\r
- LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i));\r
- }\r
- LOG("sector = %i, i = %i", sector, i);\r
- LEAVE('n');\r
- return NULL;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
- * \brief Create a new node\r
- */\r
-int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
-{\r
- return 0;\r
-}\r
-\r
-/**\r
- * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
- * \brief Rename / Delete a file\r
- */\r
-int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
-{\r
- tVFS_Node *child;\r
- fat_filetable ft = {0};\r
- int ret;\r
- \r
- child = FAT_FindDir(Node, OldName);\r
- if(!child) return ENOTFOUND;\r
- \r
- // Delete?\r
- if( NewName == NULL )\r
- {\r
- child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close\r
- \r
- // Delete from the directory\r
- ft.name[0] = '\xE9';\r
- FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft);\r
- \r
- // Return success\r
- ret = EOK;\r
- }\r
- // Rename\r
- else\r
- {\r
- Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')",\r
- Node, OldName, NewName);\r
- ret = ENOTIMPL;\r
- }\r
- \r
- // Close child\r
- child->Close( child );\r
- return ret;\r
-}\r
-#endif\r
-\r
-/**\r
- * \fn void FAT_CloseFile(tVFS_Node *Node)\r
- * \brief Close an open file\r
- */\r
-void FAT_CloseFile(tVFS_Node *Node)\r
-{\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- if(Node == NULL) return ;\r
- \r
- #if SUPPORT_WRITE\r
- // Update the node if it's dirty (don't bother if it's marked for\r
- // deletion)\r
- if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )\r
- {\r
- tFAT_VolInfo buf[16];\r
- tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ];\r
- \r
- FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf);\r
- ft->size = Node->Size;\r
- // TODO: update adate, mtime, mdate\r
- FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft);\r
- \r
- Node->ImplInt &= ~FAT_FLAG_DIRTY;\r
- }\r
- #endif\r
- \r
- // TODO: Make this more thread safe somehow, probably by moving the\r
- // Inode_UncacheNode higher up and saving the cluster value somewhere\r
- if( Node->ReferenceCount == 1 )\r
- { \r
- #if SUPPORT_WRITE\r
- // Delete File\r
- if( Node->ImplInt & FAT_FLAG_DELETE ) {\r
- // Since the node is marked, we only need to remove it's data\r
- Uint32 cluster = Node->Inode & 0xFFFFFFFF;\r
- while( cluster != -1 )\r
- cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster);\r
- }\r
- #endif\r
- }\r
- \r
- Inode_UncacheNode(disk->inodeHandle, Node->Inode);\r
- return ;\r
-}\r
+++ /dev/null
-/*\r
- * Acess2\r
- * FAT12/16/32 Driver\r
- * vfs/fs/fs_fat.h\r
- */\r
-#ifndef _FS_FAT_H_\r
-#define _FS_FAT_H_\r
-\r
-// === On Disk Structures ===\r
-/**\r
- * \struct fat_bootsect_s\r
- * \brief Bootsector format\r
- */\r
-struct fat_bootsect_s\r
-{\r
- Uint8 jmp[3]; //!< Jump Instruction\r
- char oemname[8]; //!< OEM Name. Typically MSDOS1.1\r
- Uint16 bps; //!< Bytes per Sector. Assumed to be 512\r
- Uint8 spc; //!< Sectors per Cluster\r
- Uint16 resvSectCount; //!< Number of reserved sectors at beginning of volume\r
- // +0x10\r
- Uint8 fatCount; //!< Number of copies of the FAT\r
- Uint16 files_in_root; //!< Count of files in the root directory\r
- Uint16 totalSect16; //!< Total sector count (FAT12/16)\r
- Uint8 mediaDesc; //!< Media Desctiptor\r
- Uint16 fatSz16; //!< FAT Size (FAT12/16)\r
- // +0x18\r
- Uint16 spt; //!< Sectors per track. Ignored (Acess uses LBA)\r
- Uint16 heads; //!< Heads. Ignored (Acess uses LBA)\r
- Uint32 hiddenCount; //!< ???\r
- Uint32 totalSect32; //!< Total sector count (FAT32)\r
- union {\r
- struct {\r
- Uint8 drvNum; //!< Drive Number. BIOS Drive ID (E.g. 0x80)\r
- Uint8 resv; //!< Reserved byte\r
- Uint8 bootSig; //!< Boot Signature. ???\r
- Uint32 volId; //!< Volume ID\r
- char label[11]; //!< Disk Label\r
- char fsType[8]; //!< FS Type. ???\r
- } __attribute__((packed)) fat16; //!< FAT16 Specific information\r
- struct {\r
- Uint32 fatSz32; //!< 32-Bit FAT Size\r
- Uint16 extFlags; //!< Extended flags\r
- Uint16 fsVer; //!< Filesystem Version\r
- Uint32 rootClust; //!< Root Cluster ID\r
- Uint16 fsInfo; //!< FS Info. ???\r
- Uint16 backupBS; //!< Backup Bootsector Sector Offset\r
- char resv[12]; //!< Reserved Data\r
- Uint8 drvNum; //!< Drive Number\r
- char resv2; //!< Reserved Data\r
- Uint8 bootSig; //!< Boot Signature. ???\r
- Uint32 volId; //!< Volume ID\r
- char label[11]; //!< Disk Label\r
- char fsType[8]; //!< Filesystem Type. ???\r
- } __attribute__((packed)) fat32; //!< FAT32 Specific Information\r
- }__attribute__((packed)) spec; //!< Non Shared Data\r
- char pad[512-90]; //!< Bootsector Data (Code/Boot Signature 0xAA55)\r
-} __attribute__((packed));\r
-\r
-/**\r
- \struct fat_filetable_s\r
- \brief Format of a 8.3 file entry on disk\r
-*/\r
-struct fat_filetable_s {\r
- char name[11]; //!< 8.3 Name\r
- Uint8 attrib; //!< File Attributes.\r
- Uint8 ntres; //!< Reserved for NT - Set to 0\r
- Uint8 ctimems; //!< 10ths of a second ranging from 0-199 (2 seconds)\r
- Uint16 ctime; //!< Creation Time\r
- Uint16 cdate; //!< Creation Date\r
- Uint16 adate; //!< Accessed Date. No Time feild though\r
- Uint16 clusterHi; //!< High Cluster. 0 for FAT12 and FAT16\r
- Uint16 mtime; //!< Last Modified Time\r
- Uint16 mdate; //!< Last Modified Date\r
- Uint16 cluster; //!< Low Word of First cluster\r
- Uint32 size; //!< Size of file\r
-} __attribute__((packed));\r
-\r
-/**\r
- * \struct fat_longfilename_s\r
- * \brief Format of a long file name entry on disk\r
- */\r
-struct fat_longfilename_s {\r
- Uint8 id; //!< ID of entry. Bit 6 is set for last entry\r
- Uint16 name1[5]; //!< 5 characters of name\r
- Uint8 attrib; //!< Attributes. Must be ATTR_LFN\r
- Uint8 type; //!< Type. ???\r
- Uint8 checksum; //!< Checksum\r
- Uint16 name2[6]; //!< 6 characters of name\r
- Uint16 firstCluster; //!< Used for non LFN compatability. Set to 0\r
- Uint16 name3[2]; //!< Last 2 characters of name\r
-} __attribute__((packed));\r
-\r
-/**\r
- * \name File Attributes\r
- * \brief Flag values for ::fat_filetable_s.attrib\r
- * \{\r
- */\r
-#define ATTR_READONLY 0x01 //!< Read-only file\r
-#define ATTR_HIDDEN 0x02 //!< Hidden File\r
-#define ATTR_SYSTEM 0x04 //!< System File\r
-#define ATTR_VOLUMEID 0x08 //!< Volume ID (Deprecated)\r
-#define ATTR_DIRECTORY 0x10 //!< Directory\r
-/**\r
- * \brief File needs archiving\r
- * \note User set flag, no significance to the FS driver\r
- */\r
-#define ATTR_ARCHIVE 0x20\r
-/**\r
- * \brief Meta Attribute \r
- * \r
- * If ::fat_filetable_s.attrib equals ATTR_LFN the file is a LFN entry\r
- */\r
-#define ATTR_LFN (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUMEID)\r
-/**\r
- * \}\r
- */\r
-\r
-/**\r
- * \brief Internal IDs for FAT types\r
- */\r
-enum eFatType\r
-{\r
- FAT12, //!< FAT12 Volume\r
- FAT16, //!< FAT16 Volume\r
- FAT32, //!< FAT32 Volume\r
-};\r
-\r
-/**\r
- * \name End of Cluster marks\r
- * \brief FAT values that indicate the end of a cluster chain in\r
- * different versions.\r
- * \{\r
- */\r
-#define EOC_FAT12 0x0FFF //!< FAT-12 Mark\r
-#define EOC_FAT16 0xFFFF //!< FAT-16 Mark\r
-#define EOC_FAT32 0x00FFFFFF //!< FAT-32 Mark\r
-/**\r
- * \}\r
- */\r
-\r
-typedef struct fat_bootsect_s fat_bootsect;\r
-typedef struct fat_filetable_s fat_filetable;\r
-typedef struct fat_longfilename_s fat_longfilename;\r
-\r
-// === Memory Structures ===\r
-/**\r
- * \struct drv_fat_volinfo_s\r
- * \brief Representation of a volume in memory\r
- */\r
-struct drv_fat_volinfo_s\r
-{\r
- int fileHandle; //!< File Handle\r
- int type; //!< FAT Type. See eFatType\r
- char name[12]; //!< Volume Name (With NULL Terminator)\r
- tMutex lFAT; //!< Lock to prevent double-writing to the FAT\r
- Uint32 firstDataSect; //!< First data sector\r
- Uint32 rootOffset; //!< Root Offset (clusters)\r
- Uint32 ClusterCount; //!< Total Cluster Count\r
- fat_bootsect bootsect; //!< Boot Sector\r
- tVFS_Node rootNode; //!< Root Node\r
- int BytesPerCluster;\r
- int inodeHandle; //!< Inode Cache Handle\r
- #if CACHE_FAT\r
- Uint32 *FATCache; //!< FAT Cache\r
- #endif\r
-};\r
-\r
-typedef struct drv_fat_volinfo_s tFAT_VolInfo;\r
-\r
-#endif\r
+++ /dev/null
-files.*.c*
+++ /dev/null
-<?php
-$lGenDate = date("Y-m-d H:i");
-$gOutput = <<<EOF
-/*
- * Acess2 InitRD
- * InitRD Data
- * Generated $lGenDate
- */
-#include "initrd.h"
-
-EOF;
-
-$ACESSDIR = getenv("ACESSDIR");
-$ARCH = getenv("ARCH");
-
-$gInputFile = $argv[1];
-$gOutputFile = $argv[2];
-$gOutputLDOptsFile = $argv[3];
-$gDepFile = ($argc > 4 ? $argv[4] : false);
-
-$gDependencies = array();
-
-$lines = file($argv[1]);
-
-$lDepth = 0;
-$lTree = array();
-$lStack = array( array("",array()) );
-foreach($lines as $line)
-{
- $line = trim($line);
- // Directory
- if(preg_match('/^Dir\s+"([^"]+)"\s+{$/', $line, $matches))
- {
- $new = array($matches[1], array());
- array_push($lStack, $new);
- $lDepth ++;
- continue;
- }
- // End of a block
- if($line == "}")
- {
- $lDepth --;
- $lStack[$lDepth][1][] = array_pop($lStack);
- continue;
- }
- // File
- if(preg_match('/^File\s+"([^"]+)"\s+"([^"]+)"$/', $line, $matches))
- {
- $lStack[$lDepth][1][] = array($matches[1], $matches[2]);
- continue;
- }
- echo "ERROR: $line\n";
- exit(0);
-}
-
-function hd($fp)
-{
- //return "0x".str_pad( dechex(ord(fgetc($fp))), 8, "0", STR_PAD_LEFT );
- $val = unpack("I", fread($fp, 4));
- //print_r($val); exit -1;
- return "0x".dechex($val[1]);
-}
-
-function hd8($fp)
-{
- return "0x".str_pad( dechex(ord(fgetc($fp))), 2, "0", STR_PAD_LEFT );
-}
-
-$inode = 0;
-$gSymFiles = array();
-function ProcessFolder($prefix, $items)
-{
- global $gOutput, $gDependencies;
- global $ACESSDIR, $ARCH;
- global $inode;
- global $gSymFiles;
- foreach($items as $i=>$item)
- {
- $inode ++;
- if(is_array($item[1]))
- {
- ProcessFolder("{$prefix}_{$i}", $item[1]);
-
- $gOutput .= "tInitRD_File {$prefix}_{$i}_entries[] = {\n";
- foreach($item[1] as $j=>$child)
- {
- if($j) $gOutput .= ",\n";
- $gOutput .= "\t{\"".addslashes($child[0])."\",&{$prefix}_{$i}_{$j}}";
- }
- $gOutput .= "\n};\n";
-
- $size = count($item[1]);
- $gOutput .= <<<EOF
-tVFS_Node {$prefix}_{$i} = {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Size = $size,
- .Inode = {$inode},
- .ImplPtr = {$prefix}_{$i}_entries,
- .Type = &gInitRD_DirType
-};
-
-EOF;
- }
- else
- {
- $path = $item[1];
-
- // Parse path components
- $path = str_replace("__BIN__", "$ACESSDIR/Usermode/Output/$ARCH", $path);
- $path = str_replace("__FS__", "$ACESSDIR/Usermode/Filesystem", $path);
- $path = str_replace("__SRC__", "$ACESSDIR", $path);
- echo $path,"\n";
- // ---
-
- $gDependencies[] = $path;
-
- if(!file_exists($path)) {
- echo "ERROR: '{$path}' does not exist\n",
- exit(1);
- }
- $size = filesize($path);
-
-/*
- $_sym = $prefix."_".$i."_data";
- $fp = fopen($path, "rb");
-
- $gOutput .= "Uint8 $_sym[] = {\n";
- for( $j = 0; $j + 16 < $size; $j += 16 ) {
- $gOutput .= "\t";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",";
- $gOutput .= hd8($fp).",".hd8($fp).",\n";
- }
- $gOutput .= "\t";
- for( ; $j < $size; $j ++ ) {
- if( $j & 15 ) $gOutput .= ",";
- $gOutput .= hd8($fp);
- }
- fclose($fp);
- $gOutput .= "\n};\n";
-*/
-
-//*
- $_sym = "_binary_".str_replace(array("/","-","."), "_", $path)."_start";
- $gOutput .= "extern Uint8 {$_sym}[];";
- $gSymFiles[] = $path;
-//*/
- $gOutput .= <<<EOF
-tVFS_Node {$prefix}_{$i} = {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = 0,
- .Size = $size,
- .Inode = {$inode},
- .ImplPtr = $_sym,
- .Type = &gInitRD_FileType
-};
-
-EOF;
- }
- }
-}
-
-//print_r($lStack);
-//exit(1);
-
-ProcessFolder("gInitRD_Files", $lStack[0][1]);
-
-$gOutput .= "tInitRD_File gInitRD_Root_Files[] = {\n";
-foreach($lStack[0][1] as $j=>$child)
-{
- if($j) $gOutput .= ",\n";
- $gOutput .= "\t{\"".addslashes($child[0])."\",&gInitRD_Files_{$j}}";
-}
-$gOutput .= "\n};\n";
-$nRootFiles = count($lStack[0][1]);
-$gOutput .= <<<EOF
-tVFS_Node gInitRD_RootNode = {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Size = $nRootFiles,
- .ImplPtr = gInitRD_Root_Files,
- .Type = &gInitRD_DirType
-};
-EOF;
-
-$gOutput .= <<<EOF
-
-tVFS_Node * const gInitRD_FileList[] = {
-&gInitRD_RootNode
-EOF;
-
-function PutNodePointers($prefix, $items)
-{
- global $gOutput;
- foreach($items as $i=>$item)
- {
- $gOutput .= ",&{$prefix}_{$i}";
- if(is_array($item[1]))
- {
- PutNodePointers("{$prefix}_{$i}", $item[1]);
- }
- }
-}
-
-PutNodePointers("gInitRD_Files", $lStack[0][1]);
-
-$gOutput .= <<<EOF
-};
-const int giInitRD_NumFiles = sizeof(gInitRD_FileList)/sizeof(gInitRD_FileList[0]);
-
-EOF;
-
-
-$fp = fopen($gOutputFile, "w");
-fputs($fp, $gOutput);
-fclose($fp);
-
-// - Create options call
-$fp = fopen($gOutputLDOptsFile, "w");
-fputs($fp, "--format binary\n");
-foreach($gSymFiles as $sym=>$file)
-{
- fputs($fp, "$file\n");
-// fputs($fp, "--defsym $sym=_binary_".$sym_filename."_start\n");
-}
-fclose($fp);
-
-if($gDepFile !== false)
-{
- $fp = fopen($gDepFile, "w");
- $line = $gOutputFile.":\t".implode(" ", $gDependencies);
- fputs($fp, $line);
- fclose($fp);
-}
-
-?>
+++ /dev/null
-# InitRD Filesystem Driver
-#
-
-OBJ = main.o files.$(ARCH).o
-EXTRA = files.c
-NAME = InitRD
-EXTRA = files.$(ARCH).c files.$(ARCH).c.dep files.$(ARCH).c.ldopts
-LDFLAGS += @files.$(ARCH).c.ldopts
-
--include ../Makefile.tpl
-
-
-files.$(ARCH).c: GenerateInitRD.php files.lst
-
--include files.$(ARCH).c.dep
+++ /dev/null
-Dir "SBin" {
- File "init" "__BIN__/SBin/init"
- File "login" "__BIN__/SBin/login"
-}
-Dir "Bin" {
- File "CLIShell" "__BIN__/Bin/CLIShell"
- File "ls" "__BIN__/Bin/ls"
- File "cat" "__BIN__/Bin/cat"
- File "mount" "__BIN__/Bin/mount"
- File "ifconfig" "__BIN__/Bin/ifconfig"
- File "telnet" "__BIN__/Bin/telnet"
- File "irc" "__BIN__/Bin/irc"
-}
-Dir "Libs" {
- File "ld-acess.so" "__BIN__/Libs/ld-acess.so"
- File "libld-acess.so" "__BIN__/Libs/libld-acess.so"
- File "libc.so" "__BIN__/Libs/libc.so"
- File "libgcc.so" "__BIN__/Libs/libgcc.so"
- File "libreadline.so" "__BIN__/Libs/libreadline.so"
- File "libnet.so" "__BIN__/Libs/libnet.so"
- File "liburi.so" "__BIN__/Libs/liburi.so"
- File "libimage_sif.so" "__BIN__/Libs/libimage_sif.so"
- File "libaxwin3.so" "__BIN__/Libs/libaxwin3.so"
-}
-Dir "Conf" {
- File "BootConf.cfg" "__FS__/Conf/BootConf.cfg"
-}
-Dir "Apps" {
- Dir "AxWin" {
- Dir "3.0" {
- File "AxWinWM" "__BIN__/Apps/AxWin/3.0/AxWinWM"
- File "AxWinUI" "__BIN__/Apps/AxWin/3.0/AxWinUI"
- File "AcessLogoSmall.sif" "__SRC__/Usermode/Applications/axwin3_src/AcessLogoSmall.sif"
- }
- }
-}
+++ /dev/null
-/*
- */
-#ifndef _INITRD_H_
-#define _INITRD_H_
-
-#include <acess.h>
-#include <vfs.h>
-
-typedef struct sInitRD_File
-{
- char *Name;
- tVFS_Node *Node;
-} tInitRD_File;
-
-
-// === Functions ===
-extern Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer);
-extern char *InitRD_ReadDir(tVFS_Node *Node, int ID);
-extern tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name);
-
-// === Globals ===
-tVFS_NodeType gInitRD_DirType;
-tVFS_NodeType gInitRD_FileType;
-
-#endif
+++ /dev/null
-/*
- * Acess OS
- * InitRD Driver Version 1
- */
-#include "initrd.h"
-#include <modules.h>
-
-#define DUMP_ON_MOUNT 1
-
-// === IMPORTS ==
-extern tVFS_Node gInitRD_RootNode;
-extern const int giInitRD_NumFiles;
-extern tVFS_Node * const gInitRD_FileList[];
-
-// === PROTOTYPES ===
- int InitRD_Install(char **Arguments);
-tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments);
-void InitRD_Unmount(tVFS_Node *Node);
-tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
-Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer);
-char *InitRD_ReadDir(tVFS_Node *Node, int ID);
-tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name);
-void InitRD_DumpDir(tVFS_Node *Node, int Indent);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x0A, FS_InitRD, InitRD_Install, NULL);
-tVFS_Driver gInitRD_FSInfo = {
- "initrd", 0, InitRD_InitDevice, InitRD_Unmount, InitRD_GetNodeFromINode
- };
-tVFS_NodeType gInitRD_DirType = {
- .ReadDir = InitRD_ReadFile,
- .FindDir = InitRD_FindDir
- };
-tVFS_NodeType gInitRD_FileType = {
- .Read = InitRD_ReadFile
- };
-
-/**
- * \brief Register initrd with the kernel
- */
-int InitRD_Install(char **Arguments)
-{
- Log_Notice("InitRD", "Installed");
- VFS_AddDriver( &gInitRD_FSInfo );
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Mount the InitRD
- */
-tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments)
-{
- #if DUMP_ON_MOUNT
- InitRD_DumpDir( &gInitRD_RootNode, 0 );
- #endif
- Log_Notice("InitRD", "Mounted (%i files)", giInitRD_NumFiles);
- return &gInitRD_RootNode;
-}
-
-/**
- * \brief Unmount the InitRD
- */
-void InitRD_Unmount(tVFS_Node *Node)
-{
-}
-
-/**
- */
-tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
-{
- if( Inode >= giInitRD_NumFiles ) return NULL;
- return gInitRD_FileList[Inode];
-}
-
-/**
- * \brief Read from a file
- */
-Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- if(Offset > Node->Size)
- return 0;
- if(Offset + Length > Node->Size)
- Length = Node->Size - Offset;
-
- memcpy(Buffer, Node->ImplPtr+Offset, Length);
-
- return Length;
-}
-
-/**
- * \brief Read from a directory
- */
-char *InitRD_ReadDir(tVFS_Node *Node, int ID)
-{
- tInitRD_File *dir = Node->ImplPtr;
-
- if(ID >= Node->Size)
- return NULL;
-
- return strdup(dir[ID].Name);
-}
-
-/**
- * \brief Find an element in a directory
- */
-tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name)
-{
- int i;
- tInitRD_File *dir = Node->ImplPtr;
-
- LOG("Name = '%s'", Name);
-
- for( i = 0; i < Node->Size; i++ )
- {
- if(strcmp(Name, dir[i].Name) == 0)
- return dir[i].Node;
- }
-
- return NULL;
-}
-
-void InitRD_DumpDir(tVFS_Node *Node, int Indent)
-{
- int i;
- char indent[Indent+1];
- tInitRD_File *dir = Node->ImplPtr;
-
- for( i = 0; i < Indent; i++ ) indent[i] = ' ';
- indent[i] = '\0';
-
- for( i = 0; i < Node->Size; i++ )
- {
- Log_Debug("InitRD", "%s- %p %s", indent, dir[i].Node, dir[i].Name);
- if(dir[i].Node->Flags & VFS_FFLAG_DIRECTORY)
- InitRD_DumpDir(dir[i].Node, Indent+1);
- }
-}
+++ /dev/null
-CATEGORY = FS
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-OBJ = main.o
-NAME = NFS
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 - NFS Driver
- * By John Hodge (thePowersGang)
- * This file is published under the terms of the Acess licence. See the
- * file COPYING for details.
- *
- * common.h - Common definitions
- */
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-typedef struct sNFS_Connection
-{
- int FD;
- tIPAddr Host;
- char *Base;
- tVFS_Node Node;
-} tNFS_Connection;
-
-#endif
+++ /dev/null
-/*
- * Acess2 - NFS Driver
- * By John Hodge (thePowersGang)
- * This file is published under the terms of the Acess licence. See the
- * file COPYING for details.
- *
- * main.c - Driver core
- */
-#define DEBUG 1
-#define VERBOSE 0
-#include "common.h"
-#include <modules.h>
-
-// === PROTOTYPES ===
- int NFS_Install(char **Arguments);
-tVFS_Node *NFS_InitDevice(char *Devices, char **Options);
-void NFS_Unmount(tVFS_Node *Node);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x32 /*v0.5*/, FS_NFS, NFS_Install, NULL);
-tVFS_Driver gNFS_FSInfo = {"nfs", 0, NFS_InitDevice, NFS_Unmount, NULL};
-
-tNFS_Connection *gpNFS_Connections;
-
-// === CODE ===
-/**
- * \brief Installs the NFS driver
- */
-int NFS_Install(char **Arguments)
-{
- VFS_AddDriver( &gNFS_FSInfo );
- return 1;
-}
-
-/**
- * \brief Mount a NFS share
- */
-tVFS_Node *NFS_InitDevice(char *Device, char **Options)
-{
- char *path, *host;
- tNFS_Connection *conn;
-
- path = strchr( Device, ':' ) + 1;
- host = strndup( Device, (int)(path-Device)-1 );
-
- conn = malloc( sizeof(tNFS_Connection) );
-
- if( !IPTools_GetAddress(host, &conn->IP) ) {
- free(conn);
- return NULL;
- }
- free(host);
-
- conn->FD = IPTools_OpenUdpClient( &conn->Host );
- if(conn->FD == -1) {
- free(conn);
- return NULL;
- }
-
- conn->Base = strdup( path );
- conn->RootNode.ImplPtr = conn;
- conn->RootNode.Flags = VFS_FFLAG_DIRECTORY;
-
- conn->RootNode.ReadDir = NFS_ReadDir;
- conn->RootNode.FindDir = NFS_FindDir;
- conn->RootNode.Close = NULL;
-
- return &conn->RootNode;
-}
-
-void NFS_Unmount(tVFS_Node *Node)
-{
-
-}
+++ /dev/null
-#
-#
-
-OBJ = main.o dir.o
-NAME = NTFS
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 - NTFS Driver
- * By John Hodge (thePowersGang)
- * This file is published under the terms of the Acess licence. See the
- * file COPYING for details.
- *
- * attributes.h - MFT Attribute Types
- */
-#ifndef _ATTRIBUTES_H_
-#define _ATTRIBUTES_H_
-
-typedef struct
-{
- Uint64 ParentDirectory; //!< Parent directory MFT entry
- Sint64 CreationTime; //!< Time the file was created
- Sint64 LastDataModTime; //!< Last change time for the data
- Sint64 LastMtfModTime; //!< Last change time for the MFT entry
- Sint64 LastAccessTime; //!< Last Access Time (unreliable on most systems)
-
- Uint64 AllocatedSize; //!< Allocated data size for $DATA unnamed stream
- Uint64 DataSize; //!< Actual size of $DATA unnamed stream
- Uint32 Flags; //!< File attribute files
-
- union {
- struct {
- Uint16 PackedSize; //!< Size of buffer needed for extended attributes
- Uint16 _reserved;
- } PACKED ExtAttrib;
- struct {
- Uint32 Tag; //!< Type of reparse point
- } PACKED ReparsePoint;
- } PACKED Type;
-
- Uint8 FilenameType; //!< Filename namespace (DOS, Windows, Unix)
- WCHAR Filename[0];
-} PACKED tNTFS_Attrib_Filename;
-
-#endif
+++ /dev/null
-/*
- * Acess2 - NTFS Driver
- * By John Hodge (thePowersGang)
- * This file is published under the terms of the Acess licence. See the
- * file COPYING for details.
- *
- * common.h - Common Types and Definitions
- */
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-#include <acess.h>
-#include <vfs.h>
-
-typedef Uint16 WCHAR;
-
-// === STRUCTURES ===
-/**
- * In-memory representation of an NTFS Disk
- */
-typedef struct sNTFS_Disk
-{
- int FD;
- int CacheHandle;
-
- int ClusterSize;
- Uint64 MFTBase;
- Uint32 MFTRecSize;
-
- tVFS_Node RootNode;
-} tNTFS_Disk;
-
-typedef struct sNTFS_BootSector
-{
- // 0
- Uint8 Jump[3];
- Uint8 SystemID[8]; // = "NTFS "
- Uint16 BytesPerSector;
- Uint8 SectorsPerCluster;
-
- // 0xE
- Uint8 Unused[7];
- Uint8 MediaDescriptor;
- Uint16 Unused2;
- Uint16 SectorsPerTrack;
- Uint16 Heads;
-
- // 0x1C
- Uint64 Unused3;
- Uint32 Unkown; // Usually 0x00800080 (according to Linux docs)
-
- // 0x28
- Uint64 TotalSectorCount; // Size of volume in sectors
- Uint64 MFTStart; // Logical Cluster Number of Cluster 0 of MFT
- Uint64 MFTMirrorStart; // Logical Cluster Number of Cluster 0 of MFT Backup
-
- // 0x40
- // If either of these are -ve, the size can be obtained via
- // SizeInBytes = 2^(-1 * Value)
- Sint8 ClustersPerMFTRecord;
- Uint8 Unused4[3];
- Sint8 ClustersPerIndexRecord;
- Uint8 Unused5[3];
-
- Uint64 SerialNumber;
-
- Uint8 Padding[512-0x50];
-
-} PACKED tNTFS_BootSector;
-
-/**
- * FILE header, an entry in the MFT
- */
-typedef struct sNTFS_FILE_Header
-{
- Uint32 Magic; // 'FILE'
- Uint16 UpdateSequenceOfs;
- Uint16 UpdateSequenceSize; // Size in words of the UpdateSequenceArray
-
- Uint64 LSN; // $LogFile Sequence Number
-
- Uint16 SequenceNumber;
- Uint16 HardLinkCount;
- Uint16 FirstAttribOfs; // Size of header?
- Uint16 Flags; // 0: In Use, 1: Directory
-
- Uint32 RecordSize; // Real Size of FILE Record
- Uint32 RecordSpace; // Allocated Size for FILE Record
-
- /**
- * Base address of the MFT containing this record
- */
- Uint64 Reference; // "File reference to the base FILE record" ???
-
- Uint16 NextAttribID;
- union
- {
- // Only in XP
- struct {
- Uint16 AlignTo4Byte;
- Uint16 RecordNumber; // Number of this MFT Record
- Uint16 UpdateSequenceNumber;
- Uint16 UpdateSequenceArray[];
- } XP;
- struct {
- Uint16 UpdateSequenceNumber;
- Uint16 UpdateSequenceArray[];
- } All;
- } OSDep;
-
-} PACKED tNTFS_FILE_Header;
-
-/**
- * File Attribute, follows the FILE header
- */
-typedef struct sNTFS_FILE_Attrib
-{
- Uint32 Type; // See eNTFS_FILE_Attribs
- Uint32 Size; // Includes header
-
- Uint8 ResidentFlag; // (What does this mean?)
- Uint8 NameLength;
- Uint16 NameOffset;
- Uint16 Flags; // 0: Compressed, 14: Encrypted, 15: Sparse
- Uint16 AttributeID;
-
- union
- {
- struct {
- Uint32 AttribLen; // In words
- Uint16 AttribOfs;
- Uint8 IndexedFlag;
- Uint8 Padding;
-
- Uint16 Name[]; // UTF-16
- // Attribute Data
- } Resident;
- struct {
- Uint64 StartingVCN;
- Uint64 LastVCN;
- Uint16 DataRunOfs;
- Uint16 CompressionUnitSize;
- Uint32 Padding;
- Uint64 AllocatedSize;
- Uint64 RealSize;
- Uint64 InitiatedSize; // One assumes, ammount of actual data stored
- Uint16 Name[]; // UTF-16
- // Data Runs
- } NonResident;
- };
-} PACKED tNTFS_FILE_Attrib;
-
-#endif
+++ /dev/null
-/*
- * Acess2 - NTFS Driver
- * By John Hodge (thePowersGang)
- * This file is published under the terms of the Acess licence. See the
- * file COPYING for details.
- *
- * dir.c - Directory Handling
- */
-#include "common.h"
-#include "index.h"
-
-// === PROTOTYPES ===
-char *NTFS_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name);
-Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str);
-
-// === CODE ===
-/**
- * \brief Get the name of an indexed directory entry
- */
-char *NTFS_ReadDir(tVFS_Node *Node, int Pos)
-{
- return NULL;
-}
-
-/**
- * \brief Get an entry from a directory by name
- */
-tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name)
-{
- tNTFS_Disk *disk = Node->ImplPtr;
- Uint64 inode = NTFS_int_IndexLookup(Node->Inode, "$I30", Name);
- tVFS_Node node;
-
- if(!inode) return NULL;
-
- node.Inode = inode;
-
- return Inode_CacheNode(disk->CacheHandle, &node);
-}
-
-/**
- * \brief Scans an index for the requested value and returns the associated ID
- */
-Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str)
-{
- return 0;
-}
+++ /dev/null
-/*
- * Acess2 - NTFS Driver
- * By John Hodge (thePowersGang)
- * This file is published under the terms of the Acess licence. See the
- * file COPYING for details.
- *
- * index.h - Index Types
- */
-#ifndef _INDEX_H_
-#define _INDEX_H_
-
-#include "attributes.h"
-
-typedef struct
-{
- Uint32 EntryOffset;
- Uint32 IndexLength;
- Uint32 AllocateSize;
- Uint8 Flags;
- Uint8 _reserved[3];
-} PACKED tNTFS_IndexHeader;
-
-typedef struct
-{
- Uint32 Type;
- Uint32 CollationRule;
- Uint32 IndexBlockSize;
- Uint8 ClustersPerIndexBlock;
- Uint8 _reserved[3];
- tNTFS_IndexHeader Header;
-} PACKED tNTFS_IndexRoot;
-
-typedef struct
-{
- union {
- struct {
- Uint64 File; // MFT Index of file
- } PACKED Dir;
- /**
- * Views/Indexes
- */
- struct {
- Uint16 DataOffset;
- Uint16 DataLength;
- Uint32 _reserved;
- } PACKED ViewIndex;
- } PACKED Header;
-
- Uint16 Length; //!< Size of the index entry (multiple of 8 bytes)
- Uint16 KeyLength; //!< Size of key value
- Uint16 Flags; //!< Flags Bitfield
- Uint16 _reserved;
-
- /**
- * \brief Key Data
- * \note Only valid if \a Flags does not have \a INDEX_ENTRY_END set
- * \note In NTFS3 only \a Filename is used
- */
- union {
- tNTFS_Attrib_Filename Filename;
- //TODO: more key types
- } PACKED Key;
-} PACKED tNTFS_IndexEntry;
-
-#endif
+++ /dev/null
-/*
- * Acess2 - NTFS Driver
- * By John Hodge (thePowersGang)
- *
- * main.c - Driver core
- */
-#define DEBUG 1
-#define VERBOSE 0
-#include <acess.h>
-#include <vfs.h>
-#include "common.h"
-#include <modules.h>
-
-// === IMPORTS ===
-extern char *NTFS_ReadDir(tVFS_Node *Node, int Pos);
-extern tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name);
-
-// === PROTOTYPES ===
- int NTFS_Install(char **Arguments);
-tVFS_Node *NTFS_InitDevice(const char *Devices, const char **Options);
-void NTFS_Unmount(tVFS_Node *Node);
-void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x0A /*v0.1*/, FS_NTFS, NTFS_Install, NULL);
-tVFS_Driver gNTFS_FSInfo = {"ntfs", 0, NTFS_InitDevice, NTFS_Unmount, NULL};
-tVFS_NodeType gNTFS_DirType = {
- .TypeName = "NTFS-File",
- .ReadDir = NTFS_ReadDir,
- .FindDir = NTFS_FindDir,
- .Close = NULL
- };
-
-tNTFS_Disk gNTFS_Disks;
-
-// === CODE ===
-/**
- * \brief Installs the NTFS driver
- */
-int NTFS_Install(char **Arguments)
-{
- VFS_AddDriver( &gNTFS_FSInfo );
- return 0;
-}
-
-/**
- * \brief Mount a NTFS volume
- */
-tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options)
-{
- tNTFS_Disk *disk;
- tNTFS_BootSector bs;
-
- disk = malloc( sizeof(tNTFS_Disk) );
-
- disk->FD = VFS_Open(Device, VFS_OPENFLAG_READ);
- if(!disk->FD) {
- free(disk);
- return NULL;
- }
-
- Log_Debug("FS_NTFS", "&bs = %p", &bs);
- VFS_ReadAt(disk->FD, 0, 512, &bs);
-
- Log_Debug("FS_NTFS", "Jump = %02x%02x%02x",
- bs.Jump[0],
- bs.Jump[1],
- bs.Jump[2]);
- Log_Debug("FS_NTFS", "SystemID = %02x%02x%02x%02x%02x%02x%02x%02x (%8C)",
- bs.SystemID[0], bs.SystemID[1], bs.SystemID[2], bs.SystemID[3],
- bs.SystemID[4], bs.SystemID[5], bs.SystemID[6], bs.SystemID[7],
- bs.SystemID
- );
- Log_Debug("FS_NTFS", "BytesPerSector = %i", bs.BytesPerSector);
- Log_Debug("FS_NTFS", "SectorsPerCluster = %i", bs.SectorsPerCluster);
- Log_Debug("FS_NTFS", "MediaDescriptor = 0x%x", bs.MediaDescriptor);
- Log_Debug("FS_NTFS", "SectorsPerTrack = %i", bs.SectorsPerTrack);
- Log_Debug("FS_NTFS", "Heads = %i", bs.Heads);
- Log_Debug("FS_NTFS", "TotalSectorCount = 0x%llx", bs.TotalSectorCount);
- Log_Debug("FS_NTFS", "MFTStart = 0x%llx", bs.MFTStart);
- Log_Debug("FS_NTFS", "MFTMirrorStart = 0x%llx", bs.MFTMirrorStart);
- Log_Debug("FS_NTFS", "ClustersPerMFTRecord = %i", bs.ClustersPerMFTRecord);
- Log_Debug("FS_NTFS", "ClustersPerIndexRecord = %i", bs.ClustersPerIndexRecord);
- Log_Debug("FS_NTFS", "SerialNumber = 0x%llx", bs.SerialNumber);
-
- disk->ClusterSize = bs.BytesPerSector * bs.SectorsPerCluster;
- Log_Debug("NTFS", "Cluster Size = %i KiB", disk->ClusterSize/1024);
- disk->MFTBase = bs.MFTStart;
- Log_Debug("NTFS", "MFT Base = %i", disk->MFTBase);
- Log_Debug("NTFS", "TotalSectorCount = 0x%x", bs.TotalSectorCount);
-
- if( bs.ClustersPerMFTRecord < 0 ) {
- disk->MFTRecSize = 1 << (-bs.ClustersPerMFTRecord);
- }
- else {
- disk->MFTRecSize = bs.ClustersPerMFTRecord * disk->ClusterSize;
- }
-
- disk->RootNode.Inode = 5; // MFT Ent #5 is filesystem root
- disk->RootNode.ImplPtr = disk;
-
- disk->RootNode.UID = 0;
- disk->RootNode.GID = 0;
-
- disk->RootNode.NumACLs = 1;
- disk->RootNode.ACLs = &gVFS_ACL_EveryoneRX;
-
- disk->RootNode.Type = &gNTFS_DirType;
-
-
- NTFS_DumpEntry(disk, 5);
-
- return &disk->RootNode;
-}
-
-/**
- * \brief Unmount an NTFS Disk
- */
-void NTFS_Unmount(tVFS_Node *Node)
-{
-
-}
-
-/**
- * \brief Dumps a MFT Entry
- */
-void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry)
-{
- void *buf = malloc( Disk->MFTRecSize );
- tNTFS_FILE_Header *hdr = buf;
- tNTFS_FILE_Attrib *attr;
- int i;
-
- if(!buf) {
- Log_Warning("FS_NTFS", "malloc() fail!");
- return ;
- }
-
- VFS_ReadAt( Disk->FD,
- Disk->MFTBase * Disk->ClusterSize + Entry * Disk->MFTRecSize,
- Disk->MFTRecSize,
- buf);
-
- Log_Debug("FS_NTFS", "MFT Entry #%i", Entry);
- Log_Debug("FS_NTFS", "- Magic = 0x%08x (%4C)", hdr->Magic, &hdr->Magic);
- Log_Debug("FS_NTFS", "- UpdateSequenceOfs = 0x%x", hdr->UpdateSequenceOfs);
- Log_Debug("FS_NTFS", "- UpdateSequenceSize = 0x%x", hdr->UpdateSequenceSize);
- Log_Debug("FS_NTFS", "- LSN = 0x%x", hdr->LSN);
- Log_Debug("FS_NTFS", "- SequenceNumber = %i", hdr->SequenceNumber);
- Log_Debug("FS_NTFS", "- HardLinkCount = %i", hdr->HardLinkCount);
- Log_Debug("FS_NTFS", "- FirstAttribOfs = 0x%x", hdr->FirstAttribOfs);
- Log_Debug("FS_NTFS", "- Flags = 0x%x", hdr->Flags);
- Log_Debug("FS_NTFS", "- RecordSize = 0x%x", hdr->RecordSize);
- Log_Debug("FS_NTFS", "- RecordSpace = 0x%x", hdr->RecordSpace);
- Log_Debug("FS_NTFS", "- Reference = 0x%llx", hdr->Reference);
- Log_Debug("FS_NTFS", "- NextAttribID = 0x%04x", hdr->NextAttribID);
-
- attr = (void*)( (char*)hdr + hdr->FirstAttribOfs );
- i = 0;
- while( (tVAddr)attr < (tVAddr)hdr + hdr->RecordSize )
- {
- if(attr->Type == 0xFFFFFFFF) break;
- Log_Debug("FS_NTFS", "- Attribute %i", i ++);
- Log_Debug("FS_NTFS", " > Type = 0x%x", attr->Type);
- Log_Debug("FS_NTFS", " > Size = 0x%x", attr->Size);
- Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->ResidentFlag);
- Log_Debug("FS_NTFS", " > NameLength = %i", attr->NameLength);
- Log_Debug("FS_NTFS", " > NameOffset = 0x%x", attr->NameOffset);
- Log_Debug("FS_NTFS", " > Flags = 0x%x", attr->Flags);
- Log_Debug("FS_NTFS", " > AttributeID = 0x%x", attr->AttributeID);
- if( !attr->ResidentFlag ) {
- Log_Debug("FS_NTFS", " > AttribLen = 0x%x", attr->Resident.AttribLen);
- Log_Debug("FS_NTFS", " > AttribOfs = 0x%x", attr->Resident.AttribOfs);
- Log_Debug("FS_NTFS", " > IndexedFlag = 0x%x", attr->Resident.IndexedFlag);
- Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->Resident.Name);
- Debug_HexDump("FS_NTFS",
- (void*)( (tVAddr)attr + attr->Resident.AttribOfs ),
- attr->Resident.AttribLen
- );
- }
- else {
- Log_Debug("FS_NTFS", " > StartingVCN = 0x%llx", attr->NonResident.StartingVCN);
- Log_Debug("FS_NTFS", " > LastVCN = 0x%llx", attr->NonResident.LastVCN);
- Log_Debug("FS_NTFS", " > DataRunOfs = 0x%x", attr->NonResident.DataRunOfs);
- Log_Debug("FS_NTFS", " > CompressionUnitSize = 0x%x", attr->NonResident.CompressionUnitSize);
- Log_Debug("FS_NTFS", " > AllocatedSize = 0x%llx", attr->NonResident.AllocatedSize);
- Log_Debug("FS_NTFS", " > RealSize = 0x%llx", attr->NonResident.RealSize);
- Log_Debug("FS_NTFS", " > InitiatedSize = 0x%llx", attr->NonResident.InitiatedSize);
- Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->NonResident.Name);
- }
-
- attr = (void*)( (tVAddr)attr + attr->Size );
- }
-
- free(buf);
-}
+++ /dev/null
-#
-#
-
-OBJ := main.o interface.o
-OBJ += link.o arp.o
-OBJ += ipv4.o icmp.o
-OBJ += ipv6.o
-OBJ += firewall.o routing.o
-OBJ += udp.o tcp.o
-NAME := IPStack
-CATEGORY :=
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Address Resolution Protocol
- * - Part of the IPv4 protocol
- */
-#define DEBUG 0
-#include "ipstack.h"
-#include "arp.h"
-#include "link.h"
-
-#define ARPv6 0
-#define ARP_CACHE_SIZE 64
-#define ARP_MAX_AGE (60*60*1000) // 1Hr
-
-// === IMPORTS ===
-extern tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast);
-#if ARPv6
-extern tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
-#endif
-
-// === PROTOTYPES ===
- int ARP_Initialise();
-tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address);
-void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer);
-
-// === GLOBALS ===
-struct sARP_Cache4 {
- tIPv4 IP;
- tMacAddr MAC;
- Sint64 LastUpdate;
- Sint64 LastUsed;
-} *gaARP_Cache4;
- int giARP_Cache4Space;
-tMutex glARP_Cache4;
-#if ARPv6
-struct sARP_Cache6 {
- tIPv6 IP;
- tMacAddr MAC;
- Sint64 LastUpdate;
- Sint64 LastUsed;
-} *gaARP_Cache6;
- int giARP_Cache6Space;
-tMutex glARP_Cache6;
-#endif
-volatile int giARP_LastUpdateID = 0;
-
-// === CODE ===
-/**
- * \fn int ARP_Initialise()
- * \brief Initalise the ARP section
- */
-int ARP_Initialise()
-{
- gaARP_Cache4 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) );
- memset( gaARP_Cache4, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) );
- giARP_Cache4Space = ARP_CACHE_SIZE;
-
- #if ARPv6
- gaARP_Cache6 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) );
- memset( gaARP_Cache6, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) );
- giARP_Cache6Space = ARP_CACHE_SIZE;
- #endif
-
- Link_RegisterType(0x0806, ARP_int_GetPacket);
- return 1;
-}
-
-/**
- * \brief Resolves a MAC address from an IPv4 address
- */
-tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address)
-{
- int lastID;
- int i;
- struct sArpRequest4 req;
- Sint64 timeout;
-
- ENTER("pInterface xAddress", Interface, Address);
-
- // Check for broadcast
- if( Address.L == -1 )
- {
- LOG("Broadcast");
- LEAVE('-');
- return cMAC_BROADCAST;
- }
-
- // Check routing tables if not on this subnet
- if( IPStack_CompareAddress(4, &Address, Interface->Address, Interface->SubnetBits) == 0 )
- {
- tRoute *route = IPStack_FindRoute(4, Interface, &Address);
- // If the next hop is defined, use it
- // - 0.0.0.0 as the next hop means "no next hop / direct"
- if( route && ((tIPv4*)route->NextHop)->L != 0 )
- {
- // Recursion: see /Recursion/
- LOG("Recursing with %s", IPStack_PrintAddress(4, route->NextHop));
- LEAVE('-');
- return ARP_Resolve4(Interface, *(tIPv4*)route->NextHop);
- }
- // No route, fall though
- }
- else
- {
- Uint32 netmask;
- // Check for broadcast
- netmask = IPv4_Netmask(Interface->SubnetBits);
- if( (Address.L & ~netmask) == (0xFFFFFFFF & ~netmask) )
- {
- LOG("Local Broadcast");
- LEAVE('-');
- return cMAC_BROADCAST;
- }
- }
-
- // Check ARP Cache
- Mutex_Acquire( &glARP_Cache4 );
- for( i = 0; i < giARP_Cache4Space; i++ )
- {
- if(gaARP_Cache4[i].IP.L != Address.L) continue;
-
- // Check if the entry needs to be refreshed
- if( now() - gaARP_Cache4[i].LastUpdate > ARP_MAX_AGE ) break;
-
- Mutex_Release( &glARP_Cache4 );
- LOG("Return %x:%x:%x:%x:%x:%x",
- gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1],
- gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3],
- gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5]
- );
- LEAVE('-');
- return gaARP_Cache4[i].MAC;
- }
- Mutex_Release( &glARP_Cache4 );
-
- lastID = giARP_LastUpdateID;
-
- // Create request
- Log_Log("ARP4", "Asking for address %i.%i.%i.%i",
- Address.B[0], Address.B[1], Address.B[2], Address.B[3]
- );
- req.HWType = htons(0x0001); // Ethernet
- req.Type = htons(0x0800);
- req.HWSize = 6;
- req.SWSize = 4;
- req.Request = htons(1);
- req.SourceMac = Interface->Adapter->MacAddr;
- req.SourceIP = *(tIPv4*)Interface->Address;
- req.DestMac = cMAC_BROADCAST;
- req.DestIP = Address;
-
- // Send Request
- Link_SendPacket(Interface->Adapter, 0x0806, req.DestMac, sizeof(struct sArpRequest4), &req);
-
- timeout = now() + Interface->TimeoutDelay;
-
- // Wait for a reply
- for(;;)
- {
- while(lastID == giARP_LastUpdateID && now() < timeout) {
- Threads_Yield();
- }
-
- if( now() >= timeout ) {
- Log_Log("ARP4", "Timeout");
- break; // Timeout
- }
-
- lastID = giARP_LastUpdateID;
-
- Mutex_Acquire( &glARP_Cache4 );
- for( i = 0; i < giARP_Cache4Space; i++ )
- {
- if(gaARP_Cache4[i].IP.L != Address.L) continue;
-
- Mutex_Release( &glARP_Cache4 );
- Log_Debug("ARP4", "Return %02x:%02x:%02x:%02x:%02x:%02x",
- gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1],
- gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3],
- gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5]);
- return gaARP_Cache4[i].MAC;
- }
- Mutex_Release( &glARP_Cache4 );
- }
- {
- tMacAddr ret = {{0,0,0,0,0,0}};
- return ret;
- }
-}
-
-/**
- * \brief Updates the ARP Cache entry for an IPv4 Address
- */
-void ARP_UpdateCache4(tIPv4 SWAddr, tMacAddr HWAddr)
-{
- int i;
- int free = -1;
- int oldest = 0;
-
- // Find an entry for the IP address in the cache
- Mutex_Acquire(&glARP_Cache4);
- for( i = giARP_Cache4Space; i--; )
- {
- if(gaARP_Cache4[oldest].LastUpdate > gaARP_Cache4[i].LastUpdate) {
- oldest = i;
- }
- if( gaARP_Cache4[i].IP.L == SWAddr.L ) break;
- if( gaARP_Cache4[i].LastUpdate == 0 && free == -1 ) free = i;
- }
- // If there was no match, we need to make one
- if(i == -1) {
- if(free != -1)
- i = free;
- else
- i = oldest;
- }
-
- Log_Log("ARP4", "Caching %i.%i.%i.%i (%02x:%02x:%02x:%02x:%02x:%02x) in %i",
- SWAddr.B[0], SWAddr.B[1], SWAddr.B[2], SWAddr.B[3],
- HWAddr.B[0], HWAddr.B[1], HWAddr.B[2], HWAddr.B[3], HWAddr.B[4], HWAddr.B[5],
- i
- );
-
- gaARP_Cache4[i].IP = SWAddr;
- gaARP_Cache4[i].MAC = HWAddr;
- gaARP_Cache4[i].LastUpdate = now();
- giARP_LastUpdateID ++;
- Mutex_Release(&glARP_Cache4);
-}
-
-#if ARPv6
-/**
- * \brief Updates the ARP Cache entry for an IPv6 Address
- */
-void ARP_UpdateCache6(tIPv6 SWAddr, tMacAddr HWAddr)
-{
- int i;
- int free = -1;
- int oldest = 0;
-
- // Find an entry for the MAC address in the cache
- Mutex_Acquire(&glARP_Cache6);
- for( i = giARP_Cache6Space; i--; )
- {
- if(gaARP_Cache6[oldest].LastUpdate > gaARP_Cache6[i].LastUpdate) {
- oldest = i;
- }
- if( MAC_EQU(gaARP_Cache6[i].MAC, HWAddr) ) break;
- if( gaARP_Cache6[i].LastUpdate == 0 && free == -1 ) free = i;
- }
- // If there was no match, we need to make one
- if(i == -1) {
- if(free != -1)
- i = free;
- else
- i = oldest;
- gaARP_Cache6[i].MAC = HWAddr;
- }
-
- gaARP_Cache6[i].IP = SWAddr;
- gaARP_Cache6[i].LastUpdate = now();
- giARP_LastUpdateID ++;
- Mutex_Release(&glARP_Cache6);
-}
-#endif
-
-/**
- * \fn void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
- * \brief Called when an ARP packet is recieved
- */
-void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
-{
- tArpRequest4 *req4 = Buffer;
- #if ARPv6
- tArpRequest6 *req6 = Buffer;
- #endif
- tInterface *iface;
-
- // Sanity Check Packet
- if( Length < (int)sizeof(tArpRequest4) ) {
- Log_Log("ARP", "Recieved undersized packet");
- return ;
- }
- if( ntohs(req4->Type) != 0x0800 ) {
- Log_Log("ARP", "Recieved a packet with a bad type (0x%x)", ntohs(req4->Type));
- return ;
- }
- if( req4->HWSize != 6 ) {
- Log_Log("ARP", "Recieved a packet with HWSize != 6 (%i)", req4->HWSize);
- return;
- }
- #if ARP_DETECT_SPOOFS
- if( !MAC_EQU(req4->SourceMac, From) ) {
- Log_Log("ARP", "ARP spoofing detected "
- "(%02x%02x:%02x%02x:%02x%02x != %02x%02x:%02x%02x:%02x%02x)",
- req4->SourceMac.B[0], req4->SourceMac.B[1], req4->SourceMac.B[2],
- req4->SourceMac.B[3], req4->SourceMac.B[4], req4->SourceMac.B[5],
- From.B[0], From.B[1], From.B[2],
- From.B[3], From.B[4], From.B[5]
- );
- return;
- }
- #endif
-
- switch( ntohs(req4->Request) )
- {
- case 1: // You want my IP?
- // Check what type of IP it is
- switch( req4->SWSize )
- {
- case 4:
- Log_Debug("ARP", "ARP Request IPv4 Address %i.%i.%i.%i from %i.%i.%i.%i",
- req4->DestIP.B[0], req4->DestIP.B[1], req4->DestIP.B[2],
- req4->DestIP.B[3],
- req4->SourceIP.B[0], req4->SourceIP.B[1],
- req4->SourceIP.B[2], req4->SourceIP.B[3]);
- Log_Debug("ARP", " from MAC %02x:%02x:%02x:%02x:%02x:%02x",
- req4->SourceMac.B[0], req4->SourceMac.B[1],
- req4->SourceMac.B[2], req4->SourceMac.B[3],
- req4->SourceMac.B[4], req4->SourceMac.B[5]);
- iface = IPv4_GetInterface(Adapter, req4->DestIP, 0);
- if( iface )
- {
- ARP_UpdateCache4(req4->SourceIP, req4->SourceMac);
-
- req4->DestIP = req4->SourceIP;
- req4->DestMac = req4->SourceMac;
- req4->SourceIP = *(tIPv4*)iface->Address;;
- req4->SourceMac = Adapter->MacAddr;
- req4->Request = htons(2);
- Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)",
- req4->SourceMac.B[0], req4->SourceMac.B[1],
- req4->SourceMac.B[2], req4->SourceMac.B[3],
- req4->SourceMac.B[4], req4->SourceMac.B[5]);
- Link_SendPacket(Adapter, 0x0806, req4->DestMac, sizeof(tArpRequest4), req4);
- }
- break;
- #if ARPv6
- case 6:
- if( Length < (int)sizeof(tArpRequest6) ) {
- Log_Log("ARP", "Recieved undersized packet (IPv6)");
- return ;
- }
- Log_Debug("ARP", "ARP Request IPv6 Address %08x:%08x:%08x:%08x",
- ntohl(req6->DestIP.L[0]), ntohl(req6->DestIP.L[1]),
- ntohl(req6->DestIP.L[2]), ntohl(req6->DestIP.L[3])
- );
- iface = IPv6_GetInterface(Adapter, req6->DestIP, 0);
- if( iface )
- {
- req6->DestIP = req6->SourceIP;
- req6->DestMac = req6->SourceMac;
- req6->SourceIP = *(tIPv6*)iface->Address;
- req6->SourceMac = Adapter->MacAddr;
- req6->Request = htons(2);
- Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)",
- req4->SourceMac.B[0], req4->SourceMac.B[1],
- req4->SourceMac.B[2], req4->SourceMac.B[3],
- req4->SourceMac.B[4], req4->SourceMac.B[5]);
- Link_SendPacket(Adapter, 0x0806, req6->DestMac, sizeof(tArpRequest6), req6);
- }
- break;
- #endif
- default:
- Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize);
- return ;
- }
-
- break;
-
- case 2: // Ooh! A response!
- // Check what type of IP it is
- switch( req4->SWSize )
- {
- case 4:
- ARP_UpdateCache4( req4->SourceIP, From );
- break;
- #if ARPv6
- case 6:
- if( Length < (int)sizeof(tArpRequest6) ) {
- Log_Debug("ARP", "Recieved undersized packet (IPv6)");
- return ;
- }
- ARP_UpdateCache6( req6->SourceIP, From );
- break;
- #endif
- default:
- Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize);
- return ;
- }
-
- break;
-
- default:
- Log_Warning("ARP", "Unknown Request ID %i", ntohs(req4->Request));
- break;
- }
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Common Header
- */
-#ifndef _ARP_H_
-#define _ARP_H_
-
-#include "ipstack.h"
-
-typedef struct sArpRequest4 tArpRequest4;
-typedef struct sArpRequest6 tArpRequest6;
-
-struct sArpRequest4 {
- Uint16 HWType;
- Uint16 Type;
- Uint8 HWSize, SWSize;
- Uint16 Request;
- tMacAddr SourceMac;
- tIPv4 SourceIP;
- tMacAddr DestMac;
- tIPv4 DestIP;
-} __attribute__((packed));
-
-struct sArpRequest6 {
- Uint16 HWType;
- Uint16 Type;
- Uint8 HWSize, SWSize;
- Uint16 Request;
- tMacAddr SourceMac;
- tIPv6 SourceIP;
- tMacAddr DestMac;
- tIPv6 DestIP;
-} __attribute__((packed));
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Firewall Rules
- */
-#include "ipstack.h"
-#include "firewall.h"
-
-#define MAX_ADDRTYPE 9
-
-// === IMPORTS ===
-
-// === TYPES ===
-typedef struct sKeyValue tKeyValue;
-typedef struct sFirewallMod tFirewallMod;
-typedef struct sModuleRule tModuleRule;
-typedef struct sRule tRule;
-typedef struct sChain tChain;
-
-// === STRUCTURES ===
-struct sKeyValue
-{
- const char *Key;
- const char *Value;
-};
-
-struct sFirewallMod
-{
- const char *Name;
-
- int (*Match)(tModuleRule *Rule, int AddrType,
- const void *Src, const void *Dest,
- Uint8 Type, Uint32 Flags,
- size_t Length, const void *Data);
-
- tModuleRule *(*Create)(tKeyValue *Params);
-};
-
-struct sModuleRule
-{
- tModuleRule *Next;
-
- tFirewallMod *Mod;
-
- char Data[];
-};
-
-struct sRule
-{
- tRule *Next;
-
- int PacketCount; // Number of packets seen
- int ByteCount; // Number of bytes seen (IP Payload bytes)
-
- int bInvertSource; // Boolean NOT flag on source
- void *Source; // Source address bytes
- int SourceMask; // Source address mask bits
-
- int bInvertDest; // Boolean NOT flag on destination
- void *Dest; // Destination address bytes
- int DestMask; // Destination address mask bits
-
- tModuleRule *Modules; // Modules loaded for this rule
-
- char Target[]; // Target rule name
-};
-
-struct sChain
-{
- tChain *Next;
-
- tRule *FirstRule;
- tRule *LastRule;
-
- char Name[];
-};
-
-// === PROTOTYPES ===
- int IPTables_TestChain(
- const char *RuleName,
- const int AddressType,
- const void *Src, const void *Dest,
- Uint8 Type, Uint32 Flags,
- size_t Length, const void *Data
- );
-
-// === GLOBALS ===
-tChain *gapFirewall_Chains[MAX_ADDRTYPE+1];
-tChain gFirewall_DROP = {.Name="DROP"};
-tChain gFirewall_ACCEPT = {.Name="ACCEPT"};
-tChain gFirewall_RETURN = {.Name="RETURN"};
-
-// === CODE ===
-/**
- * \brief Apply a rule to a packet
- * \return -1 for no match, -2 for RETURN, eFirewallAction otherwise
- */
-int IPTables_DoRule(
- tRule *Rule, int AddrType,
- const void *Src, const void *Dest,
- Uint8 Type, Uint32 Flags,
- size_t Length, const void *Data)
-{
- int rv;
- // Check if source doesn't match
- if( !IPStack_CompareAddress(AddrType, Src, Rule->Source, Rule->SourceMask) == !Rule->bInvertSource )
- return -1;
- // Check if destination doesn't match
- if( !IPStack_CompareAddress(AddrType, Dest, Rule->Dest, Rule->DestMask) == !Rule->bInvertDest )
- return -1;
-
- // TODO: Handle modules (UDP/TCP/etc)
- tModuleRule *modrule;
- for( modrule = Rule->Modules; modrule; modrule = modrule->Next )
- {
- if( !modrule->Mod->Match ) continue;
- rv = modrule->Mod->Match(modrule, AddrType, Src, Dest, Type, Flags, Length, Data);
- if(rv != 0) return rv; // No match / action
- }
-
- // Update statistics
- Rule->PacketCount ++;
- Rule->ByteCount += Length;
-
- return IPTables_TestChain(Rule->Target, AddrType, Src, Dest, Type, Flags, Length, Data);
-}
-
-/**
- * \brief Tests an IPv4 chain on a packet
- * \return Boolean Disallow (0: Packet Allowed, 1: Drop, 2: Reject, 3: Continue, -1 no match)
- */
-int IPTables_TestChain(
- const char *RuleName,
- const int AddressType,
- const void *Src, const void *Dest,
- Uint8 Type, Uint32 Flags,
- size_t Length, const void *Data
- )
-{
- int rv;
- tChain *chain;
- tRule *rule;
-
- if( AddressType >= MAX_ADDRTYPE ) return -1; // Bad address type
-
- // Catch builtin targets
- if(strcmp(RuleName, "") == 0) return -1; // No action
- if(strcmp(RuleName, "ACCEPT") == 0) return 0; // Accept packet
- if(strcmp(RuleName, "DROP") == 0) return 1; // Drop packet
- if(strcmp(RuleName, "RETURN") == 0) return -2; // Return from rule
-
- // Find the rule
- for( chain = gapFirewall_Chains[AddressType]; chain; chain = chain->Next )
- {
- if( strcmp(chain->Name, RuleName) == 0 )
- break;
- }
- if( !chain ) return -1; // Bad rule name
-
- // Check the rules
- for( rule = chain->FirstRule; rule; rule = rule->Next )
- {
- rv = IPTables_DoRule(rule, AddressType, Src, Dest, Type, Flags, Length, Data);
- if( rv == -1 )
- continue ;
- if( rv == -2 ) // -2 = Return from a chain/table, pretend no match
- return -1;
-
- return rv;
- }
-
-
- return 0; // Accept all for now
-}
+++ /dev/null
-/*
- */
-#ifndef _FIREWALL_H_
-#define _FIREWALL_H_
-
-enum eFirewallActions
-{
- FIREWALL_ACCEPT,
- FIREWALL_DROP
-};
-
-/**
- * \brief Tests a packet on a chain
- */
-extern int IPTables_TestChain(
- const char *RuleName,
- const int AddressType,
- const void *Src, const void *Dest,
- Uint8 Type, Uint32 Flags,
- size_t Length, const void *Data
- );
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - ICMP Handling
- */
-#include "ipstack.h"
-#include "ipv4.h"
-#include "icmp.h"
-
-// === CONSTANTS ===
-#define PING_SLOTS 64
-
-// === PROTOTYPES ===
-void ICMP_Initialise();
-void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-
-// === GLOBALS ===
-struct {
- tInterface *Interface;
- int bArrived;
-} gICMP_PingSlots[PING_SLOTS];
-
-// === CODE ===
-/**
- * \fn void ICMP_Initialise()
- * \brief Initialise the ICMP Layer
- */
-void ICMP_Initialise()
-{
- IPv4_RegisterCallback(IP4PROT_ICMP, ICMP_GetPacket);
-}
-
-/**
- * \fn void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
- * \brief Handles a packet from the IP Layer
- */
-void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
-{
- tICMPHeader *hdr = Buffer;
-
- //Log_Debug("ICMPv4", "Length = %i", Length);
- Log_Debug("ICMPv4", "hdr->Type, hdr->Code = %i, %i", hdr->Type, hdr->Code);
- //Log_Debug("ICMPv4", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
- Log_Debug("ICMPv4", "hdr->ID = 0x%x", ntohs(hdr->ID));
- Log_Debug("ICMPv4", "hdr->Sequence = 0x%x", ntohs(hdr->Sequence));
-
- switch(hdr->Type)
- {
- // -- 0: Echo Reply
- case ICMP_ECHOREPLY:
- if(hdr->Code != 0) {
- Log_Warning("ICMPv4", "Code == %i for ICMP Echo Reply, should be 0", hdr->Code);
- return ;
- }
- if(hdr->ID != (Uint16)~hdr->Sequence) {
- Log_Warning("ICMPv4", "ID and Sequence values do not match");
- //return ;
- }
- gICMP_PingSlots[hdr->ID].bArrived = 1;
- break;
-
- // -- 3: Destination Unreachable
- case ICMP_UNREACHABLE:
- switch(hdr->Code)
- {
- case 3: // Port Unreachable
- Log_Debug("ICMPv4", "Destination Unreachable (Port Unreachable)");
- break;
- default:
- Log_Debug("ICMPv4", "Destination Unreachable (Code %i)", hdr->Code);
- break;
- }
-// IPv4_Unreachable( Interface, hdr->Code, htons(hdr->Length)-sizeof(tICMPHeader), hdr->Data );
- break;
-
- // -- 8: Echo Request
- case ICMP_ECHOREQ:
- if(hdr->Code != 0) {
- Log_Warning("ICMPv4", "Code == %i for ICMP Echo Request, should be 0", hdr->Code);
- return ;
- }
- //Log_Debug("ICMPv4", "Replying");
- hdr->Type = ICMP_ECHOREPLY;
- hdr->Checksum = 0;
- hdr->Checksum = htons( IPv4_Checksum( (Uint16*)hdr, Length/2 ) );
- //Log_Debug("ICMPv4", "Checksum = 0x%04x", hdr->Checksum);
- IPv4_SendPacket(Interface, *(tIPv4*)Address, 1, ntohs(hdr->Sequence), Length, hdr);
- break;
- default:
- break;
- }
-
-}
-
-/**
- * \brief Sends ICMP Echo and waits for the reply
- * \note Times out after \a Interface->TimeoutDelay has elapsed
- */
-int ICMP_Ping(tInterface *Interface, tIPv4 Addr)
-{
- Sint64 ts;
- Sint64 end;
- char buf[32] = "\x8\0\0\0\0\0\0\0Acess2 I"
- "P/TCP Stack 1.0\0";
- tICMPHeader *hdr = (void*)buf;
- int i;
-
- for(;;)
- {
- for(i=0;i<PING_SLOTS;i++)
- {
- if(gICMP_PingSlots[i].Interface == NULL) break;
- }
- if( i < PING_SLOTS ) break;
- Threads_Yield();
- }
- gICMP_PingSlots[i].Interface = Interface;
- gICMP_PingSlots[i].bArrived = 0;
- hdr->ID = i;
- hdr->Sequence = ~i;
- hdr->Checksum = htons( IPv4_Checksum((Uint16*)hdr, sizeof(buf)/2) );
-
- ts = now();
-
- IPv4_SendPacket(Interface, Addr, 1, i, sizeof(buf), buf);
-
- end = ts + Interface->TimeoutDelay;
- while( !gICMP_PingSlots[i].bArrived && now() < end) Threads_Yield();
-
- if(now() > end)
- return -1;
-
- return (int)( now() - ts );
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - ICMP Handling
- */
-#ifndef _ICMP_H_
-#define _ICMP_H_
-
-// === TYPEDEFS ===
-typedef struct sICMPHeader tICMPHeader;
-
-// === STRUCTURES ===
-struct sICMPHeader
-{
- Uint8 Type;
- Uint8 Code;
- Uint16 Checksum;
- Uint16 ID;
- Uint16 Sequence;
- Uint8 Data[];
-};
-
-// === CONSTANTS ===
-enum eICMPTypes
-{
- ICMP_ECHOREPLY = 0,
- ICMP_UNREACHABLE = 3,
- ICMP_QUENCH = 4,
- ICMP_REDIRECT = 5,
- ICMP_ALTADDR = 6,
- ICMP_ECHOREQ = 8,
- ICMP_TRACE = 30 // Information Request
-};
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Interface Control
- */
-#define DEBUG 0
-#define VERSION VER2(0,10)
-#include "ipstack.h"
-#include "link.h"
-#include <api_drv_common.h>
-#include <api_drv_network.h>
-
-// === CONSTANTS ===
-//! Default timeout value, 30 seconds
-#define DEFAULT_TIMEOUT (30*1000)
-
-// === IMPORTS ===
-extern int IPv4_Ping(tInterface *Iface, tIPv4 Addr);
-//extern int IPv6_Ping(tInterface *Iface, tIPv6 Addr);
-extern tVFS_Node gIP_RouteNode;
-
-// === PROTOTYPES ===
-char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name);
- int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data);
-
- int IPStack_AddFile(tSocketFile *File);
-tInterface *IPStack_AddInterface(const char *Device, const char *Name);
-tAdapter *IPStack_GetAdapter(const char *Path);
-
-char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name);
- int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data);
-
-// === GLOBALS ===
-tVFS_NodeType gIP_InterfaceNodeType = {
- .ReadDir = IPStack_Iface_ReadDir,
- .FindDir = IPStack_Iface_FindDir,
- .IOCtl = IPStack_Iface_IOCtl
-};
-//! Loopback (127.0.0.0/8, ::1) Pseudo-Interface
-tInterface gIP_LoopInterface = {
- .Node = {
- .ImplPtr = &gIP_LoopInterface,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Size = -1,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Type = &gIP_InterfaceNodeType
- },
- .Adapter = NULL,
- .Type = 0
-};
-tShortSpinlock glIP_Interfaces;
-tInterface *gIP_Interfaces = NULL;
-tInterface *gIP_Interfaces_Last = NULL;
-
-tSocketFile *gIP_FileTemplates;
-
-tAdapter gIP_LoopAdapter = {
- .DeviceLen = 8,
- .Device = "LOOPBACK"
- };
-tMutex glIP_Adapters;
-tAdapter *gIP_Adapters = NULL;
- int giIP_NextIfaceId = 1;
-
-// === CODE ===
-
-/**
- * \brief Read from the IP Stack's Device Directory
- */
-char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos)
-{
- tInterface *iface;
- char *name;
- ENTER("pNode iPos", Node, Pos);
-
-
- // Routing Subdir
- if( Pos == 0 ) {
- LEAVE('s', "routes");
- return strdup("routes");
- }
- // Pseudo Interfaces
- if( Pos == 1 ) {
- LEAVE('s', "lo");
- return strdup("lo");
- }
- Pos -= 2;
-
- // Traverse the list
- for( iface = gIP_Interfaces; iface && Pos--; iface = iface->Next ) ;
-
- // Did we run off the end?
- if(!iface) {
- LEAVE('n');
- return NULL;
- }
-
- name = malloc(4);
- if(!name) {
- Log_Warning("IPStack", "IPStack_Root_ReadDir - malloc error");
- LEAVE('n');
- return NULL;
- }
-
- // Create the name
- Pos = iface->Node.ImplInt;
- if(Pos < 10) {
- name[0] = '0' + Pos;
- name[1] = '\0';
- }
- else if(Pos < 100) {
- name[0] = '0' + Pos/10;
- name[1] = '0' + Pos%10;
- name[2] = '\0';
- }
- else {
- name[0] = '0' + Pos/100;
- name[1] = '0' + (Pos/10)%10;
- name[2] = '0' + Pos%10;
- name[3] = '\0';
- }
-
- LEAVE('s', name);
- // Return the pre-generated name
- return name;
-}
-
-/**
- * \brief Get the node of an interface
- */
-tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name)
-{
- #if 0
- int i, num;
- #endif
- tInterface *iface;
-
- ENTER("pNode sName", Node, Name);
-
- // Routing subdir
- if( strcmp(Name, "routes") == 0 ) {
- LEAVE('p', &gIP_RouteNode);
- return &gIP_RouteNode;
- }
-
- // Loopback
- if( strcmp(Name, "lo") == 0 ) {
- LEAVE('p', &gIP_LoopInterface.Node);
- return &gIP_LoopInterface.Node;
- }
-
- for( iface = gIP_Interfaces; iface; iface = iface->Next )
- {
- if( strcmp(iface->Name, Name) == 0 )
- {
- LEAVE('p', &iface->Node);
- return &iface->Node;
- }
- }
-
- LEAVE('p', NULL);
- return NULL;
-}
-
-static const char *casIOCtls_Root[] = { DRV_IOCTLNAMES, "add_interface", NULL };
-/**
- * \brief Handles IOCtls for the IPStack root
- */
-int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- int tmp;
- ENTER("pNode iID pData", Node, ID, Data);
-
- switch(ID)
- {
- // --- Standard IOCtls (0-3) ---
- BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Root)
-
- /*
- * add_interface
- * - Adds a new IP interface and binds it to a device
- */
- case 4:
- if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1);
- if( !CheckString( Data ) ) LEAVE_RET('i', -1);
- LOG("New interface for '%s'", Data);
- {
- char name[4] = "";
- tInterface *iface = IPStack_AddInterface(Data, name);
- if(iface == NULL) LEAVE_RET('i', -1);
- tmp = iface->Node.ImplInt;
- }
- LEAVE_RET('i', tmp);
- }
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \fn tInterface *IPStack_AddInterface(char *Device)
- * \brief Adds an interface to the list
- */
-tInterface *IPStack_AddInterface(const char *Device, const char *Name)
-{
- tInterface *iface;
- tAdapter *card;
- int nameLen;
-
- ENTER("sDevice", Device);
-
- card = IPStack_GetAdapter(Device);
- if( !card ) {
- Log_Debug("IPStack", "Unable to open card '%s'", Device);
- LEAVE('n');
- return NULL; // ERR_YOURBAD
- }
-
- nameLen = sprintf(NULL, "%i", giIP_NextIfaceId);
-
- iface = malloc(
- sizeof(tInterface)
- + nameLen + 1
- + IPStack_GetAddressSize(-1)*3 // Address, Route->Network, Route->NextHop
- );
- if(!iface) {
- Log_Warning("IPStack", "AddInterface - malloc() failed");
- LEAVE('n');
- return NULL; // Return ERR_MYBAD
- }
-
- iface->Next = NULL;
- iface->Type = 0; // Unset type
- iface->Address = iface->Name + nameLen + 1; // Address
- iface->Route.Network = iface->Address + IPStack_GetAddressSize(-1);
- iface->Route.NextHop = iface->Route.Network + IPStack_GetAddressSize(-1);
-
- // Create Node
- iface->Node.ImplPtr = iface;
- iface->Node.Flags = VFS_FFLAG_DIRECTORY;
- iface->Node.Size = -1;
- iface->Node.NumACLs = 1;
- iface->Node.ACLs = &gVFS_ACL_EveryoneRX;
- iface->Node.Type = &gIP_InterfaceNodeType;
-
- // Set Defaults
- iface->TimeoutDelay = DEFAULT_TIMEOUT;
-
- // Get adapter handle
- iface->Adapter = card;
-
- // Delay setting ImplInt until after the adapter is opened
- // Keeps things simple
- iface->Node.ImplInt = giIP_NextIfaceId++;
- sprintf(iface->Name, "%i", (int)iface->Node.ImplInt);
-
- // Append to list
- SHORTLOCK( &glIP_Interfaces );
- if( gIP_Interfaces ) {
- gIP_Interfaces_Last->Next = iface;
- gIP_Interfaces_Last = iface;
- }
- else {
- gIP_Interfaces = iface;
- gIP_Interfaces_Last = iface;
- }
- SHORTREL( &glIP_Interfaces );
-
-// gIP_DriverInfo.RootNode.Size ++;
-
- // Success!
- LEAVE('p', iface);
- return iface;
-}
-
-/**
- * \brief Adds a file to the socket list
- */
-int IPStack_AddFile(tSocketFile *File)
-{
- Log_Log("IPStack", "Added file '%s'", File->Name);
- File->Next = gIP_FileTemplates;
- gIP_FileTemplates = File;
- return 0;
-}
-
-// ---
-// VFS Functions
-// ---
-/**
- * \brief Read from an interface's directory
- */
-char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos)
-{
- tSocketFile *file = gIP_FileTemplates;
- while(Pos-- && file) {
- file = file->Next;
- }
-
- if(!file) return NULL;
-
- return strdup(file->Name);
-}
-
-/**
- * \brief Gets a named node from an interface directory
- */
-tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name)
-{
- tSocketFile *file = gIP_FileTemplates;
-
- // Get file definition
- for(;file;file = file->Next)
- {
- if( strcmp(file->Name, Name) == 0 ) break;
- }
- if(!file) return NULL;
-
- // Pass the buck!
- return file->Init(Node->ImplPtr);
-}
-
-/**
- * \brief Names for interface IOCtl Calls
- */
-static const char *casIOCtls_Iface[] = {
- DRV_IOCTLNAMES,
- "getset_type",
- "get_address", "set_address",
- "getset_subnet",
- "get_device",
- "ping",
- NULL
- };
-/**
- * \brief Handles IOCtls for the IPStack interfaces
- */
-int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- int tmp, size;
- tInterface *iface = (tInterface*)Node->ImplPtr;
- ENTER("pNode iID pData", Node, ID, Data);
-
- switch(ID)
- {
- // --- Standard IOCtls (0-3) ---
- BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Iface)
-
- /*
- * getset_type
- * - Get/Set the interface type
- */
- case 4:
- // Set Type?
- if( Data )
- {
- // Ok, it's set type
- if( Threads_GetUID() != 0 ) {
- LOG("Attempt by non-root to alter an interface (%i)", Threads_GetUID());
- LEAVE('i', -1);
- return -1;
- }
- if( !CheckMem( Data, sizeof(int) ) ) {
- LOG("Invalid pointer %p", Data);
- LEAVE('i', -1);
- return -1;
- }
-
- // Set type
- iface->Type = *(int*)Data;
- LOG("Interface type set to %i", iface->Type);
- size = IPStack_GetAddressSize(iface->Type);
- // Check it's actually valid
- if( iface->Type != 0 && size == 0 ) {
- iface->Type = 0;
- LEAVE('i', -1);
- return -1;
- }
-
- // Clear address
- memset(iface->Address, 0, size);
- }
- LEAVE('i', iface->Type);
- return iface->Type;
-
- /*
- * get_address
- * - Get the interface's address
- */
- case 5:
- size = IPStack_GetAddressSize(iface->Type);
- if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1);
- memcpy( Data, iface->Address, size );
- LEAVE('i', 1);
- return 1;
-
- /*
- * set_address
- * - Set the interface's address
- */
- case 6:
- if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1);
-
- size = IPStack_GetAddressSize(iface->Type);
- if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1);
- // TODO: Protect against trashing
- LOG("Interface address set to '%s'", IPStack_PrintAddress(iface->Type, Data));
- memcpy( iface->Address, Data, size );
- LEAVE_RET('i', 1);
-
- /*
- * getset_subnet
- * - Get/Set the bits in the address subnet
- */
- case 7:
- // Do we want to set the value?
- if( Data )
- {
- // Are we root? (TODO: Check Owner/Group)
- if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1);
- // Is the memory valid
- if( !CheckMem(Data, sizeof(int)) ) LEAVE_RET('i', -1);
-
- // Is the mask sane?
- if( *(int*)Data < 0 || *(int*)Data > IPStack_GetAddressSize(iface->Type)*8-1 )
- LEAVE_RET('i', -1);
- LOG("Set subnet bits to %i", *(int*)Data);
- // Ok, set it
- iface->SubnetBits = *(int*)Data;
- }
- LEAVE_RET('i', iface->SubnetBits);
-
- /*
- * get_device
- * - Gets the name of the attached device
- */
- case 8:
- if( iface->Adapter == NULL )
- LEAVE_RET('i', 0);
- if( Data == NULL )
- LEAVE_RET('i', iface->Adapter->DeviceLen);
- if( !CheckMem( Data, iface->Adapter->DeviceLen+1 ) )
- LEAVE_RET('i', -1);
- strcpy( Data, iface->Adapter->Device );
- LEAVE_RET('i', iface->Adapter->DeviceLen);
-
- /*
- * ping
- * - Send an ICMP Echo
- */
- case 9:
- switch(iface->Type)
- {
- case 0:
- LEAVE_RET('i', 1);
-
- case 4:
- if( !CheckMem( Data, sizeof(tIPv4) ) ) LEAVE_RET('i', -1);
- tmp = IPv4_Ping(iface, *(tIPv4*)Data);
- LEAVE_RET('i', tmp);
-
- case 6:
- LEAVE_RET('i', 1);
- }
- break;
-
- }
-
- LEAVE('i', 0);
- return 0;
-}
-
-// --- Internal ---
-/**
- * \fn tAdapter *IPStack_GetAdapter(const char *Path)
- * \brief Gets/opens an adapter given the path
- */
-tAdapter *IPStack_GetAdapter(const char *Path)
-{
- tAdapter *dev;
- int tmp;
-
- ENTER("sPath", Path);
-
- // Check for loopback
- if( strcmp(Path, "LOOPBACK") == 0 )
- {
- // Initialise if required
- if( gIP_LoopAdapter.DeviceFD == 0 )
- {
- dev = &gIP_LoopAdapter;
-
- dev->NRef = 1;
- dev->DeviceLen = 8;
-
- dev->DeviceFD = VFS_Open( "/Devices/fifo/anon", VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE );
- if( dev->DeviceFD == -1 ) {
- Log_Warning("IPStack", "Unable to open FIFO '/Devices/fifo/anon' for loopback");
- return NULL;
- }
-
- dev->MacAddr.B[0] = 'A';
- dev->MacAddr.B[1] = 'c';
- dev->MacAddr.B[2] = 'e';
- dev->MacAddr.B[3] = 's';
- dev->MacAddr.B[4] = 's';
- dev->MacAddr.B[5] = '2';
-
- // Start watcher
- Link_WatchDevice( dev );
- }
- LEAVE('p', &gIP_LoopAdapter);
- return &gIP_LoopAdapter;
- }
-
- Mutex_Acquire( &glIP_Adapters );
-
- // Check if this adapter is already open
- for( dev = gIP_Adapters; dev; dev = dev->Next )
- {
- if( strcmp(dev->Device, Path) == 0 ) {
- dev->NRef ++;
- Mutex_Release( &glIP_Adapters );
- LEAVE('p', dev);
- return dev;
- }
- }
-
- // Ok, so let's open it
- dev = malloc( sizeof(tAdapter) + strlen(Path) + 1 );
- if(!dev) {
- Log_Warning("IPStack", "GetAdapter - malloc() failed");
- Mutex_Release( &glIP_Adapters );
- LEAVE('n');
- return NULL;
- }
-
- // Fill Structure
- strcpy( dev->Device, Path );
- dev->NRef = 1;
- dev->DeviceLen = strlen(Path);
-
- // Open Device
- dev->DeviceFD = VFS_Open( dev->Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE );
- if( dev->DeviceFD == -1 ) {
- free( dev );
- Mutex_Release( &glIP_Adapters );
- LEAVE('n');
- return NULL;
- }
-
- // Check that it is a network interface
- tmp = VFS_IOCtl(dev->DeviceFD, 0, NULL);
- LOG("Device type = %i", tmp);
- if( tmp != DRV_TYPE_NETWORK ) {
- Log_Warning("IPStack", "IPStack_GetAdapter: '%s' is not a network interface", dev->Device);
- VFS_Close( dev->DeviceFD );
- free( dev );
- Mutex_Release( &glIP_Adapters );
- LEAVE('n');
- return NULL;
- }
-
- // Get MAC Address
- VFS_IOCtl(dev->DeviceFD, NET_IOCTL_GETMAC, &dev->MacAddr);
-
- // Add to list
- dev->Next = gIP_Adapters;
- gIP_Adapters = dev;
-
- Mutex_Release( &glIP_Adapters );
-
- // Start watcher
- Link_WatchDevice( dev );
-
- LEAVE('p', dev);
- return dev;
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Common Header
- */
-#ifndef _IPSTACK_H_
-#define _IPSTACK_H_
-
-#include <acess.h>
-#include <vfs.h>
-
-typedef union uIPv4 tIPv4;
-typedef union uIPv6 tIPv6;
-typedef struct sMacAddr tMacAddr;
-typedef struct sAdapter tAdapter;
-typedef struct sInterface tInterface;
-typedef struct sSocketFile tSocketFile;
-
-typedef void (*tIPCallback)(tInterface *Interface, void *Address, int Length, void *Buffer);
-
-enum eInterfaceTypes {
- AF_NULL,
- AF_INET4 = 4, // tIPv4
- AF_INET6 = 6 // tIPv6
-};
-
-union uIPv4 {
- Uint32 L;
- Uint8 B[4];
-} __attribute__((packed));
-
-union uIPv6 {
- Uint16 W[8];
- Uint32 L[4];
- Uint8 B[16];
-} __attribute__((packed));
-
-struct sMacAddr {
- Uint8 B[6];
-} __attribute__((packed));
-
-/**
- * \brief Route definition structure
- */
-typedef struct sRoute {
- struct sRoute *Next;
-
- tVFS_Node Node; //!< Node for route manipulation
-
- tInterface *Interface; //!< Interface for this route
- int AddressType; //!< 0: Invalid, 4: IPv4, 6: IPv4
- void *Network; //!< Network - Pointer to tIPv4/tIPv6/... at end of structure
- int SubnetBits; //!< Number of bits in \a Network that are valid
- void *NextHop; //!< Next Hop address - Pointer to tIPv4/tIPv6/... at end of structure
- int Metric; //!< Route priority
-} tRoute;
-
-struct sInterface {
- struct sInterface *Next; //!< Next interface in list
-
- tVFS_Node Node; //!< Node to use the interface
-
- tAdapter *Adapter; //!< Adapter the interface is associated with
- int TimeoutDelay; //!< Time in miliseconds before a packet times out
- int Type; //!< Interface type, see ::eInterfaceTypes
-
- void *Address; //!< IP address (stored after the Name)
- int SubnetBits; //!< Number of bits that denote the address network
-
- tRoute Route; //!< Interface route
-
- char Name[];
-};
-
-/**
- * \brief Represents a network adapter
- */
-struct sAdapter {
- struct sAdapter *Next;
-
- int DeviceFD; //!< File descriptor of the device
- int NRef; //!< Number of times it's been referenced
-
- tMacAddr MacAddr; //!< Physical address of the adapter
- int DeviceLen; //!< Device name length
- char Device[]; //!< Device name
-};
-
-/**
- * \brief Describes a socket file definition
- */
-struct sSocketFile
-{
- struct sSocketFile *Next;
- const char *Name;
-
- tVFS_Node *(*Init)(tInterface *Interface);
-};
-
-static const tMacAddr cMAC_BROADCAST = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
-static const tMacAddr cMAC_ZERO = {{0x00,0x00,0x00,0x00,0x00,0x00}};
-
-#define MAC_SET(t,v) memcpy(&(t),&(v),sizeof(tMacAddr))
-#define IP4_SET(t,v) (t).L = (v).L;
-#define IP6_SET(t,v) memcpy(&(t),&(v),sizeof(tIPv6))
-
-#define MAC_EQU(a,b) (memcmp(&(a),&(b),sizeof(tMacAddr))==0)
-#define IP4_EQU(a,b) ((a).L==(b).L)
-#define IP6_EQU(a,b) (memcmp(&(a),&(b),sizeof(tIPv6))==0)
-
-// === FUNCTIONS ===
-#define htonb(v) (v)
-#define htons(v) BigEndian16(v)
-#define htonl(v) BigEndian32(v)
-#define ntonb(v) (v)
-#define ntohs(v) BigEndian16(v)
-#define ntohl(v) BigEndian32(v)
-
-extern int IPStack_AddFile(tSocketFile *File);
-extern int IPStack_GetAddressSize(int AddressType);
-extern int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits);
-extern const char *IPStack_PrintAddress(int AddressType, const void *Address);
-
-extern tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address);
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - IPv4 Protcol Handling
- */
-#define DEBUG 1
-#include "ipstack.h"
-#include "link.h"
-#include "ipv4.h"
-#include "firewall.h"
-
-#define DEFAULT_TTL 32
-
-// === IMPORTS ===
-extern tInterface *gIP_Interfaces;
-extern void ICMP_Initialise();
-extern int ICMP_Ping(tInterface *Interface, tIPv4 Addr);
-extern tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address);
-
-// === PROTOTYPES ===
- int IPv4_Initialise();
- int IPv4_RegisterCallback(int ID, tIPCallback Callback);
-void IPv4_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
-tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast);
-Uint32 IPv4_Netmask(int FixedBits);
-Uint16 IPv4_Checksum(const void *Buf, size_t Length);
- int IPv4_Ping(tInterface *Iface, tIPv4 Addr);
-
-// === GLOBALS ===
-tIPCallback gaIPv4_Callbacks[256];
-
-// === CODE ===
-/**
- * \brief Initialise the IPv4 Code
- */
-int IPv4_Initialise()
-{
- ICMP_Initialise();
- Link_RegisterType(IPV4_ETHERNET_ID, IPv4_int_GetPacket);
- return 1;
-}
-
-/**
- * \brief Registers a callback
- * \param ID 8-bit packet type ID
- * \param Callback Callback function
- */
-int IPv4_RegisterCallback(int ID, tIPCallback Callback)
-{
- if( ID < 0 || ID > 255 ) return 0;
- if( gaIPv4_Callbacks[ID] ) return 0;
- gaIPv4_Callbacks[ID] = Callback;
- return 1;
-}
-
-/**
- * \brief Creates and sends an IPv4 Packet
- * \param Iface Interface
- * \param Address Destination IP
- * \param Protocol Protocol ID
- * \param ID Some random ID number
- * \param Length Data Length
- * \param Data Packet Data
- * \return Boolean Success
- */
-int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data)
-{
- tMacAddr to;
- int bufSize = sizeof(tIPv4Header) + Length;
- char buf[bufSize];
- tIPv4Header *hdr = (void*)buf;
- int ret;
-
- to = ARP_Resolve4(Iface, Address);
- if( MAC_EQU(to, cMAC_ZERO) ) {
- // No route to host
- Log_Notice("IPv4", "No route to host %i.%i.%i.%i",
- Address.B[0], Address.B[1], Address.B[2], Address.B[3]);
- return 0;
- }
-
- // OUTPUT Firewall rule go here
- ret = IPTables_TestChain("OUTPUT",
- 4, (tIPv4*)Iface->Address, &Address,
- Protocol, 0,
- Length, Data);
- if(ret > 0) {
- // Just drop it (with an error)
- Log_Notice("IPv4", "Firewall dropped packet");
- return 0;
- }
-
- memcpy(&hdr->Options[0], Data, Length);
- hdr->Version = 4;
- hdr->HeaderLength = sizeof(tIPv4Header)/4;
- hdr->DiffServices = 0; // TODO: Check
-
- hdr->Reserved = 0;
- hdr->DontFragment = 0;
- hdr->MoreFragments = 0;
- hdr->FragOffLow = 0;
- hdr->FragOffHi = 0;
-
- hdr->TotalLength = htons( bufSize );
- hdr->Identifcation = htons( ID ); // TODO: Check
- hdr->TTL = DEFAULT_TTL;
- hdr->Protocol = Protocol;
- hdr->HeaderChecksum = 0; // Will be set later
- hdr->Source = *(tIPv4*)Iface->Address;
- hdr->Destination = Address;
- hdr->HeaderChecksum = htons( IPv4_Checksum(hdr, sizeof(tIPv4Header)) );
-
- Log_Log("IPv4", "Sending packet to %i.%i.%i.%i",
- Address.B[0], Address.B[1], Address.B[2], Address.B[3]);
- Link_SendPacket(Iface->Adapter, IPV4_ETHERNET_ID, to, bufSize, buf);
- return 1;
-}
-
-/**
- * \fn void IPv4_int_GetPacket(tInterface *Adapter, tMacAddr From, int Length, void *Buffer)
- * \brief Process an IPv4 Packet
- */
-void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
-{
- tIPv4Header *hdr = Buffer;
- tInterface *iface;
- Uint8 *data;
- int dataLength;
- int ret;
-
- if(Length < sizeof(tIPv4Header)) return;
-
- #if 0
- //Log_Log("IPv4", "Version = %i", hdr->Version);
- //Log_Log("IPv4", "HeaderLength = %i", hdr->HeaderLength);
- //Log_Log("IPv4", "DiffServices = %i", hdr->DiffServices);
- Log_Debug("IPv4", "TotalLength = %i", ntohs(hdr->TotalLength) );
- //Log_Log("IPv4", "Identifcation = %i", ntohs(hdr->Identifcation) );
- //Log_Log("IPv4", "TTL = %i", hdr->TTL );
- Log_Debug("IPv4", "Protocol = %i", hdr->Protocol );
- //Log_Log("IPv4", "HeaderChecksum = 0x%x", ntohs(hdr->HeaderChecksum) );
- Log_Debug("IPv4", "Source = %i.%i.%i.%i",
- hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3] );
- Log_Debug("IPv4", "Destination = %i.%i.%i.%i",
- hdr->Destination.B[0], hdr->Destination.B[1],
- hdr->Destination.B[2], hdr->Destination.B[3] );
- #endif
-
- // Check that the version IS IPv4
- if(hdr->Version != 4) {
- Log_Log("IPv4", "hdr->Version(%i) != 4", hdr->Version);
- return;
- }
-
- // Check Header checksum
- {
- Uint16 hdrVal, compVal;
- hdrVal = ntohs(hdr->HeaderChecksum);
- hdr->HeaderChecksum = 0;
- compVal = IPv4_Checksum(hdr, hdr->HeaderLength * 4);
- if(hdrVal != compVal) {
- Log_Log("IPv4", "Header checksum fails (%04x != %04x)", hdrVal, compVal);
- return ;
- }
- hdr->HeaderChecksum = hdrVal;
- }
-
- // Check Packet length
- if( ntohs(hdr->TotalLength) > Length) {
- Log_Log("IPv4", "hdr->TotalLength(%i) > Length(%i)", ntohs(hdr->TotalLength), Length);
- return;
- }
-
- // TODO: Handle packet fragmentation
-
-
- Log_Debug("IPv4", " From %i.%i.%i.%i to %i.%i.%i.%i",
- hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3],
- hdr->Destination.B[0], hdr->Destination.B[1], hdr->Destination.B[2], hdr->Destination.B[3]
- );
-
- // Get Data and Data Length
- dataLength = ntohs(hdr->TotalLength) - sizeof(tIPv4Header);
- data = &hdr->Options[0];
-
- // Get Interface (allowing broadcasts)
- iface = IPv4_GetInterface(Adapter, hdr->Destination, 1);
-
- // Firewall rules
- if( iface ) {
- // Incoming Packets
- ret = IPTables_TestChain("INPUT",
- 4, &hdr->Source, &hdr->Destination,
- hdr->Protocol, 0,
- dataLength, data
- );
- }
- else {
- // Routed packets
- ret = IPTables_TestChain("FORWARD",
- 4, &hdr->Source, &hdr->Destination,
- hdr->Protocol, 0,
- dataLength, data
- );
- }
- switch(ret)
- {
- // 0 - Allow
- case 0: break;
- // 1 - Silent Drop
- case 1:
- Log_Debug("IPv4", "Silently dropping packet");
- return ;
- case -1:
- // Bad rule
- break ;
- // Unknown, silent drop
- default:
- Log_Warning("IPv4", "Unknown firewall rule");
- return ;
- }
-
- // Routing
- if(!iface)
- {
- tMacAddr to;
- tRoute *rt;
-
- Log_Debug("IPv4", "Route the packet");
- // Drop the packet if the TTL is zero
- if( hdr->TTL == 0 ) {
- Log_Warning("IPv4", "TODO: Send ICMP-Timeout when TTL exceeded");
- return ;
- }
-
- hdr->TTL --;
-
- rt = IPStack_FindRoute(4, NULL, &hdr->Destination); // Get the route (gets the interface)
- if( !rt || !rt->Interface )
- return ;
- to = ARP_Resolve4(rt->Interface, hdr->Destination); // Resolve address
- if( MAC_EQU(to, cMAC_ZERO) )
- return ;
-
- // Send packet
- Log_Log("IPv4", "Forwarding packet to %i.%i.%i.%i (via %i.%i.%i.%i)",
- hdr->Destination.B[0], hdr->Destination.B[1],
- hdr->Destination.B[2], hdr->Destination.B[3],
- ((tIPv4*)rt->NextHop)->B[0], ((tIPv4*)rt->NextHop)->B[1],
- ((tIPv4*)rt->NextHop)->B[2], ((tIPv4*)rt->NextHop)->B[3]);
- Link_SendPacket(rt->Interface->Adapter, IPV4_ETHERNET_ID, to, Length, Buffer);
-
-
- return ;
- }
-
- // Send it on
- if( !gaIPv4_Callbacks[hdr->Protocol] ) {
- Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol);
- return ;
- }
-
- gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data );
-}
-
-/**
- * \fn tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address)
- * \brief Searches an adapter for a matching address
- * \param Adapter Incoming Adapter
- * \param Address Destination Address
- * \param Broadcast Allow broadcast packets
- */
-tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast)
-{
- tInterface *iface = NULL;
- Uint32 netmask;
- Uint32 addr, this;
-
- ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast);
-
- addr = ntohl( Address.L );
- LOG("addr = 0x%x", addr);
-
- for( iface = gIP_Interfaces; iface; iface = iface->Next)
- {
- if( iface->Adapter != Adapter ) continue;
- if( iface->Type != 4 ) continue;
- if( IP4_EQU(Address, *(tIPv4*)iface->Address) ) {
- LOG("Exact match");
- LEAVE('p', iface);
- return iface;
- }
-
- if( !Broadcast ) continue;
-
- // Check for broadcast
- this = ntohl( ((tIPv4*)iface->Address)->L );
- netmask = IPv4_Netmask(iface->SubnetBits);
- LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits);
-
- if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) )
- {
- LOG("Broadcast match");
- LEAVE('p', iface);
- return iface;
- }
- }
- LEAVE('n');
- return NULL;
-}
-
-/**
- * \brief Convert a network prefix to a netmask
- * \param FixedBits Netmask size (/n)
- *
- * For example /24 will become 255.255.255.0 (0xFFFFFF00)
- */
-Uint32 IPv4_Netmask(int FixedBits)
-{
- Uint32 ret = 0xFFFFFFFF;
- if( FixedBits == 0 )
- return 0;
- if( FixedBits < 32 )
- {
- ret >>= (32-FixedBits);
- ret <<= (32-FixedBits);
- }
- // Returns a native endian netmask
- return ret;
-}
-
-/**
- * \brief Calculate the IPv4 Checksum
- * \param Buf Input buffer
- * \param Size Size of input
- *
- * One's complement sum of all 16-bit words (bitwise inverted)
- */
-Uint16 IPv4_Checksum(const void *Buf, size_t Length)
-{
- const Uint16 *words = Buf;
- Uint32 sum = 0;
- int i;
-
- // Sum all whole words
- for(i = 0; i < Length/2; i++ )
- {
- sum += ntohs(words[i]);
- }
- if( Length & 1 )
- sum += ntohs( words[i] & 0xFF );
-
- // Apply one's complement
- while (sum >> 16)
- sum = (sum & 0xFFFF) + (sum >> 16);
-
- return ~sum;
-}
-
-/**
- * \brief Sends an ICMP Echo and waits for a reply
- * \param IFace Interface
- * \param Addr Destination address
- */
-int IPv4_Ping(tInterface *IFace, tIPv4 Addr)
-{
- return ICMP_Ping(IFace, Addr);
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - IPv4 Definitions
- */
-#ifndef _IPV4_H_
-#define _IPV4_H_
-
-#include "ipstack.h"
-
-typedef struct sIPv4Header tIPv4Header;
-
-struct sIPv4Header
-{
- struct {
- // Spec says Version is first, but stupid bit ordering
- unsigned HeaderLength: 4; // in 4-byte chunks
- unsigned Version: 4; // = 4
- } __attribute__((packed));
- Uint8 DiffServices; // Differentiated Services
- Uint16 TotalLength;
- Uint16 Identifcation;
-
- struct {
- unsigned Reserved: 1;
- unsigned DontFragment: 1;
- unsigned MoreFragments: 1;
- unsigned FragOffLow: 5;
- } __attribute__((packed));
- Uint8 FragOffHi; // Number of 8-byte blocks from the original start
-
- Uint8 TTL; // Max number of hops, effectively
- Uint8 Protocol;
- Uint16 HeaderChecksum; // One's Complement Sum of the entire header must equal zero
-
- tIPv4 Source;
- tIPv4 Destination;
-
- Uint8 Options[];
-} __attribute__((packed));
-
-#define IP4PROT_ICMP 1
-#define IP4PROT_TCP 6
-#define IP4PROT_UDP 17
-
-#define IPV4_ETHERNET_ID 0x0800
-
-// === FUNCTIONS ===
-extern int IPv4_RegisterCallback(int ID, tIPCallback Callback);
-extern Uint16 IPv4_Checksum(const void *Buf, size_t Length);
-extern int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data);
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - IPv6 Protcol Handling
- */
-#include "ipstack.h"
-#include "link.h"
-#include "ipv6.h"
-#include "firewall.h"
-
-// === IMPORTS ===
-extern tInterface *gIP_Interfaces;
-extern Uint32 IPv4_Netmask(int FixedBits);
-
-// === PROTOTYPES ===
- int IPv6_Initialise();
- int IPv6_RegisterCallback(int ID, tIPCallback Callback);
-void IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
-tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
-
-// === GLOBALS ===
-tIPCallback gaIPv6_Callbacks[256];
-
-// === CODE ===
-/**
- * \brief Initialise the IPv6 handling code
- */
-int IPv6_Initialise()
-{
- Link_RegisterType(IPV6_ETHERNET_ID, IPv6_int_GetPacket);
- return 1;
-}
-
-/**
- * \brief Registers a callback
- * \param ID 8-bit packet type ID
- * \param Callback Callback function
- */
-int IPv6_RegisterCallback(int ID, tIPCallback Callback)
-{
- if( ID < 0 || ID > 255 ) return 0;
- if( gaIPv6_Callbacks[ID] ) return 0;
- gaIPv6_Callbacks[ID] = Callback;
- return 1;
-}
-
-/**
- * \brief Creates and sends an IPv6 Packet
- * \param Iface Interface
- * \param Destination Destination IP
- * \param Protocol Protocol ID
- * \param Length Data Length
- * \param Data Packet Data
- * \return Boolean Success
- */
-int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data)
-{
- return 0;
-}
-
-/**
- * \fn void IPv6_int_GetPacket(tInterface *Interface, tMacAddr From, int Length, void *Buffer)
- * \brief Process an IPv6 Packet
- * \param Interface Input interface
- * \param From Source MAC address
- * \param Length Packet length
- * \param Buffer Packet data
- */
-void IPv6_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
-{
- tInterface *iface;
- tIPv6Header *hdr = Buffer;
- int ret, dataLength;
- char *dataPtr;
- Uint8 nextHeader;
-
- if(Length < sizeof(tIPv6Header)) return;
-
- hdr->Head = ntohl(hdr->Head);
-
- //if( ((hdr->Head >> (20+8)) & 0xF) != 6 )
- if( hdr->Version != 6 )
- return;
-
- #if 1
- Log_Debug("IPv6", "hdr = {");
- Log_Debug("IPv6", " .Version = %i", hdr->Version );
- Log_Debug("IPv6", " .TrafficClass = %i", hdr->TrafficClass );
- Log_Debug("IPv6", " .FlowLabel = %i", hdr->FlowLabel );
- Log_Debug("IPv6", " .PayloadLength = 0x%04x", ntohs(hdr->PayloadLength) );
- Log_Debug("IPv6", " .NextHeader = 0x%02x", hdr->NextHeader );
- Log_Debug("IPv6", " .HopLimit = 0x%02x", hdr->HopLimit );
- Log_Debug("IPv6", " .Source = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Source );
- Log_Debug("IPv6", " .Destination = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Destination );
- Log_Debug("IPv6", "}");
- #endif
-
- // No checksum in IPv6
-
- // Check Packet length
- if( ntohs(hdr->PayloadLength)+sizeof(tIPv6Header) > Length) {
- Log_Log("IPv6", "hdr->PayloadLength(%i) > Length(%i)", ntohs(hdr->PayloadLength), Length);
- return;
- }
-
- // Process Options
- nextHeader = hdr->NextHeader;
- dataPtr = hdr->Data;
- dataLength = hdr->PayloadLength;
- for( ;; )
- {
- struct {
- Uint8 NextHeader;
- Uint8 Length; // In 8-byte chunks, with 0 being 8 bytes long
- Uint8 Data[];
- } *optionHdr;
- optionHdr = (void*)dataPtr;
- // Hop-by-hop options
- if(nextHeader == 0)
- {
- // TODO: Parse the options (actually, RFC2460 doesn't specify any)
- }
- // Routing Options
- else if(nextHeader == 43)
- {
- // TODO: Routing Header options
- }
- else
- {
- break; // Unknown, pass on
- }
- nextHeader = optionHdr->NextHeader;
- dataPtr += (optionHdr->Length + 1) * 8; // 8-octet length (0 = 8 bytes long)
- }
-
- // Get Interface (allowing broadcasts)
- iface = IPv6_GetInterface(Adapter, hdr->Destination, 1);
-
- // Firewall rules
- if( iface ) {
- // Incoming Packets
- ret = IPTables_TestChain("INPUT",
- 6, &hdr->Source, &hdr->Destination,
- hdr->NextHeader, 0,
- hdr->PayloadLength, hdr->Data
- );
- }
- else {
- // Routed packets
- ret = IPTables_TestChain("FORWARD",
- 6, &hdr->Source, &hdr->Destination,
- hdr->NextHeader, 0,
- hdr->PayloadLength, hdr->Data
- );
- }
-
- switch(ret)
- {
- // 0 - Allow
- case 0: break;
- // 1 - Silent Drop
- case 1:
- Log_Debug("IPv6", "Silently dropping packet");
- return ;
- // Unknown, silent drop
- default:
- return ;
- }
-
- // Routing
- if(!iface)
- {
- #if 0
- tMacAddr to;
- tRoute *rt;
-
- Log_Debug("IPv6", "Route the packet");
- // Drop the packet if the TTL is zero
- if( hdr->HopLimit == 0 ) {
- Log_Warning("IPv6", "TODO: Sent ICMP-Timeout when TTL exceeded");
- return ;
- }
-
- hdr->HopLimit --;
-
- rt = IPStack_FindRoute(6, NULL, &hdr->Destination); // Get the route (gets the interface)
- to = ICMP6_ResolveHWAddr(rt->Interface, hdr->Destination); // Resolve address
-
- // Send packet
- Log_Log("IPv6", "Forwarding packet");
- Link_SendPacket(rt->Interface->Adapter, IPV6_ETHERNET_ID, to, Length, Buffer);
- #endif
-
- return ;
- }
-
- // Send it on
- if( !gaIPv6_Callbacks[hdr->NextHeader] ) {
- Log_Log("IPv6", "Unknown Protocol %i", hdr->NextHeader);
- return ;
- }
-
- gaIPv6_Callbacks[hdr->NextHeader]( iface, &hdr->Source, hdr->PayloadLength, hdr->Data );
-}
-
-/**
- * \fn tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address)
- * \brief Searches an adapter for a matching address
- * \param Adapter Source adapter
- * \param Address Destination Address
- * \param Broadcast Allow broadcast?
- */
-tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast)
-{
- int i, j;
- tInterface *iface = NULL;
- Uint32 netmask;
-
- for( iface = gIP_Interfaces; iface; iface = iface->Next)
- {
- tIPv6 *thisAddr;
-
- // Check for this adapter
- if( iface->Adapter != Adapter ) continue;
-
- // Skip non-IPv6 Interfaces
- if( iface->Type != 6 ) continue;
-
- thisAddr = (tIPv6*)iface->Address;
- // If the address is a perfect match, return this interface
- if( IP6_EQU(Address, *thisAddr) ) return iface;
-
- // Check if we want to match broadcast addresses
- if( !Broadcast ) continue;
-
- // Check for broadcast
- // - Check first DWORDs
- if( iface->SubnetBits > 32 && Address.L[0] != thisAddr->L[0] )
- continue;
- if( iface->SubnetBits > 64 && Address.L[1] != thisAddr->L[1] )
- continue;
- if( iface->SubnetBits > 96 && Address.L[2] != thisAddr->L[2] )
- continue;
-
- // Check final DWORD
- j = iface->SubnetBits / 32;
- i = iface->SubnetBits % 32;
- netmask = IPv4_Netmask( iface->SubnetBits % 32 );
-
- // Check the last bit of the netmask
- if( (Address.L[j] >> i) != (thisAddr->L[j] >> i) ) continue;
-
- // Check that the host portion is one
- if( (Address.L[j] & ~netmask) != (0xFFFFFFFF & ~netmask) ) continue;
- if( j >= 2 && Address.L[3] != 0xFFFFFFFF) continue;
- if( j >= 1 && Address.L[2] != 0xFFFFFFFF) continue;
- if( j >= 0 && Address.L[1] != 0xFFFFFFFF) continue;
-
- return iface;
- }
- return NULL;
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - IPv6 Definitions
- */
-#ifndef _IPV6_H_
-#define _IPV6_H_
-
-#include "ipstack.h"
-
-typedef struct sIPv6Header tIPv6Header;
-
-struct sIPv6Header
-{
- #if 0
- // High 4: Version
- // Next 8: Traffic Class
- // Low 20: Flow Label
- Uint32 Head;
- #else
- union {
- Uint32 Head; // Allow a ntohl to happen
- struct {
- unsigned Version: 4;
- unsigned TrafficClass: 8;
- unsigned FlowLabel: 20;
- } PACKED;
- } PACKED;
- #endif
- Uint16 PayloadLength;
- Uint8 NextHeader; // Type of payload data
- Uint8 HopLimit;
- tIPv6 Source;
- tIPv6 Destination;
- char Data[];
-};
-
-#define IPV6_ETHERNET_ID 0x86DD
-
-extern int IPv6_RegisterCallback(int ID, tIPCallback Callback);
-extern int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data);
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Link/Media Layer Interface
- */
-#include "ipstack.h"
-#include "link.h"
-
-// === CONSTANTS ===
-#define MAX_PACKET_SIZE 2048
-
-// === PROTOTYPES ===
-void Link_RegisterType(Uint16 Type, tPacketCallback Callback);
-void Link_InitCRC();
-Uint32 Link_CalculateCRC(void *Data, int Length);
-void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer);
-void Link_WatchDevice(tAdapter *Adapter);
-
-// === GLOBALS ===
- int giRegisteredTypes = 0;
- int giRegisteredTypeSpace = 0;
-struct {
- Uint16 Type;
- tPacketCallback Callback;
-} *gaRegisteredTypes;
- int gbLink_CRCTableGenerated = 0;
-Uint32 gaiLink_CRCTable[256];
-
-// === CODE ===
-/**
- * \fn void Link_RegisterType(Uint16 Type, tPacketCallback Callback)
- * \brief Registers a callback for a specific packet type
- *
- * \todo Make thread safe (place a mutex on the list)
- */
-void Link_RegisterType(Uint16 Type, tPacketCallback Callback)
-{
- int i;
- void *tmp;
-
- for( i = giRegisteredTypes; i -- ; )
- {
- if(gaRegisteredTypes[i].Type == Type) {
- Log_Warning("Net Link", "Attempt to register 0x%x twice", Type);
- return ;
- }
- // Ooh! Free slot!
- if(gaRegisteredTypes[i].Callback == NULL) break;
- }
-
- if(i == -1)
- {
- giRegisteredTypeSpace += 5;
- tmp = realloc(gaRegisteredTypes, giRegisteredTypeSpace*sizeof(*gaRegisteredTypes));
- if(!tmp) {
- Log_Warning("Net Link",
- "Out of heap space! (Attempted to allocate %i)",
- giRegisteredTypeSpace*sizeof(*gaRegisteredTypes)
- );
- return ;
- }
- gaRegisteredTypes = tmp;
- i = giRegisteredTypes;
- giRegisteredTypes ++;
- }
-
- gaRegisteredTypes[i].Callback = Callback;
- gaRegisteredTypes[i].Type = Type;
-}
-
-/**
- * \fn void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer)
- * \brief Formats and sends a packet on the specified interface
- */
-void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer)
-{
- int bufSize = sizeof(tEthernetHeader) + ((Length+3)&~3) + 4;
- Uint8 buf[bufSize]; // dynamic stack arrays ftw!
- tEthernetHeader *hdr = (void*)buf;
-
- Log_Log("Net Link", "Sending %i bytes to %02x:%02x:%02x:%02x:%02x:%02x (Type 0x%x)",
- Length, To.B[0], To.B[1], To.B[2], To.B[3], To.B[4], To.B[5], Type);
-
- hdr->Dest = To;
- hdr->Src = Adapter->MacAddr;
- hdr->Type = htons(Type);
-
- memcpy(hdr->Data, Buffer, Length);
-
- *(Uint32*) &hdr->Data[bufSize-4] = 0;
- *(Uint32*) &hdr->Data[bufSize-4] = htonl( Link_CalculateCRC(buf, bufSize) );
-
- VFS_Write(Adapter->DeviceFD, bufSize, buf);
-}
-
-void Link_WorkerThread(void *Ptr)
-{
- tAdapter *Adapter = Ptr;
-
- Threads_SetName(Adapter->Device);
- Log_Log("Net Link", "Thread %i watching '%s'", Threads_GetTID(), Adapter->Device);
-
- // Child Thread
- while(Adapter->DeviceFD != -1)
- {
- Uint8 buf[MAX_PACKET_SIZE];
- tEthernetHeader *hdr = (void*)buf;
- int ret, i;
- Uint32 checksum;
-
- // Wait for a packet (Read on a network device is blocking)
- //Log_Debug("NET", "Waiting on adapter FD#0x%x", Adapter->DeviceFD);
- ret = VFS_Read(Adapter->DeviceFD, MAX_PACKET_SIZE, buf);
- if(ret == -1) break;
-
- if(ret < sizeof(tEthernetHeader)) {
- Log_Log("Net Link", "Recieved an undersized packet (%i < %i)",
- ret, sizeof(tEthernetHeader));
- continue;
- }
-
- Log_Log("Net Link",
- "Packet from %02x:%02x:%02x:%02x:%02x:%02x"
- " to %02x:%02x:%02x:%02x:%02x:%02x (Type=%04x)",
- hdr->Src.B[0], hdr->Src.B[1], hdr->Src.B[2],
- hdr->Src.B[3], hdr->Src.B[4], hdr->Src.B[5],
- hdr->Dest.B[0], hdr->Dest.B[1], hdr->Dest.B[2],
- hdr->Dest.B[3], hdr->Dest.B[4], hdr->Dest.B[5],
- ntohs(hdr->Type)
- );
- checksum = *(Uint32*)&hdr->Data[ret-sizeof(tEthernetHeader)-4];
- //Log_Log("NET", "Checksum 0x%08x", checksum);
- // TODO: Check checksum
-
- // Check if there is a registered callback for this packet type
- for( i = giRegisteredTypes; i--; )
- {
- if(gaRegisteredTypes[i].Type == ntohs(hdr->Type)) break;
- }
- // No? Ignore it
- if( i == -1 ) {
- Log_Log("Net Link", "Unregistered type 0x%x", ntohs(hdr->Type));
- continue;
- }
-
- // Call the callback
- gaRegisteredTypes[i].Callback(
- Adapter,
- hdr->Src,
- ret - sizeof(tEthernetHeader),
- hdr->Data
- );
- }
-
- Log_Log("Net Link", "Watcher terminated (file closed)");
-
- Threads_Exit(0, 0);
-}
-
-/**
- * \fn void Link_WatchDevice(tAdapter *Adapter)
- * \brief Spawns a worker thread to watch the specified adapter
- */
-void Link_WatchDevice(tAdapter *Adapter)
-{
- int tid;
-
- if( !gbLink_CRCTableGenerated )
- Link_InitCRC();
-
- tid = Proc_SpawnWorker(Link_WorkerThread, Adapter); // Create a new worker thread
-
- if(tid < 0) {
- Log_Warning("Net Link", "Unable to create watcher thread for '%s'", Adapter->Device);
- return ;
- }
-
- Log_Log("Net Link", "Watching '%s' using tid %i", Adapter->Device, tid);
-}
-
-// From http://www.cl.cam.ac.uk/research/srg/bluebook/21/crc/node6.html
-#define QUOTIENT 0x04c11db7
-void Link_InitCRC(void)
-{
- int i, j;
- Uint32 crc;
-
- for (i = 0; i < 256; i++)
- {
- crc = i << 24;
- for (j = 0; j < 8; j++)
- {
- if (crc & 0x80000000)
- crc = (crc << 1) ^ QUOTIENT;
- else
- crc = crc << 1;
- }
- gaiLink_CRCTable[i] = crc;
- }
-
- gbLink_CRCTableGenerated = 1;
-}
-
-Uint32 Link_CalculateCRC(void *Data, int Length)
-{
- // x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
- Uint32 result;
- int i;
- Uint32 *data = Data;
-
- if(Length < 4) return 0;
-
- result = *data++ << 24;
- result |= *data++ << 16;
- result |= *data++ << 8;
- result |= *data++;
- result = ~ result;
- Length -= 4;
-
- for( i = 0; i < Length; i++ )
- {
- result = (result << 8 | *data++) ^ gaiLink_CRCTable[result >> 24];
- }
-
- return ~result;
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Link/Media Layer Header
- */
-#ifndef _LINK_H_
-#define _LINK_H_
-
-// === EXTERNAL ===
-typedef void (*tPacketCallback)(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
-
-extern void Link_RegisterType(Uint16 Type, tPacketCallback Callback);
-extern void Link_SendPacket(tAdapter *Interface, Uint16 Type, tMacAddr To, int Length, void *Buffer);
-extern void Link_WatchDevice(tAdapter *Adapter);
-
-// === INTERNAL ===
-typedef struct sEthernetHeader tEthernetHeader;
-typedef struct sEthernetFooter tEthernetFooter;
-struct sEthernetHeader {
- tMacAddr Dest;
- tMacAddr Src;
- Uint16 Type;
- Uint8 Data[];
-};
-
-struct sEthernetFooter {
- //Uint32 CRC;
-};
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Stack Initialisation
- */
-#define DEBUG 0
-#define VERSION VER2(0,10)
-#include "ipstack.h"
-#include "link.h"
-#include <modules.h>
-#include <fs_devfs.h>
-
-// === IMPORTS ===
-extern int ARP_Initialise();
-extern void UDP_Initialise();
-extern void TCP_Initialise();
-extern int IPv4_Initialise();
-extern int IPv6_Initialise();
-
-extern tAdapter *IPStack_GetAdapter(const char *Path);
-extern char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos);
-extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name);
-extern int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data);
-extern tInterface gIP_LoopInterface;
-extern tInterface *IPStack_AddInterface(const char *Device, const char *Name);
-extern tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric);
-
-// === PROTOTYPES ===
- int IPStack_Install(char **Arguments);
- int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, IPStack, IPStack_Install, NULL, NULL);
-tVFS_NodeType gIP_RootNodeType = {
- .ReadDir = IPStack_Root_ReadDir,
- .FindDir = IPStack_Root_FindDir,
- .IOCtl = IPStack_Root_IOCtl
-};
-tDevFS_Driver gIP_DriverInfo = {
- NULL, "ip",
- {
- .Size = -1, // Number of interfaces
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gIP_RootNodeType
- }
-};
-
-// === CODE ===
-/**
- * \fn int IPStack_Install(char **Arguments)
- * \brief Intialise the relevant parts of the stack and register with DevFS
- */
-int IPStack_Install(char **Arguments)
-{
- int i = 0;
-
- // Layer 3 - Network Layer Protocols
- ARP_Initialise();
- IPv4_Initialise();
- IPv6_Initialise();
- // Layer 4 - Transport Layer Protocols
- TCP_Initialise();
- UDP_Initialise();
-
- if(Arguments)
- {
- // Parse module arguments
- for( i = 0; Arguments[i]; i++ )
- {
- // TODO:
- // Define interfaces by <Device>:<Type>:<HexStreamAddress>:<Bits>
- // Where:
- // - <Device> is the device path (E.g. /Devices/ne2k/0)
- // - <Type> is a number (e.g. 4) or symbol (e.g. AF_INET4)
- // - <HexStreamAddress> is a condensed hexadecimal stream (in big endian)
- // (E.g. 0A000201 for 10.0.2.1 IPv4)
- // - <Bits> is the number of subnet bits (E.g. 24 for an IPv4 Class C)
- // Example: /Devices/ne2k/0:4:0A00020A:24
- // would define an interface with the address 10.0.2.10/24
- if( Arguments[i][0] == '/' ) {
- // Define Interface
- char *dev, *type, *addr, *bits;
-
- // Read definition
- dev = Arguments[i];
- type = strchr(dev, ':');
- if( !type ) {
- Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
- continue;
- }
- *type = '\0'; type ++;
-
- addr = strchr(type, ':');
- if( !addr ) {
- Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
- continue;
- }
- *addr = '\0'; addr ++;
-
- bits = strchr(addr, ':');
- if( !bits ) {
- Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
- continue;
- }
- *bits = '\0'; bits ++;
-
- // Define interface
- {
- int iType = atoi(type);
- int size = IPStack_GetAddressSize(iType);
- Uint8 addrData[size];
- int iBits = atoi(bits);
-
- UnHex(addrData, size, addr);
-
- tInterface *iface = IPStack_AddInterface(dev, "");
- if( !iface ) {
- Log_Warning("IPStack", "Unable to add interface on '%s'", dev);
- continue ;
- }
- iface->Type = iType;
- memcpy(iface->Address, addrData, size);
- iface->SubnetBits = iBits;
-
- // Route for addrData/iBits, no next hop, default metric
- IPStack_AddRoute(iface->Name, iface->Address, iBits, NULL, 0);
-
- Log_Notice("IPStack", "Boot interface %s/%i on %s",
- IPStack_PrintAddress(iType, addrData), iBits,
- dev);
- }
-
- continue;
- }
-
- // I could also define routes using <Interface>:<HexStreamNetwork>:<Bits>[:<HexStreamGateway>]
- // Example: 1:00000000:0:0A000201
- if( '0' <= Arguments[i][0] && Arguments[i][0] <= '9' )
- {
- // Define Interface
- char *ifaceName, *network, *bits, *gateway;
-
- // Read definition
- ifaceName = Arguments[i];
-
- network = strchr(ifaceName, ':');
- if( !network ) {
- Log_Warning("IPStack", "<iface>:<HexStreamNetwork>:<Bits>:<HexStreamGateway>");
- continue;
- }
- *network = '\0'; network ++;
-
- bits = strchr(network, ':');
- if( !bits ) {
- Log_Warning("IPStack", "<Device>:<Type>:<HexStreamAddress>:<Bits>");
- continue;
- }
- *bits = '\0'; bits ++;
-
- gateway = strchr(bits, ':');
- if( gateway ) {
- *gateway = '\0'; gateway ++;
- }
-
- // Define route
- {
- tVFS_Node *node = IPStack_Root_FindDir(NULL, ifaceName);
- if( !node ) {
- Log_Warning("IPStack", "Unknown interface '%s' in arg %i", ifaceName, i);
- continue ;
- }
- tInterface *iface = node->ImplPtr;
-
- int size = IPStack_GetAddressSize(iface->Type);
- Uint8 netData[size];
- Uint8 gwData[size];
- int iBits = atoi(bits);
-
- UnHex(netData, size, network);
- if( gateway )
- UnHex(gwData, size, gateway);
- else
- memset(gwData, 0, size);
-
- IPStack_AddRoute(ifaceName, netData, iBits, gwData, 30);
- }
-
- continue;
- }
- }
- }
-
- // Initialise loopback interface
- gIP_LoopInterface.Adapter = IPStack_GetAdapter("LOOPBACK");
-
- DevFS_AddDevice( &gIP_DriverInfo );
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Gets the size (in bytes) of a specified form of address
- */
-int IPStack_GetAddressSize(int AddressType)
-{
- switch(AddressType)
- {
- case -1: // -1 = maximum
- return sizeof(tIPv6);
-
- case AF_NULL:
- return 0;
-
- case AF_INET4:
- return sizeof(tIPv4);
- case AF_INET6:
- return sizeof(tIPv6);
-
- default:
- return 0;
- }
-}
-
-/**
- * \brief Compare two IP Addresses masked by CheckBits
- */
-int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits)
-{
- int size = IPStack_GetAddressSize(AddressType);
- Uint8 mask;
- const Uint8 *addr1 = Address1, *addr2 = Address2;
-
- // Sanity check size
- if( CheckBits < 0 ) CheckBits = size*8;
- if( CheckBits > size*8 ) CheckBits = size*8;
-
- if( CheckBits == 0 ) return 1; // /0 matches anything
-
- // Check first bits/8 bytes
- if( memcmp(Address1, Address2, CheckBits/8) != 0 ) return 0;
-
- // Check if the mask is a multiple of 8
- if( CheckBits % 8 == 0 ) return 1;
-
- // Check last bits
- mask = 0xFF << (8 - (CheckBits % 8));
- if( (addr1[CheckBits/8] & mask) == (addr2[CheckBits/8] & mask) )
- return 1;
-
- return 0;
-}
-
-const char *IPStack_PrintAddress(int AddressType, const void *Address)
-{
- switch( AddressType )
- {
- case 4: {
- static char ret[4*3+3+1];
- const Uint8 *addr = Address;
- sprintf(ret, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]);
- return ret;
- }
-
- case 6: { // TODO: address compression
- static char ret[8*4+7+1];
- const Uint16 *addr = Address;
- sprintf(ret, "%x:%x:%x:%x:%x:%x:%x:%x",
- ntohs(addr[0]), ntohs(addr[1]), ntohs(addr[2]), ntohs(addr[3]),
- ntohs(addr[4]), ntohs(addr[5]), ntohs(addr[6]), ntohs(addr[7])
- );
- return ret;
- }
-
- default:
- return "";
- }
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - Routing Tables
- */
-#define DEBUG 0
-#define VERSION VER2(0,10)
-#include <acess.h>
-#include <api_drv_common.h>
-#include "ipstack.h"
-#include "link.h"
-
-#define DEFAUTL_METRIC 30
-
-// === IMPORTS ===
-extern tInterface *gIP_Interfaces;
-extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Filename);
-
-// === PROTOTYPES ===
-// - Routes directory
-char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name);
- int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
- int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName);
-tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric);
- int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric);
- int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data);
-// - Route Management
-tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric);
-tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric);
-tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address);
-// - Individual Routes
- int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data);
-
-// === GLOBALS ===
- int giIP_NextRouteId = 1;
-tRoute *gIP_Routes;
-tRoute *gIP_RoutesEnd;
-tVFS_NodeType gIP_RouteNodeType = {
- .IOCtl = IPStack_Route_IOCtl
-};
-tVFS_NodeType gIP_RouteDirNodeType = {
- .ReadDir = IPStack_RouteDir_ReadDir,
- .FindDir = IPStack_RouteDir_FindDir,
- .MkNod = IPStack_RouteDir_MkNod,
- .Relink = IPStack_RouteDir_Relink,
- .IOCtl = IPStack_RouteDir_IOCtl
-};
-tVFS_Node gIP_RouteNode = {
- .Flags = VFS_FFLAG_DIRECTORY,
- .Size = -1,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Type = &gIP_RouteDirNodeType
-};
-
-// === CODE ===
-/**
- * \brief ReadDir for the /Devices/ip/routes/ directory
- */
-char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos)
-{
- tRoute *rt;
-
- for(rt = gIP_Routes; rt && Pos --; rt = rt->Next);
- if( !rt ) return NULL;
-
- {
- int addrlen = IPStack_GetAddressSize(rt->AddressType);
- int len = sprintf(NULL, "%i::%i:%i", rt->AddressType, rt->SubnetBits, rt->Metric) + addrlen*2;
- char buf[len+1];
- int ofs;
- ofs = sprintf(buf, "%i:", rt->AddressType);
- ofs += Hex(buf+ofs, addrlen, rt->Network);
- sprintf(buf+ofs, ":%i:%i", rt->SubnetBits, rt->Metric);
- return strdup(buf);
- }
-}
-
-/**
- * \brief FindDir for the /Devices/ip/routes/ directory
- */
-tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name)
-{
- // Interpret the name as <type>:<addr>, returning the interface for
- // needed to access that address.
- // E.g. '@4:0A02000A' - 10.2.0.10
- // Hm... It could do with a way to have a better address type representation
- if( Name[0] == '@' )
- {
- tRoute *rt;
- int ofs = 1;
- int type;
-
- ofs += ParseInt(Name+ofs, &type);
- int addrSize = IPStack_GetAddressSize(type);
- Uint8 addrData[addrSize];
-
- // Separator
- if( Name[ofs] != ':' ) return NULL;
- ofs ++;
-
- // Error if the size is invalid
- if( strlen(Name+ofs) != addrSize*2 )
- return NULL;
-
- // Parse the address
- // - Error if the address data is not fully hex
- if( UnHex(addrData, addrSize, Name + ofs) != addrSize )
- return NULL;
-
- // Find the route
- rt = IPStack_FindRoute(type, NULL, addrData);
- if(!rt) return NULL;
-
- if( rt->Interface )
- {
- // Return the interface node
- // - Sure it's hijacking it from inteface.c's area, but it's
- // simpler this way
- return &rt->Interface->Node;
- }
- else
- {
- return NULL;
- }
- }
- else if( Name[0] == '#' )
- {
- int num, ofs = 1;
-
- ofs = ParseInt(Name+ofs, &num);
- if( ofs == 1 ) return NULL;
- if( Name[ofs] != '\0' ) return NULL;
- if( num < 0) return NULL;
-
- for( tRoute *rt = gIP_Routes; rt; rt = rt->Next )
- {
- if( rt->Node.Inode > num ) return NULL;
- if( rt->Node.Inode == num ) return &rt->Node;
- }
- return NULL;
- }
- else
- {
- int type = _Route_ParseRouteName(Name, NULL, NULL, NULL);
- if( type <= 0 ) return NULL;
-
- int addrSize = IPStack_GetAddressSize(type);
- Uint8 addrData[addrSize];
- int subnet_bits, metric;
-
- _Route_ParseRouteName(Name, addrData, &subnet_bits, &metric);
-
- tRoute *rt = _Route_FindExactRoute(type, addrData, subnet_bits, metric);
- if(rt) return &rt->Node;
- return NULL;
- }
-}
-
-/**
- * \brief Create a new route node
- */
-int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
-{
- if( Flags ) return -EINVAL;
- if( Threads_GetUID() != 0 ) return -EACCES;
-
- int type = _Route_ParseRouteName(Name, NULL, NULL, NULL);
- if( type <= 0 ) return -EINVAL;
-
- int size = IPStack_GetAddressSize(type);
- Uint8 addrdata[size];
- int subnet, metric;
-
- _Route_ParseRouteName(Name, addrdata, &subnet, &metric);
-
- // Check for duplicates
- if( _Route_FindExactRoute(type, addrdata, subnet, metric) )
- return -EEXIST;
-
- IPStack_Route_Create(type, addrdata, subnet, metric);
-
- return 0;
-}
-
-/**
- * \brief Rename / Delete a route
- */
-int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName)
-{
- tRoute *rt;
-
- if( Threads_GetUID() != 0 ) return -EACCES;
-
- // Get the original route entry
- {
- int type = _Route_ParseRouteName(OldName, NULL, NULL, NULL);
- if(type <= 0) return -EINVAL;
- Uint8 addr[IPStack_GetAddressSize(type)];
- int subnet, metric;
- _Route_ParseRouteName(OldName, addr, &subnet, &metric);
-
- rt = _Route_FindExactRoute(type, addr, subnet, metric);
- }
-
- if( NewName == NULL )
- {
- // Delete the route
- tRoute *prev = NULL;
- for(tRoute *r = gIP_Routes; r && r != rt; prev = r, r = r->Next);
-
- if(prev)
- prev->Next = rt->Next;
- else
- gIP_Routes = rt->Next;
- free(rt);
- }
- else
- {
- // Change the route
- int type = _Route_ParseRouteName(NewName, NULL, NULL, NULL);
- if(type <= 0) return -EINVAL;
- Uint8 addr[IPStack_GetAddressSize(type)];
- int subnet, metric;
- _Route_ParseRouteName(NewName, addr, &subnet, &metric);
-
- return -ENOTIMPL;
- }
- return 0;
-}
-
-tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric)
-{
- for(tRoute *rt = gIP_Routes; rt; rt = rt->Next)
- {
- if( rt->AddressType != Type ) continue;
- if( rt->Metric != Metric ) continue;
- if( rt->SubnetBits != Subnet ) continue;
- if( IPStack_CompareAddress(Type, rt->Network, Network, -1) == 0 )
- continue ;
- return rt;
- }
- return NULL;
-}
-
-int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric)
-{
- int type, addrlen;
- int ofs = 0, ilen;
-
- ENTER("sName pAddr pSubnetBits pMetric", Name, Addr, SubnetBits, Metric);
-
- ilen = ParseInt(Name, &type);
- if(ilen == 0) {
- LOG("Type failed to parse");
- LEAVE_RET('i', -1);
- }
- ofs += ilen;
-
- if(Name[ofs] != ':') LEAVE_RET('i', -1);
- ofs ++;
-
- addrlen = IPStack_GetAddressSize(type);
- if( Addr )
- {
- if( UnHex(Addr, addrlen, Name + ofs) != addrlen )
- return -1;
- }
- ofs += addrlen*2;
-
- if(Name[ofs] != ':') LEAVE_RET('i', -1);
- ofs ++;
-
- ilen = ParseInt(Name+ofs, SubnetBits);
- if(ilen == 0) {
- LOG("Subnet failed to parse");
- LEAVE_RET('i', -1);
- }
- ofs += ilen;
-
- if(Name[ofs] != ':') LEAVE_RET('i', -1);
- ofs ++;
-
- ilen = ParseInt(Name+ofs, Metric);
- if(ilen == 0) {
- LOG("Metric failed to parse");
- LEAVE_RET('i', -1);
- }
- ofs += ilen;
-
- if(Name[ofs] != '\0') LEAVE_RET('i', -1);
-
- LEAVE('i', type);
- return type;
-}
-
-/**
- * \brief Names for the route list IOCtl Calls
- */
-static const char *casIOCtls_RouteDir[] = {
- DRV_IOCTLNAMES,
- "locate_route", // Find the best route for an address - struct {int Type, char Address[]} *
- NULL
- };
-
-/**
- * \brief IOCtl for /Devices/ip/routes/
- */
-int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tRoute *rt;
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- // --- Standard IOCtls (0-3) ---
- BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_RouteDir)
-
- case 4: // Locate Route
- {
- struct {
- int Type;
- Uint8 Addr[];
- } *data = Data;
-
- if( !CheckMem(Data, sizeof(int)) )
- LEAVE_RET('i', -1);
- if( !CheckMem(Data, sizeof(int) + IPStack_GetAddressSize(data->Type)) )
- LEAVE_RET('i', -1);
-
- Log_Debug("IPStack", "Route_RouteDir_IOCtl - FindRoute %i, %s",
- data->Type, IPStack_PrintAddress(data->Type, data->Addr) );
- rt = IPStack_FindRoute(data->Type, NULL, data->Addr);
-
- if( !rt )
- LEAVE_RET('i', 0);
-
- LEAVE('i', rt->Node.Inode);
- return rt->Node.Inode;
- }
- break;
- }
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \brief Create a new route entry
- * \param InterfaceName Name of the interface using this route
- */
-tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric)
-{
- tRoute *rt;
- int size;
-
- // Get the size of the specified address type
- size = IPStack_GetAddressSize(AddrType);
- if( size == 0 ) {
- return NULL;
- }
-
- // Allocate space
- rt = calloc(1, sizeof(tRoute) + size*2 );
-
- // Set up node
- rt->Node.ImplPtr = rt;
- rt->Node.Inode = giIP_NextRouteId ++;
- rt->Node.Size = 0;
- rt->Node.NumACLs = 1,
- rt->Node.ACLs = &gVFS_ACL_EveryoneRO;
- rt->Node.Type = &gIP_RouteNodeType;
-
- // Set up state
- rt->AddressType = AddrType;
- rt->Network = (void *)( (tVAddr)rt + sizeof(tRoute) );
- rt->SubnetBits = SubnetBits;
- rt->NextHop = (void *)( (tVAddr)rt + sizeof(tRoute) + size );
- rt->Interface = NULL;
- rt->Metric = Metric;
- memcpy(rt->Network, Network, size);
- memset(rt->NextHop, 0, size);
-
- // Clear non-fixed bits
- {
- Uint8 *data = rt->Network;
- int i;
- i = SubnetBits / 8;
- if( SubnetBits % 8 ) {
- data[i] &= ~((1 << (8 - SubnetBits % 8)) - 1);
- i ++;
- }
- memset(data + i, 0, size - i);
- }
-
-
- // Add to list
- if( gIP_RoutesEnd ) {
- gIP_RoutesEnd->Next = rt;
- gIP_RoutesEnd = rt;
- }
- else {
- gIP_Routes = gIP_RoutesEnd = rt;
- }
-
-// Log_Log("IPStack", "Route entry for '%s' created", InterfaceName);
-
- return rt;
-}
-
-/**
- * \brief Add and fill a route
- */
-tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric)
-{
- tInterface *iface;
- tRoute *rt;
- int addrSize;
-
- {
- tVFS_Node *tmp;
- tmp = IPStack_Root_FindDir(NULL, Interface);
- if(!tmp) return NULL;
- iface = tmp->ImplPtr;
- if(tmp->Type->Close) tmp->Type->Close(tmp);
- }
-
- rt = IPStack_Route_Create(iface->Type, Network, SubnetBits, Metric);
- if( !rt ) return NULL;
-
- addrSize = IPStack_GetAddressSize(iface->Type);
- rt->Interface = iface;
-
- if( NextHop )
- memcpy(rt->NextHop, NextHop, addrSize);
-
- return rt;
-}
-
-/**
- */
-tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address)
-{
- tRoute *rt;
- tRoute *best = NULL;
- tInterface *iface;
- int addrSize;
-
- ENTER("iAddressType pInterface sAddress",
- AddressType, Interface, IPStack_PrintAddress(AddressType, Address));
-
- if( Interface && AddressType != Interface->Type ) {
- LOG("Interface->Type (%i) != AddressType", Interface->Type);
- LEAVE('n');
- return NULL;
- }
-
- // Get address size
- addrSize = IPStack_GetAddressSize(AddressType);
-
- // Check against explicit routes
- for( rt = gIP_Routes; rt; rt = rt->Next )
- {
- // Check interface
- if( Interface && rt->Interface != Interface ) continue;
- // Check address type
- if( rt->AddressType != AddressType ) continue;
-
- LOG("Checking network %s/%i", IPStack_PrintAddress(AddressType, rt->Network), rt->SubnetBits);
-
- // Check if the address matches
- if( !IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) )
- continue;
-
- if( best ) {
- // More direct routes are preferred
- if( best->SubnetBits > rt->SubnetBits ) {
- LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits);
- continue;
- }
- // If equally direct, choose the best metric
- if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) {
- LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric);
- continue;
- }
- }
-
- best = rt;
- }
-
- // Check against implicit routes
- if( !best && !Interface )
- {
- for( iface = gIP_Interfaces; iface; iface = iface->Next )
- {
- if( Interface && iface != Interface ) continue;
- if( iface->Type != AddressType ) continue;
-
-
- // Check if the address matches
- if( !IPStack_CompareAddress(AddressType, iface->Address, Address, iface->SubnetBits) )
- continue;
-
- if( best ) {
- // More direct routes are preferred
- if( best->SubnetBits > rt->SubnetBits ) {
- LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits);
- continue;
- }
- // If equally direct, choose the best metric
- if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) {
- LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric);
- continue;
- }
- }
-
- rt = &iface->Route;
- memcpy(rt->Network, iface->Address, addrSize);
- memset(rt->NextHop, 0, addrSize);
- rt->Metric = DEFAUTL_METRIC;
- rt->SubnetBits = iface->SubnetBits;
-
- best = rt;
- }
- }
- if( !best && Interface )
- {
- rt = &Interface->Route;
- // Make sure route is up to date
- memcpy(rt->Network, Interface->Address, addrSize);
- memset(rt->NextHop, 0, addrSize);
- rt->Metric = DEFAUTL_METRIC;
- rt->SubnetBits = Interface->SubnetBits;
-
- if( IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) )
- {
- best = rt;
- }
- }
-
- LEAVE('p', best);
- return best;
-}
-
-/**
- * \brief Names for route IOCtl Calls
- */
-static const char *casIOCtls_Route[] = {
- DRV_IOCTLNAMES,
- "get_nexthop", // Get next hop - (void *Data), returns boolean success
- "set_nexthop", // Set next hop - (void *Data), returns boolean success
- "get_interface", // Get interface name - (char *Name), returns name length, NULL OK
- "set_interface", // Set interface - (const char *Name)
- NULL
- };
-
-/**
- * \brief IOCtl for /Devices/ip/routes/#
- */
-int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tRoute *rt = Node->ImplPtr;
- int addrSize = IPStack_GetAddressSize(rt->AddressType);
-
- switch(ID)
- {
- // --- Standard IOCtls (0-3) ---
- BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_Route)
-
- // Get Next Hop
- case 4:
- if( !CheckMem(Data, addrSize) ) return -1;
- memcpy(Data, rt->NextHop, addrSize);
- return 1;
- // Set Next Hop
- case 5:
- if( Threads_GetUID() != 0 ) return -1;
- if( !CheckMem(Data, addrSize) ) return -1;
- memcpy(rt->NextHop, Data, addrSize);
- return 1;
-
- // Get interface name
- case 6:
- if( !rt->Interface ) {
- if(Data && !CheckMem(Data, 1) )
- return -1;
- if(Data)
- *(char*)Data = 0;
- return 0;
- }
- if( Data ) {
- if( !CheckMem(Data, strlen(rt->Interface->Name) + 1) )
- return -1;
- strcpy(Data, rt->Interface->Name);
- }
- return strlen(rt->Interface->Name);
- // Set interface name
- case 7:
- if( Threads_GetUID() != 0 )
- return -1;
- if( !CheckString(Data) )
- return -1;
- else
- {
- tInterface *iface;
- tVFS_Node *tmp;
- tmp = IPStack_Root_FindDir(NULL, Data);
- if(!tmp)
- return -1;
- iface = tmp->ImplPtr;
- if(tmp->Type->Close) tmp->Type->Close(tmp);
-
- if( iface->Type != rt->AddressType )
- return -1;
-
- // TODO: Other checks?
-
- rt->Interface = iface;
- }
- return 0;
-
- default:
- return -1;
- }
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - SCTP (Stream Control Transmission Protocol) Handling
- */
-#include "ipstack.h"
-#include <api_drv_common.h>
-#include "sctp.h"
-
-#define SCTP_ALLOC_BASE 0xC000
-
-// === PROTOTYPES ===
-void SCTP_Initialise();
-void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
-void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length);
-// --- Listening Server
-tVFS_Node *SCTP_Server_Init(tInterface *Interface);
-char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID);
-tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name);
- int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data);
-void SCTP_Server_Close(tVFS_Node *Node);
-// --- Client Channels
-tVFS_Node *SCTP_Channel_Init(tInterface *Interface);
-Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data);
-void SCTP_Channel_Close(tVFS_Node *Node);
-// --- Helpers
-Uint16 SCTP_int_AllocatePort();
- int SCTP_int_MarkPortAsUsed(Uint16 Port);
-void SCTP_int_FreePort(Uint16 Port);
-
-// === GLOBALS ===
-tMutex glSCTP_Servers;
-tSCTPServer *gpSCTP_Servers;
-
-tMutex glSCTP_Channels;
-tSCTPChannel *gpSCTP_Channels;
-
-tMutex glSCTP_Ports;
-Uint32 gSCTP_Ports[0x10000/32];
-
-tSocketFile gSCTP_ServerFile = {NULL, "sctps", SCTP_Server_Init};
-tSocketFile gSCTP_ClientFile = {NULL, "sctpc", SCTP_Channel_Init};
-
-// === CODE ===
-/**
- * \fn void TCP_Initialise()
- * \brief Initialise the TCP Layer
- */
-void SCTP_Initialise()
-{
- IPStack_AddFile(&gSCTP_ServerFile);
- IPStack_AddFile(&gSCTP_ClientFile);
- //IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket, SCTP_Unreachable);
- IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket);
-}
-
-/**
- * \brief Scan a list of tSCTPChannels and find process the first match
- * \return 0 if no match was found, -1 on error and 1 if a match was found
- */
-int SCTP_int_ScanList(tSCTPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
-{
- tSCTPHeader *hdr = Buffer;
- tSCTPChannel *chan;
- tSCTPPacket *pack;
- int len;
-
- for(chan = List;
- chan;
- chan = chan->Next)
- {
- if(chan->Interface != Interface) continue;
- if(chan->LocalPort != ntohs(hdr->DestPort)) continue;
- if(chan->RemotePort != ntohs(hdr->SourcePort)) continue;
-
- if(Interface->Type == 4) {
- if(!IP4_EQU(chan->RemoteAddr.v4, *(tIPv4*)Address)) continue;
- }
- else if(Interface->Type == 6) {
- if(!IP6_EQU(chan->RemoteAddr.v6, *(tIPv6*)Address)) continue;
- }
- else {
- Log_Warning("SCTP", "Address type %i unknown", Interface->Type);
- Mutex_Release(&glSCTP_Channels);
- return -1;
- }
-
- Log_Log("SCTP", "Recieved packet for %p", chan);
- // Create the cached packet
- len = ntohs(hdr->Length);
- pack = malloc(sizeof(tSCTPPacket) + len);
- pack->Next = NULL;
- pack->Length = len;
- memcpy(pack->Data, hdr->Data, len);
-
- // Add the packet to the channel's queue
- SHORTLOCK(&chan->lQueue);
- if(chan->Queue)
- chan->QueueEnd->Next = pack;
- else
- chan->QueueEnd = chan->Queue = pack;
- SHORTREL(&chan->lQueue);
- Mutex_Release(&glSCTP_Channels);
- return 1;
- }
- return 0;
-}
-
-/**
- * \fn void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
- * \brief Handles a packet from the IP Layer
- */
-void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
-{
- tSCTPHeader *hdr = Buffer;
- tSCTPServer *srv;
- int ret;
-
- Log_Log("SCTP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort));
- Log_Log("SCTP", "hdr->DestPort = %i", ntohs(hdr->DestPort));
- Log_Log("SCTP", "hdr->VerifcationTag = %i", ntohs(hdr->VerifcationTag));
- Log_Log("SCTP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
-
- // Check registered connections
- Mutex_Acquire(&glSCTP_Channels);
- ret = SCTP_int_ScanList(gpSCTP_Channels, Interface, Address, Length, Buffer);
- Mutex_Release(&glSCTP_Channels);
- if(ret != 0) return ;
-
-
- // TODO: Server/Listener
- Mutex_Acquire(&glSCTP_Servers);
- for(srv = gpSCTP_Servers;
- srv;
- srv = srv->Next)
- {
- if(srv->Interface != Interface) continue;
- if(srv->ListenPort != ntohs(hdr->DestPort)) continue;
- ret = SCTP_int_ScanList(srv->Channels, Interface, Address, Length, Buffer);
- if(ret != 0) break;
-
- // Add connection
- Log_Warning("SCTP", "TODO - Add channel on connection");
- //TODO
- }
- Mutex_Release(&glSCTP_Servers);
-
-}
-
-/**
- * \brief Handle an ICMP Unrechable Error
- */
-void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
-{
-
-}
-
-/**
- * \brief Send a packet
- * \param Channel Channel to send the packet from
- * \param Data Packet data
- * \param Length Length in bytes of packet data
- */
-void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length)
-{
- tSCTPHeader *hdr;
-
- switch(Channel->Interface->Type)
- {
- case 4:
- // Create the packet
- hdr = malloc(sizeof(tSCTPHeader)+Length);
- hdr->SourcePort = htons( Channel->LocalPort );
- hdr->DestPort = htons( Channel->RemotePort );
- hdr->Length = htons( sizeof(tSCTPHeader) + Length );
- hdr->Checksum = 0; // Checksum can be zero on IPv4
- memcpy(hdr->Data, Data, Length);
- // Pass on the the IPv4 Layer
- IPv4_SendPacket(Channel->Interface, Channel->RemoteAddr.v4, IP4PROT_SCTP, 0, sizeof(tSCTPHeader)+Length, hdr);
- // Free allocated packet
- free(hdr);
- break;
- }
-}
-
-// --- Listening Server
-tVFS_Node *SCTP_Server_Init(tInterface *Interface)
-{
- tSCTPServer *new;
- new = calloc( sizeof(tSCTPServer), 1 );
- if(!new) return NULL;
-
- new->Node.ImplPtr = new;
- new->Node.Flags = VFS_FFLAG_DIRECTORY;
- new->Node.NumACLs = 1;
- new->Node.ACLs = &gVFS_ACL_EveryoneRX;
- new->Node.ReadDir = SCTP_Server_ReadDir;
- new->Node.FindDir = SCTP_Server_FindDir;
- new->Node.IOCtl = SCTP_Server_IOCtl;
- new->Node.Close = SCTP_Server_Close;
-
- Mutex_Acquire(&glSCTP_Servers);
- new->Next = gpSCTP_Servers;
- gpSCTP_Servers = new;
- Mutex_Release(&glSCTP_Servers);
-
- return &new->Node;
-}
-
-/**
- * \brief Wait for a connection and return its ID in a string
- */
-char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID)
-{
- tSCTPServer *srv = Node->ImplPtr;
- tSCTPChannel *chan;
- char *ret;
-
- if( srv->ListenPort == 0 ) return NULL;
-
- // Lock (so another thread can't collide with us here) and wait for a connection
- Mutex_Acquire( &srv->Lock );
- while( srv->NewChannels == NULL ) Threads_Yield();
- // Pop the connection off the new list
- chan = srv->NewChannels;
- srv->NewChannels = chan->Next;
- // Release the lock
- Mutex_Release( &srv->Lock );
-
- // Create the ID string and return it
- ret = malloc(11+1);
- sprintf(ret, "%i", chan->Node.ImplInt);
-
- return ret;
-}
-
-/**
- * \brief Take a string and find the channel
- */
-tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name)
-{
- tSCTPServer *srv = Node->ImplPtr;
- tSCTPChannel *chan;
- int id = atoi(Name);
-
- for(chan = srv->Channels;
- chan;
- chan = chan->Next)
- {
- if( chan->Node.ImplInt < id ) continue;
- if( chan->Node.ImplInt > id ) break; // Go sorted lists!
-
- return &chan->Node;
- }
-
- return NULL;
-}
-
-/**
- * \brief Names for server IOCtl Calls
- */
-static const char *casIOCtls_Server[] = {
- DRV_IOCTLNAMES,
- "getset_listenport",
- NULL
- };
-/**
- * \brief Channel IOCtls
- */
-int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tSCTPServer *srv = Node->ImplPtr;
-
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Server", 0x100, casIOCtls_Server);
-
- case 4: // getset_localport (returns bool success)
- if(!Data) LEAVE_RET('i', srv->ListenPort);
- if(!CheckMem( Data, sizeof(Uint16) ) ) {
- LOG("Invalid pointer %p", Data);
- LEAVE_RET('i', -1);
- }
- // Set port
- srv->ListenPort = *(Uint16*)Data;
- // Permissions check (Ports lower than 1024 are root-only)
- if(srv->ListenPort != 0 && srv->ListenPort < 1024) {
- if( Threads_GetUID() != 0 ) {
- LOG("Attempt by non-superuser to listen on port %i", srv->ListenPort);
- srv->ListenPort = 0;
- LEAVE_RET('i', -1);
- }
- }
- // Allocate a random port if requested
- if( srv->ListenPort == 0 )
- srv->ListenPort = SCTP_int_AllocatePort();
- else
- {
- // Else, mark the requested port as used
- if( SCTP_int_MarkPortAsUsed(srv->ListenPort) == 0 ) {
- LOG("Port %i us currently in use", srv->ListenPort);
- srv->ListenPort = 0;
- LEAVE_RET('i', -1);
- }
- LEAVE_RET('i', 1);
- }
- LEAVE_RET('i', 1);
-
- default:
- LEAVE_RET('i', -1);
- }
- LEAVE_RET('i', 0);
-}
-
-void SCTP_Server_Close(tVFS_Node *Node)
-{
- tSCTPServer *srv = Node->ImplPtr;
- tSCTPServer *prev;
- tSCTPChannel *chan;
- tSCTPPacket *tmp;
-
-
- // Remove from the main list first
- Mutex_Acquire(&glSCTP_Servers);
- if(gpSCTP_Servers == srv)
- gpSCTP_Servers = gpSCTP_Servers->Next;
- else
- {
- for(prev = gpSCTP_Servers;
- prev->Next && prev->Next != srv;
- prev = prev->Next);
- if(!prev->Next)
- Log_Warning("SCTP", "Bookeeping Fail, server %p is not in main list", srv);
- else
- prev->Next = prev->Next->Next;
- }
- Mutex_Release(&glSCTP_Servers);
-
-
- Mutex_Acquire(&srv->Lock);
- for(chan = srv->Channels;
- chan;
- chan = chan->Next)
- {
- // Clear Queue
- SHORTLOCK(&chan->lQueue);
- while(chan->Queue)
- {
- tmp = chan->Queue;
- chan->Queue = tmp->Next;
- free(tmp);
- }
- SHORTREL(&chan->lQueue);
-
- // Free channel structure
- free(chan);
- }
- Mutex_Release(&srv->Lock);
-
- free(srv);
-}
-
-// --- Client Channels
-tVFS_Node *SCTP_Channel_Init(tInterface *Interface)
-{
- tSCTPChannel *new;
- new = calloc( sizeof(tSCTPChannel), 1 );
- new->Interface = Interface;
- new->Node.ImplPtr = new;
- new->Node.NumACLs = 1;
- new->Node.ACLs = &gVFS_ACL_EveryoneRW;
- new->Node.Read = SCTP_Channel_Read;
- new->Node.Write = SCTP_Channel_Write;
- new->Node.IOCtl = SCTP_Channel_IOCtl;
- new->Node.Close = SCTP_Channel_Close;
-
- Mutex_Acquire(&glSCTP_Channels);
- new->Next = gpSCTP_Channels;
- gpSCTP_Channels = new;
- Mutex_Release(&glSCTP_Channels);
-
- return &new->Node;
-}
-
-/**
- * \brief Read from the channel file (wait for a packet)
- */
-Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tSCTPChannel *chan = Node->ImplPtr;
- tSCTPPacket *pack;
-
- if(chan->LocalPort == 0) return 0;
- if(chan->RemotePort == 0) return 0;
-
- while(chan->Queue == NULL) Threads_Yield();
-
- for(;;)
- {
- SHORTLOCK(&chan->lQueue);
- if(chan->Queue == NULL) {
- SHORTREL(&chan->lQueue);
- continue;
- }
- pack = chan->Queue;
- chan->Queue = pack->Next;
- if(!chan->Queue) chan->QueueEnd = NULL;
- SHORTREL(&chan->lQueue);
- break;
- }
-
- // Clip length to packet length
- if(Length > pack->Length) Length = pack->Length;
- // Copy packet data from cache
- memcpy(Buffer, pack->Data, Length);
- // Free cached packet
- free(pack);
-
- return Length;
-}
-
-/**
- * \brief Write to the channel file (send a packet)
- */
-Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tSCTPChannel *chan = Node->ImplPtr;
- if(chan->RemotePort == 0) return 0;
-
- SCTP_SendPacket(chan, Buffer, (size_t)Length);
-
- return 0;
-}
-
-/**
- * \brief Names for channel IOCtl Calls
- */
-static const char *casIOCtls_Channel[] = {
- DRV_IOCTLNAMES,
- "getset_localport",
- "getset_remoteport",
- "set_remoteaddr",
- NULL
- };
-/**
- * \brief Channel IOCtls
- */
-int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tSCTPChannel *chan = Node->ImplPtr;
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Channel", 0x100, casIOCtls_Channel);
-
- case 4: // getset_localport (returns bool success)
- if(!Data) LEAVE_RET('i', chan->LocalPort);
- if(!CheckMem( Data, sizeof(Uint16) ) ) {
- LOG("Invalid pointer %p", Data);
- LEAVE_RET('i', -1);
- }
- // Set port
- chan->LocalPort = *(Uint16*)Data;
- // Permissions check (Ports lower than 1024 are root-only)
- if(chan->LocalPort != 0 && chan->LocalPort < 1024) {
- if( Threads_GetUID() != 0 ) {
- LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort);
- chan->LocalPort = 0;
- LEAVE_RET('i', -1);
- }
- }
- // Allocate a random port if requested
- if( chan->LocalPort == 0 )
- chan->LocalPort = SCTP_int_AllocatePort();
- else
- {
- // Else, mark the requested port as used
- if( SCTP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) {
- LOG("Port %i us currently in use", chan->LocalPort);
- chan->LocalPort = 0;
- LEAVE_RET('i', 0);
- }
- LEAVE_RET('i', 1);
- }
- LEAVE_RET('i', 1);
-
- case 5: // getset_remoteport (returns bool success)
- if(!Data) LEAVE_RET('i', chan->RemotePort);
- if(!CheckMem( Data, sizeof(Uint16) ) ) {
- LOG("Invalid pointer %p", Data);
- LEAVE_RET('i', -1);
- }
- chan->RemotePort = *(Uint16*)Data;
- return 1;
-
- case 6: // set_remoteaddr (returns bool success)
- switch(chan->Interface->Type)
- {
- case 4:
- if(!CheckMem(Data, sizeof(tIPv4))) {
- LOG("Invalid pointer %p", Data);
- LEAVE_RET('i', -1);
- }
- chan->RemoteAddr.v4 = *(tIPv4*)Data;
- break;
- }
- break;
- }
- LEAVE_RET('i', 0);
-}
-
-/**
- * \brief Close and destroy an open channel
- */
-void SCTP_Channel_Close(tVFS_Node *Node)
-{
- tSCTPChannel *chan = Node->ImplPtr;
- tSCTPChannel *prev;
-
- // Remove from the main list first
- Mutex_Acquire(&glSCTP_Channels);
- if(gpSCTP_Channels == chan)
- gpSCTP_Channels = gpSCTP_Channels->Next;
- else
- {
- for(prev = gpSCTP_Channels;
- prev->Next && prev->Next != chan;
- prev = prev->Next);
- if(!prev->Next)
- Log_Warning("SCTP", "Bookeeping Fail, channel %p is not in main list", chan);
- else
- prev->Next = prev->Next->Next;
- }
- Mutex_Release(&glSCTP_Channels);
-
- // Clear Queue
- SHORTLOCK(&chan->lQueue);
- while(chan->Queue)
- {
- tSCTPPacket *tmp;
- tmp = chan->Queue;
- chan->Queue = tmp->Next;
- free(tmp);
- }
- SHORTREL(&chan->lQueue);
-
- // Free channel structure
- free(chan);
-}
-
-/**
- * \return Port Number on success, or zero on failure
- */
-Uint16 SCTP_int_AllocatePort()
-{
- int i;
- Mutex_Acquire(&glSCTP_Ports);
- // Fast Search
- for( i = SCTP_ALLOC_BASE; i < 0x10000; i += 32 )
- if( gSCTP_Ports[i/32] != 0xFFFFFFFF )
- break;
- if(i == 0x10000) return 0;
- for( ;; i++ )
- {
- if( !(gSCTP_Ports[i/32] & (1 << (i%32))) )
- return i;
- }
- Mutex_Release(&glSCTP_Ports);
-}
-
-/**
- * \brief Allocate a specific port
- * \return Boolean Success
- */
-int SCTP_int_MarkPortAsUsed(Uint16 Port)
-{
- Mutex_Acquire(&glSCTP_Ports);
- if( gSCTP_Ports[Port/32] & (1 << (Port%32)) ) {
- return 0;
- Mutex_Release(&glSCTP_Ports);
- }
- gSCTP_Ports[Port/32] |= 1 << (Port%32);
- Mutex_Release(&glSCTP_Ports);
- return 1;
-}
-
-/**
- * \brief Free an allocated port
- */
-void SCTP_int_FreePort(Uint16 Port)
-{
- Mutex_Acquire(&glSCTP_Ports);
- gSCTP_Ports[Port/32] &= ~(1 << (Port%32));
- Mutex_Release(&glSCTP_Ports);
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - TCP Handling
- */
-#define DEBUG 1
-#include "ipstack.h"
-#include "ipv4.h"
-#include "ipv6.h"
-#include "tcp.h"
-
-#define USE_SELECT 1
-#define HEXDUMP_INCOMING 0
-#define HEXDUMP_OUTGOING 0
-#define CACHE_FUTURE_PACKETS_IN_BYTES 1 // Use a ring buffer to cache out of order packets
-
-#define TCP_MIN_DYNPORT 0xC000
-#define TCP_MAX_HALFOPEN 1024 // Should be enough
-
-#define TCP_MAX_PACKET_SIZE 1024
-#define TCP_WINDOW_SIZE 0x2000
-#define TCP_RECIEVE_BUFFER_SIZE 0x4000
-
-// === PROTOTYPES ===
-void TCP_Initialise(void);
-void TCP_StartConnection(tTCPConnection *Conn);
-void TCP_SendPacket(tTCPConnection *Conn, size_t Length, tTCPHeader *Data);
-void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
-int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length);
-void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection);
-Uint16 TCP_GetUnusedPort();
- int TCP_AllocatePort(Uint16 Port);
- int TCP_DeallocatePort(Uint16 Port);
-// --- Server
-tVFS_Node *TCP_Server_Init(tInterface *Interface);
-char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name);
- int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data);
-void TCP_Server_Close(tVFS_Node *Node);
-// --- Client
-tVFS_Node *TCP_Client_Init(tInterface *Interface);
-Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
- int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data);
-void TCP_Client_Close(tVFS_Node *Node);
-// --- Helpers
- int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue);
-
-// === TEMPLATES ===
-tSocketFile gTCP_ServerFile = {NULL, "tcps", TCP_Server_Init};
-tSocketFile gTCP_ClientFile = {NULL, "tcpc", TCP_Client_Init};
-tVFS_NodeType gTCP_ServerNodeType = {
- .TypeName = "TCP Server",
- .ReadDir = TCP_Server_ReadDir,
- .FindDir = TCP_Server_FindDir,
- .IOCtl = TCP_Server_IOCtl,
- .Close = TCP_Server_Close
- };
-tVFS_NodeType gTCP_ClientNodeType = {
- .TypeName = "TCP Client/Connection",
- .Read = TCP_Client_Read,
- .Write = TCP_Client_Write,
- .IOCtl = TCP_Client_IOCtl,
- .Close = TCP_Client_Close
- };
-
-// === GLOBALS ===
- int giTCP_NumHalfopen = 0;
-tShortSpinlock glTCP_Listeners;
-tTCPListener *gTCP_Listeners;
-tShortSpinlock glTCP_OutbountCons;
-tTCPConnection *gTCP_OutbountCons;
-Uint32 gaTCP_PortBitmap[0x800];
- int giTCP_NextOutPort = TCP_MIN_DYNPORT;
-
-// === CODE ===
-/**
- * \brief Initialise the TCP Layer
- *
- * Registers the client and server files and the GetPacket callback
- */
-void TCP_Initialise(void)
-{
- giTCP_NextOutPort += rand()%32;
- IPStack_AddFile(&gTCP_ServerFile);
- IPStack_AddFile(&gTCP_ClientFile);
- IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
- IPv6_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
-}
-
-/**
- * \brief Sends a packet from the specified connection, calculating the checksums
- * \param Conn Connection
- * \param Length Length of data
- * \param Data Packet data (cast as a TCP Header)
- */
-void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data )
-{
- Uint16 checksum[2];
-
- Data->Checksum = 0;
- checksum[1] = htons( ~IPv4_Checksum( (void*)Data, Length ) ); // Partial checksum
- if(Length & 1)
- ((Uint8*)Data)[Length] = 0;
-
- // TODO: Fragment packet
-
- switch( Conn->Interface->Type )
- {
- case 4:
- // Append IPv4 Pseudo Header
- {
- Uint32 buf[3];
- buf[0] = ((tIPv4*)Conn->Interface->Address)->L;
- buf[1] = Conn->RemoteIP.v4.L;
- buf[2] = (htons(Length)<<16) | (6<<8) | 0;
- checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum
- }
- Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two
- IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, Length, Data);
- break;
-
- case 6:
- // Append IPv6 Pseudo Header
- {
- Uint32 buf[4+4+1+1];
- memcpy(buf, Conn->Interface->Address, 16);
- memcpy(&buf[4], &Conn->RemoteIP, 16);
- buf[8] = htonl(Length);
- buf[9] = htonl(6);
- checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum
- }
- Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two
- IPv6_SendPacket(Conn->Interface, Conn->RemoteIP.v6, IP4PROT_TCP, Length, Data);
- break;
- }
-}
-
-/**
- * \brief Handles a packet from the IP Layer
- * \param Interface Interface the packet arrived from
- * \param Address Pointer to the addres structure
- * \param Length Size of packet in bytes
- * \param Buffer Packet data
- */
-void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
-{
- tTCPHeader *hdr = Buffer;
- tTCPListener *srv;
- tTCPConnection *conn;
-
- Log_Log("TCP", "TCP_GetPacket: <Local>:%i from [%s]:%i, Flags= %s%s%s%s%s%s%s%s",
- ntohs(hdr->DestPort),
- IPStack_PrintAddress(Interface->Type, Address),
- ntohs(hdr->SourcePort),
- (hdr->Flags & TCP_FLAG_CWR) ? "CWR " : "",
- (hdr->Flags & TCP_FLAG_ECE) ? "ECE " : "",
- (hdr->Flags & TCP_FLAG_URG) ? "URG " : "",
- (hdr->Flags & TCP_FLAG_ACK) ? "ACK " : "",
- (hdr->Flags & TCP_FLAG_PSH) ? "PSH " : "",
- (hdr->Flags & TCP_FLAG_RST) ? "RST " : "",
- (hdr->Flags & TCP_FLAG_SYN) ? "SYN " : "",
- (hdr->Flags & TCP_FLAG_FIN) ? "FIN " : ""
- );
-
- if( Length > (hdr->DataOffset >> 4)*4 )
- {
- Log_Log("TCP", "TCP_GetPacket: SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber));
-#if HEXDUMP_INCOMING
- Debug_HexDump(
- "TCP_GetPacket: Packet Data = ",
- (Uint8*)hdr + (hdr->DataOffset >> 4)*4,
- Length - (hdr->DataOffset >> 4)*4
- );
-#endif
- }
-
- // Check Servers
- {
- for( srv = gTCP_Listeners; srv; srv = srv->Next )
- {
- // Check if the server is active
- if(srv->Port == 0) continue;
- // Check the interface
- if(srv->Interface && srv->Interface != Interface) continue;
- // Check the destination port
- if(srv->Port != htons(hdr->DestPort)) continue;
-
- Log_Log("TCP", "TCP_GetPacket: Matches server %p", srv);
- // Is this in an established connection?
- for( conn = srv->Connections; conn; conn = conn->Next )
- {
- // Check that it is coming in on the same interface
- if(conn->Interface != Interface) continue;
-
- // Check Source Port
- Log_Log("TCP", "TCP_GetPacket: conn->RemotePort(%i) == hdr->SourcePort(%i)",
- conn->RemotePort, ntohs(hdr->SourcePort));
- if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
-
- // Check Source IP
- Log_Debug("TCP", "TCP_GetPacket: conn->RemoteIP(%s)",
- IPStack_PrintAddress(conn->Interface->Type, &conn->RemoteIP));
- Log_Debug("TCP", " == Address(%s)",
- IPStack_PrintAddress(conn->Interface->Type, Address));
- if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
- continue ;
-
- Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn);
- // We have a response!
- TCP_INT_HandleConnectionPacket(conn, hdr, Length);
-
- return;
- }
-
- Log_Log("TCP", "TCP_GetPacket: Opening Connection");
- // Open a new connection (well, check that it's a SYN)
- if(hdr->Flags != TCP_FLAG_SYN) {
- Log_Log("TCP", "TCP_GetPacket: Packet is not a SYN");
- return ;
- }
-
- // TODO: Check for halfopen max
-
- conn = calloc(1, sizeof(tTCPConnection));
- conn->State = TCP_ST_SYN_RCVD;
- conn->LocalPort = srv->Port;
- conn->RemotePort = ntohs(hdr->SourcePort);
- conn->Interface = Interface;
-
- switch(Interface->Type)
- {
- case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break;
- case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break;
- }
-
- conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE );
-
- conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1;
- conn->NextSequenceSend = rand();
-
- // Create node
- conn->Node.NumACLs = 1;
- conn->Node.ACLs = &gVFS_ACL_EveryoneRW;
- conn->Node.ImplPtr = conn;
- conn->Node.ImplInt = srv->NextID ++;
- conn->Node.Type = &gTCP_ClientNodeType; // TODO: Special type for the server end?
-
- // Hmm... Theoretically, this lock will never have to wait,
- // as the interface is locked to the watching thread, and this
- // runs in the watching thread. But, it's a good idea to have
- // it, just in case
- // Oh, wait, there is a case where a wildcard can be used
- // (srv->Interface == NULL) so having the lock is a good idea
- SHORTLOCK(&srv->lConnections);
- if( !srv->Connections )
- srv->Connections = conn;
- else
- srv->ConnectionsTail->Next = conn;
- srv->ConnectionsTail = conn;
- if(!srv->NewConnections)
- srv->NewConnections = conn;
- VFS_MarkAvaliable( &srv->Node, 1 );
- SHORTREL(&srv->lConnections);
-
- // Send the SYN ACK
- hdr->Flags |= TCP_FLAG_ACK;
- hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
- hdr->SequenceNumber = htonl(conn->NextSequenceSend);
- hdr->DestPort = hdr->SourcePort;
- hdr->SourcePort = htons(srv->Port);
- hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4;
- TCP_SendPacket( conn, sizeof(tTCPHeader), hdr );
- conn->NextSequenceSend ++;
- return ;
- }
- }
-
-
- // Check Open Connections
- {
- for( conn = gTCP_OutbountCons; conn; conn = conn->Next )
- {
- // Check that it is coming in on the same interface
- if(conn->Interface != Interface) continue;
-
- // Check Source Port
- if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
-
- // Check Source IP
- if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
- continue;
- if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
- continue;
-
- TCP_INT_HandleConnectionPacket(conn, hdr, Length);
- return ;
- }
- }
-
- Log_Log("TCP", "TCP_GetPacket: No Match");
-}
-
-/**
- * \brief Handles a packet sent to a specific connection
- * \param Connection TCP Connection pointer
- * \param Header TCP Packet pointer
- * \param Length Length of the packet
- */
-void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
-{
- int dataLen;
- Uint32 sequence_num;
-
- // Silently drop once finished
- // TODO: Check if this needs to be here
- if( Connection->State == TCP_ST_FINISHED ) {
- Log_Log("TCP", "Packet ignored - connection finnished");
- return ;
- }
-
- // Syncronise sequence values
- if(Header->Flags & TCP_FLAG_SYN) {
- // TODO: What if the packet also has data?
- Connection->NextSequenceRcv = ntohl(Header->SequenceNumber);
- }
-
- // Ackowledge a sent packet
- if(Header->Flags & TCP_FLAG_ACK) {
- // TODO: Process an ACKed Packet
- Log_Log("TCP", "Conn %p, Sent packet 0x%x ACKed", Connection, Header->AcknowlegementNumber);
- }
-
- // Get length of data
- dataLen = Length - (Header->DataOffset>>4)*4;
- Log_Log("TCP", "HandleConnectionPacket - dataLen = %i", dataLen);
-
- //
- // State Machine
- //
- switch( Connection->State )
- {
- // Pre-init connection?
- case TCP_ST_CLOSED:
- Log_Log("TCP", "Packets to a closed connection?!");
- break;
-
- // --- Init States ---
- // SYN sent, expecting SYN-ACK Connection Opening
- case TCP_ST_SYN_SENT:
- if( Header->Flags & TCP_FLAG_SYN )
- {
- Connection->NextSequenceRcv ++;
- Header->DestPort = Header->SourcePort;
- Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
- Header->SequenceNumber = htonl(Connection->NextSequenceSend);
- Header->WindowSize = htons(TCP_WINDOW_SIZE);
- Header->Flags = TCP_FLAG_ACK;
- Header->DataOffset = (sizeof(tTCPHeader)/4) << 4;
- TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
-
- if( Header->Flags & TCP_FLAG_ACK )
- {
- Log_Log("TCP", "ACKing SYN-ACK");
- Connection->State = TCP_ST_OPEN;
- }
- else
- {
- Log_Log("TCP", "ACKing SYN");
- Connection->State = TCP_ST_SYN_RCVD;
- }
- }
- break;
-
- // SYN-ACK sent, expecting ACK
- case TCP_ST_SYN_RCVD:
- if( Header->Flags & TCP_FLAG_ACK )
- {
- // TODO: Handle max half-open limit
- Connection->State = TCP_ST_OPEN;
- Log_Log("TCP", "Connection fully opened");
- }
- break;
-
- // --- Established State ---
- case TCP_ST_OPEN:
- // - Handle State changes
- //
- if( Header->Flags & TCP_FLAG_FIN ) {
- Log_Log("TCP", "Conn %p closed, recieved FIN", Connection);
- VFS_MarkError(&Connection->Node, 1);
- Connection->State = TCP_ST_CLOSE_WAIT;
-// Header->Flags &= ~TCP_FLAG_FIN;
- // CLOSE WAIT requires the client to close (or does it?)
- #if 0
-
- #endif
- }
-
- // Check for an empty packet
- if(dataLen == 0) {
- if( Header->Flags == TCP_FLAG_ACK )
- {
- Log_Log("TCP", "ACK only packet");
- return ;
- }
- Connection->NextSequenceRcv ++; // TODO: Is this right? (empty packet counts as one byte)
- Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number");
- Header->DestPort = Header->SourcePort;
- Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
- Header->SequenceNumber = htonl(Connection->NextSequenceSend);
- Header->Flags |= TCP_FLAG_ACK;
- TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
- return ;
- }
-
- // NOTES:
- // Flags
- // PSH - Has Data?
- // /NOTES
-
- sequence_num = ntohl(Header->SequenceNumber);
-
- Log_Log("TCP", "0x%08x <= 0x%08x < 0x%08x",
- Connection->NextSequenceRcv,
- ntohl(Header->SequenceNumber),
- Connection->NextSequenceRcv + TCP_WINDOW_SIZE
- );
-
- // Is this packet the next expected packet?
- if( sequence_num == Connection->NextSequenceRcv )
- {
- int rv;
- // Ooh, Goodie! Add it to the recieved list
- rv = TCP_INT_AppendRecieved(Connection,
- (Uint8*)Header + (Header->DataOffset>>4)*4,
- dataLen
- );
- if(rv != 0) {
- break;
- }
- Log_Log("TCP", "0x%08x += %i", Connection->NextSequenceRcv, dataLen);
- Connection->NextSequenceRcv += dataLen;
-
- // TODO: This should be moved out of the watcher thread,
- // so that a single lost packet on one connection doesn't cause
- // all connections on the interface to lag.
- // - Meh, no real issue, as the cache shouldn't be that large
- TCP_INT_UpdateRecievedFromFuture(Connection);
-
- // ACK Packet
- Header->DestPort = Header->SourcePort;
- Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
- Header->SequenceNumber = htonl(Connection->NextSequenceSend);
- Header->WindowSize = htons(TCP_WINDOW_SIZE);
- Header->Flags &= TCP_FLAG_SYN; // Eliminate all flags save for SYN
- Header->Flags |= TCP_FLAG_ACK; // Add ACK
- Log_Log("TCP", "Sending ACK for 0x%08x", Connection->NextSequenceRcv);
- TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
- //Connection->NextSequenceSend ++;
- }
- // Check if the packet is in window
- else if( WrapBetween(Connection->NextSequenceRcv, sequence_num,
- Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) )
- {
- Uint8 *dataptr = (Uint8*)Header + (Header->DataOffset>>4)*4;
- #if CACHE_FUTURE_PACKETS_IN_BYTES
- Uint32 index;
- int i;
-
- index = sequence_num % TCP_WINDOW_SIZE;
- for( i = 0; i < dataLen; i ++ )
- {
- Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8);
- Connection->FuturePacketData[index] = dataptr[i];
- // Do a wrap increment
- index ++;
- if(index == TCP_WINDOW_SIZE) index = 0;
- }
- #else
- tTCPStoredPacket *pkt, *tmp, *prev = NULL;
-
- // Allocate and fill cached packet
- pkt = malloc( sizeof(tTCPStoredPacket) + dataLen );
- pkt->Next = NULL;
- pkt->Sequence = ntohl(Header->SequenceNumber);
- pkt->Length = dataLen;
- memcpy(pkt->Data, dataptr, dataLen);
-
- Log_Log("TCP", "We missed a packet, caching",
- pkt->Sequence, Connection->NextSequenceRcv);
-
- // No? Well, let's cache it and look at it later
- SHORTLOCK( &Connection->lFuturePackets );
- for(tmp = Connection->FuturePackets;
- tmp;
- prev = tmp, tmp = tmp->Next)
- {
- if(tmp->Sequence >= pkt->Sequence) break;
- }
-
- // Add if before first, or sequences don't match
- if( !tmp || tmp->Sequence != pkt->Sequence )
- {
- if(prev)
- prev->Next = pkt;
- else
- Connection->FuturePackets = pkt;
- pkt->Next = tmp;
- }
- // Replace if larger
- else if(pkt->Length > tmp->Length)
- {
- if(prev)
- prev->Next = pkt;
- pkt->Next = tmp->Next;
- free(tmp);
- }
- else
- {
- free(pkt); // TODO: Find some way to remove this
- }
- SHORTREL( &Connection->lFuturePackets );
- #endif
- }
- // Badly out of sequence packet
- else
- {
- Log_Log("TCP", "Fully out of sequence packet (0x%08x not between 0x%08x and 0x%08x), dropped",
- sequence_num, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE);
- // TODO: Spec says we should send an empty ACK with the current state
- }
- break;
-
- // --- Remote close states
- case TCP_ST_CLOSE_WAIT:
-
- // Ignore everything, CLOSE_WAIT is terminated by the client
- Log_Debug("TCP", "CLOSE WAIT - Ignoring packets");
-
- break;
-
- // LAST-ACK - Waiting for the ACK of FIN (from CLOSE WAIT)
- case TCP_ST_LAST_ACK:
- if( Header->Flags & TCP_FLAG_ACK )
- {
- Connection->State = TCP_ST_FINISHED; // Connection completed
- Log_Log("TCP", "LAST-ACK to CLOSED - Connection remote closed");
- // TODO: Destrory the TCB
- }
- break;
-
- // --- Local close States
- case TCP_ST_FIN_WAIT1:
- if( Header->Flags & TCP_FLAG_FIN )
- {
- Connection->State = TCP_ST_CLOSING;
- Log_Debug("TCP", "Conn %p closed, sent FIN and recieved FIN", Connection);
- VFS_MarkError(&Connection->Node, 1);
-
- // ACK Packet
- Header->DestPort = Header->SourcePort;
- Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = Header->SequenceNumber;
- Header->SequenceNumber = htonl(Connection->NextSequenceSend);
- Header->WindowSize = htons(TCP_WINDOW_SIZE);
- Header->Flags = TCP_FLAG_ACK;
- TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
- break ;
- }
-
- // TODO: Make sure that the packet is actually ACKing the FIN
- if( Header->Flags & TCP_FLAG_ACK )
- {
- Connection->State = TCP_ST_FIN_WAIT2;
- Log_Debug("TCP", "Conn %p closed, sent FIN ACKed", Connection);
- VFS_MarkError(&Connection->Node, 1);
- return ;
- }
- break;
-
- case TCP_ST_FIN_WAIT2:
- if( Header->Flags & TCP_FLAG_FIN )
- {
- Connection->State = TCP_ST_TIME_WAIT;
- Log_Debug("TCP", "FIN sent and recieved, ACKing and going into TIME WAIT %p FINWAIT-2 -> TIME WAIT", Connection);
- // Send ACK
- Header->DestPort = Header->SourcePort;
- Header->SourcePort = htons(Connection->LocalPort);
- Header->AcknowlegementNumber = Header->SequenceNumber;
- Header->SequenceNumber = htonl(Connection->NextSequenceSend);
- Header->WindowSize = htons(TCP_WINDOW_SIZE);
- Header->Flags = TCP_FLAG_ACK;
- TCP_SendPacket( Connection, sizeof(tTCPHeader), Header );
- }
- break;
-
- case TCP_ST_CLOSING:
- // TODO: Make sure that the packet is actually ACKing the FIN
- if( Header->Flags & TCP_FLAG_ACK )
- {
- Connection->State = TCP_ST_TIME_WAIT;
- Log_Debug("TCP", "Conn %p CLOSING -> TIME WAIT", Connection);
- VFS_MarkError(&Connection->Node, 1);
- return ;
- }
- break;
-
- // --- Closed (or near closed) states) ---
- case TCP_ST_TIME_WAIT:
- Log_Log("TCP", "Packets on Time-Wait, ignored");
- break;
-
- case TCP_ST_FINISHED:
- Log_Log("TCP", "Packets when CLOSED, ignoring");
- break;
-
- //default:
- // Log_Warning("TCP", "Unhandled TCP state %i", Connection->State);
- // break;
- }
-
-}
-
-/**
- * \brief Appends a packet to the recieved list
- * \param Connection Connection structure
- * \param Data Packet contents
- * \param Length Length of \a Data
- */
-int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length)
-{
- Mutex_Acquire( &Connection->lRecievedPackets );
-
- if(Connection->RecievedBuffer->Length + Length > Connection->RecievedBuffer->Space )
- {
- VFS_MarkAvaliable(&Connection->Node, 1);
- Log_Error("TCP", "Buffer filled, packet dropped (:%i) - %i + %i > %i",
- Connection->LocalPort, Connection->RecievedBuffer->Length, Length,
- Connection->RecievedBuffer->Space
- );
- Mutex_Release( &Connection->lRecievedPackets );
- return 1;
- }
-
- RingBuffer_Write( Connection->RecievedBuffer, Data, Length );
-
- VFS_MarkAvaliable(&Connection->Node, 1);
-
- Mutex_Release( &Connection->lRecievedPackets );
- return 0;
-}
-
-/**
- * \brief Updates the connections recieved list from the future list
- * \param Connection Connection structure
- *
- * Updates the recieved packets list with packets from the future (out
- * of order) packets list that are now able to be added in direct
- * sequence.
- */
-void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection)
-{
- #if CACHE_FUTURE_PACKETS_IN_BYTES
- int i, length = 0;
- Uint32 index;
-
- // Calculate length of contiguous bytes
- length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv;
- index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
- for( i = 0; i < length; i ++ )
- {
- if( Connection->FuturePacketValidBytes[i / 8] == 0xFF ) {
- i += 7; index += 7;
- continue;
- }
- else if( !(Connection->FuturePacketValidBytes[i / 8] & (1 << (i%8))) )
- break;
-
- index ++;
- if(index > TCP_WINDOW_SIZE)
- index -= TCP_WINDOW_SIZE;
- }
- length = i;
-
- index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
-
- // Write data to to the ring buffer
- if( TCP_WINDOW_SIZE - index > length )
- {
- // Simple case
- RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length );
- }
- else
- {
- int endLen = TCP_WINDOW_SIZE - index;
- // 2-part case
- RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, endLen );
- RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - length );
- }
-
- // Mark (now saved) bytes as invalid
- // - Align index
- while(index % 8 && length)
- {
- Connection->FuturePacketData[index] = 0;
- Connection->FuturePacketData[index/8] &= ~(1 << (index%8));
- index ++;
- if(index > TCP_WINDOW_SIZE)
- index -= TCP_WINDOW_SIZE;
- length --;
- }
- while( length > 7 )
- {
- Connection->FuturePacketData[index] = 0;
- Connection->FuturePacketValidBytes[index/8] = 0;
- length -= 8;
- index += 8;
- if(index > TCP_WINDOW_SIZE)
- index -= TCP_WINDOW_SIZE;
- }
- while(length)
- {
- Connection->FuturePacketData[index] = 0;
- Connection->FuturePacketData[index/8] &= ~(1 << (index%8));
- index ++;
- if(index > TCP_WINDOW_SIZE)
- index -= TCP_WINDOW_SIZE;
- length --;
- }
-
- #else
- tTCPStoredPacket *pkt;
- for(;;)
- {
- SHORTLOCK( &Connection->lFuturePackets );
-
- // Clear out duplicates from cache
- // - If a packet has just been recieved, and it is expected, then
- // (since NextSequenceRcv = rcvd->Sequence + rcvd->Length) all
- // packets in cache that are smaller than the next expected
- // are now defunct.
- pkt = Connection->FuturePackets;
- while(pkt && pkt->Sequence < Connection->NextSequenceRcv)
- {
- tTCPStoredPacket *next = pkt->Next;
- free(pkt);
- pkt = next;
- }
-
- // If there's no packets left in cache, stop looking
- if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) {
- SHORTREL( &Connection->lFuturePackets );
- return;
- }
-
- // Delete packet from future list
- Connection->FuturePackets = pkt->Next;
-
- // Release list
- SHORTREL( &Connection->lFuturePackets );
-
- // Looks like we found one
- TCP_INT_AppendRecieved(Connection, pkt);
- Connection->NextSequenceRcv += pkt->Length;
- free(pkt);
- }
- #endif
-}
-
-/**
- * \fn Uint16 TCP_GetUnusedPort()
- * \brief Gets an unused port and allocates it
- */
-Uint16 TCP_GetUnusedPort()
-{
- Uint16 ret;
-
- // Get Next outbound port
- ret = giTCP_NextOutPort++;
- while( gaTCP_PortBitmap[ret/32] & (1UL << (ret%32)) )
- {
- ret ++;
- giTCP_NextOutPort++;
- if(giTCP_NextOutPort == 0x10000) {
- ret = giTCP_NextOutPort = TCP_MIN_DYNPORT;
- }
- }
-
- // Mark the new port as used
- gaTCP_PortBitmap[ret/32] |= 1 << (ret%32);
-
- return ret;
-}
-
-/**
- * \fn int TCP_AllocatePort(Uint16 Port)
- * \brief Marks a port as used
- */
-int TCP_AllocatePort(Uint16 Port)
-{
- // Check if the port has already been allocated
- if( gaTCP_PortBitmap[Port/32] & (1 << (Port%32)) )
- return 0;
-
- // Allocate
- gaTCP_PortBitmap[Port/32] |= 1 << (Port%32);
-
- return 1;
-}
-
-/**
- * \fn int TCP_DeallocatePort(Uint16 Port)
- * \brief Marks a port as unused
- */
-int TCP_DeallocatePort(Uint16 Port)
-{
- // Check if the port has already been allocated
- if( !(gaTCP_PortBitmap[Port/32] & (1 << (Port%32))) )
- return 0;
-
- // Allocate
- gaTCP_PortBitmap[Port/32] &= ~(1 << (Port%32));
-
- return 1;
-}
-
-// --- Server
-tVFS_Node *TCP_Server_Init(tInterface *Interface)
-{
- tTCPListener *srv;
-
- srv = calloc( 1, sizeof(tTCPListener) );
-
- if( srv == NULL ) {
- Log_Warning("TCP", "malloc failed for listener (%i) bytes", sizeof(tTCPListener));
- return NULL;
- }
-
- srv->Interface = Interface;
- srv->Port = 0;
- srv->NextID = 0;
- srv->Connections = NULL;
- srv->ConnectionsTail = NULL;
- srv->NewConnections = NULL;
- srv->Next = NULL;
- srv->Node.Flags = VFS_FFLAG_DIRECTORY;
- srv->Node.Size = -1;
- srv->Node.ImplPtr = srv;
- srv->Node.NumACLs = 1;
- srv->Node.ACLs = &gVFS_ACL_EveryoneRW;
- srv->Node.Type = &gTCP_ServerNodeType;
-
- SHORTLOCK(&glTCP_Listeners);
- srv->Next = gTCP_Listeners;
- gTCP_Listeners = srv;
- SHORTREL(&glTCP_Listeners);
-
- return &srv->Node;
-}
-
-/**
- * \brief Wait for a new connection and return the connection ID
- * \note Blocks until a new connection is made
- * \param Node Server node
- * \param Pos Position (ignored)
- */
-char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos)
-{
- tTCPListener *srv = Node->ImplPtr;
- tTCPConnection *conn;
- char *ret;
-
- ENTER("pNode iPos", Node, Pos);
-
- Log_Log("TCP", "Thread %i waiting for a connection", Threads_GetTID());
- for(;;)
- {
- SHORTLOCK( &srv->lConnections );
- if( srv->NewConnections != NULL ) break;
- SHORTREL( &srv->lConnections );
- Threads_Yield(); // TODO: Sleep until poked
- }
-
-
- // Increment the new list (the current connection is still on the
- // normal list)
- conn = srv->NewConnections;
- srv->NewConnections = conn->Next;
-
- if( srv->NewConnections == NULL )
- VFS_MarkAvaliable( Node, 0 );
-
- SHORTREL( &srv->lConnections );
-
- LOG("conn = %p", conn);
- LOG("srv->Connections = %p", srv->Connections);
- LOG("srv->NewConnections = %p", srv->NewConnections);
- LOG("srv->ConnectionsTail = %p", srv->ConnectionsTail);
-
- ret = malloc(9);
- itoa(ret, conn->Node.ImplInt, 16, 8, '0');
- Log_Log("TCP", "Thread %i got '%s'", Threads_GetTID(), ret);
- LEAVE('s', ret);
- return ret;
-}
-
-/**
- * \brief Gets a client connection node
- * \param Node Server node
- * \param Name Hexadecimal ID of the node
- */
-tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name)
-{
- tTCPConnection *conn;
- tTCPListener *srv = Node->ImplPtr;
- char tmp[9];
- int id = atoi(Name);
-
- ENTER("pNode sName", Node, Name);
-
- // Check for a non-empty name
- if( Name[0] )
- {
- // Sanity Check
- itoa(tmp, id, 16, 8, '0');
- if(strcmp(tmp, Name) != 0) {
- LOG("'%s' != '%s' (%08x)", Name, tmp, id);
- LEAVE('n');
- return NULL;
- }
-
- Log_Debug("TCP", "srv->Connections = %p", srv->Connections);
- Log_Debug("TCP", "srv->NewConnections = %p", srv->NewConnections);
- Log_Debug("TCP", "srv->ConnectionsTail = %p", srv->ConnectionsTail);
-
- // Search
- SHORTLOCK( &srv->lConnections );
- for(conn = srv->Connections;
- conn;
- conn = conn->Next)
- {
- LOG("conn->Node.ImplInt = %i", conn->Node.ImplInt);
- if(conn->Node.ImplInt == id) break;
- }
- SHORTREL( &srv->lConnections );
-
- // If not found, ret NULL
- if(!conn) {
- LOG("Connection %i not found", id);
- LEAVE('n');
- return NULL;
- }
- }
- // Empty Name - Check for a new connection and if it's there, open it
- else
- {
- SHORTLOCK( &srv->lConnections );
- conn = srv->NewConnections;
- if( conn != NULL )
- srv->NewConnections = conn->Next;
- VFS_MarkAvaliable( Node, srv->NewConnections != NULL );
- SHORTREL( &srv->lConnections );
- if( !conn ) {
- LOG("No new connections");
- LEAVE('n');
- return NULL;
- }
- }
-
- // Return node
- LEAVE('p', &conn->Node);
- return &conn->Node;
-}
-
-/**
- * \brief Handle IOCtl calls
- */
-int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tTCPListener *srv = Node->ImplPtr;
-
- switch(ID)
- {
- case 4: // Get/Set Port
- if(!Data) // Get Port
- return srv->Port;
-
- if(srv->Port) // Wait, you can't CHANGE the port
- return -1;
-
- if(!CheckMem(Data, sizeof(Uint16))) // Sanity check
- return -1;
-
- // Permissions check
- if(Threads_GetUID() != 0
- && *(Uint16*)Data != 0
- && *(Uint16*)Data < 1024)
- return -1;
-
- // TODO: Check if a port is in use
-
- // Set Port
- srv->Port = *(Uint16*)Data;
- if(srv->Port == 0) // Allocate a random port
- srv->Port = TCP_GetUnusedPort();
- else // Else, mark this as used
- TCP_AllocatePort(srv->Port);
-
- Log_Log("TCP", "Server %p listening on port %i", srv, srv->Port);
-
- return srv->Port;
- }
- return 0;
-}
-
-void TCP_Server_Close(tVFS_Node *Node)
-{
- free(Node->ImplPtr);
-}
-
-// --- Client
-/**
- * \brief Create a client node
- */
-tVFS_Node *TCP_Client_Init(tInterface *Interface)
-{
- tTCPConnection *conn = calloc( sizeof(tTCPConnection) + TCP_WINDOW_SIZE + TCP_WINDOW_SIZE/8, 1 );
-
- conn->State = TCP_ST_CLOSED;
- conn->Interface = Interface;
- conn->LocalPort = -1;
- conn->RemotePort = -1;
-
- conn->Node.ImplPtr = conn;
- conn->Node.NumACLs = 1;
- conn->Node.ACLs = &gVFS_ACL_EveryoneRW;
- conn->Node.Type = &gTCP_ClientNodeType;
-
- conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE );
- #if 0
- conn->SentBuffer = RingBuffer_Create( TCP_SEND_BUFFER_SIZE );
- Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name);
- #endif
-
- #if CACHE_FUTURE_PACKETS_IN_BYTES
- // Future recieved data (ahead of the expected sequence number)
- conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection);
- conn->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE;
- #endif
-
- SHORTLOCK(&glTCP_OutbountCons);
- conn->Next = gTCP_OutbountCons;
- gTCP_OutbountCons = conn;
- SHORTREL(&glTCP_OutbountCons);
-
- return &conn->Node;
-}
-
-/**
- * \brief Wait for a packet and return it
- * \note If \a Length is smaller than the size of the packet, the rest
- * of the packet's data will be discarded.
- */
-Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tTCPConnection *conn = Node->ImplPtr;
- size_t len;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
- LOG("conn = %p {State:%i}", conn, conn->State);
-
- // Check if connection is estabilishing
- // - TODO: Sleep instead (maybe using VFS_SelectNode to wait for the
- // data to be availiable
- while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT )
- Threads_Yield();
-
- // If the conneciton is not open, then clean out the recieved buffer
- if( conn->State != TCP_ST_OPEN )
- {
- Mutex_Acquire( &conn->lRecievedPackets );
- len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
- Mutex_Release( &conn->lRecievedPackets );
-
- if( len == 0 ) {
- VFS_MarkAvaliable(Node, 0);
- LEAVE('i', -1);
- return -1;
- }
-
- LEAVE('i', len);
- return len;
- }
-
- // Wait
- VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, NULL, "TCP_Client_Read");
-
- // Lock list and read as much as possible (up to `Length`)
- Mutex_Acquire( &conn->lRecievedPackets );
- len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
-
- if( len == 0 || conn->RecievedBuffer->Length == 0 ) {
- LOG("Marking as none avaliable (len = %i)", len);
- VFS_MarkAvaliable(Node, 0);
- }
-
- // Release the lock (we don't need it any more)
- Mutex_Release( &conn->lRecievedPackets );
-
- LEAVE('i', len);
- return len;
-}
-
-/**
- * \brief Send a data packet on a connection
- */
-void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const void *Data)
-{
- char buf[sizeof(tTCPHeader)+Length];
- tTCPHeader *packet = (void*)buf;
-
- packet->SourcePort = htons(Connection->LocalPort);
- packet->DestPort = htons(Connection->RemotePort);
- packet->DataOffset = (sizeof(tTCPHeader)/4)*16;
- packet->WindowSize = htons(TCP_WINDOW_SIZE);
-
- packet->AcknowlegementNumber = htonl(Connection->NextSequenceRcv);
- packet->SequenceNumber = htonl(Connection->NextSequenceSend);
- packet->Flags = TCP_FLAG_PSH|TCP_FLAG_ACK; // Hey, ACK if you can!
-
- memcpy(packet->Options, Data, Length);
-
- Log_Debug("TCP", "Send sequence 0x%08x", Connection->NextSequenceSend);
-#if HEXDUMP_OUTGOING
- Debug_HexDump("TCP_INT_SendDataPacket: Data = ", Data, Length);
-#endif
-
- TCP_SendPacket( Connection, sizeof(tTCPHeader)+Length, packet );
-
- Connection->NextSequenceSend += Length;
-}
-
-/**
- * \brief Send some bytes on a connection
- */
-Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tTCPConnection *conn = Node->ImplPtr;
- size_t rem = Length;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
-// #if DEBUG
-// Debug_HexDump("TCP_Client_Write: Buffer = ",
-// Buffer, Length);
-// #endif
-
- // Check if connection is open
- while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT )
- Threads_Yield();
-
- if( conn->State != TCP_ST_OPEN ) {
- VFS_MarkError(Node, 1);
- LEAVE('i', -1);
- return -1;
- }
-
- do
- {
- int len = (rem < TCP_MAX_PACKET_SIZE) ? rem : TCP_MAX_PACKET_SIZE;
-
- #if 0
- // Wait for space in the buffer
- Semaphore_Signal( &Connection->SentBufferSpace, len );
-
- // Save data to buffer (and update the length read by the ammount written)
- len = RingBuffer_Write( &Connection->SentBuffer, Buffer, len);
- #endif
-
- // Send packet
- TCP_INT_SendDataPacket(conn, len, Buffer);
-
- Buffer += len;
- rem -= len;
- } while( rem > 0 );
-
- LEAVE('i', Length);
- return Length;
-}
-
-/**
- * \brief Open a connection to another host using TCP
- * \param Conn Connection structure
- */
-void TCP_StartConnection(tTCPConnection *Conn)
-{
- tTCPHeader hdr = {0};
-
- Conn->State = TCP_ST_SYN_SENT;
-
- hdr.SourcePort = htons(Conn->LocalPort);
- hdr.DestPort = htons(Conn->RemotePort);
- Conn->NextSequenceSend = rand();
- hdr.SequenceNumber = htonl(Conn->NextSequenceSend);
- hdr.DataOffset = (sizeof(tTCPHeader)/4) << 4;
- hdr.Flags = TCP_FLAG_SYN;
- hdr.WindowSize = htons(TCP_WINDOW_SIZE); // Max
- hdr.Checksum = 0; // TODO
-
- TCP_SendPacket( Conn, sizeof(tTCPHeader), &hdr );
-
- Conn->NextSequenceSend ++;
- Conn->State = TCP_ST_SYN_SENT;
-
- return ;
-}
-
-/**
- * \brief Control a client socket
- */
-int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tTCPConnection *conn = Node->ImplPtr;
-
- ENTER("pNode iID pData", Node, ID, Data);
-
- switch(ID)
- {
- case 4: // Get/Set local port
- if(!Data)
- LEAVE_RET('i', conn->LocalPort);
- if(conn->State != TCP_ST_CLOSED)
- LEAVE_RET('i', -1);
- if(!CheckMem(Data, sizeof(Uint16)))
- LEAVE_RET('i', -1);
-
- if(Threads_GetUID() != 0 && *(Uint16*)Data < 1024)
- LEAVE_RET('i', -1);
-
- conn->LocalPort = *(Uint16*)Data;
- LEAVE_RET('i', conn->LocalPort);
-
- case 5: // Get/Set remote port
- if(!Data) LEAVE_RET('i', conn->RemotePort);
- if(conn->State != TCP_ST_CLOSED) LEAVE_RET('i', -1);
- if(!CheckMem(Data, sizeof(Uint16))) LEAVE_RET('i', -1);
- conn->RemotePort = *(Uint16*)Data;
- LEAVE_RET('i', conn->RemotePort);
-
- case 6: // Set Remote IP
- if( conn->State != TCP_ST_CLOSED )
- LEAVE_RET('i', -1);
- if( conn->Interface->Type == 4 )
- {
- if(!CheckMem(Data, sizeof(tIPv4))) LEAVE_RET('i', -1);
- conn->RemoteIP.v4 = *(tIPv4*)Data;
- }
- else if( conn->Interface->Type == 6 )
- {
- if(!CheckMem(Data, sizeof(tIPv6))) LEAVE_RET('i', -1);
- conn->RemoteIP.v6 = *(tIPv6*)Data;
- }
- LEAVE_RET('i', 0);
-
- case 7: // Connect
- if(conn->LocalPort == 0xFFFF)
- conn->LocalPort = TCP_GetUnusedPort();
- if(conn->RemotePort == -1)
- LEAVE_RET('i', 0);
-
- {
- tTime timeout_end = now() + conn->Interface->TimeoutDelay;
-
- TCP_StartConnection(conn);
- // TODO: Wait for connection to open
- while( conn->State == TCP_ST_SYN_SENT && timeout_end > now() ) {
- Threads_Yield();
- }
- if( conn->State == TCP_ST_SYN_SENT )
- LEAVE_RET('i', 0);
- }
-
- LEAVE_RET('i', 1);
-
- // Get recieve buffer length
- case 8:
- LEAVE_RET('i', conn->RecievedBuffer->Length);
- }
-
- return 0;
-}
-
-void TCP_Client_Close(tVFS_Node *Node)
-{
- tTCPConnection *conn = Node->ImplPtr;
- tTCPHeader packet;
-
- ENTER("pNode", Node);
-
- if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN )
- {
- packet.SourcePort = htons(conn->LocalPort);
- packet.DestPort = htons(conn->RemotePort);
- packet.DataOffset = (sizeof(tTCPHeader)/4)*16;
- packet.WindowSize = TCP_WINDOW_SIZE;
-
- packet.AcknowlegementNumber = 0;
- packet.SequenceNumber = htonl(conn->NextSequenceSend);
- packet.Flags = TCP_FLAG_FIN;
-
- TCP_SendPacket( conn, sizeof(tTCPHeader), &packet );
- }
-
- switch( conn->State )
- {
- case TCP_ST_CLOSE_WAIT:
- conn->State = TCP_ST_LAST_ACK;
- break;
- case TCP_ST_OPEN:
- conn->State = TCP_ST_FIN_WAIT1;
- while( conn->State == TCP_ST_FIN_WAIT1 ) Threads_Yield();
- break;
- default:
- Log_Warning("TCP", "Unhandled connection state in TCP_Client_Close");
- break;
- }
-
- free(conn);
-
- LEAVE('-');
-}
-
-/**
- * \brief Checks if a value is between two others (after taking into account wrapping)
- */
-int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue)
-{
- if( MaxValue < 0xFFFFFFFF )
- {
- Lower %= MaxValue + 1;
- Value %= MaxValue + 1;
- Higher %= MaxValue + 1;
- }
-
- // Simple Case, no wrap ?
- // Lower Value Higher
- // | ... + ... + ... + ... |
-
- if( Lower < Higher ) {
- return Lower < Value && Value < Higher;
- }
- // Higher has wrapped below lower
-
- // Value > Lower ?
- // Higher Lower Value
- // | ... + ... + ... + ... |
- if( Value > Lower ) {
- return 1;
- }
-
- // Value < Higher ?
- // Value Higher Lower
- // | ... + ... + ... + ... |
- if( Value < Higher ) {
- return 1;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - TCP Definitions
- */
-#ifndef _TCP_H_
-#define _TCP_H_
-
-#include "ipstack.h"
-#include <adt.h> // tRingBuffer
-
-typedef struct sTCPHeader tTCPHeader;
-typedef struct sTCPListener tTCPListener;
-typedef struct sTCPStoredPacket tTCPStoredPacket;
-typedef struct sTCPConnection tTCPConnection;
-
-struct sTCPHeader
-{
- Uint16 SourcePort;
- Uint16 DestPort;
- Uint32 SequenceNumber;
- Uint32 AcknowlegementNumber;
- #if 0
- struct { // Lowest to highest
- unsigned Reserved: 4;
- unsigned DataOffset: 4; // Size of the header in 32-bit words
- } __attribute__ ((packed));
- #else
- Uint8 DataOffset;
- #endif
- #if 0
- struct { // Lowest to Highest
- unsigned FIN: 1; // Last packet
- unsigned SYN: 1; // Synchronise Sequence Numbers
- unsigned RST: 1; // Reset Connection
- unsigned PSH: 1; // Push Function
- unsigned ACK: 1; // Acknowlegement field is significant
- unsigned URG: 1; // Urgent pointer is significant
- unsigned ECE: 1; // ECN-Echo
- unsigned CWR: 1; // Congestion Window Reduced
- } __attribute__ ((packed)) Flags;
- #else
- Uint8 Flags;
- #endif
- Uint16 WindowSize;
-
- Uint16 Checksum;
- Uint16 UrgentPointer;
-
- Uint8 Options[];
-} __attribute__ ((packed));
-
-enum eTCPFlags
-{
- TCP_FLAG_FIN = 0x01,
- TCP_FLAG_SYN = 0x02,
- TCP_FLAG_RST = 0x04,
- TCP_FLAG_PSH = 0x08,
- TCP_FLAG_ACK = 0x10,
- TCP_FLAG_URG = 0x20,
- TCP_FLAG_ECE = 0x40,
- TCP_FLAG_CWR = 0x80
-};
-
-struct sTCPListener
-{
- struct sTCPListener *Next; //!< Next server in the list
- Uint16 Port; //!< Listening port (0 disables the server)
- tInterface *Interface; //!< Listening Interface
- tVFS_Node Node; //!< Server Directory node
- int NextID; //!< Name of the next connection
- tShortSpinlock lConnections; //!< Spinlock for connections
- tTCPConnection *Connections; //!< Connections (linked list)
- tTCPConnection *volatile NewConnections;
- tTCPConnection *ConnectionsTail;
-};
-
-struct sTCPStoredPacket
-{
- struct sTCPStoredPacket *Next;
- size_t Length;
- Uint32 Sequence;
- Uint8 Data[];
-};
-
-enum eTCPConnectionState
-{
- TCP_ST_CLOSED, // 0 - Connection invalid
-
- TCP_ST_SYN_SENT, // 1 - SYN sent by local, waiting for SYN-ACK
- TCP_ST_SYN_RCVD, // 2 - SYN recieved, SYN-ACK sent
-
- TCP_ST_OPEN, // 3 - Connection open
-
- // Local Close
- TCP_ST_FIN_WAIT1, // 4 - FIN sent, waiting for reply (ACK or FIN)
- TCP_ST_FIN_WAIT2, // 5 - sent FIN acked, waiting for FIN from peer
- TCP_ST_CLOSING, // 6 - Waiting for ACK of FIN (FIN sent and recieved)
- TCP_ST_TIME_WAIT, // 7 - Waiting for timeout after local close
- // Remote close
- TCP_ST_CLOSE_WAIT, // 8 - FIN recieved, waiting for user to close (error set, wait for node close)
- TCP_ST_LAST_ACK, // 9 - FIN sent and recieved, waiting for ACK
- TCP_ST_FINISHED // 10 - Essentially closed, all packets are invalid
-};
-
-struct sTCPConnection
-{
- struct sTCPConnection *Next;
- enum eTCPConnectionState State; //!< Connection state (see ::eTCPConnectionState)
- Uint16 LocalPort; //!< Local port
- Uint16 RemotePort; //!< Remote port
- tInterface *Interface; //!< Listening Interface
- tVFS_Node Node; //!< Node
-
- Uint32 NextSequenceSend; //!< Next sequence value for outbound packets
- Uint32 NextSequenceRcv; //!< Next expected sequence value for inbound
-
- #if 0
- /**
- * \brief Non-ACKed packets
- * \note Ring buffer
- * \{
- */
- tMutex lNonACKedPackets;
- tTCPStoredPacket *SentPackets; //!< Non-acknowleged packets
- /**
- * \}
- */
- #endif
-
- /**
- * \brief Unread Packets
- * \note Ring buffer
- * \{
- */
- tMutex lRecievedPackets;
- tRingBuffer *RecievedBuffer;
- /**
- * \}
- */
-
- /**
- * \brief Out of sequence packets
- * \note Sorted list to improve times
- * \todo Convert this to a ring buffer and a bitmap of valid bytes
- * \{
- */
- #if CACHE_FUTURE_PACKETS_OR_BYTES == bytes
- Uint32 HighestSequenceRcvd; //!< Highest sequence number (within window) recieved
- Uint8 *FuturePacketData; //!< Future packet data (indexed by sequence number)
- Uint8 *FuturePacketValidBytes; //!< Valid byte bitmap (WINDOW_SIZE/8 bytes)
- #else
- tShortSpinlock lFuturePackets; //!< Future packets spinlock
- tTCPStoredPacket *FuturePackets; //!< Out of sequence packets
- #endif
- /**
- * \}
- */
-
- union {
- tIPv4 v4;
- tIPv6 v6;
- } RemoteIP; //!< Remote IP Address
- // Type is determined by LocalInterface->Type
-};
-
-#endif
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - UDP Handling
- */
-#include "ipstack.h"
-#include <api_drv_common.h>
-#include "udp.h"
-
-#define UDP_ALLOC_BASE 0xC000
-
-// === PROTOTYPES ===
-void UDP_Initialise();
-void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
-void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length);
-// --- Client Channels
-tVFS_Node *UDP_Channel_Init(tInterface *Interface);
-Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
- int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data);
-void UDP_Channel_Close(tVFS_Node *Node);
-// --- Helpers
-Uint16 UDP_int_AllocatePort();
- int UDP_int_MarkPortAsUsed(Uint16 Port);
-void UDP_int_FreePort(Uint16 Port);
-
-// === GLOBALS ===
-tVFS_NodeType gUDP_NodeType = {
- .Read = UDP_Channel_Read,
- .Write = UDP_Channel_Write,
- .IOCtl = UDP_Channel_IOCtl,
- .Close = UDP_Channel_Close
-};
-tMutex glUDP_Channels;
-tUDPChannel *gpUDP_Channels;
-
-tMutex glUDP_Ports;
-Uint32 gUDP_Ports[0x10000/32];
-
-tSocketFile gUDP_SocketFile = {NULL, "udp", UDP_Channel_Init};
-
-// === CODE ===
-/**
- * \fn void TCP_Initialise()
- * \brief Initialise the TCP Layer
- */
-void UDP_Initialise()
-{
- IPStack_AddFile(&gUDP_SocketFile);
- //IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable);
- IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket);
-}
-
-/**
- * \brief Scan a list of tUDPChannels and find process the first match
- * \return 0 if no match was found, -1 on error and 1 if a match was found
- */
-int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
-{
- tUDPHeader *hdr = Buffer;
- tUDPChannel *chan;
- tUDPPacket *pack;
- int len;
-
- for(chan = List;
- chan;
- chan = chan->Next)
- {
- // Match local endpoint
- if(chan->Interface && chan->Interface != Interface) continue;
- if(chan->LocalPort != ntohs(hdr->DestPort)) continue;
-
- // Check for remote port restriction
- if(chan->Remote.Port && chan->Remote.Port != ntohs(hdr->SourcePort))
- continue;
- // Check for remote address restriction
- if(chan->RemoteMask)
- {
- if(chan->Remote.AddrType != Interface->Type)
- continue;
- if(!IPStack_CompareAddress(Interface->Type, Address,
- &chan->Remote.Addr, chan->RemoteMask)
- )
- continue;
- }
-
- Log_Log("UDP", "Recieved packet for %p", chan);
- // Create the cached packet
- len = ntohs(hdr->Length);
- pack = malloc(sizeof(tUDPPacket) + len);
- pack->Next = NULL;
- memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type));
- pack->Remote.Port = ntohs(hdr->SourcePort);
- pack->Remote.AddrType = Interface->Type;
- pack->Length = len;
- memcpy(pack->Data, hdr->Data, len);
-
- // Add the packet to the channel's queue
- SHORTLOCK(&chan->lQueue);
- if(chan->Queue)
- chan->QueueEnd->Next = pack;
- else
- chan->QueueEnd = chan->Queue = pack;
- SHORTREL(&chan->lQueue);
- VFS_MarkAvaliable(&chan->Node, 1);
- Mutex_Release(&glUDP_Channels);
- return 1;
- }
- return 0;
-}
-
-/**
- * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
- * \brief Handles a packet from the IP Layer
- */
-void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
-{
- tUDPHeader *hdr = Buffer;
-
- Log_Debug("UDP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort));
- Log_Debug("UDP", "hdr->DestPort = %i", ntohs(hdr->DestPort));
- Log_Debug("UDP", "hdr->Length = %i", ntohs(hdr->Length));
- Log_Debug("UDP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
-
- // Check registered connections
- Mutex_Acquire(&glUDP_Channels);
- UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer);
- Mutex_Release(&glUDP_Channels);
-}
-
-/**
- * \brief Handle an ICMP Unrechable Error
- */
-void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
-{
-
-}
-
-/**
- * \brief Send a packet
- * \param Channel Channel to send the packet from
- * \param Data Packet data
- * \param Length Length in bytes of packet data
- */
-void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length)
-{
- tUDPHeader *hdr;
-
- if(Channel->Interface && Channel->Interface->Type != AddrType) return ;
-
- switch(AddrType)
- {
- case 4:
- // Create the packet
- hdr = malloc(sizeof(tUDPHeader)+Length);
- hdr->SourcePort = htons( Channel->LocalPort );
- hdr->DestPort = htons( Port );
- hdr->Length = htons( sizeof(tUDPHeader) + Length );
- hdr->Checksum = 0; // Checksum can be zero on IPv4
- memcpy(hdr->Data, Data, Length);
- // Pass on the the IPv4 Layer
- // TODO: What if Channel->Interface is NULL here?
- IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, sizeof(tUDPHeader)+Length, hdr);
- // Free allocated packet
- free(hdr);
- break;
- }
-}
-
-// --- Client Channels
-tVFS_Node *UDP_Channel_Init(tInterface *Interface)
-{
- tUDPChannel *new;
- new = calloc( sizeof(tUDPChannel), 1 );
- new->Interface = Interface;
- new->Node.ImplPtr = new;
- new->Node.NumACLs = 1;
- new->Node.ACLs = &gVFS_ACL_EveryoneRW;
- new->Node.Type = &gUDP_NodeType;
-
- Mutex_Acquire(&glUDP_Channels);
- new->Next = gpUDP_Channels;
- gpUDP_Channels = new;
- Mutex_Release(&glUDP_Channels);
-
- return &new->Node;
-}
-
-/**
- * \brief Read from the channel file (wait for a packet)
- */
-Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tUDPChannel *chan = Node->ImplPtr;
- tUDPPacket *pack;
- tUDPEndpoint *ep;
- int ofs, addrlen;
-
- if(chan->LocalPort == 0) {
- Log_Notice("UDP", "Channel %p sent with no local port", chan);
- return 0;
- }
-
- while(chan->Queue == NULL) Threads_Yield();
-
- for(;;)
- {
- VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read");
- SHORTLOCK(&chan->lQueue);
- if(chan->Queue == NULL) {
- SHORTREL(&chan->lQueue);
- continue;
- }
- pack = chan->Queue;
- chan->Queue = pack->Next;
- if(!chan->Queue) {
- chan->QueueEnd = NULL;
- VFS_MarkAvaliable(Node, 0); // Nothing left
- }
- SHORTREL(&chan->lQueue);
- break;
- }
-
- // Check that the header fits
- addrlen = IPStack_GetAddressSize(pack->Remote.AddrType);
- ep = Buffer;
- ofs = 4 + addrlen;
- if(Length < ofs) {
- free(pack);
- Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs);
- return 0;
- }
-
- // Fill header
- ep->Port = pack->Remote.Port;
- ep->AddrType = pack->Remote.AddrType;
- memcpy(&ep->Addr, &pack->Remote.Addr, addrlen);
-
- // Copy packet data
- if(Length > ofs + pack->Length) Length = ofs + pack->Length;
- memcpy((char*)Buffer + ofs, pack->Data, Length - ofs);
-
- // Free cached packet
- free(pack);
-
- return Length;
-}
-
-/**
- * \brief Write to the channel file (send a packet)
- */
-Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tUDPChannel *chan = Node->ImplPtr;
- const tUDPEndpoint *ep;
- const void *data;
- int ofs;
- if(chan->LocalPort == 0) return 0;
-
- ep = Buffer;
- ofs = 2 + 2 + IPStack_GetAddressSize( ep->AddrType );
-
- data = (const char *)Buffer + ofs;
-
- UDP_SendPacketTo(chan, ep->AddrType, &ep->Addr, ep->Port, data, (size_t)Length - ofs);
-
- return 0;
-}
-
-/**
- * \brief Names for channel IOCtl Calls
- */
-static const char *casIOCtls_Channel[] = {
- DRV_IOCTLNAMES,
- "getset_localport",
- "getset_remoteport",
- "getset_remotemask",
- "set_remoteaddr",
- NULL
- };
-/**
- * \brief Channel IOCtls
- */
-int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tUDPChannel *chan = Node->ImplPtr;
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_MISC, "UDP Channel", 0x100, casIOCtls_Channel);
-
- case 4: // getset_localport (returns bool success)
- if(!Data) LEAVE_RET('i', chan->LocalPort);
- if(!CheckMem( Data, sizeof(Uint16) ) ) {
- LOG("Invalid pointer %p", Data);
- LEAVE_RET('i', -1);
- }
- // Set port
- chan->LocalPort = *(Uint16*)Data;
- // Permissions check (Ports lower than 1024 are root-only)
- if(chan->LocalPort != 0 && chan->LocalPort < 1024) {
- if( Threads_GetUID() != 0 ) {
- LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort);
- chan->LocalPort = 0;
- LEAVE_RET('i', -1);
- }
- }
- // Allocate a random port if requested
- if( chan->LocalPort == 0 )
- chan->LocalPort = UDP_int_AllocatePort();
- else
- {
- // Else, mark the requested port as used
- if( UDP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) {
- LOG("Port %i us currently in use", chan->LocalPort);
- chan->LocalPort = 0;
- LEAVE_RET('i', 0);
- }
- LEAVE_RET('i', 1);
- }
- LEAVE_RET('i', 1);
-
- case 5: // getset_remoteport (returns bool success)
- if(!Data) LEAVE_RET('i', chan->Remote.Port);
- if(!CheckMem( Data, sizeof(Uint16) ) ) {
- LOG("Invalid pointer %p", Data);
- LEAVE_RET('i', -1);
- }
- chan->Remote.Port = *(Uint16*)Data;
- return 1;
-
- case 6: // getset_remotemask (returns bool success)
- if(!Data) LEAVE_RET('i', chan->RemoteMask);
- if(!CheckMem(Data, sizeof(int))) LEAVE_RET('i', -1);
- if( !chan->Interface ) {
- LOG("Can't set remote mask on NULL interface");
- LEAVE_RET('i', -1);
- }
- if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) )
- LEAVE_RET('i', -1);
- chan->RemoteMask = *(int*)Data;
- return 1;
-
- case 7: // set_remoteaddr (returns bool success)
- if( !chan->Interface ) {
- LOG("Can't set remote address on NULL interface");
- LEAVE_RET('i', -1);
- }
- if(!CheckMem(Data, IPStack_GetAddressSize(chan->Interface->Type))) {
- LOG("Invalid pointer");
- LEAVE_RET('i', -1);
- }
- memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type));
- return 0;
- }
- LEAVE_RET('i', 0);
-}
-
-/**
- * \brief Close and destroy an open channel
- */
-void UDP_Channel_Close(tVFS_Node *Node)
-{
- tUDPChannel *chan = Node->ImplPtr;
- tUDPChannel *prev;
-
- // Remove from the main list first
- Mutex_Acquire(&glUDP_Channels);
- if(gpUDP_Channels == chan)
- gpUDP_Channels = gpUDP_Channels->Next;
- else
- {
- for(prev = gpUDP_Channels;
- prev->Next && prev->Next != chan;
- prev = prev->Next);
- if(!prev->Next)
- Log_Warning("UDP", "Bookeeping Fail, channel %p is not in main list", chan);
- else
- prev->Next = prev->Next->Next;
- }
- Mutex_Release(&glUDP_Channels);
-
- // Clear Queue
- SHORTLOCK(&chan->lQueue);
- while(chan->Queue)
- {
- tUDPPacket *tmp;
- tmp = chan->Queue;
- chan->Queue = tmp->Next;
- free(tmp);
- }
- SHORTREL(&chan->lQueue);
-
- // Free channel structure
- free(chan);
-}
-
-/**
- * \return Port Number on success, or zero on failure
- */
-Uint16 UDP_int_AllocatePort()
-{
- int i;
- Mutex_Acquire(&glUDP_Ports);
- // Fast Search
- for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 )
- if( gUDP_Ports[i/32] != 0xFFFFFFFF )
- break;
- if(i == 0x10000) return 0;
- for( ;; i++ )
- {
- if( !(gUDP_Ports[i/32] & (1 << (i%32))) )
- return i;
- }
- Mutex_Release(&glUDP_Ports);
-}
-
-/**
- * \brief Allocate a specific port
- * \return Boolean Success
- */
-int UDP_int_MarkPortAsUsed(Uint16 Port)
-{
- Mutex_Acquire(&glUDP_Ports);
- if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) {
- return 0;
- Mutex_Release(&glUDP_Ports);
- }
- gUDP_Ports[Port/32] |= 1 << (Port%32);
- Mutex_Release(&glUDP_Ports);
- return 1;
-}
-
-/**
- * \brief Free an allocated port
- */
-void UDP_int_FreePort(Uint16 Port)
-{
- Mutex_Acquire(&glUDP_Ports);
- gUDP_Ports[Port/32] &= ~(1 << (Port%32));
- Mutex_Release(&glUDP_Ports);
-}
+++ /dev/null
-/*
- * Acess2 IP Stack
- * - UDP Definitions
- */
-#ifndef _UDP_H_
-#define _UDP_H_
-
-#include "ipstack.h"
-#include "ipv4.h"
-
-typedef struct sUDPHeader tUDPHeader;
-typedef struct sUDPEndpoint tUDPEndpoint;
-typedef struct sUDPPacket tUDPPacket;
-typedef struct sUDPChannel tUDPChannel;
-
-struct sUDPHeader
-{
- Uint16 SourcePort;
- Uint16 DestPort;
- Uint16 Length;
- Uint16 Checksum;
- Uint8 Data[];
-};
-
-struct sUDPEndpoint
-{
- Uint16 Port;
- Uint16 AddrType;
- union {
- tIPv4 v4;
- tIPv6 v6;
- } Addr;
-};
-
-struct sUDPPacket
-{
- struct sUDPPacket *Next;
- tUDPEndpoint Remote;
- size_t Length;
- Uint8 Data[];
-};
-
-struct sUDPChannel
-{
- struct sUDPChannel *Next;
- tInterface *Interface;
- Uint16 LocalPort;
-
- tUDPEndpoint Remote; // Only accept packets form this address/port pair
- int RemoteMask; // Mask on the address
-
- tVFS_Node Node;
- tShortSpinlock lQueue;
- tUDPPacket * volatile Queue;
- tUDPPacket *QueueEnd;
-};
-
-#endif
-
+++ /dev/null
-CATEGORY = Input
-
--include ../../Makefile.tpl
+++ /dev/null
-/*
- * Acess2
- * - By thePowersGang (John Hodge)
- *
- * 8042 (or comaptible) Driver
- */
-#include <acess.h>
-#include "common.h"
-
-// === PROTOTYPES ===
-void KBC8042_Init(void);
-void KBC8042_KeyboardHandler(int IRQ, void *Ptr);
-void KBC8042_MouseHandler(int IRQ, void *Ptr);
-void KBC8042_EnableMouse(void);
-static inline void KBC8042_SendDataAlt(Uint8 data);
-static inline void KBC8042_SendData(Uint8 data);
-static inline Uint8 KBC8042_ReadData(void);
-static void KBC8042_SendMouseCommand(Uint8 cmd);
-
-// === CODE ===
-void KBC8042_Init(void)
-{
- IRQ_AddHandler(1, KBC8042_KeyboardHandler, NULL);
- IRQ_AddHandler(12, KBC8042_MouseHandler, NULL); // Set IRQ
-
- {
- Uint8 temp;
- // Attempt to get around a strange bug in Bochs/Qemu by toggling
- // the controller on and off
- temp = inb(0x61);
- outb(0x61, temp | 0x80);
- outb(0x61, temp & 0x7F);
- inb(0x60); // Clear keyboard buffer
- }
-}
-
-void KBC8042_KeyboardHandler(int IRQ, void *Ptr)
-{
- Uint8 scancode;
-
-// Log("KBC8042_KeyboardHandler: (IRQ=%i, Ptr=%p)", IRQ, Ptr);
-
- scancode = inb(0x60);
- KB_HandleScancode( scancode );
-}
-
-void KBC8042_MouseHandler(int IRQ, void *Ptr)
-{
- PS2Mouse_HandleInterrupt( inb(0x60) );
-}
-
-void KBC8042_SetLEDs(Uint8 leds)
-{
- while( inb(0x64) & 2 ); // Wait for bit 2 to unset
- outb(0x60, 0xED); // Send update command
-
- while( inb(0x64) & 2 ); // Wait for bit 2 to unset
- outb(0x60, leds);
-}
-
-void KBC8042_EnableMouse(void)
-{
- Uint8 status;
- Log_Log("8042", "Enabling Mouse...");
-
- // Enable AUX PS/2
- KBC8042_SendDataAlt(0xA8);
-
- // Enable AUX PS/2 (Compaq Status Byte)
- KBC8042_SendDataAlt(0x20); // Send Command
- status = KBC8042_ReadData(); // Get Status
- status &= ~0x20; // Clear "Disable Mouse Clock"
- status |= 0x02; // Set IRQ12 Enable
- KBC8042_SendDataAlt(0x60); // Send Command
- KBC8042_SendData(status); // Set Status
-
- //mouseSendCommand(0xF6); // Set Default Settings
- KBC8042_SendMouseCommand(0xF4); // Enable Packets
-}
-
-static inline void KBC8042_SendDataAlt(Uint8 data)
-{
- int timeout=100000;
- while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear
- outb(0x64, data); // Send Command
-}
-static inline void KBC8042_SendData(Uint8 data)
-{
- int timeout=100000;
- while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear
- outb(0x60, data); // Send Command
-}
-static inline Uint8 KBC8042_ReadData(void)
-{
- int timeout=100000;
- while( timeout-- && (inb(0x64) & 1) == 0); // Wait for Flag to set
- return inb(0x60);
-}
-static inline void KBC8042_SendMouseCommand(Uint8 cmd)
-{
- KBC8042_SendDataAlt(0xD4);
- KBC8042_SendData(cmd);
-}
-
+++ /dev/null
-#
-#
-
-OBJ = main.o kb.o ps2mouse.o
-OBJ += 8042.o pl050.o
-NAME = PS2KbMouse
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2
- *
- * PS2 Keyboard/Mouse Driver
- *
- * common.h
- * - Shared definitions
- */
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-extern int KB_Install(char **Arguments);
-extern int PS2Mouse_Install(char **Arguments);
-
-extern void KBC8042_Init(void);
-extern void KBC8042_EnableMouse(void);
-
-extern void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ);
-extern void PL050_EnableMouse(void);
-
-extern void KB_HandleScancode(Uint8 scancode);
-extern void PS2Mouse_HandleInterrupt(Uint8 InputByte);
-
-extern void (*gpMouse_EnableFcn)(void);
-
-#endif
+++ /dev/null
-/*
- * Acess2
- * PS2 Keyboard Driver
- */
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <api_drv_common.h>
-#include <api_drv_keyboard.h>
-#include "kb_kbdus.h"
-
-// === CONSTANTS ===
-#define KB_BUFFER_SIZE 1024
-#define USE_KERNEL_MAGIC 1
-
-// === IMPORTS ===
-extern void Threads_ToggleTrace(int TID);
-extern void Threads_Dump(void);
-extern void Heap_Stats(void);
-
-// === PROTOTYPES ===
- int KB_Install(char **Arguments);
-void KB_HandleScancode(Uint8 scancode);
-void KB_UpdateLEDs(void);
- int KB_IOCtl(tVFS_Node *Node, int Id, void *Data);
-
-// === GLOBALS ===
-tVFS_NodeType gKB_NodeType = {
- .IOCtl = KB_IOCtl
-};
-tDevFS_Driver gKB_DevInfo = {
- NULL, "PS2Keyboard",
- { .Type = &gKB_NodeType }
-};
-tKeybardCallback gKB_Callback = NULL;
-Uint32 **gpKB_Map = gpKBDUS;
-Uint8 gbaKB_States[3][256];
- int gbKB_ShiftState = 0;
- int gbKB_CapsState = 0;
- int gbKB_KeyUp = 0;
- int giKB_KeyLayer = 0;
-#if USE_KERNEL_MAGIC
- int gbKB_MagicState = 0;
- int giKB_MagicAddress = 0;
- int giKB_MagicAddressPos = 0;
-#endif
-
-// === CODE ===
-/**
- * \brief Install the keyboard driver
- */
-int KB_Install(char **Arguments)
-{
- DevFS_AddDevice( &gKB_DevInfo );
- //Log("KB_Install: Installed");
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Called on a keyboard IRQ
- * \param IRQNum IRQ number (unused)
- */
-void KB_HandleScancode(Uint8 scancode)
-{
- Uint32 ch;
- int bCaseSwitch = (gbKB_ShiftState != 0) != (gbKB_CapsState != 0);
-
- //Log_Debug("Keyboard", "scancode = %02x", scancode);
-
- // Ignore ACKs
- if(scancode == 0xFA) {
- // Oh man! This is anarchic (I'm leaving it here to represent
- // the mess that Acess once was)
- //kb_lastChar = KB_ACK;
- return;
- }
-
- // Layer +1
- if(scancode == 0xE0) {
- giKB_KeyLayer = 1;
- return;
- }
- // Layer +2
- if(scancode == 0xE1) {
- giKB_KeyLayer = 2;
- return;
- }
-
- #if KB_ALT_SCANCODES
- if(scancode == 0xF0)
- {
- gbKB_KeyUp = 1;
- return;
- }
- #else
- if(scancode & 0x80)
- {
- scancode &= 0x7F;
- gbKB_KeyUp = 1;
- }
- #endif
-
- if( gKB_Callback )
- gKB_Callback( (giKB_KeyLayer << 8) | scancode | KEY_ACTION_RAWSYM );
-
- // Translate
- ch = gpKB_Map[giKB_KeyLayer*2+bCaseSwitch][scancode];
- // - Unknown characters in the shift layer fall through to lower
- if(bCaseSwitch && ch == 0)
- ch = gpKB_Map[giKB_KeyLayer*2][scancode];
- // Check for unknown key
- if(!ch)
- {
- if(!gbKB_KeyUp)
- Log_Warning("Keyboard", "UNK %i %x", giKB_KeyLayer, scancode);
-// return ;
- // Can pass through to ensure each raw message has a up/down with it
- }
-
- // Key Up?
- if (gbKB_KeyUp)
- {
- gbKB_KeyUp = 0;
- gbaKB_States[giKB_KeyLayer][scancode] = 0; // Unset key state flag
-
- #if USE_KERNEL_MAGIC
- if(ch == KEY_LCTRL) gbKB_MagicState &= ~1;
- if(ch == KEY_LALT) gbKB_MagicState &= ~2;
- #endif
-
- if(ch == KEY_LSHIFT) gbKB_ShiftState &= ~1;
- if(ch == KEY_RSHIFT) gbKB_ShiftState &= ~2;
-
- // Call callback
- if(gKB_Callback) gKB_Callback( ch | KEY_ACTION_RELEASE );
-
- // Reset Layer
- giKB_KeyLayer = 0;
- return;
- }
-
- // Refire?
- if( gbaKB_States[giKB_KeyLayer][scancode] == 1 )
- {
- if(gKB_Callback) gKB_Callback(ch | KEY_ACTION_REFIRE);
- giKB_KeyLayer = 0;
- return ;
- }
-
- // Set the bit relating to the key
- gbaKB_States[giKB_KeyLayer][scancode] = 1;
- // Set shift key bits
- if(ch == KEY_LSHIFT) gbKB_ShiftState |= 1;
- if(ch == KEY_RSHIFT) gbKB_ShiftState |= 2;
-
- // Check for Caps Lock
- if(ch == KEY_CAPSLOCK) {
- gbKB_CapsState = !gbKB_CapsState;
- KB_UpdateLEDs();
- }
-
- // --- Check for Kernel Magic Combos
- #if USE_KERNEL_MAGIC
- if(ch == KEY_LCTRL) {
- gbKB_MagicState |= 1;
- //Log_Log("Keyboard", "Kernel Magic LCTRL Down\n");
- }
- if(ch == KEY_LALT) {
- gbKB_MagicState |= 2;
- //Log_Log("Keyboard", "Kernel Magic LALT Down\n");
- }
- if(gbKB_MagicState == 3)
- {
- switch(ch)
- {
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- case '8': case '9': case 'a': case 'b':
- case 'c': case 'd': case 'e': case 'f':
- {
- char str[4] = {'0', 'x', ch, 0};
- if(giKB_MagicAddressPos == BITS/4) return;
- giKB_MagicAddress |= atoi(str) << giKB_MagicAddressPos;
- giKB_MagicAddressPos ++;
- }
- return;
-
- // Instruction Tracing
- case 't':
- Log("Toggle instruction tracing on %i\n", giKB_MagicAddress);
- Threads_ToggleTrace( giKB_MagicAddress );
- giKB_MagicAddress = 0; giKB_MagicAddressPos = 0;
- return;
-
- // Thread List Dump
- case 'p': Threads_Dump(); return;
- // Heap Statistics
- case 'h': Heap_Stats(); return;
- // Dump Structure
- case 's': return;
- }
- }
- #endif
-
- if(gKB_Callback)
- gKB_Callback(ch | KEY_ACTION_PRESS);
-
- // Reset Layer
- giKB_KeyLayer = 0;
-}
-
-/**
- * \fn void KB_UpdateLEDs(void)
- * \brief Updates the status of the keyboard LEDs
- */
-void KB_UpdateLEDs(void)
-{
- Uint8 leds;
-
- leds = (gbKB_CapsState ? 4 : 0);
-
- // TODO: Update LEDS
- Log_Warning("Keyboard", "TODO: Update LEDs");
-}
-
-static const char *csaIOCTL_NAMES[] = {DRV_IOCTLNAMES, DRV_KEYBAORD_IOCTLNAMES, NULL};
-
-/**
- * \fn int KB_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief Calls an IOCtl Command
- */
-int KB_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
- switch(Id)
- {
- BASE_IOCTLS(DRV_TYPE_KEYBOARD, "KB", 0x100, csaIOCTL_NAMES);
-
- // Sets the Keyboard Callback
- case KB_IOCTL_SETCALLBACK:
- // Sanity Check
- if((Uint)Data < KERNEL_BASE) return 0;
- // Can only be set once
- if(gKB_Callback != NULL) return 0;
- // Set Callback
- gKB_Callback = Data;
- return 1;
-
- default:
- return 0;
- }
-}
+++ /dev/null
-\r
-#ifndef _KBDUS_H\r
-#define _KBDUS_H\r
-\r
-// - Base (NO PREFIX)\r
-Uint32 gpKBDUS1[256] = {\r
- 0,\r
- KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', // 0x01 - 0x0e\r
- '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x0f - 0x1c\r
- KEY_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', // 0x1d - 0x28\r
- '`', KEY_LSHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, // 0x29 - 0x3e\r
- KEY_KPSTAR,\r
- KEY_LALT, ' ', KEY_CAPSLOCK,\r
- KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,\r
- KEY_NUMLOCK, KEY_SCROLLLOCK,\r
- KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS,\r
- KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, KEY_KPPLUS,\r
- KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN,\r
- KEY_KPINS, KEY_KPDEL,\r
- 0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0,\r
-/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-};\r
-// Shift Key pressed\r
-Uint32 gpKBDUS1s[256] = {\r
- 0,\r
- KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', // 0x01 - 0x0e\r
- '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', // 0x0f - 0x1c\r
- KEY_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':','"', // 0x1d - 0x28\r
- '~', KEY_LSHIFT,'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT, // 0x29 - 0x3e\r
- 0\r
- };\r
-// - 0xE0 Prefixed\r
-Uint32 gpKBDUS2[256] = {\r
-// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
-/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F\r
-/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_KPENTER, KEY_RCTRL, 0, 0,\r
-/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*30*/ 0, 0, 0, 0, 0, KEY_KPSLASH, 0, 0, KEY_RALT, 0, 0, 0, 0, 0, 0, 0,\r
-/*40*/ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END,\r
-/*50*/ KEY_DOWN, KEY_PGDOWN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, 0, KEY_LWIN, KEY_RWIN, KEY_MENU, 0, 0,\r
-/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-};\r
-// - 0xE1 Prefixed\r
-Uint32 gpKBDUS3[256] = {\r
-// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
-/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F\r
-/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAUSE, 0, 0,\r
-/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
-};\r
-\r
-\r
-Uint32 *gpKBDUS[6] = { gpKBDUS1, gpKBDUS1s, gpKBDUS2, gpKBDUS2, gpKBDUS3, gpKBDUS3 };\r
-\r
-#endif\r
+++ /dev/null
-/*
- * Acess2
- *
- * PS/2 Keboard / Mouse Driver
- */
-#include <acess.h>
-#include <modules.h>
-#include "common.h"
-
-// === IMPORTS ===
-// TODO: Allow runtime/compile-time switching
-// Maybe PCI will have it?
-// Integrator-CP
-#if 0
-#define KEYBOARD_IRQ 3
-#define KEYBOARD_BASE 0x18000000
-#define MOUSE_IRQ 4
-#define MOUSE_BASE 0x19000000
-#endif
-// Realview
-#if 1
-#define KEYBOARD_IRQ 20
-#define KEYBOARD_BASE 0x10006000
-#define MOUSE_IRQ 21
-#define MOUSE_BASE 0x10007000
-#endif
-
-// === PROTOTYPES ===
- int PS2_Install(char **Arguments);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x0100, Input_PS2KbMouse, PS2_Install, NULL, NULL); // Shuts the makefile up
-MODULE_DEFINE(0, 0x0100, PS2Keyboard, KB_Install, NULL, "Input_PS2KbMouse", NULL);
-MODULE_DEFINE(0, 0x0100, PS2Mouse, PS2Mouse_Install, NULL, "Input_PS2KbMouse", NULL);
-
-// === CODE ===
-int PS2_Install(char **Arguments)
-{
- #if ARCHDIR_is_x86 || ARCHDIR_is_x86_64
- KBC8042_Init();
- gpMouse_EnableFcn = KBC8042_EnableMouse;
- #elif ARCHDIR_is_armv7
- PL050_Init(KEYBOARD_BASE, KEYBOARD_IRQ, MOUSE_BASE, MOUSE_IRQ);
- gpMouse_EnableFcn = PL050_EnableMouse;
- #endif
-
- return MODULE_ERR_OK;
-}
+++ /dev/null
-/*
- * Acess2
- * - By thePowersGang (John Hodge)
- *
- * PL050 (or comaptible) Driver
- */
-#define DEBUG 1
-
-#include <acess.h>
-#include "common.h"
-
-// === CONSTANTS ===
-#define PL050_TXBUSY 0x20
-
-// === PROTOTYPES ===
-void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ);
-void PL050_KeyboardHandler(int IRQ, void *Ptr);
-void PL050_MouseHandler(int IRQ, void *Ptr);
-void PL050_EnableMouse(void);
-static inline void PL050_WriteMouseData(Uint8 data);
-static inline void PL050_WriteKeyboardData(Uint8 data);
-static inline Uint8 PL050_ReadMouseData(void);
-static inline Uint8 PL050_ReadKeyboardData(void);
-
-// === GLOBALS ===
-Uint32 *gpPL050_KeyboardBase;
-Uint32 *gpPL050_MouseBase;
-
-// === CODE ===
-void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ)
-{
- if( KeyboardBase ) {
- LOG("KeyboardBase = 0x%x", KeyboardBase);
- gpPL050_KeyboardBase = (void*)MM_MapHWPages(KeyboardBase, 1);
- LOG("gpPL050_KeyboardBase = %p", gpPL050_KeyboardBase);
- IRQ_AddHandler(KeyboardIRQ, PL050_KeyboardHandler, NULL);
-
- gpPL050_KeyboardBase[0] = 0x10;
- }
- if( MouseBase ) {
- gpPL050_MouseBase = (void*)MM_MapHWPages(MouseBase, 1);
- IRQ_AddHandler(MouseIRQ, PL050_MouseHandler, NULL);
-
- gpPL050_MouseBase[0] = 0x10;
- }
-}
-
-void PL050_KeyboardHandler(int IRQ, void *Ptr)
-{
- Uint8 scancode;
-
- scancode = PL050_ReadKeyboardData();
- KB_HandleScancode( scancode );
-}
-
-void PL050_MouseHandler(int IRQ, void *Ptr)
-{
- PS2Mouse_HandleInterrupt( PL050_ReadMouseData() );
-}
-
-void PL050_SetLEDs(Uint8 leds)
-{
- PL050_WriteKeyboardData(0xED);
- PL050_WriteKeyboardData(leds);
-}
-
-void PL050_EnableMouse(void)
-{
- Log_Log("PL050", "Enabling Mouse...");
-
- //PL050_WriteMouseData(0xD4);
- //PL050_WriteMouseData(0xF6); // Set Default Settings
- PL050_WriteMouseData(0xD4);
- PL050_WriteMouseData(0xF4); // Enable Packets
- LOG("Done");
-}
-
-static inline void PL050_WriteMouseData(Uint8 Data)
-{
- int timeout = 10000;
-
- if( !gpPL050_MouseBase ) {
- Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)");
- return ;
- }
-
- ENTER("xData", Data);
-
- while( --timeout && (gpPL050_MouseBase[1] & PL050_TXBUSY) );
- if(timeout)
- gpPL050_MouseBase[2] = Data;
- else
- Log_Error("PL050", "Write to mouse timed out");
- LEAVE('-');
-}
-
-static inline Uint8 PL050_ReadMouseData(void)
-{
- if( !gpPL050_MouseBase ) {
- Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)");
- return 0;
- }
- return gpPL050_MouseBase[2];
-}
-static inline void PL050_WriteKeyboardData(Uint8 Data)
-{
- int timeout = 10000;
-
- if( !gpPL050_KeyboardBase ) {
- Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)");
- return ;
- }
-
- while( --timeout && gpPL050_KeyboardBase[1] & PL050_TXBUSY );
- if(timeout)
- gpPL050_KeyboardBase[2] = Data;
- else
- Log_Error("PL050", "Write to keyboard timed out");
-}
-static inline Uint8 PL050_ReadKeyboardData(void)
-{
- if( !gpPL050_KeyboardBase ) {
- Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)");
- return 0;
- }
-
- return gpPL050_KeyboardBase[2];
-}
-
+++ /dev/null
-/*\r
- * Acess2 Mouse Driver\r
- */\r
-#define DEBUG 0\r
-#include <acess.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include <fs_devfs.h>\r
-#include <api_drv_common.h>\r
-#include <api_drv_joystick.h>\r
-#include "common.h"\r
-\r
-// == CONSTANTS ==\r
-#define NUM_AXIES 2 // X+Y\r
-#define NUM_BUTTONS 5 // Left, Right, Scroll Click, Scroll Up, Scroll Down\r
-\r
-// == PROTOTYPES ==\r
-// - Internal -\r
- int PS2Mouse_Install(char **Arguments);\r
-void PS2Mouse_HandleInterrupt(Uint8 InputByte);\r
-// - Filesystem -\r
-Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
-int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
-\r
-// == GLOBALS ==\r
-void (*gpMouse_EnableFcn)(void);\r
-// - Settings\r
- int giMouse_Sensitivity = 1;\r
- int giMouse_MaxX = 640, giMouse_MaxY = 480;\r
-// - File Data\r
-Uint8 gMouse_FileData[sizeof(tJoystick_FileHeader) + NUM_AXIES*sizeof(tJoystick_Axis) + NUM_BUTTONS];\r
-tJoystick_FileHeader *gMouse_FileHeader = (void *)gMouse_FileData;\r
-tJoystick_Axis *gMouse_Axies;\r
-Uint8 *gMouse_Buttons;\r
-tJoystick_Callback gMouse_Callback;\r
- int gMouse_CallbackArg;\r
- int giMouse_AxisLimits[2];\r
-// - Internal State\r
- int giMouse_Cycle = 0; // IRQ Position\r
-Uint8 gaMouse_Bytes[4] = {0,0,0,0};\r
-// - Driver definition\r
-tVFS_NodeType gMouse_NodeType = {\r
- .Read = PS2Mouse_Read,\r
- .IOCtl = PS2Mouse_IOCtl\r
-};\r
-tDevFS_Driver gMouse_DriverStruct = {\r
- NULL, "PS2Mouse",\r
- {\r
- .NumACLs = 1, .ACLs = &gVFS_ACL_EveryoneRX,\r
- .Type = &gMouse_NodeType\r
- }\r
-};\r
-\r
-// == CODE ==\r
-int PS2Mouse_Install(char **Arguments)\r
-{\r
- \r
-\r
- // Set up variables\r
- gMouse_Axies = (void*)&gMouse_FileData[4];\r
- gMouse_Buttons = (void*)&gMouse_Axies[NUM_AXIES];\r
-\r
- gMouse_FileHeader->NAxies = 2; gMouse_FileHeader->NButtons = 3;\r
- gMouse_Axies[0].MinValue = -10; gMouse_Axies[0].MaxValue = 10;\r
- gMouse_Axies[1].MinValue = -10; gMouse_Axies[1].MaxValue = 10;\r
- \r
- // Initialise Mouse Controller\r
- giMouse_Cycle = 0; // Set Current Cycle position\r
- gpMouse_EnableFcn();\r
- \r
- DevFS_AddDevice(&gMouse_DriverStruct);\r
- \r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/* Handle Mouse Interrupt\r
- */\r
-void PS2Mouse_HandleInterrupt(Uint8 InputByte)\r
-{\r
- Uint8 flags;\r
- int d[2], d_accel[2];\r
- int i;\r
- \r
- // Gather mouse data\r
- gaMouse_Bytes[giMouse_Cycle] = InputByte;\r
- LOG("gaMouse_Bytes[%i] = 0x%02x", gMouse_Axies[0].MaxValue);\r
- // - If bit 3 of the first byte is not set, it's not a valid packet?\r
- if(giMouse_Cycle == 0 && !(gaMouse_Bytes[0] & 0x08) )\r
- return ;\r
- giMouse_Cycle++;\r
- if(giMouse_Cycle < 3)\r
- return ;\r
-\r
- giMouse_Cycle = 0;\r
-\r
- // Actual Processing (once we have all bytes) \r
- flags = gaMouse_Bytes[0];\r
-\r
- LOG("flags = 0x%x", flags);\r
- \r
- // Check for X/Y Overflow\r
- if(flags & 0xC0) return;\r
- \r
- // Calculate dX and dY\r
- d[0] = gaMouse_Bytes[1]; if(flags & 0x10) d[0] = -(256-d[0]); // x\r
- d[1] = gaMouse_Bytes[2]; if(flags & 0x20) d[1] = -(256-d[1]); // y\r
- d[1] = -d[1]; // Y is negated\r
- LOG("RAW dx=%i, dy=%i\n", d[0], d[1]);\r
- // Apply scaling\r
- // TODO: Apply a form of curve to the mouse movement (dx*log(dx), dx^k?)\r
- // TODO: Independent sensitivities?\r
- // TODO: Disable acceleration via a flag?\r
- d_accel[0] = d[0]*giMouse_Sensitivity;\r
- d_accel[1] = d[1]*giMouse_Sensitivity;\r
- \r
- // Set Buttons (Primary)\r
- for( i = 0; i < 3; i ++ )\r
- {\r
- Uint8 newVal = (flags & (1 << i)) ? 0xFF : 0;\r
- if(newVal != gMouse_Buttons[i]) {\r
- if( gMouse_Callback )\r
- gMouse_Callback(gMouse_CallbackArg, 0, i, newVal - gMouse_Buttons[i]);\r
- gMouse_Buttons[i] = newVal;\r
- }\r
- }\r
- \r
- // Update X and Y Positions\r
- for( i = 0; i < 2; i ++ )\r
- {\r
- Sint16 newCursor = 0;\r
- if( giMouse_AxisLimits[i] )\r
- newCursor = MIN( MAX(0, gMouse_Axies[i].CursorPos + d_accel[i]), giMouse_AxisLimits[i] );;\r
- \r
- if( gMouse_Callback )\r
- {\r
- if(giMouse_AxisLimits[i] && gMouse_Axies[i].CursorPos != newCursor)\r
- gMouse_Callback(gMouse_CallbackArg, 1, i, newCursor - gMouse_Axies[i].CursorPos);\r
- if(!giMouse_AxisLimits[i] && gMouse_Axies[i].CurValue != d_accel[i])\r
- gMouse_Callback(gMouse_CallbackArg, 1, i, d_accel[i] - gMouse_Axies[i].CurValue);\r
- }\r
- \r
- gMouse_Axies[i].CurValue = d_accel[i];\r
- gMouse_Axies[i].CursorPos = newCursor;\r
- }\r
-\r
-// Log("Mouse at %ix%i", gMouse_Axies[0].CursorPos, gMouse_Axies[1].CursorPos);\r
- \r
- VFS_MarkAvaliable(&gMouse_DriverStruct.RootNode, 1);\r
-}\r
-\r
-/* Read mouse state (coordinates)\r
- */\r
-Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
-{\r
- if(Offset > sizeof(gMouse_FileData)) return 0;\r
- if(Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData);\r
- if(Offset + Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData) - Offset;\r
-\r
- memcpy(Buffer, &gMouse_FileData[Offset], Length);\r
- \r
- VFS_MarkAvaliable(Node, 0);\r
- return Length;\r
-}\r
-\r
-static const char *csaIOCtls[] = {DRV_IOCTLNAMES, DRV_JOY_IOCTLNAMES, NULL};\r
-/* Handle messages to the device\r
- */\r
-int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
- tJoystick_NumValue *info = Data;\r
-\r
- switch(ID)\r
- {\r
- BASE_IOCTLS(DRV_TYPE_JOYSTICK, "PS2Mouse", 0x100, csaIOCtls);\r
-\r
- case JOY_IOCTL_SETCALLBACK: // TODO: Implement\r
- return -1;\r
- \r
- case JOY_IOCTL_SETCALLBACKARG: // TODO: Implement\r
- return -1;\r
- \r
- case JOY_IOCTL_GETSETAXISLIMIT:\r
- if(!info) return 0;\r
- if(info->Num < 0 || info->Num >= 2) return 0;\r
- if(info->Value != -1)\r
- giMouse_AxisLimits[info->Num] = info->Value;\r
- return giMouse_AxisLimits[info->Num];\r
- \r
- case JOY_IOCTL_GETSETAXISPOSITION:\r
- if(!info) return 0;\r
- if(info->Num < 0 || info->Num >= 2) return 0;\r
- if(info->Value != -1)\r
- gMouse_Axies[info->Num].CursorPos = info->Value;\r
- return gMouse_Axies[info->Num].CursorPos;\r
-\r
- case JOY_IOCTL_GETSETAXISFLAGS:\r
- return -1;\r
- \r
- case JOY_IOCTL_GETSETBUTTONFLAGS:\r
- return -1;\r
-\r
- default:\r
- return 0;\r
- }\r
-}\r
-\r
+++ /dev/null
-#
-# EDI - Extensible Driver Interface
-#
-# Acess Interface
-
-
-OBJ = main.o edi.o
-NAME = EDI
-
--include ../Makefile.tpl
+++ /dev/null
-/*! \file acess-edi.h
- * \brief Acess Specific EDI Objects
- *
- * Contains documentation and information for
- * - Timers
- */
-
-/* Copyright (c) 2006 John Hodge
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#ifndef ACESS_EDI_H
-#define ACESS_EDI_H
-
-#include "edi_objects.h"
-
-/// \brief Name of Acess EDI Time Class
-#define ACESS_TIMER_CLASS "ACESSEDI-TIMER"
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief int32_t ACESSEDI-TIMER.init_timer(uint32_t Delay, void (*Callback)(int), int Arg);
- *
- * Takes a timer pointer and intialises the timer object to fire after \a Delay ms
- * When the timer fires, \a Callback is called with \a Arg passed to it.
- */
-EDI_DEFVAR int32_t (*init_timer)(object_pointer port_object, uint32_t Delay, void (*fcn)(int), int arg);
-
-/*! \brief void ACESSEDI-TIMER.disable_timer();
- *
- * Disables the timer and prevents it from firing
- * After this has been called, the timer can then be initialised again.
- */
-EDI_DEFVAR void (*disable_timer)(object_pointer port_object);
-
-
-#endif // defined(IMPLEMENTING_EDI)
-
-#endif
+++ /dev/null
-#ifndef EDI_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_H
-/*! \file edi.h
- * \brief The unitive EDI header to include others, start EDI, and stop EDI.
- *
- * Data structures and algorithms this header represents:
- * DATA STRUCTURE: CLASS QUOTAS - The runtime and the driver have the right to set a quota on how many objects of a given class
- * owned by that party the other may construct. These quotas are kept internally by the driver or runtime, are optional and are
- * exposed to the other party via the quota() function (for quotas of runtime-owned classes) and the k_quota() function pointer given
- * to the runtime by the driver.
- *
- * ALGORITHMS: INITIALIZATION AND SHUTDOWN - On initialization of the runtime's EDI environment for this driver it calls the
- * driver's driver_init() routine (which must match driver_init_t) to initialize the driver with a list of EDI objects the runtime
- * thinks the driver should run with. The driver then initializes. This can include calling edi_negotiate_resources() to try and
- * obtain more or different objects. Eventually driver_init() returns an edi_initialization_t structure containing its quota
- * function and the list of classes belonging to the driver which the runtime can construct. Either the driver or the runtime can
- * shut down EDI by calling edi_shutdown(), which in turn calls the driver's driver_finish() routine. On shutdown all objects, of
- * classes belonging to both the runtime and driver, are destroyed. */
-
-#include "edi_objects.h"
-#include "edi_dma_streams.h"
-#include "edi_pthreads.h"
-#include "edi_port_io.h"
-#include "edi_memory_mapping.h"
-#include "edi_devices.h"
-#include "edi_interrupts.h"
-
-/*! \brief A pointer to a function the runtime can call if it fails to construct one of the driver's classes to find out what the
- * runtime's quota is for that class.
- *
- * A pointer to a function which takes an edi_string_t as a parameter and returns in int32_t. This function follows the same
- * semantics as the quota() function, returning the number of objects of the given class that can be constructed, -1 for infinity or
- * -2 for an erroneous class name. It is used to tell the runtime the location of such a function in the driver so that the runtime
- * can check quotas on driver-owned classes. */
-typedef int32_t (*k_quota_t)(edi_string_t resource_class);
-/*!\struct edi_initialization_t
- * \brief Structure containing driver classes available to the runtime and the driver's quota function after the driver has initialized.
- *
- * Structure containing driver classes available to runtime, the driver's quota function and the driver's name provided to the runtime
- * after the driver has initialized. driver_bus, vendor_id, and device_id are all optional fields which coders should consider
- * supplementary information. Kernels can require these fields if they so please, but doing so for devices which don't run on a Vendor
- * ID/Product ID supporting bus is rather unwise. */
-typedef struct {
- /*!\brief The number of driver classes in the driver_classes array. */
- int32_t num_driver_classes;
- /*!\brief An array of declarations of driver classes available to the runtime.
- *
- * This array should not necessarily contain the entire list of EDI classes implemented by the driver. Instead, it should
- * contain a list of those classes which the driver has correctly initialized itself to provide instances of with full
- * functionality. */
- edi_class_declaration_t *driver_classes;
- /*!\brief The driver's quota function. */
- k_quota_t k_quota;
- /*!\brief The driver's name. */
- edi_string_t driver_name;
- /*!\brief The bus of the device this driver wants to drive, if applicable.
- *
- * The driver does not have to supply this field, and can also supply "MULTIPLE BUSES" here to indicate that it drives devices
- * on multiple buses. */
- edi_string_t driver_bus;
- /*!\brief The driver's vendor ID, if applicable.
- *
- * The driver does not need to supply this field, and should supply -1 to indicate that it does not wish to. */
- int16_t vendor_id;
- /*!\brief The driver's device ID, if applicable.
- *
- * The driver does not need to supply this field, but can supply it along with vendor_id. If either vendor_id or this field are
- * set to -1 the runtime should consider this field not supplied. */
- int16_t driver_id;
-} edi_initialization_t;
-/*!\brief A pointer to a driver's initialization function.
- *
- * The protocol for the driver's initialization function. The runtime gives the driver a set of EDI objects representing the
- * resources it thinks the driver should run with. This function returns an edi_initialization_t structure containing declarations
- * of the EDI classes the driver can make available to the runtime after initialization. If any member of that structure contains 0
- * or NULL, it is considered invalid and the runtime should destroy the driver without calling its driver_finish() routine. */
-typedef edi_initialization_t (*driver_init_t)(int32_t num_resources,edi_object_metadata_t *resources);
-/*!\brief Requests more resources from the runtime. Can be called during driver initialization.
- *
- * Called to negotiate with the runtime for the right to create further EDI objects/obtain further resources owned by the runtime.
- * When the driver calls this routine, the runtime decides whether to grant more resources. If yes, this call returns true, and the
- * driver can proceed to try and create the objects it desires, in addition to destroying EDI objects it doesn't want. Otherwise,
- * it returns false.
- * The driver must deal with whatever value this routine returns. */
-bool edi_negotiate_resources();
-
-/*! \brief Returns the driver's quota of objects for a given runtime-owned class.
- *
- * This function takes an edi_string_t with the name of a runtime-owned class in it and returns the number of objects of that class
- * which drivers can construct, -1 for infinity, or -2 for an erroneous class name. */
-int32_t quota(edi_string_t resource_class);
-/*! \brief Sends a string to the operating systems debug output or logging facilities. */
-void edi_debug_write(uint32_t debug_string_length, char *debug_string);
-/*! \brief This call destroys all objects and shuts down the entire EDI environment of the driver.
- *
- * This function shuts down EDI as described in INITIALIZATION AND SHUTDOWN above. All objects are destroyed, EDI functions can no
- * longer be successfully called, etc. This function only succeeds when EDI has already been initialized, so it returns -1 when EDI
- * hasn't been, 1 on success, or 0 for all other errors. */
-int32_t shutdown_edi(void);
-
-/*!\brief A pointer to the driver's finishing/shutdown function.
- *
- * The protocol for the driver's shutting down. This function should do anything the driver wants done before it dies. */
-typedef void (*driver_finish_t)();
-
-#endif
+++ /dev/null
-#ifndef EDI_DEVICES_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-/* Edited by thePowersGang (John Hodge) June 2009
- * - Add #ifdef EDI_MAIN_FILE
- */
-
-#define EDI_DEVICES_H
-
-/*! \file edi_devices.h
- * \brief Declaration and description of simple classes for implementation by EDI drivers to represent hardware devices.
- *
- * Data structures and algorithms this header represents:
- *
- * DATA STRUCTURE AND ALGORITHM: BASIC DEVICES - There are two functions, select() for waiting on devices and ioctl() for
- * controlling them, common to many POSIX devices. Implementations of EDI-CHARACTER-DEVICE or EDI-BLOCK-DEVICE may implement either of
- * these or both, and users of such objects much query for the methods to see if they're supported. Obviously, runtime or driver
- * developers don't *need* to support these.
- *
- * DATA STRUCTURE AND ALGORITHM: CHARACTER DEVICES - The class EDI-CHARACTER-DEVICE provides a very basic interface to character
- * devices, which read and write streams of characters. As such, this class only provides read() and write(). The calls attempt a
- * likeness to POSIX.
- *
- * DATA STRUCTURE AND ALGORITHM: BLOCK DEVICES - The class EDI-BLOCK-DEVICE provides a very basic interface to block devices, which
- * can read(), write() and seek() to blocks of a specific size in an array of blocks with a specific size. Its declarations and
- * semantics should behave like those of most POSIX operating systems.
- *
- * Note that EDI runtimes should not implement these classes. Their declarations are provided for drivers to implement. */
-
-#include "edi_objects.h"
-
-/* Methods common to all EDI device classes specified in this header. */
-
-/*!\brief EAGAIN returned by functions for block and character devices.
- *
- * Means that the amount of data the device has ready is less than count. */
-#define EAGAIN -1
-/*!\brief EBADOBJ returned by functions for block and character devices.
- *
- * Means that the object passed as the method's this point was not a valid object of the needed class. */
-#define EBADOBJ -2
-/*!\brief EINVAL returned by functions for block and character devices.
- *
- * Means that the method got passed invalid parameters. */
-#ifdef EINVAL
-# undef EINVAL
-#endif
-#define EINVAL -3
-
-/*!\brief select() type to wait until device is writable. */
-#define EDI_SELECT_WRITABLE 0
-/*!\brief select() type to wait until device is readable. */
-#define EDI_SELECT_READABLE 1
-
-/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to the given whence value. */
-#define EDI_SEEK_SET 0
-/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to its current value + whence. */
-#define EDI_SEEK_CURRENT 1
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Arguments to EDI's basic select() function. */
-edi_variable_declaration_t select_arguments[2] = {{"pointer void","device",1},
- {"unsigned int32_t","select_type",1}};
-/*!\brief Declaration of EDI's basic select() function.
- *
- * Contrary to the POSIX version, this select() puts its error codes in its return value. */
-edi_function_declaration_t select_declaration = {"int32_t","edi_device_select",0,2,select_arguments,NULL};
-#else
-extern edi_function_declaration_t select_declaration; // Declare for non main files
-#endif
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Arguments to EDI's basic ioctl() function. */
-edi_variable_declaration_t ioctl_arguments[3] = {{"pointer void","device",1},{"int32_t","request",1},{"pointer void","argp",1}};
-/*!\brief Declaration of EDI's basic ioctl() function.
- *
- * Contrary to the POSIX version, this ioctl() puts its error codes in its return value. */
-edi_function_declaration_t ioctl_declaration = {"int32_t","edi_device_ioctl",0,3,ioctl_arguments,NULL};
-#else
-extern edi_class_declaration_t ioctl_declaration; // Declare for non main files
-#endif
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Declaration of the arguments EDI-CHARACTER-DEVICE's read() and write() methods. */
-edi_variable_declaration_t chardev_read_write_arguments[3] = {{"pointer void","chardev",1},
- {"pointer void","buffer",1},
- {"unsigned int32_t","char_count",1}};
-/*!\brief Declarations of the methods of EDI-CHARACTER-DEVICE, read() and write().
- *
- * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-CHARACTER-DEVICE should
- * fill in these entries with pointers to their own functions. */
-EDI_DEFVAR edi_function_declaration_t chardev_methods[2]= {{"int32_t","edi_chardev_read",0,3,chardev_read_write_arguments,NULL},
- {"int32_t","edi_chardev_write",0,3,chardev_read_write_arguments,NULL}};
-/*!\brief Declaration of the EDI-CHARACTER-DEVICE class.
- *
- * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent
- * before passing the filled-in structure to the EDI runtime. */
-EDI_DEFVAR edi_class_declaration_t chardev_class = {"EDI-CHARACTER-DEVICE",0,2,chardev_methods,NULL,NULL,NULL};
-#else
-extern edi_class_declaration_t chardev_class; // Declare for non main files
-#endif
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Arguments to EDI-BLOCK-DEVICE's read() and write() methods. */
-edi_variable_declaration_t blockdev_read_write_arguments[3] = {{"pointer void","blockdev",1},
- {"pointer void","buffer",1},
- {"unsigned int32_t","blocks",1}};
-/*!\brief Arguments to EDI-BLOCK-DEVICE's seek() method. */
-edi_variable_declaration_t blockdev_seek_arguments[3] = {{"pointer void","blockdev",1},
- {"int32_t","offset",1},
- {"int32_t","whence",1}};
-/*!\brief Declaration of the methods of EDI-BLOCK-DEVICE, read(), write(), seek(), and get_block_size().
- *
- * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-BLOCK-DEVICE should fill
- * these entries in with pointers to their own functions. */
-edi_function_declaration_t blockdev_methods[4] = {{"int32_t","edi_blockdev_read",0,3,blockdev_read_write_arguments,NULL},
- {"int32_t","edi_blockdev_write",0,3,blockdev_read_write_arguments,NULL},
- {"int32_t","edi_blockdev_seek",0,3,blockdev_seek_arguments,NULL},
- {"unsigned int32_t","edi_blockdev_get_block_size",0,0,NULL,NULL}};
-/*!\brief Declaration of the EDI-BLOCK-DEVICE class.
- *
- * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent
- * before passing the filled-in structure to the EDI runtime. */
-edi_class_declaration_t blockdev_class = {"EDI-BLOCK-DEVICE",0,4,blockdev_methods,NULL,NULL,NULL};
-#else
-extern edi_class_declaration_t blockdev_class; // Declare for non main files
-#endif
-
-#endif
+++ /dev/null
-#ifndef EDI_DMA_STREAMS_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_DMA_STREAMS_H
-
-/*! \file edi_dma_streams.h
- * \brief EDI's stream subclass for handling Direct Memory Access hardware.
- *
- * Data structures and algorithms this header represents:
- *
- * DATA STRUCTURE: DMA STREAMS - DMA streams are objects of the class EDI-STREAM-DMA used to pass data between a buffer of
- * memory and the computer's DMA hardware. It is the responsibility of the object to allocate memory for its stream memory buffer
- * which can be used with DMA hardware and to program the DMA hardware for transmissions. DMA streams can be bidirectional if the
- * correct DMA mode is used. */
-
-#include "edi_objects.h"
-
-#define DMA_STREAM_CLASS "EDI-STREAM-DMA"
-
-/*! \brief The name of the EDI DMA stream class.
- *
- * An edi_string_t with the class name "EDI-STREAM-DMA" in it. */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t dma_stream_class = DMA_STREAM_CLASS;
-#else
-extern const edi_string_t dma_stream_class;
-#endif
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief int32_t EDI-STREAM-DMA.init_dma_stream(unsigned int32_t channel,unsigned int32_t mode,unsigned int32_t buffer_pages);
- *
- * Pointer to the init_dma_stream() method of class EDI-STREAM-DMA, which initializes a DMA stream with a DMA channel, DMA mode, and
- * the number of DMA-accessible memory pages to keep as a buffer. It will only work once per stream object. It's possible return
- * values are 1 for sucess, -1 for invalid DMA channel, -2 for invalid DMA mode, -3 for inability to allocate enough buffer pages and
- * 0 for all other errors. */
-EDI_DEFVAR int32_t (*init_dma_stream)(object_pointer stream, uint32_t channel, uint32_t mode, uint32_t buffer_pages);
-/*! \brief int32_t EDI-STREAM-DMA.transmit(data_pointer *anchor,unsigned int32 num_bytes,bool sending);
- *
- * Pointer to the dma_stream_transmit() method of class EDI-STREAM-DMA, which transmits the given number of bytes of data through
- * the DMA stream to/from the given anchor (either source or destination), in the given direction. It returns 1 on success, -1 on
- * an uninitialized or invalid DMA stream object, -2 when the anchor was NULL or otherwise invalid, -3 if the DMA stream can't
- * transmit in the given direction, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*dma_stream_transmit)(object_pointer stream, data_pointer anchor, uint32_t num_bytes, bool sending);
-#endif
-
-#endif
+++ /dev/null
-#ifndef EDI_INTERRUPTS_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_INTERRUPTS_H
-
-/*! \file edi_interrupts.h
- * \brief Declaration and description of EDI's interrupt handling class.
- *
- * Data structures and algorithms this header represents:
- * DATA STRUCTURE AND ALGORITHM: INTERRUPT OBJECTS - The class EDI-INTERRUPT encapsulates the handling of machine interrupts.
- * It is initialized with an interrupt number to handle and a handler routine to call when that interrupt occurs. Only a couple of
- * guarantees are made to the driver regarding the runtime's implementation of interrupt handling: 1) That the driver's handler is
- * called for every time the interrupt associated with a valid and initialized interrupt object occurs, in the order of the
- * occurences, 2) That the runtime handle the architecture-specific (general to the entire machine, not just this device)
- * end-of-interrupt code when the driver is called without first returning from the machine interrupt. Note that the runtime hands
- * out interrupt numbers at its own discretion and policy. */
-
-#include "edi_objects.h"
-
-/*! \brief Macro constant containing the name of the interrupt class
- */
-#define INTERRUPTS_CLASS "EDI-INTERRUPT"
-/*! \brief The name of EDI's interrupt-handling class.
- *
- * An edi_string_t holding the name of the runtime-implemented interrupt object class. It's value is "EDI-INTERRUPT". */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t interrupts_class = INTERRUPTS_CLASS;
-#else
-extern const edi_string_t interrupts_class;
-#endif
-
-/*! \brief A pointer to an interrupt handling function.
- *
- * A pointer to a function called to handle interrupts. Its unsigned int32_t parameter is the interrupt number that is being
- * handled. */
-typedef void (*interrupt_handler_t)(uint32_t interrupt_number);
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief Initializes an interrupt object with an interrupt number and a pointer to a handler function.
- *
- * A pointer to the init_interrupt() method of class EDI-INTERRUPT. This method initializes a newly-created interrupt object with an
- * interrupt number and a pointer to the driver's handler of type interrupt_handler_t. It can only be called once per object, and
- * returns 1 on success, fails with -1 when the interrupt number is invalid or unacceptable to the runtime, fails with -2 when the
- * pointer to the driver's interrupt handler is invalid, and fails with -3 for all other errors. */
-EDI_DEFVAR int32_t (*init_interrupt)(object_pointer interrupt, uint32_t interrupt_number, interrupt_handler_t handler);
-/*! \brief Get this interrupt object's interrupt number. */
-EDI_DEFVAR uint32_t (*interrupt_get_irq)(object_pointer interrupt);
-/*! \brief Set a new handler for this interrupt object. */
-EDI_DEFVAR void (*interrupt_set_handler)(object_pointer interrupt, interrupt_handler_t handler);
-/*! \brief Return from this interrupt, letting the runtime run any necessary End-Of-Interrupt code.
- *
- * A pointer to the interrupt_return() method of class EDI-INTERRUPT. This method returns from the interrupt designated by the
- * calling interrupt object. If there is a machine-wide end-of-interrupt procedure and the driver was called during the handling of
- * the machine interrupt (as opposed to delaying the handling and letting the runtime EOI), the runtime runs it during this method.
- * This method has no return value, since once it's called control leaves the calling thread. */
-EDI_DEFVAR void (*interrupt_return)(object_pointer interrupt);
-#endif
-
-#endif
+++ /dev/null
-#ifndef EDI_MEMORY_MAPPING_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_MEMORY_MAPPING_H
-
-/*! \file edi_memory_mapping.h
- * \brief Declaration and description of EDI's class for mapping physical pages into the driver's address space.
- *
- * Data structures and algorithms this header represents:
- * ALGORITHM: MEMORY MAPPINGS - Memory mapping objects of the class EDI-MEMORY-MAPPING are used to give virtual (driver-visible)
- * addresses to sections of physical memory. These can either be memory mappings belonging to hardware devices or plain RAM which
- * the driver wants page-aligned. A memory mapping object is initialized with the physical address for the memory mapping and the
- * number of pages the mapping takes up, or simply the desired length of the a physically contiguous buffer in pages. The class's
- * two methods map the section of memory into and out of the driver's virtual address space. */
-
-#include "edi_objects.h"
-
-/*! \brief The name of EDI's memory mapping class.
- *
- * An edi_string_t with the name of the memory mapping class, "EDI-MEMORY-MAPPING". */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t memory_mapping_class = "EDI-MEMORY-MAPPING";
-#else
-extern const edi_string_t memory_mapping_class;
-#endif
-
-/*! \brief Flag representing Strong Uncacheable caching method. */
-#define CACHING_STRONG_UNCACHEABLE 0
-/*! \brief Flag representing Uncacheable caching method. */
-#define CACHING_UNCACHEABLE 1
-/*! \brief Flag representing Write combining caching method. */
-#define CACHING_WRITE_COMBINING 2
-/*! \brief Flag representing Write Through caching method. */
-#define CACHING_WRITE_THROUGH 3
-/*! \brief Flag representing Write Back caching method. */
-#define CACHING_WRITE_BACK 3
-/*! \brief Flag representing Write Protected caching method. */
-#define CACHING_WRITE_PROTECTED 3
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief Initialize an EDI-MEMORY-MAPPING object with a physical address range.
- *
- * This method takes the start_physical_address of a memory mapping and the number of pages in that mapping and uses these arguments
- * to initialize an EDI-MEMORY-MAPPING object. It can only be called once per object. It returns 1 when successful, -1 when an
- * invalid physical address is given (one that the runtime knows is neither a physical memory mapping belonging to a device nor
- * normal RAM), -2 when the number of pages requested is bad (for the same reasons as the starting address can be bad), and 0 for
- * all other errors.
- *
- * Note that this method can't be invoked on an object which has already initialized via init_memory_mapping_with_pages(). */
-EDI_DEFVAR int32_t (*init_memory_mapping_with_address)(object_pointer mapping, data_pointer start_physical_address, uint32_t pages);
-/*! \brief Initialize an EDI-MEMORY-MAPPING object by requesting a number of new physical pages.
- *
- * This method takes a desired number of physical pages for a memory mapping, and uses that number to initialize an
- * EDI-MEMORY-MAPPING object by creating a buffer of contiguous physical pages. It can only be called once per object. It returns
- * 1 when successful, -1 when the request for pages cannot be fulfilled, and 0 for all other errors.
- *
- * Note that this method cannot be called if init_memory_mapping_with_address() has already been used on the given object. */
-EDI_DEFVAR int32_t (*init_memory_mapping_with_pages)(object_pointer mapping, uint32_t pages);
-/*! \brief Map the memory-mapping into this driver's visible address space.
- *
- * This asks the runtime to map a given memory mapping into the driver's virtual address space. Its parameter is the address of a
- * data_pointer to place the virtual address of the mapping into. This method returns 1 on success, -1 on an invalid argument, -2
- * for an uninitialized object, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*map_in_mapping)(object_pointer mapping, data_pointer *address_mapped_to);
-/*! \brief Unmap the memory mapping from this driver's visible address space.
- *
- * This method tries to map the given memory mapping out of the driver's virtual address space. It returns 1 for success, -1
- * for an uninitialized memory mapping object, -2 if the mapping isn't mapped into the driver's address space already, and 0
- * for all other errors. */
-EDI_DEFVAR int32_t (*map_out_mapping)(object_pointer mapping);
-
-/*! \brief Set the caching flags for a memory mapping. */
-EDI_DEFVAR void (*mapping_set_caching_method)(object_pointer mapping, uint32_t caching_method);
-/*! \brief Get the current caching method for a memory mapping. */
-EDI_DEFVAR uint32_t (*mapping_get_caching_method)(object_pointer mapping);
-/*! \brief Flush write-combining buffers on CPU to make sure changes to memory mapping actually get written. Only applies to a Write Combining caching method (I think.).*/
-EDI_DEFVAR void (*flush_write_combining_mapping)(object_pointer mapping);
-#endif
-
-#endif
+++ /dev/null
-#ifndef EDI_OBJECTS_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_OBJECTS_H
-
-/*! \file edi_objects.h
- * \brief The header file for basic EDI types and the object system.
- *
- * This file contains declarations of EDI's primitive data types, as well as structures and functions for with the object system.
- * It represents these data structures and algorithms:
- *
- * DATA STRUCTURE: THE CLASS LIST - EDI implementing runtime's must keep an internal list of classes implemented by the runtime
- * and separate lists of classes implemented by each driver. Whoever implements a class is said to "own" that class. The
- * internal format of this list is up to the runtime coders, but it must be possible to recreate the original list of
- * edi_class_declaration structures the driver declared to the runtime from it. This list is declared to the runtime in an
- * initialization function in the header edi.h. The object_class member of an edi_object_metadata structure must point to that
- * object's class's entry in this list.
- *
- * ALGORITHM AND DATA STRUCTURE: CLASSES AND INHERITANCE - Classes are described using edi_class_declaration_t structures and
- * follow very simple rules. All data is private and EDI provides no way to access instance data, so there are no member
- * variable declarations. However, if the data isn't memory-protected (for example, driver data on the driver heap) EDI allows
- * the possibility of pointer access to data, since runtime and driver coders could make use of that behavior. Classes may have
- * one ancestor by declaring so in their class declaration structure, and if child methods are different then parent methods
- * the children always override their parents. An EDI runtime must also be able to check the existence and ownership of a given
- * class given its name in an edi_string_t.
- *
- * ALGORITHM: OBJECT CREATION AND DESTRUCTION - An EDI runtime should be able to call the constructor of a named class, put the
- * resulting object_pointer into an edi_object_metadata_t and return that structure. The runtime should also be able to call an
- * object's class's destructor when given a pointer to a valid edi_metadata_t for an already-existing object. Data equivalent
- * to an edi_object_metadata_t should also be tracked by the runtime for every object in existence in case of sudden EDI shutdown
- * (see edi.h).
- *
- * ALGORITHM: RUNTIME TYPE INFORMATION - When passed the data_pointer member of an edi_object_metadata_t to a valid object, an
- * EDI runtime must be able to return an edi_string_t containing the name of that object's class and to return function_pointers
- * to methods when the required information to find the correct method is given by calling a class's method getting function.*/
-
-/* If the EDI headers are linked with the standard C library, they use its type definitions. Otherwise, equivalent definitions are
- * made.*/
-#if __STDC_VERSION__ == 199901L
-# include <stdbool.h>
-# include <stdint.h>
-#else
-# ifndef NULL
-# define NULL ((void*)0)
-# endif
-typedef unsigned char bool;
-# define true 1
-# define false 0
-typedef char int8_t;
-typedef short int16_t;
-typedef long int32_t;
-typedef long long int64_t;
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned long uint32_t;
-typedef unsigned long long uint64_t;
-#endif
-
-/*! \brief Define a variable in the header
- */
-#ifdef EDI_MAIN_FILE
-# define EDI_DEFVAR
-#else
-# define EDI_DEFVAR extern
-#endif
-
-/*! \brief A pointer to the in-memory instance of an object.
- *
- * This type is sized just like a general C pointer type (whatever*) for the target architecture. It's passed as a first parameter
- * to all methods, thus allowing EDI classes to be implemented as C++ classes and providing some protection from confusing objects
- * with normal pointers. Equivalent to a C++ this pointer or an Object Pascal Self argument. */
-typedef void *object_pointer;
-/*! \brief A basic pointer type pointing to arbitrary data in an arbitrary location. */
-typedef void *data_pointer;
-/*! \brief A basic function pointer type.
- *
- * A pointer to a piece of code which can be called and return to its caller, used to distinguish between pointers to code and
- * pointers to data. Its size is hardware-dependent. */
-typedef void (*function_pointer)(void);
-/*! \brief The length of an EDI string without its null character. */
-#define EDI_STRING_LENGTH 31
-/*! \brief A type representing a 31-character long string with a terminating NULL character at the end. All of EDI uses this type
- * for strings.
- *
- * A null-terminated string type which stores characters in int8s. It allows for 31 characters in each string, with the final
- * character being the NULL terminator. Functions which use this type must check that its final character is NULL, a string which
- * doesn't not have this property is invalid and insecure. I (the author of EDI) know and understand that this form of a string
- * suffers from C programmer's disease, but I can't use anything else without either making string use far buggier or dragging
- * everyone onto a better language than C. */
-typedef int8_t edi_string_t[0x20];
-/*! \brief A type representing a pointer form of #edi_string_t suitable for function returns
- */
-typedef int8_t *edi_string_ptr_t;
-
-/*! \var EDI_BASE_TYPES
- * \brief A constant array of edi_string_t's holding every available EDI primitive type. */
-/*! \var EDI_TYPE_MODIFIERS
- * \brief A constant array of edi_string_t's holding available modifiers for EDI primitive types. */
-#ifdef IMPLEMENTING_EDI
- const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg","edi_string_t"};
- const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"};
-#else
- //extern const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg", "edi_string_t"};
- //extern const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"};
- extern const edi_string_t EDI_BASE_TYPES[9];
- extern const edi_string_t EDI_TYPE_MODIFIERS[2];
-#endif
-
-/*! \struct edi_object_metadata_t
- * \brief A packed structure holding all data to identify an object to the EDI object system. */
-typedef struct {
- /*! \brief Points to the instance data of the object represented by this structure.
- *
- * An object_pointer to the object this structure refers to. The this pointer, so to speak. */
- object_pointer object;
- /*! \brief Points the internal record kept by the runtime describing the object's class.
- *
- * Points to wherever the runtime has stored the class data this object was built from. The class data doesn't need to be
- * readable to the driver, and so this pointer can point to an arbitrary runtime-reachable location. */
- data_pointer object_class;
-} edi_object_metadata_t;
-
-/*! \struct edi_variable_declaration_t
- * \brief The data structure used to describe a variable declaration to the EDI object system.
- *
- * The data structure used to describe a variable declaration to the EDI object system. The context of the declaration depends on
- * where the data structure appears, ie: alone, in a class declaration, in a parameter list, etc. */
-typedef struct {
- /*! \brief The type of the declared variable.
- *
- * The type of the variable, which must be a valid EDI primitive type as specified in the constant EDI_BASE_TYPES and
- * possibly modified by a modifier specified in the constant EDI_TYPE_MODIFIERS. */
- edi_string_t type;
- /*! \brief The name of the declared variable. */
- edi_string_t name;
- /*! \brief Number of array entries if this variable is an array declaration.
- *
- * An int32_t specifying the number of variables of 'type' in the array 'name'. For a single variable this value should
- * simply be set to 1, for values greater than 1 a packed array of contiguous variables is being declared, and a value of 0
- * is invalid. */
- int32_t array_length;
-} edi_variable_declaration_t;
-
-/*! \struct edi_function_declaration_t
- * \brief The data structure used to declare a function to the EDI object system. */
-typedef struct {
- /*! \brief The return type of the function. The same type rules which govern variable definitions apply here. */
- edi_string_t return_type;
- /*! \brief The name of the declared function. */
- edi_string_t name;
- /*! \brief The version number of the function, used to tell different implementations of the same function apart. */
- uint32_t version;
- /*! \brief The number of arguments passed to the function.
- *
- * The number of entries in the member arguments that the object system should care about. Caring about less misses
- * parameters to functions, caring about more results in buffer overflows. */
- uint32_t num_arguments;
- /*! \brief An array of the declared function's arguments.
- *
- * A pointer to an array num_arguments long containing edi_variable_declaration_t's for each argument to the declared
- * function.*/
- edi_variable_declaration_t *arguments;
- /*!\brief A pointer to the declared function's code in memory. */
- function_pointer code;
-} edi_function_declaration_t;
-
-/*! \brief A pointer to a function for constructing instances of a class.
- *
- * A pointer to a function which takes no parameters and returns an object_pointer pointing to the newly made instance of a class.
- * It is the constructor's responsibility to allocate memory for the new object. Each EDI class needs one of these. */
-typedef object_pointer (*edi_constructor_t)(void);
-/*! \brief A pointer to a function for destroying instances of a class.
- *
- * A pointer to a function which takes an object_pointer as a parameter and returns void. This is the destructor counterpart to a
- * class's edi_constructor_t, it destroys the object pointed to by its parameter and frees the object's memory. Every class must
- * have one */
-typedef void (*edi_destructor_t)(object_pointer);
-
-/*! \brief Information the driver must give the runtime about its classes so EDI can construct them and call their methods.
- *
- * A structure used to declare a class to an EDI runtime so instances of it can be constructed by the EDI object system. */
-typedef struct {
- /*! \brief The name of the class declared by the structure. */
- edi_string_t name;
- /*! \brief The version of the class. This number is used to tell identically named but differently
- * implemented classes apart.*/
- uint32_t version;
- /*! \brief The number of methods in the 'methods' function declaration array. */
- uint32_t num_methods;
- /*! \brief An array of edi_function_declaration_t declaring the methods of this class. */
- edi_function_declaration_t *methods;
- /*! \brief Allocates the memory for a new object of the declared class and constructs the object. Absolutely required.*/
- edi_constructor_t constructor;
- /*! \brief Destroys the given object of the declared class and frees its memory. Absolutely required. */
- edi_destructor_t destructor;
- /*! \brief A pointer to another EDI class declaration structure specifying the declared class's parent class.
- *
- * Points to a parent class declared in another class declaration. It can be NULL to mean this class has no parent. */
- struct edi_class_declaration_t *parent;
-} edi_class_declaration_t;
-
-/*! \brief Checks the existence of the named class.
- *
- * This checks for the existence on THE CLASS LIST of the class named by its edi_string_t parameter and returns a signed int32_t. If
- * the class isn't found (ie: it doesn't exist as far as EDI is concerned) -1 is returned, if the class is owned by the driver
- * (implemented by the driver and declared to the runtime by the driver) 0, and if the class is owned by the runtime (implemented by
- * the runtime) 1. */
-int32_t check_class_existence(edi_string_t class_name);
-/*! \brief Constructs an object of the named class and returns its object_pointer and a data_pointer to its class data.
- *
- * Given a valid class name in an edi_string_t this function constructs the specified class and returns an edi_metadata_t describing
- * the new object as detailed in OBJECT CREATION AND DESTRUCTION. If the construction fails it returns a structure full of NULL
- * pointers. */
-edi_object_metadata_t construct_object(edi_string_t class_name);
-/*! \brief Destroys the given object using its class data.
- *
- * As specified in OBJECT CREATION AND DESTRUCTION this function should destroy an object when given its valid edi_metadata_t. The
- * destruction is accomplished by calling the class's destructor. */
-void destroy_object(edi_object_metadata_t object);
-/*! \brief Obtains a function pointer to a named method of a given class.
- *
- * When given a valid data_pointer object_class from an edi_object_metadata_t and an edi_string_t representing the name of the
- * desired method retrieves a function_pointer to the method's machine code in memory. If the desired method isn't found, NULL is
- * returned. */
-function_pointer get_method_by_name(data_pointer object_class,edi_string_t method_name);
-/*! \brief Obtains a function pointer to a method given by a declaration of the given class if the class's method matches the
- * declaration.
- *
- * Works just like get_method_by_name(), but by giving an edi_function_declaration_t for the desired method instead of just its name.
- * Performs detailed checking against THE CLASS LIST to make sure that the method returned exactly matches the declaration passed
- * in. */
-function_pointer get_method_by_declaration(data_pointer object_class,edi_function_declaration_t declaration);
-
-/* Runtime typing information. */
-/*! \brief Returns the name of the class specified by a pointer to class data.
- *
- * Given the data_pointer to an object's class data as stored in an edi_object_metadata_t retrieves the name of the object's class
- * and returns it in an edi_string_t. */
-edi_string_ptr_t get_object_class(data_pointer object_class);
-/*! \brief Returns the name of a class's parent class.
- *
- * When given an edi_string_t with a class name in it, returns another edi_string_t containing the name of the class's parent, or an
- * empty string. */
-edi_string_ptr_t get_class_parent(edi_string_t some_class);
-/*! \brief Returns the internal class data of a named class (if it exists) or NULL.
- *
- * When given an edi_string_t with a valid class name in it, returns a pointer to the runtime's internal class data for that class.
- * Otherwise, it returns NULL. */
-data_pointer get_internal_class(edi_string_t some_class);
-
-#endif
+++ /dev/null
-#ifndef EDI_PORT_IO_H
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-/* Modified by thePowersGang (John Hodge)
- * - Surround variable definitions with an #ifdef IMPLEMENTING_EDI
- */
-
-#define EDI_PORT_IO_H
-
-/*! \file edi_port_io.h
- * \brief Declaration and description of EDI's port I/O class.
- *
- * Data structures and algorithms this header represents:
- *
- * DATA STRUCTURE AND ALGORITHM: PORT I/O OBJECTS - A class named "EDI-IO-PORT" is defined as an encapsulation of the port I/O
- * used on some machine architectures. Each object of this class represents a single I/O port which can be read from and written to
- * in various sizes. Each port can be held by one object only at a time. */
-
-#include "edi_objects.h"
-
-/*! \brief Macro to create methods for reading from ports.
- *
- * This macro creates four similar methods, differing in the size of the type they read from the I/O port held by the object. Their
- * parameter is a pointer to the output type, which is filled with the value read from the I/O port. They return 1 for success, -1
- * for an uninitialized I/O port object, and 0 for other errors. */
-#define port_read_method(type,name) int32_t (*name)(object_pointer port_object, type *out)
-/*! \brief Macro to create methods for writing to ports.
- *
- * This macro creates four more similar methods, differing in the size of the type they write to the I/O port held by the object.
- * Their parameter is the value to write to the port. They return 1 for success, -1 for an uninitialized I/O port object and 0 for
- * other errors. */
-#define port_write_method(type,name) int32_t (*name)(object_pointer port_object, type in)
-
-/*! \brief Name of EDI I/O port class. (Constant)
- *
- * A CPP constant with the value of #io_port_class */
-#define IO_PORT_CLASS "EDI-IO-PORT"
-/*! \brief Name of EDI I/O port class.
- *
- * An edi_string_t containing the class name "EDI-IO-PORT". */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t io_port_class = IO_PORT_CLASS;
-#else
-extern const edi_string_t io_port_class;
-#endif
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief int32_t EDI-IO-PORT.init_io_port(unsigned int16_t port);
- *
- * This method takes an unsigned int16_t representing a particular I/O port and initializes the invoked EDI-IO-PORT object with it.
- * The method returns 1 if successful, -1 if the I/O port could not be obtained for the object, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*init_io_port)(object_pointer port_object, uint16_t port);
-/*! \brief Get the port number from a port object. */
-EDI_DEFVAR uint16_t (*get_port_number)(object_pointer port);
-/*! \brief Method created by port_read_method() in order to read bytes (int8s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_byte_io_port)(object_pointer port_object, int8_t *out);
-/*! \brief Method created by port_read_method() in order to read words (int16s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_word_io_port)(object_pointer port_object, int16_t *out);
-/*! \brief Method created by port_read_method() in order to read longwords (int32s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_long_io_port)(object_pointer port_object, int32_t *out);
-/*! \brief Method created by port_read_method() in order to read long longwords (int64s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_longlong_io_port)(object_pointer port_object,int64_t *out);
-/*! \brief Method of EDI-IO-PORT to read long strings of data from I/O ports.
- *
- * Reads arbitrarily long strings of data from the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2
- * for a bad pointer to the destination buffer, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*read_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *out);
-/*! \brief Method created by port_write_method() in order to write bytes (int8s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_byte_io_port)(object_pointer port_object, int8_t in);
-/*! \brief Method created by port_write_method() in order to write words (int16s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_word_io_port)(object_pointer port_object, int16_t in);
-/*! \brief Method created by port_write_method() in order to write longwords (int32s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_long_io_port)(object_pointer port_object, int32_t in);
-/*! \brief Method created by port_write_method() in order to write long longwords (int64s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_longlong_io_port)(object_pointer port_object, int64_t in);
-/*! \brief Method of EDI-IO-PORT to write long strings of data to I/O ports.
- *
- * Writes arbitrarily long strings of data to the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2
- * for a bad pointer to the source buffer, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*write_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *in);
-
-#endif // defined(IMPLEMENTING_EDI)
-
-#endif
+++ /dev/null
-#ifndef EDI_PTHREADS
-
-/* Copyright (c) 2006 Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts. A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_PTHREADS
-/*!\file edi_pthreads.h
- * \brief A basic subset of POSIX Threads functionality, providing threading and thread synchronization.
- *
- * A very basic POSIX Threads interface. Note that pthreads are not a class, because none of these calls really gels with
- * object-oriented programming. Also, if drivers aren't processes or threads under the implementing operating system a small
- * threading system must be implemented in-runtime just to multiplex the pthreads of EDI drivers. Sorry about that.
- *
- * Data structures and algorithms this header represents:
- *
- * ALGORITHM AND DATA STRUCTURE: POSIX Threading - The runtime must provide enough of a POSIX threading interface to implement
- * the calls described here. The actual multithreading must be performed by the runtime, and the runtime can implement that
- * multithreading however it likes as long as the given POSIX Threads subset works. There is, however, a caveat: since the runtime
- * calls the driver like it would a library, the driver must perceive all calls made to it by the runtime as running under one thread.
- * From this thread the driver can create others. Such behavior is a quirk of EDI, and does not come from the POSIX standard.
- * However, it is necessary to provide the driver with a thread for its own main codepaths. For further details on a given POSIX
- * Threading routine, consult its Unix manual page. */
-
-#include "edi_objects.h"
-
-/* Placeholder type definitions. Users of the PThreads interface only ever need to define pointers to these types. */
-/*!\brief Opaque POSIX Threading thread attribute type. */
-typedef void pthread_attr_t;
-/*!\brief Opaque POSIX Threading mutex (mutual exclusion semaphore) type. */
-typedef void pthread_mutex_t;
-/*!\brief Opaque POSIX Threading mutex attribute type. */
-typedef void pthread_mutex_attr_t;
-
-/*!\struct sched_param
- * \brief POSIX Threading scheduler parameters for a thread. */
-typedef struct {
- /*!\brief The priority of the thread. */
- int32_t sched_priority;
-} sched_param;
-
-/*!\brief POSIX Threading thread identifier. */
-typedef uint32_t pthread_t;
-/*!\brief POSIX Threading thread function type.
- *
- * A function pointer to a thread function, with the required signature of a thread function. A thread function takes one untyped
- * pointer as an argument and returns an untyped pointer. Such a function is a thread's main routine: it's started with the thread,
- * and the thread exits if it returns. */
-typedef void *(*pthread_function_t)(void*);
-
-/*!\brief Insufficient resources. */
-#define EAGAIN -1
-/*!\brief Invalid parameter. */
-#define EINVAL -2
-/*!\brief Permission denied. */
-#define EPERM -3
-/*!\brief Operation not supported. */
-#define ENOTSUP -4
-/*!\brief Priority scheduling for POSIX/multiple schedulers is not implemented. */
-#define ENOSYS -5
-/*!\brief Out of memory. */
-#define ENOMEM -6
-/*!\brief Deadlock. Crap. */
-#define EDEADLK -7
-/*!\brief Busy. Mutex already locked. */
-#define EBUSY -8
-
-/*!\brief Scheduling policy for regular, non-realtime scheduling. The default. */
-#define SCHED_OTHER 0
-/*!\brief Real-time, first-in first-out scheduling policy. Requires special (superuser, where such a thing exists) permissions. */
-#define SCHED_FIFO 1
-/*!\brief Real-time, round-robin scheduling policy. Requires special (superuser, where such a thing exists) permissions. */
-#define SCHED_RR 0
-
-/*!\brief Creates a new thread with the given attributes, thread function and arguments, giving back the thread ID of the new
- * thread.
- *
- * pthread_create() creates a new thread of control that executes concurrently with the calling thread. The new thread applies the
- * function start_routine, passing it arg as its argument. The attr argument specifies thread attributes to apply to the new thread;
- * it can also be NULL for the default thread attributes (joinable with default scheduling policy). On success this function returns
- * 0 and places the identifier of the new thread into thread_id. On an error, pthread_create() can return EAGAIN if insufficient
- * runtime resources are available to create the requested thread, EINVAL a value specified by attributes is invalid, or EPERM if the
- * caller doesn't have permissions to set the given attributes.
- *
- * For further information: man 3 pthread_create */
-int32_t pthread_create(pthread_t *thread_id, const pthread_attr_t *attributes, pthread_function_t thread_function, void *arguments);
-/*!\brief Terminates the execution of the calling thread. The thread's exit code with by status, and this routine never returns. */
-void pthread_exit(void *status);
-/*!\brief Returns the thread identifier of the calling thread. */
-pthread_t pthread_self();
-/*!\brief Compares two thread identifiers.
- *
- * Determines of the given two thread identifiers refer to the same thread. If so, returns non-zero. Otherwise, 0 is returned. */
-int32_t pthread_equal(pthread_t thread1, pthread_t thread2);
-/*!\brief Used by the calling thread to relinquish use of the processor. The thread then waits in the run queue to be scheduled
- * again. */
-void pthread_yield();
-
-/*!\brief Gets the scheduling policy of the given attributes.
- *
- * Places the scheduling policy for attributes into policy. Returns 0 on success, EINVAL if attributes was invalid, and ENOSYS if
- * priority scheduling/multiple scheduler support is not implemented. */
-int32_t pthread_attr_getschedpolicy(const pthread_attr_t *attributes, int32_t *policy);
-/*!\brief Sets the scheduling policy of the given attributes.
- *
- * Requests a switch of scheduling policy to policy for the given attributes. Can return 0 for success, EINVAL if the given policy
- * is not one of SCHED_OTHER, SCHED_FIFO or SCHED_RR or ENOTSUP if policy is either SCHED_FIFO or SCHED_RR and the driver is not
- * running with correct privileges. */
-int32_t pthread_attr_setschedpolicy(pthread_attr_t *attributes, int32_t policy);
-
-/*!\brief Gets the scheduling paramaters (priority) from the given attributes.
- *
- * On success, stores scheduling parameters in param from attributes, and returns 0. Otherwise, returns non-zero error code, such
- * as EINVAL if the attributes object is invalid. */
-int32_t pthread_attr_getschedparam(const pthread_attr_t *attributes, sched_param *param);
-/*!\brief Sets the scheduling parameters (priority) of the given attributes.
- *
- * Requests that the runtime set the scheduling parameters (priority) of attributes from param. Returns 0 for success, EINVAL for an
- * invalid attributes object, ENOSYS when multiple schedulers/priority scheduling is not implemented, and ENOTSUP when the value of
- * param isn't supported/allowed. */
-int32_t pthread_attr_setschedparam(pthread_attr_t *attributes, const sched_param *param);
-
-/*!\brief The thread obtains its scheduling properties explicitly from its attributes structure. */
-#define PTHREAD_EXPLICIT_SCHED 1
-/*!\brief The thread inherits its scheduling properties from its parent thread. */
-#define PTHREAD_INHERIT_SCHED 0
-
-/*!\brief Returns the inheritsched attribute of the given attributes.
- *
- * On success, returns 0 and places the inheritsched attribute from attributes into inherit. This attribute specifies where the
- * thread's scheduling properites shall come from, and can be set to PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure it
- * returns EINVAL if attributes was invalid or ENOSYS if multiple schedulers/priority scheduling isn't implemented. */
-int32_t pthread_attr_getinheritsched(const pthread_attr_t *attributes, int32_t *inherit);
-/*!\brief Sets the inheritsched attribute of the given attributes.
- *
- * On success, places inherit into the inheritsched attribute of attributes and returns 0. inherit must either contain
- * PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure, this routine returns EINVAL if attributes is invalid, ENOSYS when
- * multiple schedulers/priority scheduling isn't implemented, and ENOSUP if the inheritsched attribute isn't supported. */
-int32_t pthread_attr_setinheritsched(pthread_attr_t *attributes, int32_t inherit);
-
-/*!\brief Creates a new POSIX Threads mutex, which will initially be unlocked.
- *
- * Creates a new mutex with the given attributes. If attributes is NULL, the default attributes will be used. The mutex starts out
- * unlocked. On success, the new mutex resides in the mutex structure pointed to by mutex, and this routine routines 0. On failure,
- * it returns EAGAIN if the system lacked sufficient non-memory resources to initialize the mutex, EBUSY if the given mutex is
- * already initialized and in use, EINVAL if either parameter is invalid, and ENOMEM if the system lacks the memory for a new
- * mutex. Note: All EDI mutexes are created with the default attributes, and are of type PTHREAD_MUTEX_ERRORCHECK. This means
- * undefined behavior can never result from an badly placed function call. */
-int32_t pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attributes);
-/*!\brief Locks the given mutex.
- *
- * Locks the given mutex. If the mutex is already locked, blocks the calling thread until it can acquire the lock. When this
- * routine returns successfully, it will return 0 and the calling thread will own the lock of the mutex. If the call fails, it can
- * return EINVAL when mutex is invalid or EDEADLK if the calling thread already owns the mutex. */
-int32_t pthread_mutex_lock(pthread_mutex_t *mutex);
-/*!\brief Unlocks the given mutex.
- *
- * Unlocks the given mutex, returning 0 on success. On failure, it can return EINVAL when mutex is invalid or EPERM when the
- * calling thread doesn't own the mutex. */
-int32_t pthread_mutex_unlock(pthread_mutex_t *mutex);
-/*!\brief Tries to lock the given mutex, returning immediately even if the mutex is already locked.
- *
- * Attempts to lock the given mutex, but returns immediately if it can't acquire a lock. Returns 0 when it has acquired a lock,
- * EBUSY if the mutex is already locked, or EINVAL if mutex is invalid. */
-int32_t pthread_mutex_trylock(pthread_mutex_t *mutex);
-/*!\brief Destroys the given mutex, or at least the internal structure of it.
- *
- * Deletes the given mutex, making mutex invalid until it should be initialized by pthread_mutex_init(). Returns 0 on success,
- * EINVAL when mutex is invalid, or EBUSY when mutex is locked or referenced by another thread. */
-int32_t pthread_mutex_destroy (pthread_mutex_t *mutex);
-
-#endif
+++ /dev/null
-#ifndef HELPERS_H
-
-#define HELPERS_H
-
-#include <edi.h>
-
-// Locally Defined
-bool edi_string_equal(edi_string_t x,edi_string_t y);
-bool descends_from(data_pointer object_class,edi_string_t desired_class);
-data_pointer get_actual_class(edi_string_t ancestor,int32_t num_objects,edi_object_metadata_t *objects);
-
-// Local Copy/set
-void *memcpyd(void *dest, void *src, unsigned int count);
-
-// Implementation Defined Common functions
-void *memcpy(void *dest, void *src, unsigned int count);
-void *memmove(void *dest, void *src, unsigned int count);
-void *realloc(void *ptr, unsigned int size);
-
-#endif
+++ /dev/null
-/*
- * AcessOS EDI Interface
- * - IRQ Class
- *
- * By John Hodge (thePowersGang)
- *
- * This file has been released into the public domain.
- * You are free to use it as you wish.
- */
-#include "edi/edi.h"
-
-// === TYPES ===
-typedef struct {
- uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap)
- uint16_t Num;
- interrupt_handler_t Handler;
-} tEdiIRQ;
-
-// === PROTOTYPES ===
-void EDI_Int_IRQ_Handler(tRegs *Regs);
-
-// === GLOBALS ===
-tEdiIRQ gEdi_IRQObjects[16];
-
-// === FUNCTIONS ===
-/**
- * \fn object_pointer Edi_Int_IRQ_Construct(void)
- * \brief Creates a new IRQ Object
- * \return Pointer to object
- */
-object_pointer Edi_Int_IRQ_Construct(void)
-{
- int i;
- // Search for a free irq
- for( i = 0; i < 16; i ++ )
- {
- if(gEdi_IRQObjects[i].State) continue;
- gEdi_IRQObjects[i].State = 1;
- gEdi_IRQObjects[i].Num = 0;
- gEdi_IRQObjects[i].Handler = NULL;
- return &gEdi_IRQObjects[i];
- }
- return NULL;
-}
-
-/**
- * \fn void Edi_Int_IRQ_Destruct(object_pointer Object)
- * \brief Destruct an IRQ Object
- * \param Object Object to destroy
- */
-void Edi_Int_IRQ_Destruct(object_pointer Object)
-{
- tEdiIRQ *obj;
-
- VALIDATE_PTR(Object,);
- obj = GET_DATA(Object);
-
- if( !obj->State ) return;
-
- if( obj->Handler )
- irq_uninstall_handler( obj->Num );
-
- if( obj->State & 0x8000 ) { // If in heap, free
- free(Object);
- } else { // Otherwise, mark as unallocated
- obj->State = 0;
- }
-}
-
-/**
- * \fn int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler)
- * \brief Initialises an IRQ
- * \param Object Object Pointer (this)
- * \param Num IRQ Number to use
- * \param Handler Callback for IRQ
- */
-int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler)
-{
- tEdiIRQ *obj;
-
- //LogF("Edi_Int_IRQ_InitInt: (Object=0x%x, Num=%i, Handler=0x%x)\n", Object, Num, Handler);
-
- VALIDATE_PTR(Object,0);
- obj = GET_DATA(Object);
-
- if( !obj->State ) return 0;
-
- if(Num > 15) return 0;
-
- // Install the IRQ if a handler is passed
- if(Handler) {
- if( !irq_install_handler(Num, Edi_Int_IRQ_Handler) )
- return 0;
- obj->Handler = Handler;
- }
-
- obj->Num = Num;
- obj->State &= ~0x3FFF;
- obj->State |= 2; // Set initialised flag
- return 1;
-}
-
-/**
- * \fn uint16_t Edi_Int_IRQ_GetInt(object_pointer Object)
- * \brief Returns the irq number associated with the object
- * \param Object IRQ Object to get number from
- * \return IRQ Number
- */
-uint16_t Edi_Int_IRQ_GetInt(object_pointer Object)
-{
- tEdiIRQ *obj;
-
- VALIDATE_PTR(Object,0);
- obj = GET_DATA(Object);
-
- if( !obj->State ) return 0;
- return obj->Num;
-}
-
-/**
- * \fn void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler)
- * \brief Set the IRQ handler for an IRQ object
- * \param Object IRQ Object to alter
- * \param Handler Function to use as handler
- */
-void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler)
-{
- tEdiIRQ *obj;
-
- // Get Data Pointer
- VALIDATE_PTR(Object,);
- obj = GET_DATA(Object);
-
- // Sanity Check arguments
- if( !obj->State ) return ;
-
- // Only register the mediator if it is not already
- if( Handler && !obj->Handler )
- if( !irq_install_handler(obj->Num, Edi_Int_IRQ_Handler) )
- return ;
- obj->Handler = Handler;
-}
-
-/**
- * \fn void EDI_Int_IRQ_Return(object_pointer Object)
- * \brief Return from interrupt
- * \param Object IRQ Object
- * \note Due to the structure of acess interrupts, this is a dummy
- */
-void EDI_Int_IRQ_Return(object_pointer Object)
-{
-}
-
-/**
- * \fn void Edi_Int_IRQ_Handler(struct regs *Regs)
- * \brief EDI IRQ Handler - Calls the handler
- * \param Regs Register state at IRQ call
- */
-void Edi_Int_IRQ_Handler(struct regs *Regs)
-{
- int i;
- for( i = 0; i < 16; i ++ )
- {
- if(!gEdi_IRQObjects[i].State) continue; // Unused, Skip
- if(gEdi_IRQObjects[i].Num != Regs->int_no) continue; // Another IRQ, Skip
- if(!gEdi_IRQObjects[i].Handler) continue; // No Handler, Skip
- gEdi_IRQObjects[i].Handler( Regs->int_no ); // Call Handler
- return;
- }
-}
-
-
-// === CLASS DECLARATION ===
-static edi_function_declaration_t scEdi_Int_Functions_IRQ[] = {
- {"int32_t", "init_interrupt", 1, 3, NULL, //scEdi_Int_Variables_IO[0],
- (function_pointer)Edi_Int_IRQ_InitInt
- },
- {"uint32_t", "interrupt_get_irq", 1, 1, NULL, //scEdi_Int_Variables_IO[1],
- (function_pointer)Edi_Int_IRQ_GetInt
- },
- {"void", "interrupt_set_handler", 1, 2, NULL, //scEdi_Int_Variables_IO[2],
- (function_pointer)Edi_Int_IRQ_GetInt
- },
- {"void", "interrupt_return", 1, 1, NULL, //scEdi_Int_Variables_IO[3],
- (function_pointer)Edi_Int_IRQ_GetInt
- }
- };
-static edi_class_declaration_t scEdi_Int_Class_IRQ =
- {
- INTERRUPTS_CLASS, 1, 12,
- scEdi_Int_Functions_IRQ,
- Edi_Int_IRQ_Construct,
- Edi_Int_IRQ_Destruct,
- NULL
- };
+++ /dev/null
-/*
- * AcessOS EDI Interface
- * - IO Port Class
- *
- * By John Hodge (thePowersGang)
- *
- * This file has been released into the public domain.
- * You are free to use it as you wish.
- */
-#include "edi/edi.h"
-
-// === TYPES ===
-typedef struct {
- uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap)
- uint16_t Num;
-} tEdiPort;
-
-// === GLOBALS ===
-#define NUM_PREALLOC_PORTS 128
-tEdiPort gEdi_PortObjects[NUM_PREALLOC_PORTS];
-
-// === FUNCTIONS ===
-/**
- * \fn object_pointer Edi_Int_IO_Construct(void)
- * \brief Creates a new IO Port Object
- * \return Pointer to object
- */
-object_pointer Edi_Int_IO_Construct(void)
-{
- tEdiPort *ret;
- int i;
- // Search for a free preallocated port
- for( i = 0; i < NUM_PREALLOC_PORTS; i ++ )
- {
- if(gEdi_PortObjects[i].State) continue;
- gEdi_PortObjects[i].State = 1;
- gEdi_PortObjects[i].Num = 0;
- return &gEdi_PortObjects[i];
- }
- // Else, use heap space
- ret = malloc( sizeof(tEdiPort) );
- ret->State = 0x8001;
- ret->Num = 0;
- return ret;
-}
-
-/**
- * \fn void Edi_Int_IO_Destruct(object_pointer Object)
- * \brief Destruct an IO Port Object
- * \param Object Object to destroy
- */
-void Edi_Int_IO_Destruct(object_pointer Object)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object,);
- obj = GET_DATA(Object);
-
- if(obj->State & 0x8000) { // If in heap, free
- free(Object);
- } else { // Otherwise, mark as unallocated
- obj->State = 0;
- }
-}
-
-/**
- * \fn int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port)
- * \brief Initialises an IO Port
- * \param Object Object Pointer (this)
- * \param Port Port Number to use
- */
-int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
-
- if( !obj->State ) return 0;
- obj->Num = Port;
- obj->State &= ~0x3FFF;
- obj->State |= 2; // Set initialised flag
- return 1;
-}
-
-/**
- * \fn uint16_t Edi_Int_IO_GetPortNum(object_pointer Object)
- * \brief Returns the port number associated with the object
- * \param Object Port Object to get number from
- * \return Port Number
- */
-uint16_t Edi_Int_IO_GetPortNum(object_pointer Object)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- // Check if valid
- if( !obj->State ) return 0;
- // Return Port No
- return obj->Num;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out)
- * \brief Read a byte from an IO port
- * \param Object Port Object
- * \param out Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
-
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "inb %%dx, %%al" : "=a" (*out) : "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out)
- * \brief Read a word from an IO port
- * \param Object Port Object
- * \param out Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out)
-{
-
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "inw %%dx, %%ax" : "=a" (*out) : "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out)
- * \brief Read a double word from an IO port
- * \param Object Port Object
- * \param out Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out)
-{
-
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out) : "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out)
- * \brief Read a quad word from an IO port
- * \param Object Port Object
- * \param out Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out)
-{
- uint32_t *out32 = (uint32_t*)out;
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out32) : "d" ( obj->Num ) );
- __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*(out32+1)) : "d" ( obj->Num+4 ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out)
- * \brief Read a byte from an IO port
- * \param Object Port Object
- * \param Length Number of bytes to read
- * \param out Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "rep insb" : : "c" (Length), "D" (out), "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in)
- * \brief Write a byte from an IO port
- * \param Object Port Object
- * \param in Data to write
- */
-int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "outb %%al, %%dx" : : "a" (in), "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in)
- * \brief Write a word from an IO port
- * \param Object Port Object
- * \param in Data to write
- */
-int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "outw %%ax, %%dx" : : "a" (in), "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in)
- * \brief Write a double word from an IO port
- * \param Object Port Object
- * \param in Data to write
- */
-int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (in), "d" ( obj->Num ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in)
- * \brief Write a quad word from an IO port
- * \param Object Port Object
- * \param in Data to write
- */
-int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in)
-{
- uint32_t *in32 = (uint32_t*)∈
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*in32), "d" ( obj->Num ) );
- __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*(in32+1)), "d" ( obj->Num+4 ) );
-
- return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in)
- * \brief Read a byte from an IO port
- * \param Object Port Object
- * \param Length Number of bytes to write
- * \param in Pointer to of data to write
- */
-int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in)
-{
- tEdiPort *obj;
- // Get Data Pointer
- VALIDATE_PTR(Object, 0);
- obj = GET_DATA(Object);
- if( !obj->State ) return 0;
- if( obj->State & 1 ) return -1; // Unintialised
-
- __asm__ __volatile__ ( "rep outsb" : : "c" (Length), "D" (in), "d" ( obj->Num ) );
-
- return 1;
-}
-
-// === CLASS DECLARATION ===
-/*static edi_variable_declaration_t *scEdi_Int_Variables_IO[] = {
- {
- {"pointer", "port_object", 0},
- {"uint16_t", "port", 0}
- },
- {
- {"pointer", "port_object", 0}
- },
- {
- {"pointer", "port_object", 0},
- {"pointer int8_t", "out", 0}
- }
-};*/
-static edi_function_declaration_t scEdi_Int_Functions_IO[] = {
- {"int32_t", "init_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[0],
- (function_pointer)Edi_Int_IO_InitPort
- },
- {"uint16_t", "get_port_number", 1, 1, NULL, //scEdi_Int_Variables_IO[1],
- (function_pointer)Edi_Int_IO_GetPortNum
- },
- {"int32_t", "read_byte_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[2],
- (function_pointer)Edi_Int_IO_ReadByte
- },
- {"int32_t", "read_word_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"pointer int16_t", "out", 0}
- }*/,
- (function_pointer)Edi_Int_IO_ReadWord
- },
- {"int32_t", "read_long_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"pointer int32_t", "out", 0}
- }*/,
- (function_pointer)Edi_Int_IO_ReadDWord
- },
- {"int32_t", "read_longlong_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"pointer int64_t", "out", 0}
- }*/,
- (function_pointer)Edi_Int_IO_ReadQWord
- },
- {"int32_t", "read_string_io_port", 1, 3, NULL/*{
- {"pointer", "port_object", 0},
- {"int32_T", "data_length", 0},
- {"pointer int64_t", "out", 0}
- }*/,
- (function_pointer)Edi_Int_IO_ReadString
- },
-
- {"int32_t", "write_byte_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"int8_t", "in", 0}
- }*/,
- (function_pointer)Edi_Int_IO_WriteByte},
- {"int32_t", "write_word_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"int16_t", "in", 0}
- }*/,
- (function_pointer)Edi_Int_IO_WriteWord},
- {"int32_t", "write_long_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"int32_t", "in", 0}
- }*/,
- (function_pointer)Edi_Int_IO_WriteDWord},
- {"int32_t", "write_longlong_io_port", 1, 2, NULL/*{
- {"pointer", "port_object", 0},
- {"int64_t", "in", 0}
- }*/,
- (function_pointer)Edi_Int_IO_WriteQWord}
- };
-static edi_class_declaration_t scEdi_Int_Class_IO =
- {
- IO_PORT_CLASS, 1, 12,
- scEdi_Int_Functions_IO,
- Edi_Int_IO_Construct,
- Edi_Int_IO_Destruct,
- NULL
- };
+++ /dev/null
-/*
- * Acess2 EDI Layer
- */
-#define DEBUG 0
-#define VERSION ((0<<8)|1)
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#define IMPLEMENTING_EDI 1
-#include "edi/edi.h"
-
-#define VALIDATE_PTR(_ptr,_err) do{if(!(_ptr))return _err; }while(0)
-#define GET_DATA(_ptr) (Object)
-
-#include "edi_io.inc.c"
-#include "edi_int.inc.c"
-
-// === STRUCTURES ===
-typedef struct sAcessEdiDriver {
- struct sAcessEdiDriver *Next;
- tDevFS_Driver Driver;
- int FileCount;
- struct {
- char *Name;
- tVFS_Node Node;
- } *Files;
- edi_object_metadata_t *Objects;
- edi_initialization_t Init;
- driver_finish_t Finish;
-} tAcessEdiDriver;
-
-// === PROTOTYPES ===
- int EDI_Install(char **Arguments);
- int EDI_DetectDriver(void *Base);
- int EDI_LoadDriver(void *Base);
-vfs_node *EDI_FS_ReadDir(vfs_node *Node, int Pos);
-vfs_node *EDI_FS_FindDir(vfs_node *Node, char *Name);
- int EDI_FS_CharRead(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- int EDI_FS_CharWrite(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- int EDI_FS_IOCtl(vfs_node *Node, int Id, void *Data);
-data_pointer EDI_GetInternalClass(edi_string_t ClassName);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, EDI, EDI_Install, NULL, NULL);
-tModuleLoader gEDI_Loader = {
- NULL, "EDI", EDI_DetectDriver, EDI_LoadDriver, NULL
-};
-tSpinlock glEDI_Drivers;
-tAcessEdiDriver *gEdi_Drivers = NULL;
-edi_class_declaration_t *gcEdi_IntClasses[] = {
- &scEdi_Int_Class_IO, &scEdi_Int_Class_IRQ
-};
-#define NUM_INT_CLASSES (sizeof(gcEdi_IntClasses)/sizeof(gcEdi_IntClasses[0]))
-char *csCharNumbers[] = {"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9"};
-char *csBlockNumbers[] = {"blk0", "blk1", "blk2", "blk3", "blk4", "blk5", "blk6", "blk7", "blk8", "blk9"};
-
-// === CODE ===
-/**
- * \fn int EDI_Install(char **Arguments)
- * \brief Stub intialisation routine
- */
-int EDI_Install(char **Arguments)
-{
- Module_RegisterLoader( &gEDI_Loader );
- return 1;
-}
-
-/**
- * \brief Detects if a driver should be loaded by the EDI subsystem
- */
-int EDI_DetectDriver(void *Base)
-{
- if( Binary_FindSymbol(BASE, "driver_init", NULL) == 0 )
- return 0;
-
- return 1;
-}
-
-/**
- * \fn int Edi_LoadDriver(void *Base)
- * \brief Load an EDI Driver from a loaded binary
- * \param Base Binary Handle
- * \return 0 on success, non zero on error
- */
-int EDI_LoadDriver(void *Base)
-{
- driver_init_t init;
- driver_finish_t finish;
- tAcessEdiDriver *info;
- int i, j;
- int devfsId;
- edi_class_declaration_t *classes;
-
- ENTER("pBase", Base);
-
- // Get Functions
- if( !Binary_FindSymbol(Base, "driver_init", (Uint*)&init)
- || !Binary_FindSymbol(Base, "driver_finish", (Uint*)&finish) )
- {
- Warning("[EDI ] Driver %p does not provide both `driver_init` and `driver_finish`\n", Base);
- Binary_Unload(Base);
- return 0;
- }
-
- // Allocate Driver Information
- info = malloc( sizeof(tAcessEdiDriver) );
- info->Finish = finish;
-
- // Initialise Driver
- info->Init = init( 0, NULL ); // TODO: Implement Resources
-
- LOG("info->Init.driver_name = '%s'", info->Init.driver_name);
- LOG("info->Init.num_driver_classes = %i", info->Init.num_driver_classes);
-
- // Count mappable classes
- classes = info->Init.driver_classes;
- info->FileCount = 0;
- info->Objects = NULL;
- for( i = 0, j = 0; i < info->Init.num_driver_classes; i++ )
- {
- if( strncmp(classes[i].name, "EDI-CHARACTER-DEVICE", 32) == 0 )
- {
- data_pointer *obj;
- // Initialise Object Instances
- for( ; (obj = classes[i].constructor()); j++ ) {
- LOG("%i - Constructed '%s'", j, classes[i].name);
- info->FileCount ++;
- info->Objects = realloc(info->Objects, sizeof(*info->Objects)*info->FileCount);
- info->Objects[j].object = obj;
- info->Objects[j].object_class = &classes[i];
- }
- }
- else
- LOG("%i - %s", i, classes[i].name);
- }
-
- if(info->FileCount)
- {
- int iNumChar = 0;
- // Create VFS Nodes
- info->Files = malloc( info->FileCount * sizeof(*info->Files) );
- memset(info->Files, 0, info->FileCount * sizeof(*info->Files));
- j = 0;
- for( j = 0; j < info->FileCount; j++ )
- {
- classes = info->Objects[j].object_class;
- if( strncmp(classes->name, "EDI-CHARACTER-DEVICE", 32) == 0 )
- {
- LOG("%i - %s", j, csCharNumbers[iNumChar]);
- info->Files[j].Name = csCharNumbers[iNumChar];
- info->Files[j].Node.NumACLs = 1;
- info->Files[j].Node.ACLs = &gVFS_ACL_EveryoneRW;
- info->Files[j].Node.ImplPtr = &info->Objects[j];
- info->Files[j].Node.Read = EDI_FS_CharRead;
- info->Files[j].Node.Write = EDI_FS_CharWrite;
- info->Files[j].Node.IOCtl = EDI_FS_IOCtl;
- info->Files[j].Node.CTime =
- info->Files[j].Node.MTime =
- info->Files[j].Node.ATime = now();
-
- iNumChar ++;
- continue;
- }
- }
-
- // Create DevFS Driver
- info->Driver.ioctl = EDI_FS_IOCtl;
- memsetda(&info->Driver.rootNode, 0, sizeof(vfs_node) / 4);
- info->Driver.Name = info->Init.driver_name;
- info->Driver.RootNode.Flags = VFS_FFLAG_DIRECTORY;
- info->Driver.RootNode.NumACLs = 1;
- info->Driver.RootNode.ACLs = &gVFS_ACL_EveryoneRX;
- info->Driver.RootNode.Length = info->FileCount;
- info->Driver.RootNode.ImplPtr = info;
- info->Driver.RootNode.ReadDir = EDI_FS_ReadDir;
- info->Driver.RootNode.FindDir = EDI_FS_FindDir;
- info->Driver.RootNode.IOCtl = EDI_FS_IOCtl;
-
- // Register
- devfsId = dev_addDevice( &info->Driver );
- if(devfsId == -1) {
- free(info->Files); // Free Files
- info->Finish(); // Clean up driver
- free(info); // Free info structure
- Binary_Unload(iDriverBase); // Unload library
- return -3; // Return error
- }
- }
-
- // Append to loaded list;
- LOCK(&glEDI_Drivers);
- info->Next = gEDI_Drivers;
- gEDI_Drivers = info;
- RELEASE(&glEDI_Drivers);
-
- LogF("[EDI ] Loaded Driver '%s' (%s)\n", info->Init.driver_name, Path);
- LEAVE('i', 1);
- return 1;
-}
-
-// --- Filesystem Interaction ---
-/**
- * \brief Read from a drivers class list
- * \param Node Driver's Root Node
- * \param Pos Index of file to get
- */
-char *EDI_FS_ReadDir(tVFS_Node *Node, int Pos)
-{
- tAcessEdiDriver *info;
-
- // Sanity Check
- if(!Node) return NULL;
-
- // Get Information Structure
- info = (void *) Node->ImplPtr;
- if(!info) return NULL;
-
- // Check Position
- if(Pos < 0) return NULL;
- if(Pos >= info->FileCount) return NULL;
-
- return strdup( info->Files[Pos].Name );
-}
-
-/**
- * \fn tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name)
- * \brief Find a named file in a driver
- * \param Node Driver's Root Node
- * \param Name Name of file to find
- */
-tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name)
-{
- tAcessEdiDriver *info;
- int i;
-
- // Sanity Check
- if(!Node) return NULL;
- if(!Name) return NULL;
-
- // Get Information Structure
- info = (void *) Node->ImplPtr;
- if(!info) return NULL;
-
- for( i = 0; i < info->FileCount; i++ )
- {
- if(strcmp(info->Files[i].name, Name) == 0)
- return &info->Files[i].Node;
- }
-
- return NULL;
-}
-
-/**
- * \fn Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from an EDI Character Device
- * \param Node File Node
- * \param Offset Offset into file (ignored)
- * \param Length Number of characters to read
- * \param Buffer Destination for data
- * \return Number of characters read
- */
-Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- edi_object_metadata_t *meta;
- edi_class_declaration_t *class;
-
- // Sanity Check
- if(!Node || !Buffer) return 0;
- if(Length <= 0) return 0;
- // Get Object Metadata
- meta = (void *) Node->ImplPtr;
- if(!meta) return 0;
-
- // Get Class
- class = meta->object_class;
- if(!class) return 0;
-
- // Read from object
- if( ((tAnyFunction) class->methods[0].code)( meta->object, Buffer, Length ))
- return Length;
-
- return 0;
-}
-
-/**
- * \fn Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to an EDI Character Device
- * \param Node File Node
- * \param Offset Offset into file (ignored)
- * \param Length Number of characters to write
- * \param Buffer Source for data
- * \return Number of characters written
- */
-Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- edi_object_metadata_t *meta;
- edi_class_declaration_t *class;
-
- // Sanity Check
- if(!Node || !Buffer) return 0;
- if(Length <= 0) return 0;
- // Get Object Metadata
- meta = (void *) Node->ImplPtr;
- if(!meta) return 0;
-
- // Get Class
- class = meta->object_class;
- if(!class) return 0;
-
- // Write to object
- if( ((tAnyFunction) class->methods[1].code)( meta->object, Buffer, Length ))
- return Length;
-
- return 0;
-}
-
-/**
- * \fn int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief Perfom an IOCtl call on the object
- */
-int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
- return 0;
-}
-
-// --- EDI Functions ---
-/**
- * \fn data_pointer EDI_GetDefinedClass(edi_string_t ClassName)
- * \brief Gets the structure of a driver defined class
- * \param ClassName Name of class to find
- * \return Class definition or NULL
- */
-data_pointer EDI_GetDefinedClass(edi_string_t ClassName)
-{
- int i;
- tAcessEdiDriver *drv;
- edi_class_declaration_t *classes;
-
- for(drv = gEdi_Drivers;
- drv;
- drv = drv->Next )
- {
- classes = drv->Init.driver_classes;
- for( i = 0; i < drv->Init.num_driver_classes; i++ )
- {
- if( strncmp(classes[i].name, ClassName, 32) == 0 )
- return &classes[i];
- }
- }
- return NULL;
-}
-
-/**
- * \fn int32_t EDI_CheckClassExistence(edi_string_t ClassName)
- * \brief Checks if a class exists
- * \param ClassName Name of class
- * \return 1 if the class exists, -1 otherwise
- */
-int32_t EDI_CheckClassExistence(edi_string_t ClassName)
-{
- //LogF("check_class_existence: (ClassName='%s')\n", ClassName);
- if(EDI_GetInternalClass(ClassName))
- return 1;
-
- if(EDI_GetDefinedClass(ClassName)) // Driver Defined
- return 1;
-
- return -1;
-}
-
-/**
- * \fn edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName)
- * \brief Construct an instance of an class (an object)
- * \param ClassName Name of the class to construct
- */
-edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName)
-{
- edi_object_metadata_t ret = {0, 0};
- edi_class_declaration_t *class;
-
- //LogF("EDI_ConstructObject: (ClassName='%s')\n", ClassName);
-
- // Get class definition
- if( !(class = EDI_GetInternalClass(ClassName)) ) // Internal
- if( !(class = EDI_GetDefinedClass(ClassName)) ) // Driver Defined
- return ret; // Return ERROR
-
- // Initialise
- ret.object = class->constructor();
- if( !ret.object )
- return ret; // Return ERROR
-
- // Set declaration pointer
- ret.object_class = class;
-
- //LogF("EDI_ConstructObject: RETURN {0x%x,0x%x}\n", ret.object, ret.object_class);
- return ret;
-}
-
-/**
- * \fn void EDI_DestroyObject(edi_object_metadata_t Object)
- * \brief Destroy an instance of a class
- * \param Object Object to destroy
- */
-void EDI_DestroyObject(edi_object_metadata_t Object)
-{
- if( !Object.object ) return;
- if( !Object.object_class ) return;
-
- ((edi_class_declaration_t*)(Object.object_class))->destructor( &Object );
-}
-
-/**
- * \fn function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName)
- * \brief Get a method of a class by it's name
- * \param ObjectClass Pointer to a ::edi_object_metadata_t of the object
- * \param MethodName Name of the desired method
- * \return Function address or NULL
- */
-function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName)
-{
- edi_class_declaration_t *dec = ObjectClass;
- int i;
-
- //LogF("get_method_by_name: (ObjectClass=0x%x, MethodName='%s')\n", ObjectClass, MethodName);
-
- if(!ObjectClass) return NULL;
-
- for(i = 0; i < dec->num_methods; i++)
- {
- if(strncmp(MethodName, dec->methods[i].name, 32) == 0)
- return dec->methods[i].code;
- }
- return NULL;
-}
-
-#if 0
-function_pointer get_method_by_declaration(data_pointer Class, edi_function_declaration_t Declaration);
-#endif
-
-/**
- * \fn edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName)
- * \brief Get the parent of the named class
- * \todo Implement
- */
-edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName)
-{
- WarningEx("EDI", "`get_class_parent` is unimplemented");
- return NULL;
-}
-
-/**
- * \fn data_pointer EDI_GetInternalClass(edi_string_t ClassName)
- * \brief Get a builtin class
- * \param ClassName Name of class to find
- * \return Pointer to the ::edi_class_declaration_t of the class
- */
-data_pointer EDI_GetInternalClass(edi_string_t ClassName)
-{
- int i;
- //LogF("get_internal_class: (ClassName='%s')\n", ClassName);
- for( i = 0; i < NUM_INT_CLASSES; i++ )
- {
- if( strncmp( gcEdi_IntClasses[i]->name, ClassName, 32 ) == 0 ) {
- return gcEdi_IntClasses[i];
- }
- }
- //LogF("get_internal_class: RETURN NULL\n");
- return NULL;
-}
-
-/**
- * \fn edi_string_ptr_t EDI_GetObjectClass(data_pointer Object)
- * \brief Get the name of the object of \a Object
- * \param Object Object to get name of
- * \return Pointer to the class name
- */
-edi_string_ptr_t EDI_GetObjectClass(data_pointer ObjectClass)
-{
- edi_object_metadata_t *Metadata = ObjectClass;
- // Sanity Check
- if(!ObjectClass) return NULL;
- if(!(edi_class_declaration_t*) Metadata->object_class) return NULL;
-
- // Return Class Name
- return ((edi_class_declaration_t*) Metadata->object_class)->name;
-}
-
-// === EXPORTS ===
-EXPORTAS(EDI_CheckClassExistence, check_class_existence);
-EXPORTAS(EDI_ConstructObject, construct_object);
-EXPORTAS(EDI_DestroyObject, destroy_object);
-EXPORTAS(EDI_GetMethodByName, get_method_by_name);
-EXPORTAS(EDI_GetClassParent, get_class_parent);
-EXPORTAS(EDI_GetInternalClass, get_internal_class);
-EXPORTAS(EDI_GetObjectClass, get_object_class);
+++ /dev/null
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-CPPFLAGS = -I./include
-OBJ = main.o logging.o strmem.o imc.o mem.o buf.o cb.o
-OBJ += meta_mgmt.o meta_gio.o
-OBJ += physio.o physio/meta_bus.o physio/meta_intr.o
-NAME = UDI
-
--include ../Makefile.tpl
+++ /dev/null
-
-<Love4Boobies> logical volume metalanguage - for file system drivers to use, network protocol
- metalanguage and audio support
-<Love4Boobies> madeofstaples expressed his interest in reviewing the OpenUSBDI specification to update
- it for USB 3.0
-
-
-=== Initialisation ===
-udi_init_t
- > udi_init_info
- > This is the only defined symbol in a UDI driver
-
-udi_primary_init_t
-- UDI_MAX_SCRATCH
-- UDI_OP_LONG_EXEC
-
-udi_secondary_init_t
-
-udi_ops_init_t
-
-udi_cb_init_t
-
-udi_cb_select_t
-
-udi_gcb_init_t
-
-udi_init_context_t
-
-udi_limits_t
-- UDI_MIN_ALLOC_LIMIT
-- UDI_MIN_TRACE_LOG_LIMIT
-- UDI_MIN_INSTANCE_ATTR_LIMIT
-
-udi_chan_context_t
-
-udi_child_chan_context_t
+++ /dev/null
-/**
- * \file buf.c
- * \author John Hodge (thePowersGang)
- *
- * Buffer Manipulation
- */
-#include <acess.h>
-#include <udi.h>
-
-// === EXPORTS ===
-EXPORT(udi_buf_copy);
-EXPORT(udi_buf_write);
-EXPORT(udi_buf_read);
-EXPORT(udi_buf_free);
-
-// === CODE ===
-void udi_buf_copy(
- udi_buf_copy_call_t *callback,
- udi_cb_t *gcb,
- udi_buf_t *src_buf,
- udi_size_t src_off,
- udi_size_t src_len,
- udi_buf_t *dst_buf,
- udi_size_t dst_off,
- udi_size_t dst_len,
- udi_buf_path_t path_handle
- )
-{
- UNIMPLEMENTED();
-}
-
-/**
- * \brief Write to a buffer
- * \param callback Function to call once the write has completed
- * \param gcb Control Block
- * \param src_mem Source Data
- * \param src_len Length of source data
- * \param dst_buf Destination buffer
- * \param dst_off Destination offset in the buffer
- * \param dst_len Length of destination area (What the?, Redundant
- * Department of redundacny department)
- * \param path_handle ???
- */
-void udi_buf_write(
- udi_buf_write_call_t *callback,
- udi_cb_t *gcb,
- const void *src_mem,
- udi_size_t src_len,
- udi_buf_t *dst_buf,
- udi_size_t dst_off,
- udi_size_t dst_len,
- udi_buf_path_t path_handle
- )
-{
- UNIMPLEMENTED();
-}
-
-void udi_buf_read(
- udi_buf_t *src_buf,
- udi_size_t src_off,
- udi_size_t src_len,
- void *dst_mem )
-{
- UNIMPLEMENTED();
-}
-
-void udi_buf_free(udi_buf_t *buf)
-{
- UNIMPLEMENTED();
-}
+++ /dev/null
-/**
- * \file cb.c
- * \author John Hodge (thePowersGang)
- * \brief Control block code
- */
-#include <acess.h>
-#include <udi.h>
-
-// === CODE ===
-void udi_cb_alloc (
- udi_cb_alloc_call_t *callback, //!< Function to be called when the CB is allocated
- udi_cb_t *gcb, //!< Parent Control Block
- udi_index_t cb_idx,
- udi_channel_t default_channel
- )
-{
- UNIMPLEMENTED();
-}
-
-void udi_cb_alloc_dynamic(
- udi_cb_alloc_call_t *callback,
- udi_cb_t *gcb,
- udi_index_t cb_idx,
- udi_channel_t default_channel,
- udi_size_t inline_size,
- udi_layout_t *inline_layout
- )
-{
- UNIMPLEMENTED();
-}
-
-void udi_cb_alloc_batch(
- udi_cb_alloc_batch_call_t *callback, //!<
- udi_cb_t *gcb, //!<
- udi_index_t cb_idx,
- udi_index_t count,
- udi_boolean_t with_buf,
- udi_size_t buf_size,
- udi_buf_path_t path_handle
- )
-{
- UNIMPLEMENTED();
-}
-
-void udi_cb_free(udi_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-
-void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb)
-{
- UNIMPLEMENTED();
-}
-
-// === EXPORTS ===
-EXPORT(udi_cb_alloc);
-EXPORT(udi_cb_alloc_dynamic);
-EXPORT(udi_cb_alloc_batch);
-EXPORT(udi_cb_free);
-EXPORT(udi_cancel);
+++ /dev/null
-/**
- * \file imc.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-
-// === EXPORTS ===
-EXPORT(udi_channel_anchor);
-EXPORT(udi_channel_spawn);
-EXPORT(udi_channel_set_context);
-EXPORT(udi_channel_op_abort);
-EXPORT(udi_channel_close);
-EXPORT(udi_channel_event_ind);
-EXPORT(udi_channel_event_complete);
-
-// === CODE ===
-/**
- */
-void udi_channel_anchor(
- udi_channel_anchor_call_t *callback, udi_cb_t *gcb,
- udi_channel_t channel, udi_index_t ops_idx, void *channel_context
- )
-{
- Warning("%s Unimplemented", __func__);
-}
-
-/**
- */
-extern void udi_channel_spawn(
- udi_channel_spawn_call_t *callback, udi_cb_t *gcb,
- udi_channel_t channel, udi_index_t spawn_idx,
- udi_index_t ops_idx, void *channel_context
- )
-{
- Warning("%s Unimplemented", __func__);
-}
-
-/**
- *
- */
-void udi_channel_set_context(
- udi_channel_t target_channel, void *channel_context
- )
-{
- Warning("%s Unimplemented", __func__);
-}
-
-void udi_channel_op_abort(
- udi_channel_t target_channel, udi_cb_t *orig_cb
- )
-{
- Warning("%s Unimplemented", __func__);
-}
-
-void udi_channel_close(udi_channel_t channel)
-{
- Warning("%s Unimplemented", __func__);
-}
-
-void udi_channel_event_ind(udi_channel_event_cb_t *cb)
-{
- udi_channel_event_complete(cb, UDI_OK);
-}
-
-void udi_channel_event_complete(udi_channel_event_cb_t *cb, udi_status_t status)
-{
- Warning("%s Unimplemented", __func__);
-}
+++ /dev/null
-/**
- * \file physio/meta_bus.h
- */
-#ifndef _PHYSIO_META_BUS_H_
-#define _PHYSIO_META_BUS_H_
-
-#include <udi.h>
-#include <udi_physio.h>
-
-typedef const struct udi_bus_device_ops_s udi_bus_device_ops_t;
-typedef const struct udi_bus_bridge_ops_s udi_bus_bridge_ops_t;
-typedef struct udi_bus_bind_cb_s udi_bus_bind_cb_t;
-typedef void udi_bus_unbind_req_op_t(udi_bus_bind_cb_t *cb);
-typedef void udi_bus_unbind_ack_op_t(udi_bus_bind_cb_t *cb);
-typedef void udi_bus_bind_req_op_t(udi_bus_bind_cb_t *cb);
-typedef void udi_bus_bind_ack_op_t(
- udi_bus_bind_cb_t *cb,
- udi_dma_constraints_t dma_constraints,
- udi_ubit8_t preferred_endianness,
- udi_status_t status
- );
-
-
-struct udi_bus_device_ops_s
-{
- udi_channel_event_ind_op_t *channel_event_ind_op;
- udi_bus_bind_ack_op_t *bus_bind_ack_op;
- udi_bus_unbind_ack_op_t *bus_unbind_ack_op;
- udi_intr_attach_ack_op_t *intr_attach_ack_op;
- udi_intr_detach_ack_op_t *intr_detach_ack_op;
-};
-/* Bus Device Ops Vector Number */
-#define UDI_BUS_DEVICE_OPS_NUM 1
-
-struct udi_bus_bridge_ops_s
-{
- udi_channel_event_ind_op_t *channel_event_ind_op;
- udi_bus_bind_req_op_t *bus_bind_req_op;
- udi_bus_unbind_req_op_t *bus_unbind_req_op;
- udi_intr_attach_req_op_t *intr_attach_req_op;
- udi_intr_detach_req_op_t *intr_detach_req_op;
-};
-/* Bus Bridge Ops Vector Number */
-#define UDI_BUS_BRIDGE_OPS_NUM
-
-struct udi_bus_bind_cb_s
-{
- udi_cb_t gcb;
-};
-/* Bus Bind Control Block Group Number */
-#define UDI_BUS_BIND_CB_NUM 1
-
-
-extern void udi_bus_bind_req(udi_bus_bind_cb_t *cb);
-
-extern void udi_bus_bind_ack(
- udi_bus_bind_cb_t *cb,
- udi_dma_constraints_t dma_constraints,
- udi_ubit8_t preferred_endianness,
- udi_status_t status
- );
-/* Values for preferred_endianness */
-#define UDI_DMA_BIG_ENDIAN (1U<<5)
-#define UDI_DMA_LITTLE_ENDIAN (1U<<6)
-#define UDI_DMA_ANY_ENDIAN (1U<<0)
-
-extern void udi_bus_unbind_req(udi_bus_bind_cb_t *cb);
-extern void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb);
-
-
-
-
-
-#endif
+++ /dev/null
-/**
- * \file physio/meta_intr.h
- */
-#ifndef _PHYSIO_META_INTR_H_
-#define _PHYSIO_META_INTR_H_
-
-#include <udi.h>
-#include <udi_physio.h>
-#include "pio.h"
-
-typedef struct udi_intr_attach_cb_s udi_intr_attach_cb_t;
-typedef void udi_intr_attach_req_op_t(udi_intr_attach_cb_t *intr_attach_cb);
-typedef void udi_intr_attach_ack_op_t(
- udi_intr_attach_cb_t *intr_attach_cb,
- udi_status_t status
- );
-typedef struct udi_intr_detach_cb_s udi_intr_detach_cb_t;
-typedef void udi_intr_detach_req_op_t(udi_intr_detach_cb_t *intr_detach_cb);
-typedef void udi_intr_detach_ack_op_t(udi_intr_detach_cb_t *intr_detach_cb);
-typedef const struct udi_intr_handler_ops_s udi_intr_handler_ops_t;
-typedef const struct udi_intr_dispatcher_ops_s udi_intr_dispatcher_ops_t;
-typedef struct udi_intr_event_cb_s udi_intr_event_cb_t;
-typedef void udi_intr_event_ind_op_t(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags);
-typedef void udi_intr_event_rdy_op_t(udi_intr_event_cb_t *intr_event_cb);
-
-
-struct udi_intr_attach_cb_s
-{
- udi_cb_t gcb;
- udi_index_t interrupt_idx;
- udi_ubit8_t min_event_pend;
- udi_pio_handle_t preprocessing_handle;
-};
-/* Bridge Attach Control Block Group Number */
-#define UDI_BUS_INTR_ATTACH_CB_NUM 2
-
-struct udi_intr_detach_cb_s
-{
- udi_cb_t gcb;
- udi_index_t interrupt_idx;
-};
-/* Bridge Detach Control Block Group Number */
-#define UDI_BUS_INTR_DETACH_CB_NUM 3
-
-struct udi_intr_handler_ops_s
-{
- udi_channel_event_ind_op_t *channel_event_ind_op;
- udi_intr_event_ind_op_t *intr_event_ind_op;
-};
-/* Interrupt Handler Ops Vector Number */
-#define UDI_BUS_INTR_HANDLER_OPS_NUM 3
-
-struct udi_intr_dispatcher_ops_s
-{
- udi_channel_event_ind_op_t *channel_event_ind_op;
- udi_intr_event_rdy_op_t *intr_event_rdy_op;
-};
-/* Interrupt Dispatcher Ops Vector Number */
-#define UDI_BUS_INTR_DISPATCH_OPS_NUM 4
-
-struct udi_intr_event_cb_s
-{
- udi_cb_t gcb;
- udi_buf_t *event_buf;
- udi_ubit16_t intr_result;
-};
-/* Flag values for interrupt handling */
-#define UDI_INTR_UNCLAIMED (1U<<0)
-#define UDI_INTR_NO_EVENT (1U<<1)
-/* Bus Interrupt Event Control Block Group Number */
-#define UDI_BUS_INTR_EVENT_CB_NUM 4
-
-
-
-extern void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb);
-extern void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status);
-extern void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status);
-
-extern void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb);
-extern void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb);
-extern void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb);
-
-
-extern void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags);
-/**
- * \brief Values for ::udi_intr_event_ind \a flags
- * \{
- */
-#define UDI_INTR_MASKING_NOT_REQUIRED (1U<<0)
-#define UDI_INTR_OVERRUN_OCCURRED (1U<<1)
-#define UDI_INTR_PREPROCESSED (1U<<2)
-/**
- * \}
- */
-
-extern void udi_intr_event_rdy(udi_intr_event_cb_t *intr_event_cb);
-
-
-
-#endif
+++ /dev/null
-/**
- * \file physio/pio.h
- */
-#ifndef _PHYSIO_PIO_H_
-#define _PHYSIO_PIO_H_
-
-#include <udi.h>
-#include <udi_physio.h>
-
-
-typedef _udi_handle_t udi_pio_handle_t;
-/* Null handle value for udi_pio_handle_t */
-#define UDI_NULL_PIO_HANDLE _NULL_HANDLE
-
-#endif
+++ /dev/null
-/**
- * \file udi.h
- */
-#ifndef _UDI_H_
-#define _UDI_H_
-
-// Use the core acess file to use the specific size types (plus va_arg)
-#include <acess.h>
-
-#include "udi/arch/x86.h"
-
-/**
- * \name Values and Flags for udi_status_t
- * \{
- */
-#define UDI_STATUS_CODE_MASK 0x0000FFFF
-#define UDI_STAT_META_SPECIFIC 0x00008000
-#define UDI_SPECIFIC_STATUS_MASK 0x00007FFF
-#define UDI_CORRELATE_OFFSET 16
-#define UDI_CORRELATE_MASK 0xFFFF0000
-/* Common Status Values */
-#define UDI_OK 0
-#define UDI_STAT_NOT_SUPPORTED 1
-#define UDI_STAT_NOT_UNDERSTOOD 2
-#define UDI_STAT_INVALID_STATE 3
-#define UDI_STAT_MISTAKEN_IDENTITY 4
-#define UDI_STAT_ABORTED 5
-#define UDI_STAT_TIMEOUT 6
-#define UDI_STAT_BUSY 7
-#define UDI_STAT_RESOURCE_UNAVAIL 8
-#define UDI_STAT_HW_PROBLEM 9
-#define UDI_STAT_NOT_RESPONDING 10
-#define UDI_STAT_DATA_UNDERRUN 11
-#define UDI_STAT_DATA_OVERRUN 12
-#define UDI_STAT_DATA_ERROR 13
-#define UDI_STAT_PARENT_DRV_ERROR 14
-#define UDI_STAT_CANNOT_BIND 15
-#define UDI_STAT_CANNOT_BIND_EXCL 16
-#define UDI_STAT_TOO_MANY_PARENTS 17
-#define UDI_STAT_BAD_PARENT_TYPE 18
-#define UDI_STAT_TERMINATED 19
-#define UDI_STAT_ATTR_MISMATCH 20
-/**
- * \}
- */
-
-/**
- * \name Data Layout Specifiers
- * \{
- */
-typedef const udi_ubit8_t udi_layout_t;
-/* Specific-Length Layout Type Codes */
-#define UDI_DL_UBIT8_T 1
-#define UDI_DL_SBIT8_T 2
-#define UDI_DL_UBIT16_T 3
-#define UDI_DL_SBIT16_T 4
-#define UDI_DL_UBIT32_T 5
-#define UDI_DL_SBIT32_T 6
-#define UDI_DL_BOOLEAN_T 7
-#define UDI_DL_STATUS_T 8
-/* Abstract Element Layout Type Codes */
-#define UDI_DL_INDEX_T 20
-/* Opaque Handle Element Layout Type Codes */
-#define UDI_DL_CHANNEL_T 30
-#define UDI_DL_ORIGIN_T 32
-/* Indirect Element Layout Type Codes */
-#define UDI_DL_BUF 40
-#define UDI_DL_CB 41
-#define UDI_DL_INLINE_UNTYPED 42
-#define UDI_DL_INLINE_DRIVER_TYPED 43
-#define UDI_DL_MOVABLE_UNTYPED 44
-/* Nested Element Layout Type Codes */
-#define UDI_DL_INLINE_TYPED 50
-#define UDI_DL_MOVABLE_TYPED 51
-#define UDI_DL_ARRAY 52
-#define UDI_DL_END 0
-/**
- * \}
- */
-
-
-// === INCLUDE SUB-SECTIONS ===
-#include "udi/cb.h" // Control Blocks
-#include "udi/log.h" // Logging
-#include "udi/attr.h" // Attributes
-#include "udi/strmem.h" // String/Memory
-#include "udi/buf.h" // Buffers
-#include "udi/mem.h" // Memory Management
-#include "udi/imc.h" // Inter-module Communication
-#include "udi/meta_mgmt.h" // Management Metalanguage
-#include "udi/meta_gio.h" // General IO Metalanguage
-#include "udi/init.h" // Init
-
-#endif
+++ /dev/null
-
-#ifndef _UDI_ARCH_x86_H_
-#define _UDI_ARCH_x86_H_
-
-typedef Sint8 udi_sbit8_t; /* signed 8-bit: -2^7..2^7-1 */
-typedef Sint16 udi_sbit16_t; /* signed 16-bit: -2^15..2^15-1 */
-typedef Sint32 udi_sbit32_t; /* signed 32-bit: -2^31..2^31-1 */
-typedef Uint8 udi_ubit8_t; /* unsigned 8-bit: 0..28-1 */
-typedef Uint16 udi_ubit16_t; /* unsigned 16-bit: 0..216-1 */
-typedef Uint32 udi_ubit32_t; /* unsigned 32-bit: 0..232-1 */
-
-typedef udi_ubit8_t udi_boolean_t; /* 0=False; 1..28-1=True */
-#define FALSE 0
-#define TRUE 1
-
-typedef size_t udi_size_t; /* buffer size */
-typedef size_t udi_index_t; /* zero-based index type */
-
-typedef void *_udi_handle_t;
-#define _NULL_HANDLE NULL
-
-/* Channel Handle */
-typedef _udi_handle_t *udi_channel_t;
-#define UDI_NULL_CHANNEL _NULL_HANDLE
-
-/**
- * \brief Buffer Path
- */
-typedef _udi_handle_t udi_buf_path_t;
-#define UDI_NULL_BUF_PATH _NULL_HANDLE
-
-typedef _udi_handle_t udi_origin_t;
-#define UDI_NULL_ORIGIN _NULL_HANDLE
-
-typedef Sint64 udi_timestamp_t;
-
-#define UDI_HANDLE_IS_NULL(handle, handle_type) (handle == NULL)
-#define UDI_HANDLE_ID(handle, handle_type) ((Uint32)handle)
-
-/**
- * \name va_arg wrapper
- * \{
- */
-#define UDI_VA_ARG(pvar, type, va_code) va_arg(pvar,type)
-#define UDI_VA_UBIT8_T
-#define UDI_VA_SBIT8_T
-#define UDI_VA_UBIT16_T
-#define UDI_VA_SBIT16_T
-#define UDI_VA_UBIT32_T
-#define UDI_VA_SBIT32_T
-#define UDI_VA_BOOLEAN_T
-#define UDI_VA_INDEX_T
-#define UDI_VA_SIZE_T
-#define UDI_VA_STATUS_T
-#define UDI_VA_CHANNEL_T
-#define UDI_VA_ORIGIN_T
-#define UDI_VA_POINTER
-/**
- * \}
- */
-
-/**
- * \brief Status Type
- */
-typedef udi_ubit32_t udi_status_t;
-
-#endif
+++ /dev/null
-/**
- * \file udi_attr.h
- */
-#ifndef _UDI_ATTR_H_
-#define _UDI_ATTR_H_
-
-typedef struct udi_instance_attr_list_s udi_instance_attr_list_t;
-typedef udi_ubit8_t udi_instance_attr_type_t;
-
-/* Instance attribute limits */
-#define UDI_MAX_ATTR_NAMELEN 32
-#define UDI_MAX_ATTR_SIZE 64
-
-/**
- * \brief Instance Attribute
- */
-struct udi_instance_attr_list_s
-{
- char attr_name[UDI_MAX_ATTR_NAMELEN];
- udi_ubit8_t attr_value[UDI_MAX_ATTR_SIZE];
- udi_ubit8_t attr_length;
- udi_instance_attr_type_t attr_type;
-};
-
-
-/**
- * \brief Instance Attribute Types
- * \see ::udi_instance_attr_type_t
- */
-enum
-{
- UDI_ATTR_NONE,
- UDI_ATTR_STRING,
- UDI_ATTR_ARRAY8,
- UDI_ATTR_UBIT32,
- UDI_ATTR_BOOLEAN,
- UDI_ATTR_FILE
-};
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_buf.h
- */
-#ifndef _UDI_BUF_H_
-#define _UDI_BUF_H_
-
-
-typedef struct udi_buf_s udi_buf_t;
-typedef struct udi_xfer_constraints_s udi_xfer_constraints_t;
-typedef void udi_buf_copy_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf);
-typedef void udi_buf_write_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf);
-
-/**
- * \brief Describes a buffer
- * \note Semi-Opaque
- */
-struct udi_buf_s
-{
- udi_size_t buf_size;
- Uint8 Data[]; //!< ENVIRONMENT ONLY
-};
-
-/**
- * \brief
- */
-struct udi_xfer_constraints_s
-{
- udi_ubit32_t udi_xfer_max;
- udi_ubit32_t udi_xfer_typical;
- udi_ubit32_t udi_xfer_granularity;
- udi_boolean_t udi_xfer_one_piece;
- udi_boolean_t udi_xfer_exact_size;
- udi_boolean_t udi_xfer_no_reorder;
-};
-
-// --- MACROS ---
-/**
- * \brief Allocates a buffer
- */
-#define UDI_BUF_ALLOC(callback, gcb, init_data, size, path_handle) \
- udi_buf_write(callback, gcb, init_data, size, NULL, 0, 0, path_handle)
-
-/**
- * \brief Inserts data into a buffer
- */
-#define UDI_BUF_INSERT(callback, gcb, new_data, size, dst_buf, dst_off) \
- udi_buf_write(callback, gcb, new_data, size, dst_buf, dst_off, 0, UDI_NULL_BUF_PATH)
-
-/**
- * \brief Removes data from a buffer (data afterwards will be moved forewards)
- */
-#define UDI_BUF_DELETE(callback, gcb, size, dst_buf, dst_off) \
- udi_buf_write(callback, gcb, NULL, 0, dst_buf, dst_off, size, UDI_NULL_BUF_PATH)
-
-/**
- * \brief Duplicates \a src_buf
- */
-#define UDI_BUF_DUP(callback, gcb, src_buf, path_handle) \
- udi_buf_copy(callback, gcb, src_buf, 0, (src_buf)->buf_size, NULL, 0, 0, path_handle)
-
-
-/**
- * \brief Copies data from one buffer to another
- */
-extern void udi_buf_copy(
- udi_buf_copy_call_t *callback,
- udi_cb_t *gcb,
- udi_buf_t *src_buf,
- udi_size_t src_off,
- udi_size_t src_len,
- udi_buf_t *dst_buf,
- udi_size_t dst_off,
- udi_size_t dst_len,
- udi_buf_path_t path_handle );
-
-/**
- * \brief Copies data from driver space to a buffer
- */
-extern void udi_buf_write(
- udi_buf_write_call_t *callback,
- udi_cb_t *gcb,
- const void *src_mem,
- udi_size_t src_len,
- udi_buf_t *dst_buf,
- udi_size_t dst_off,
- udi_size_t dst_len,
- udi_buf_path_t path_handle
- );
-
-/**
- * \brief Reads data from a buffer into driver space
- */
-extern void udi_buf_read(
- udi_buf_t *src_buf,
- udi_size_t src_off,
- udi_size_t src_len,
- void *dst_mem );
-
-/**
- * \brief Frees a buffer
- */
-extern void udi_buf_free(udi_buf_t *buf);
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_cb.h
- */
-#ifndef _UDI_CB_H_
-#define _UDI_CB_H_
-
-typedef struct udi_cb_s udi_cb_t;
-typedef void udi_cb_alloc_call_t(udi_cb_t *gcb, udi_cb_t *new_cb);
-typedef void udi_cb_alloc_batch_call_t(udi_cb_t *gcb, udi_cb_t *first_new_cb);
-typedef void udi_cancel_call_t(udi_cb_t *gcb);
-
-#define UDI_GCB(mcb) (&(mcb)->gcb)
-#define UDI_MCB(gcb, cb_type) ((cb_type *)(gcb))
-
-/**
- * \brief Describes a generic control block
- * \note Semi-opaque
- */
-struct udi_cb_s
-{
- /**
- * \brief Channel associated with the control block
- */
- udi_channel_t channel;
- /**
- * \brief Current state
- * \note Driver changable
- */
- void *context;
- /**
- * \brief CB's scratch area
- */
- void *scratch;
- /**
- * \brief ???
- */
- void *initiator_context;
- /**
- * \brief Request Handle?
- */
- udi_origin_t origin;
-};
-
-extern void udi_cb_alloc (
- udi_cb_alloc_call_t *callback,
- udi_cb_t *gcb,
- udi_index_t cb_idx,
- udi_channel_t default_channel
- );
-
-extern void udi_cb_alloc_dynamic(
- udi_cb_alloc_call_t *callback,
- udi_cb_t *gcb,
- udi_index_t cb_idx,
- udi_channel_t default_channel,
- udi_size_t inline_size,
- udi_layout_t *inline_layout
- );
-
-extern void udi_cb_alloc_batch(
- udi_cb_alloc_batch_call_t *callback,
- udi_cb_t *gcb,
- udi_index_t cb_idx,
- udi_index_t count,
- udi_boolean_t with_buf,
- udi_size_t buf_size,
- udi_buf_path_t path_handle
- );
-
-extern void udi_cb_free(udi_cb_t *cb);
-
-extern void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb);
-
-
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_imc.h
- * \brief Inter-Module Communication
- */
-#ifndef _UDI_IMC_H_
-#define _UDI_IMC_H_
-
-typedef void udi_channel_anchor_call_t(udi_cb_t *gcb, udi_channel_t anchored_channel);
-typedef void udi_channel_spawn_call_t(udi_cb_t *gcb, udi_channel_t new_channel);
-
-typedef struct udi_channel_event_cb_s udi_channel_event_cb_t;
-
-typedef void udi_channel_event_ind_op_t(udi_channel_event_cb_t *cb);
-
-/**
- * \brief Anchors a channel end to the current region
- */
-extern void udi_channel_anchor(
- udi_channel_anchor_call_t *callback, udi_cb_t *gcb,
- udi_channel_t channel, udi_index_t ops_idx, void *channel_context
- );
-
-/**
- * \brief Created a new channel between two regions
- */
-extern void udi_channel_spawn(
- udi_channel_spawn_call_t *callback,
- udi_cb_t *gcb,
- udi_channel_t channel,
- udi_index_t spawn_idx,
- udi_index_t ops_idx,
- void *channel_context
- );
-
-/**
- * \brief Attaches a new context pointer to the current channel
- */
-extern void udi_channel_set_context(
- udi_channel_t target_channel,
- void *channel_context
- );
-/**
- * \brief
- */
-extern void udi_channel_op_abort(
- udi_channel_t target_channel,
- udi_cb_t *orig_cb
- );
-
-/**
- * \brief Closes an open channel
- */
-extern void udi_channel_close(udi_channel_t channel);
-
-/**
- * \brief Describes a channel event
- */
-struct udi_channel_event_cb_s
-{
- udi_cb_t gcb;
- udi_ubit8_t event;
- union {
- struct {
- udi_cb_t *bind_cb;
- } internal_bound;
- struct {
- udi_cb_t *bind_cb;
- udi_ubit8_t parent_ID;
- udi_buf_path_t *path_handles;
- } parent_bound;
- udi_cb_t *orig_cb;
- } params;
-};
-/* Channel event types */
-#define UDI_CHANNEL_CLOSED 0
-#define UDI_CHANNEL_BOUND 1
-#define UDI_CHANNEL_OP_ABORTED 2
-
-/**
- * \brief Proxy function
- */
-extern void udi_channel_event_ind(udi_channel_event_cb_t *cb);
-
-/**
- * \brief Called when channel event is completed
- */
-extern void udi_channel_event_complete(
- udi_channel_event_cb_t *cb, udi_status_t status
- );
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_init.h
- */
-#ifndef _UDI_INIT_H_
-#define _UDI_INIT_H_
-
-typedef struct udi_init_s udi_init_t;
-typedef struct udi_primary_init_s udi_primary_init_t;
-typedef struct udi_secondary_init_s udi_secondary_init_t;
-typedef struct udi_ops_init_s udi_ops_init_t;
-typedef struct udi_cb_init_s udi_cb_init_t;
-typedef struct udi_cb_select_s udi_cb_select_t;
-typedef struct udi_gcb_init_s udi_gcb_init_t;
-
-typedef struct udi_init_context_s udi_init_context_t;
-typedef struct udi_limits_s udi_limits_t;
-typedef struct udi_chan_context_s udi_chan_context_t;
-typedef struct udi_child_chan_context_s udi_child_chan_context_t;
-
-typedef void udi_op_t(void);
-typedef udi_op_t * const udi_ops_vector_t;
-
-/**
- * \brief UDI Initialisation Structure
- *
- * Defines how to initialise and use a UDI driver
- */
-struct udi_init_s
-{
- /**
- * \brief Defines the primary region
- * \note For secondary modules this must be NULL
- */
- udi_primary_init_t *primary_init_info;
-
- /**
- * \brief Defines all secondary regions
- * Pointer to a list (so, essentially an array) of ::udi_secondary_init_t
- * It is terminated by an entry with ::udi_secondary_init_t.region_idx
- * set to zero.
- * \note If NULL, it is to be treated as an empty list
- */
- udi_secondary_init_t *secondary_init_list;
-
- /**
- * \brief Channel operations
- * Pointer to a ::udi_ops_init_t.ops_idx == 0 terminated list that
- * defines the channel opterations usage for each ops vector implemented
- * in this module.
- * \note Must contain at least one entry for each metalanguage used
- */
- udi_ops_init_t *ops_init_list;
-
- /**
- * \brief Control Blocks
- */
- udi_cb_init_t *cb_init_list;
-
- /**
- * \brief Generic Control Blocks
- */
- udi_gcb_init_t *gcb_init_list;
-
- /**
- * \brief Overrides for control blocks
- * Allows a control block to override the ammount of scratch space it
- * gets for a specific ops vector.
- */
- udi_cb_select_t *cb_select_list;
-};
-
-
-/**
- * \name Flags for ::udi_primary_init_t.mgmt_op_flags
- * \{
- */
-
-/**
- * \brief Tells the environment that this operation may take some time
- * Used as a hint in scheduling tasks
- */
-#define UDI_OP_LONG_EXEC 0x01
-
-/**
- * \}
- */
-
-/**
- * \brief Describes the Primary Region
- * Tells the environment how to set up the driver's primary region.
- */
-struct udi_primary_init_s
-{
- /**
- * \brief Management Ops Vector
- * Pointer to a list of functions for the Management Metalanguage
- */
- udi_mgmt_ops_t *mgmt_ops;
-
- /**
- * \brief Flags for \a mgmt_ops
- * Each entry in \a mgmt_ops is acommanied by an entry in this array.
- * Each entry contains the flags that apply to the specified ops vector.
- * \see UDI_OP_LONG_EXEC
- */
- const udi_ubit8_t *mgmt_op_flags;
-
- /**
- * \brief Scratch space size
- * Specifies the number of bytes to allocate for each control block
- * passed by the environment.
- * \note must not exceed ::UDI_MAX_SCRATCH
- */
- udi_size_t mgmt_scratch_requirement;
-
- /**
- * \todo What is this?
- */
- udi_ubit8_t enumeration_attr_list_length;
-
- /**
- * \brief Size in bytes to allocate to each instance of the primary
- * region
- * Essentially the size of the driver's instance state
- * \note Must be at least sizeof(udi_init_context_t) and not more
- * than UDI_MIN_ALLOC_LIMIT
- */
- udi_size_t rdata_size;
-
- /**
- * \brief Size in bytes to allocate for each call to ::udi_enumerate_req
- * \note Must not exceed UDI_MIN_ALLOC_LIMIT
- */
- udi_size_t child_data_size;
-
- /**
- * \brief Number of path handles for each parent bound to this driver
- * \todo What the hell are path handles?
- */
- udi_ubit8_t per_parent_paths;
-};
-
-/**
- * \brief Tells the environment how to create a secondary region
- */
-struct udi_secondary_init_s
-{
- /**
- * \brief Region Index
- * Non-zero driver-dependent index value that identifies the region
- * \note This corresponds to a "region" declaration in the udiprops.txt
- * file.
- */
- udi_index_t region_idx;
- /**
- * \brief Number of bytes to allocate
- *
- * \note Again, must be between sizeof(udi_init_context_t) and
- * UDI_MIN_ALLOC_LIMIT
- */
- udi_size_t rdata_size;
-};
-
-/**
- * \brief Defines channel endpoints (ways of communicating with the driver)
- *
- */
-struct udi_ops_init_s
-{
- /**
- * \brief ops index number
- * Used to uniquely this entry
- * \note If this is zero, it marks the end of the list
- */
- udi_index_t ops_idx;
- /**
- * \brief Metalanguage Index
- * Defines what metalanguage is used
- */
- udi_index_t meta_idx;
- /**
- * \brief Metalanguage Operation
- * Defines what metalanguage operation is used
- */
- udi_index_t meta_ops_num;
- /**
- * \brief Size of the context area
- * \note If non-zero, must be at least
- */
- udi_size_t chan_context_size;
- /**
- * \brief Pointer to the operations
- * Pointer to a <<meta>>_<<role>>_ops_t structure
- */
- udi_ops_vector_t *ops_vector;
- /**
- * \brief Flags for each entry in \a ops_vector
- */
- //const udi_ubit8_t *op_flags;
-};
-
-/**
- * \brief Defines control blocks
- * Much the same as ::udi_ops_init_t
- */
-struct udi_cb_init_s
-{
- udi_index_t cb_idx;
- udi_index_t meta_idx;
- udi_index_t meta_cb_num;
- udi_size_t scratch_requirement;
- /**
- * \brief Size of inline memory
- */
- udi_size_t inline_size;
- /**
- * \brief Layout of inline memory
- */
- udi_layout_t *inline_layout;
-};
-
-/**
- * \brief Overrides the scratch size for an operation
- */
-struct udi_cb_select_s
-{
- udi_index_t ops_idx;
- udi_index_t cb_idx;
-};
-
-/**
- * \brief General Control Blocks
- * These control blocks can only be used as general data storage, not
- * for any channel operations.
- */
-struct udi_gcb_init_s
-{
- udi_index_t cb_idx;
- udi_size_t scratch_requirement;
-};
-
-
-// ===
-// ===
-/**
- * \brief Environement Imposed Limits
- */
-struct udi_limits_s
-{
- /**
- * \brief Maximum legal ammount of memory that can be allocated
- */
- udi_size_t max_legal_alloc;
-
- /**
- * \brief Maximum ammount of guaranteed memory
- */
- udi_size_t max_safe_alloc;
- /**
- * \brief Maximum size of the final string from ::udi_trace_write
- * or ::udi_log_write
- */
- udi_size_t max_trace_log_formatted_len;
- /**
- * \brief Maximum legal size of an instanct attribute value
- */
- udi_size_t max_instance_attr_len;
- /**
- * \brief Minumum time difference (in nanoseconds between unique values
- * returned by ::udi_time_current
- */
- udi_ubit32_t min_curtime_res;
- /**
- * \brief Minimum resolution of timers
- * \see ::udi_timer_start_repeating, ::udi_timer_start
- */
- udi_ubit32_t min_timer_res;
-} PACKED;
-
-/**
- * \brief Primary Region Context data
- */
-struct udi_init_context_s
-{
- udi_index_t region_idx;
- udi_limits_t limits;
-};
-
-/**
- * \brief Channel context data
- */
-struct udi_chan_context_s
-{
- /**
- * \brief Pointer to the driver instance's initial region data
- */
- void *rdata;
-} PACKED;
-
-/**
- * \brief Child Channel context
- */
-struct udi_child_chan_context_s
-{
- /**
- * \brief Pointer to the driver instance's initial region data
- */
- void *rdata;
- /**
- * \brief Some sort of unique ID number
- */
- udi_ubit32_t child_ID;
-};
-
-#endif
+++ /dev/null
-/**
- * \file udi_log.h
- */
-#ifndef _UDI_LOG_H_
-#define _UDI_LOG_H_
-
-/**
- * \brief Trace Event
- */
-typedef udi_ubit32_t udi_trevent_t;
-
-/**
- * \brief Log Callback
- */
-typedef void udi_log_write_call_t(udi_cb_t *gcb, udi_status_t correlated_status);
-
-/**
- * \name Log Severities
- * \brief Values for severity
- * \{
- */
-#define UDI_LOG_DISASTER 1
-#define UDI_LOG_ERROR 2
-#define UDI_LOG_WARNING 3
-#define UDI_LOG_INFORMATION 4
-/**
- * \}
- */
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_mem.h
- */
-#ifndef _UDI_MEM_H_
-#define _UDI_MEM_H_
-
-/**
- * \brief Callback type for ::udi_mem_alloc
- */
-typedef void udi_mem_alloc_call_t(udi_cb_t *gcb, void *new_mem);
-
-/**
- * \brief Allocate memory
- */
-extern void udi_mem_alloc(
- udi_mem_alloc_call_t *callback,
- udi_cb_t *gcb,
- udi_size_t size,
- udi_ubit8_t flags
- );
-
-/**
- * \brief Values for ::udi_mem_alloc \a flags
- * \{
- */
-#define UDI_MEM_NOZERO (1U<<0) //!< No need to zero the memory
-#define UDI_MEM_MOVABLE (1U<<1) //!< Globally accessable memory?
-/**
- * \}
- */
-
-/**
- * \brief Free allocated memory
- */
-extern void udi_mem_free(void *target_mem);
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_meta_gio.h
- */
-#ifndef _UDI_META_GIO_H_
-#define _UDI_META_GIO_H_
-
-typedef const struct udi_gio_provider_ops_s udi_gio_provider_ops_t;
-typedef const struct udi_gio_client_ops_s udi_gio_client_ops_t;
-typedef struct udi_gio_bind_cb_s udi_gio_bind_cb_t;
-typedef struct udi_gio_xfer_cb_s udi_gio_xfer_cb_t;
-typedef struct udi_gio_rw_params_s udi_gio_rw_params_t;
-typedef struct udi_gio_event_cb_s udi_gio_event_cb_t;
-
-typedef void udi_gio_bind_req_op_t(udi_gio_bind_cb_t *cb);
-typedef void udi_gio_unbind_req_op_t(udi_gio_bind_cb_t *cb);
-typedef void udi_gio_xfer_req_op_t(udi_gio_bind_cb_t *cb);
-typedef void udi_gio_event_res_op_t(udi_gio_bind_cb_t *cb);
-
-typedef void udi_gio_bind_ack_op_t(
- udi_gio_bind_cb_t *cb,
- udi_ubit32_t device_size_lo,
- udi_ubit32_t device_size_hi,
- udi_status_t status
- );
-typedef void udi_gio_unbind_ack_op_t(udi_gio_bind_cb_t *cb);
-typedef void udi_gio_xfer_ack_op_t(udi_gio_bind_cb_t *cb);
-typedef void udi_gio_xfer_nak_op_t(udi_gio_bind_cb_t *cb, udi_status_t status);
-typedef void udi_gio_event_ind_op_t(udi_gio_bind_cb_t *cb);
-
-typedef udi_ubit8_t udi_gio_op_t;
-/* Limit values for udi_gio_op_t */
-#define UDI_GIO_OP_CUSTOM 16
-#define UDI_GIO_OP_MAX 64
-/* Direction flag values for op */
-#define UDI_GIO_DIR_READ (1U<<6)
-#define UDI_GIO_DIR_WRITE (1U<<7)
-/* Standard Operation Codes */
-#define UDI_GIO_OP_READ UDI_GIO_DIR_READ
-#define UDI_GIO_OP_WRITE UDI_GIO_DIR_WRITE
-
-
-
-struct udi_gio_provider_ops_s
-{
- udi_channel_event_ind_op_t *channel_event_ind_op;
- udi_gio_bind_req_op_t *gio_bind_req_op;
- udi_gio_unbind_req_op_t *gio_unbind_req_op;
- udi_gio_xfer_req_op_t *gio_xfer_req_op;
- udi_gio_event_res_op_t *gio_event_res_op;
-};
-/* Ops Vector Number */
-#define UDI_GIO_PROVIDER_OPS_NUM 1
-
-struct udi_gio_client_ops_s
-{
- udi_channel_event_ind_op_t *channel_event_ind_op;
- udi_gio_bind_ack_op_t *gio_bind_ack_op;
- udi_gio_unbind_ack_op_t *gio_unbind_ack_op;
- udi_gio_xfer_ack_op_t *gio_xfer_ack_op;
- udi_gio_xfer_nak_op_t *gio_xfer_nak_op;
- udi_gio_event_ind_op_t *gio_event_ind_op;
-};
-/* Ops Vector Number */
-#define UDI_GIO_CLIENT_OPS_NUM 2
-
-struct udi_gio_bind_cb_s
-{
- udi_cb_t gcb;
- udi_xfer_constraints_t xfer_constraints;
-};
-/* Control Block Group Number */
-#define UDI_GIO_BIND_CB_NUM 1
-
-
-struct udi_gio_xfer_cb_s
-{
- udi_cb_t gcb;
- udi_gio_op_t op;
- void *tr_params;
- udi_buf_t *data_buf;
-};
-/* Control Block Group Number */
-#define UDI_GIO_XFER_CB_NUM 2
-
-struct udi_gio_rw_params_s
-{
- udi_ubit32_t offset_lo;
- udi_ubit32_t offset_hi;
-};
-
-struct udi_gio_event_cb_s
-{
- udi_cb_t gcb;
- udi_ubit8_t event_code;
- void *event_params;
-};
-/* Control Block Group Number */
-#define UDI_GIO_EVENT_CB_NUM 3
-
-
-extern void udi_gio_bind_req(udi_gio_bind_cb_t *cb);
-extern void udi_gio_bind_ack(
- udi_gio_bind_cb_t *cb,
- udi_ubit32_t device_size_lo,
- udi_ubit32_t device_size_hi,
- udi_status_t status
- );
-
-extern void udi_gio_unbind_req(udi_gio_bind_cb_t *cb);
-extern void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb);
-
-extern void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb);
-extern void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb);
-extern void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status);
-
-extern void udi_gio_event_res(udi_gio_event_cb_t *cb);
-extern void udi_gio_event_ind(udi_gio_event_cb_t *cb);
-extern void udi_gio_event_res_unused(udi_gio_event_cb_t *cb);
-
-#endif
+++ /dev/null
-/**
- * \file udi_meta_mgmt.h
- */
-#ifndef _UDI_META_MGMT_H_
-#define _UDI_META_MGMT_H_
-
-typedef struct udi_mgmt_ops_s udi_mgmt_ops_t;
-typedef struct udi_mgmt_cb_s udi_mgmt_cb_t;
-typedef struct udi_usage_cb_s udi_usage_cb_t;
-typedef struct udi_filter_element_s udi_filter_element_t;
-typedef struct udi_enumerate_cb_s udi_enumerate_cb_t;
-
-/**
- * \name Specify Usage
- * \{
- */
-typedef void udi_usage_ind_op_t(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
-/* Values for resource_level */
-#define UDI_RESOURCES_CRITICAL 1
-#define UDI_RESOURCES_LOW 2
-#define UDI_RESOURCES_NORMAL 3
-#define UDI_RESOURCES_PLENTIFUL 4
-/* Proxy */
-extern void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
-/**
- * \}
- */
-
-typedef void udi_usage_res_op_t(udi_usage_cb_t *cb);
-
-/**
- * \name Enumerate this driver
- * \{
- */
-typedef void udi_enumerate_req_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level);
-/* Values for enumeration_level */
-#define UDI_ENUMERATE_START 1
-#define UDI_ENUMERATE_START_RESCAN 2
-#define UDI_ENUMERATE_NEXT 3
-#define UDI_ENUMERATE_NEW 4
-#define UDI_ENUMERATE_DIRECTED 5
-#define UDI_ENUMERATE_RELEASE 6
-/* Proxy */
-extern void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level);
-/**
- * \}
- */
-
-/**
- * \name Enumeration Acknowlagement
- * \{
- */
-typedef void udi_enumerate_ack_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_result, udi_index_t ops_idx);
-/* Values for enumeration_result */
-#define UDI_ENUMERATE_OK 0
-#define UDI_ENUMERATE_LEAF 1
-#define UDI_ENUMERATE_DONE 2
-#define UDI_ENUMERATE_RESCAN 3
-#define UDI_ENUMERATE_REMOVED 4
-#define UDI_ENUMERATE_REMOVED_SELF 5
-#define UDI_ENUMERATE_RELEASED 6
-#define UDI_ENUMERATE_FAILED 255
-/**
- * \}
- */
-
-/**
- * \name
- * \{
- */
-typedef void udi_devmgmt_req_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID);
-
-typedef void udi_devmgmt_ack_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status);
-/**
- * \}
- */
-typedef void udi_final_cleanup_req_op_t(udi_mgmt_cb_t *cb);
-typedef void udi_final_cleanup_ack_op_t(udi_mgmt_cb_t *cb);
-
-
-
-
-
-struct udi_mgmt_ops_s
-{
- udi_usage_ind_op_t *usage_ind_op;
- udi_enumerate_req_op_t *enumerate_req_op;
- udi_devmgmt_req_op_t *devmgmt_req_op;
- udi_final_cleanup_req_op_t *final_cleanup_req_op;
-};
-
-struct udi_mgmt_cb_s
-{
- udi_cb_t gcb;
-};
-
-struct udi_usage_cb_s
-{
- udi_cb_t gcb;
- udi_trevent_t trace_mask;
- udi_index_t meta_idx;
-};
-
-
-struct udi_filter_element_s
-{
- char attr_name[UDI_MAX_ATTR_NAMELEN];
- udi_ubit8_t attr_min[UDI_MAX_ATTR_SIZE];
- udi_ubit8_t attr_min_len;
- udi_ubit8_t attr_max[UDI_MAX_ATTR_SIZE];
- udi_ubit8_t attr_max_len;
- udi_instance_attr_type_t attr_type;
- udi_ubit32_t attr_stride;
-};
-struct udi_enumerate_cb_s
-{
- udi_cb_t gcb;
- udi_ubit32_t child_ID;
- void *child_data;
- udi_instance_attr_list_t *attr_list;
- udi_ubit8_t attr_valid_length;
- const udi_filter_element_t *filter_list;
- udi_ubit8_t filter_list_length;
- udi_ubit8_t parent_ID;
-};
-/* Special parent_ID filter values */
-#define UDI_ANY_PARENT_ID 0
-
-/**
- * \brief
- */
-extern void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID );
-/**
- * \brief Values for ::udi_devmgmt_req \a mgmt_op
- */
-enum eDMGMT
-{
- UDI_DMGMT_PREPARE_TO_SUSPEND = 1,
- UDI_DMGMT_SUSPEND,
- UDI_DMGMT_SHUTDOWN,
- UDI_DMGMT_PARENT_SUSPENDED,
- UDI_DMGMT_RESUME,
- UDI_DMGMT_UNBIND
-};
-
-extern void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status);
-//!\brief Values for flags
-#define UDI_DMGMT_NONTRANSPARENT (1U<<0)
-//!\brief Meta-Specific Status Codes
-#define UDI_DMGMT_STAT_ROUTING_CHANGE (UDI_STAT_META_SPECIFIC|1)
-
-extern void udi_final_cleanup_req(udi_mgmt_cb_t *cb);
-extern void udi_final_cleanup_ack(udi_mgmt_cb_t *cb);
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_strmem.h
- */
-#ifndef _UDI_STRMEM_H_
-#define _UDI_STRMEM_H_
-
-/**
- * \brief Gets the length of a C style string
- */
-extern udi_size_t udi_strlen(const char *s);
-
-/**
- * \brief Appends to a string
- */
-extern char *udi_strcat(char *s1, const char *s2);
-extern char *udi_strncat(char *s1, const char *s2, udi_size_t n);
-
-/**
- * \brief Compares Strings/Memory
- */
-extern udi_sbit8_t udi_strcmp(const char *s1, const char *s2);
-extern udi_sbit8_t udi_strncmp(const char *s1, const char *s2, udi_size_t n);
-extern udi_sbit8_t udi_memcmp(const void *s1, const void *s2, udi_size_t n);
-
-extern char *udi_strcpy(char *s1, const char *s2);
-extern char *udi_strncpy(char *s1, const char *s2, udi_size_t n);
-extern void *udi_memcpy(void *s1, const void *s2, udi_size_t n);
-extern void *udi_memmove(void *s1, const void *s2, udi_size_t n);
-
-extern char *udi_strncpy_rtrim(char *s1, const char *s2, udi_size_t n);
-
-extern char *udi_strchr(const char *s, char c);
-extern char *udi_strrchr(const char *s, char c);
-extern void *udi_memchr (const void *s, udi_ubit8_t c, udi_size_t n);
-
-extern void *udi_memset(void *s, udi_ubit8_t c, udi_size_t n);
-extern udi_ubit32_t udi_strtou32(const char *s, char **endptr, int base);
-
-
-extern udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...);
-
-
-
-#endif
+++ /dev/null
-/**
- * \file udi_physio.h
- */
-#ifndef _UDI_PHYSIO_H_
-#define _UDI_PHYSIO_H_
-
-#include <udi.h>
-
-// === TYPEDEFS ===
-// DMA Core
-typedef _udi_handle_t udi_dma_handle_t;
-#define UDI_NULL_DMA_HANDLE _NULL_HANDLE
-typedef Uint64 udi_busaddr64_t; //!< \note Opaque
-typedef struct udi_scgth_element_32_s udi_scgth_element_32_t;
-typedef struct udi_scgth_element_64_s udi_scgth_element_64_t;
-typedef struct udi_scgth_s udi_scgth_t;
-typedef _udi_handle_t udi_dma_constraints_t;
-#define UDI_NULL_DMA_CONSTRAINTS _NULL_HANDLE
-/**
- * \name DMA constraints attributes
- * \{
- */
-typedef udi_ubit8_t udi_dma_constraints_attr_t;
-/* DMA Convenience Attribute Codes */
-#define UDI_DMA_ADDRESSABLE_BITS 100
-#define UDI_DMA_ALIGNMENT_BITS 101
-/* DMA Constraints on the Entire Transfer */
-#define UDI_DMA_DATA_ADDRESSABLE_BITS 110
-#define UDI_DMA_NO_PARTIAL 111
-/* DMA Constraints on the Scatter/Gather List */
-#define UDI_DMA_SCGTH_MAX_ELEMENTS 120
-#define UDI_DMA_SCGTH_FORMAT 121
-#define UDI_DMA_SCGTH_ENDIANNESS 122
-#define UDI_DMA_SCGTH_ADDRESSABLE_BITS 123
-#define UDI_DMA_SCGTH_MAX_SEGMENTS 124
-/* DMA Constraints on Scatter/Gather Segments */
-#define UDI_DMA_SCGTH_ALIGNMENT_BITS 130
-#define UDI_DMA_SCGTH_MAX_EL_PER_SEG 131
-#define UDI_DMA_SCGTH_PREFIX_BYTES 132
-/* DMA Constraints on Scatter/Gather Elements */
-#define UDI_DMA_ELEMENT_ALIGNMENT_BITS 140
-#define UDI_DMA_ELEMENT_LENGTH_BITS 141
-#define UDI_DMA_ELEMENT_GRANULARITY_BITS 142
-/* DMA Constraints for Special Addressing */
-#define UDI_DMA_ADDR_FIXED_BITS 150
-#define UDI_DMA_ADDR_FIXED_TYPE 151
-#define UDI_DMA_ADDR_FIXED_VALUE_LO 152
-#define UDI_DMA_ADDR_FIXED_VALUE_HI 153
-/* DMA Constraints on DMA Access Behavior */
-#define UDI_DMA_SEQUENTIAL 160
-#define UDI_DMA_SLOP_IN_BITS 161
-#define UDI_DMA_SLOP_OUT_BITS 162
-#define UDI_DMA_SLOP_OUT_EXTRA 163
-#define UDI_DMA_SLOP_BARRIER_BITS 164
-/* Values for UDI_DMA_SCGTH_ENDIANNESS */
-#define UDI_DMA_LITTLE_ENDIAN (1U<<6)
-#define UDI_DMA_BIG_ENDIAN (1U<<5)
-/* Values for UDI_DMA_ADDR_FIXED_TYPE */
-#define UDI_DMA_FIXED_ELEMENT 1
-/**
- * \}
- */
-// DMA Constraints Management
-typedef struct udi_dma_constraints_attr_spec_s udi_dma_constraints_attr_spec_t;
-typedef void udi_dma_constraints_attr_set_call_t(
- udi_cb_t *gcb, udi_dma_constraints_t new_constraints, udi_status_t status
- );
-typedef struct udi_dma_limits_s udi_dma_limits_t;
-
-
-// === STRUCTURES ===
-// --- DMA Constraints Management ---
-struct udi_dma_constraints_attr_spec_s
-{
- udi_dma_constraints_attr_t attr_type;
- udi_ubit32_t attr_value;
-};
-// --- DMA Core ---
-struct udi_dma_limits_s
-{
- udi_size_t max_legal_contig_alloc;
- udi_size_t max_safe_contig_alloc;
- udi_size_t cache_line_size;
-};
-struct udi_scgth_element_32_s
-{
- udi_ubit32_t block_busaddr;
- udi_ubit32_t block_length;
-};
-struct udi_scgth_element_64_s
-{
- udi_busaddr64_t block_busaddr;
- udi_ubit32_t block_length;
- udi_ubit32_t el_reserved;
-};
-/* Extension Flag */
-#define UDI_SCGTH_EXT 0x80000000
-struct udi_scgth_s
-{
- udi_ubit16_t scgth_num_elements;
- udi_ubit8_t scgth_format;
- udi_boolean_t scgth_must_swap;
- union {
- udi_scgth_element_32_t *el32p;
- udi_scgth_element_64_t *el64p;
- } scgth_elements;
- union {
- udi_scgth_element_32_t el32;
- udi_scgth_element_64_t el64;
- } scgth_first_segment;
-};
-/* Values for scgth_format */
-#define UDI_SCGTH_32 (1U<<0)
-#define UDI_SCGTH_64 (1U<<1)
-#define UDI_SCGTH_DMA_MAPPED (1U<<6)
-#define UDI_SCGTH_DRIVER_MAPPED (1U<<7)
-
-
-
-// === FUNCTIONS ===
-// --- DMA Constraints Management ---
-extern void udi_dma_constraints_attr_set(
- udi_dma_constraints_attr_set_call_t *callback,
- udi_cb_t *gcb,
- udi_dma_constraints_t src_constraints,
- const udi_dma_constraints_attr_spec_t *attr_list,
- udi_ubit16_t list_length,
- udi_ubit8_t flags
- );
-/* Constraints Flags */
-#define UDI_DMA_CONSTRAINTS_COPY (1U<<0)
-
-extern void udi_dma_constraints_attr_reset(
- udi_dma_constraints_t constraints,
- udi_dma_constraints_attr_t attr_type
- );
-
-extern void udi_dma_constraints_free(udi_dma_constraints_t constraints);
-
-#include <physio/meta_intr.h>
-#include <physio/meta_bus.h>
-
-
-#endif
+++ /dev/null
-/**
- * \file logging.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-
-// === PROTOTYPES ===
-
-// === CODE ===
-void udi_log_write( udi_log_write_call_t *callback, udi_cb_t *gcb,
- udi_trevent_t trace_event, udi_ubit8_t severity, udi_index_t meta_idx,
- udi_status_t original_status, udi_ubit32_t msgnum, ... )
-{
- Log("UDI Log");
-}
-
-EXPORT(udi_log_write);
+++ /dev/null
-/*
- * Acess2 UDI Layer
- */
-#define DEBUG 0
-#define VERSION ((0<<8)|1)
-#include <acess.h>
-#include <modules.h>
-#include <udi.h>
-
-// === PROTOTYPES ===
- int UDI_Install(char **Arguments);
- int UDI_DetectDriver(void *Base);
- int UDI_LoadDriver(void *Base);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, UDI, UDI_Install, NULL, NULL);
-tModuleLoader gUDI_Loader = {
- NULL, "UDI", UDI_DetectDriver, UDI_LoadDriver, NULL
-};
-
-// === CODE ===
-/**
- * \fn int UDI_Install(char **Arguments)
- * \brief Stub intialisation routine
- */
-int UDI_Install(char **Arguments)
-{
- Module_RegisterLoader( &gUDI_Loader );
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Detects if a driver should be loaded by the UDI subsystem
- */
-int UDI_DetectDriver(void *Base)
-{
- if( Binary_FindSymbol(Base, "udi_init_info", NULL) == 0) {
- return 0;
- }
-
- return 1;
-}
-
-/**
- * \fn int UDI_LoadDriver(void *Base)
- */
-int UDI_LoadDriver(void *Base)
-{
- udi_init_t *info;
- char *udiprops = NULL;
- int udiprops_size = 0;
- int i;
- // int j;
-
- Log_Debug("UDI", "UDI_LoadDriver: (Base=%p)", Base);
-
- if( Binary_FindSymbol(Base, "udi_init_info", (Uint*)&info) == 0) {
- Binary_Unload(Base);
- return 0;
- }
-
- if( Binary_FindSymbol(Base, "_udiprops", (Uint*)&udiprops) == 0 ) {
- Log_Warning("UDI", "_udiprops is not defined, this is usually bad");
- }
- else {
- Uint udiprops_end = 0;
- int i, j, nLines;
- char **udipropsptrs;
-
- if( Binary_FindSymbol(Base, "_udiprops_end", (Uint*)&udiprops_end) == 0)
- Log_Warning("UDI", "_udiprops_end is not defined");
- Log_Debug("UDI", "udiprops_end = %p", udiprops_end);
- udiprops_size = udiprops_end - (Uint)udiprops;
- Log_Debug("UDI", "udiprops = %p, udiprops_size = 0x%x", udiprops, udiprops_size);
-
- Debug_HexDump("UDI_LoadDriver", udiprops, udiprops_size);
-
- nLines = 1;
- for( i = 0; i < udiprops_size; i++ )
- {
- if( udiprops[i] == '\0' )
- nLines ++;
- }
-
- Log_Debug("UDI", "nLines = %i", nLines);
-
- udipropsptrs = malloc( sizeof(char*)*nLines );
- udipropsptrs[0] = udiprops;
- j = 0;
- for( i = 0; i < udiprops_size; i++ )
- {
- if( udiprops[i] == '\0' ) {
- //Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]);
- udipropsptrs[j++] = &udiprops[i+1];
- }
- }
- Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]);
- Log_Debug("UDI", "udiprops = \"%s\"", udiprops);
- }
-
-
- Log("primary_init_info = %p = {", info->primary_init_info);
- {
- Log(" .mgmt_ops = %p = {", info->primary_init_info->mgmt_ops);
- Log(" .usage_ind_op: %p() - 0x%02x",
- info->primary_init_info->mgmt_ops->usage_ind_op,
- info->primary_init_info->mgmt_op_flags[0]
- );
- Log(" .enumerate_req_op: %p() - 0x%02x",
- info->primary_init_info->mgmt_ops->enumerate_req_op,
- info->primary_init_info->mgmt_op_flags[1]
- );
- Log(" .devmgmt_req_op: %p() - 0x%02x",
- info->primary_init_info->mgmt_ops->devmgmt_req_op,
- info->primary_init_info->mgmt_op_flags[2]
- );
- Log(" .final_cleanup_req_op: %p() - 0x%02x",
- info->primary_init_info->mgmt_ops->final_cleanup_req_op,
- info->primary_init_info->mgmt_op_flags[3]
- );
- Log(" }");
- Log(" .mgmt_scratch_requirement = 0x%x", info->primary_init_info->mgmt_scratch_requirement);
- Log(" .enumeration_attr_list_length = 0x%x", info->primary_init_info->enumeration_attr_list_length);
- Log(" .rdata_size = 0x%x", info->primary_init_info->rdata_size);
- Log(" .child_data_size = 0x%x", info->primary_init_info->child_data_size);
- Log(" .per_parent_paths = 0x%x", info->primary_init_info->per_parent_paths);
- }
- Log("}");
- Log("secondary_init_list = %p", info->secondary_init_list);
- Log("ops_init_list = %p", info->ops_init_list);
-
- for( i = 0; info->ops_init_list[i].ops_idx; i++ )
- {
- Log("info->ops_init_list[%i] = {", i);
- Log(" .ops_idx = 0x%x", info->ops_init_list[i].ops_idx);
- Log(" .meta_idx = 0x%x", info->ops_init_list[i].meta_idx);
- Log(" .meta_ops_num = 0x%x", info->ops_init_list[i].meta_ops_num);
- Log(" .chan_context_size = 0x%x", info->ops_init_list[i].chan_context_size);
- Log(" .ops_vector = %p", info->ops_init_list[i].ops_vector);
-// Log(" .op_flags = %p", info->ops_init_list[i].op_flags);
- Log("}");
- }
-
- return 0;
-}
+++ /dev/null
-/**
- * \file mem.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-
-// === EXPORTS ===
-EXPORT(udi_mem_alloc);
-EXPORT(udi_mem_free);
-
-// === CODE ===
-void udi_mem_alloc(
- udi_mem_alloc_call_t *callback,
- udi_cb_t *gcb,
- udi_size_t size,
- udi_ubit8_t flags
- )
-{
- void *buf = malloc(size);
- if(buf)
- {
- if( !(flags & UDI_MEM_NOZERO) )
- memset(buf, 0, size);
- }
- callback(gcb, buf);
-}
-
-void udi_mem_free(void *target_mem)
-{
- free(target_mem);
-}
+++ /dev/null
-/**
- * \file meta_gio.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-
-// === EXPORTS ===
-EXPORT(udi_gio_bind_req);
-EXPORT(udi_gio_bind_ack);
-EXPORT(udi_gio_unbind_req);
-EXPORT(udi_gio_unbind_ack);
-EXPORT(udi_gio_xfer_req);
-EXPORT(udi_gio_xfer_ack);
-EXPORT(udi_gio_xfer_nak);
-EXPORT(udi_gio_event_res);
-EXPORT(udi_gio_event_ind);
-EXPORT(udi_gio_event_res_unused);
-
-// === CODE ===
-void udi_gio_bind_req(udi_gio_bind_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_gio_bind_ack(
- udi_gio_bind_cb_t *cb,
- udi_ubit32_t device_size_lo,
- udi_ubit32_t device_size_hi,
- udi_status_t status
- )
-{
- UNIMPLEMENTED();
-}
-
-void udi_gio_unbind_req(udi_gio_bind_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-
-void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status)
-{
- UNIMPLEMENTED();
-}
-
-void udi_gio_event_res(udi_gio_event_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_gio_event_ind(udi_gio_event_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_gio_event_res_unused(udi_gio_event_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
+++ /dev/null
-/**
- * \file meta_mgmt.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-
-// === EXPORTS ===
-EXPORT(udi_devmgmt_req);
-EXPORT(udi_devmgmt_ack);
-EXPORT(udi_final_cleanup_req);
-EXPORT(udi_final_cleanup_ack);
-EXPORT(udi_static_usage);
-EXPORT(udi_enumerate_no_children);
-
-// === CODE ===
-void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID )
-{
- ENTER("pcb imgmt_op iparent_ID", cb, mgmt_op, parent_ID);
- LEAVE('-');
-}
-
-void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status)
-{
- ENTER("pcb xflags istatus", cb, flags, status);
- LEAVE('-');
-}
-
-void udi_final_cleanup_req(udi_mgmt_cb_t *cb)
-{
- ENTER("pcb", cb);
- LEAVE('-');
-}
-
-void udi_final_cleanup_ack(udi_mgmt_cb_t *cb)
-{
- ENTER("pcb", cb);
- LEAVE('-');
-}
-
-void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
-{
- UNIMPLEMENTED();
-}
-
-void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
-{
- UNIMPLEMENTED();
-}
+++ /dev/null
-/**
- * \file physio.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-#include <udi_physio.h>
-
-// === EXPORTS ===
-EXPORT(udi_dma_constraints_attr_reset);
-EXPORT(udi_dma_constraints_free);
-
-// === CODE ===
-void udi_dma_constraints_attr_reset(
- udi_dma_constraints_t constraints,
- udi_dma_constraints_attr_t attr_type
- )
-{
- UNIMPLEMENTED();
-}
-
-void udi_dma_constraints_free(udi_dma_constraints_t constraints)
-{
- UNIMPLEMENTED();
-}
+++ /dev/null
-/**
- * \file physio/meta_bus.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-#include <udi_physio.h>
-
-// === EXPORTS ===
-EXPORT(udi_bus_unbind_req);
-EXPORT(udi_bus_unbind_ack);
-EXPORT(udi_bus_bind_req);
-EXPORT(udi_bus_bind_ack);
-
-// === CODE ===
-void udi_bus_unbind_req(udi_bus_bind_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-
-void udi_bus_bind_req(udi_bus_bind_cb_t *cb)
-{
- UNIMPLEMENTED();
-}
-
-void udi_bus_bind_ack(
- udi_bus_bind_cb_t *cb,
- udi_dma_constraints_t dma_constraints,
- udi_ubit8_t preferred_endianness,
- udi_status_t status
- )
-{
- UNIMPLEMENTED();
-}
+++ /dev/null
-/**
- * \file physio/meta_intr.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-#include <udi_physio.h>
-
-// === EXPORTS ===
-EXPORT(udi_intr_attach_req);
-EXPORT(udi_intr_attach_ack);
-EXPORT(udi_intr_attach_ack_unused);
-EXPORT(udi_intr_detach_req);
-EXPORT(udi_intr_detach_ack);
-EXPORT(udi_intr_detach_ack_unused);
-EXPORT(udi_intr_event_ind);
-
-// === CODE ===
-void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb)
-{
- UNIMPLEMENTED();
-}
-void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status)
-{
- UNIMPLEMENTED();
-}
-void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status)
-{
- UNIMPLEMENTED();
-}
-
-void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb)
-{
- UNIMPLEMENTED();
-}
-void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb)
-{
- UNIMPLEMENTED();
-}
-void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb)
-{
- UNIMPLEMENTED();
-}
-
-void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags)
-{
- UNIMPLEMENTED();
-}
+++ /dev/null
-/**
- * \file logging.c
- * \author John Hodge (thePowersGang)
- */
-#include <common.h>
-#include <udi.h>
-#include <udi_physio.h>
-
-// === CODE ===
-void udi_bus_bind_req(udi_bus_bind_cb_t *cb)
-{
-}
-
-void udi_bus_unbind_req(udi_bus_bind_cb_t *cb)
-{
-}
+++ /dev/null
-/**
- * \file strmem.c
- * \author John Hodge (thePowersGang)
- */
-#include <acess.h>
-#include <udi.h>
-
-// === EXPORTS ===
-EXPORT(udi_snprintf);
-
-// === CODE ===
-udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...)
-{
- udi_size_t ret;
- va_list args;
- va_start(args, format);
-
- ret = vsnprintf(s, max_bytes, format, args);
-
- va_end(args);
- return ret;
-}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - Sun RPC (RFC 1057) implementation
- * proto.h
- * - Protocol definition
- */
-#ifndef _SUNRPC_PROTO_H_
-#define _SUNRPC_PROTO_H_
-
-struct sRPC_CallBody
-{
- Uint32 RPCVers; //!< Version (2)
- Uint32 Program; //!< Program Identifier
- Uint32 Version; //!< Program Version
-
-};
-
-struct sRPC_Message
-{
- tNet32 XID; //!< Transaction Identifier
- union
- {
- struct sRPC_CallBody Call;
- };
-};
-
-#endif
-
+++ /dev/null
-
-# Acess2 Module/Driver Templater Makefile
-# Makefile.tpl
-
-_CPPFLAGS := $(CPPFLAGS)
-
--include $(dir $(lastword $(MAKEFILE_LIST)))../Makefile.cfg
-
-LIBINCLUDES := $(addprefix -I$(ACESSDIR)/Modules/,$(DEPS))
-LIBINCLUDES := $(addsuffix /include,$(LIBINCLUDES))
-
-CPPFLAGS := -I$(ACESSDIR)/Kernel/include -I$(ACESSDIR)/Kernel/arch/$(ARCHDIR)/include
-CPPFLAGS += -DARCH=$(ARCH) -DARCH_is_$(ARCH) -DARCHDIR_is_$(ARCHDIR)
-CPPFLAGS += $(_CPPFLAGS)
-CPPFLAGS += $(LIBINCLUDES)
-CFLAGS := -std=gnu99 -Wall -fno-stack-protector -g -O3
-
-ifneq ($(CATEGORY),)
- FULLNAME := $(CATEGORY)_$(NAME)
-else
- FULLNAME := $(NAME)
-endif
-
-CPPFLAGS += -D_MODULE_NAME_=\"$(FULLNAME)\"
-
-ifneq ($(BUILDTYPE),static)
- _SUFFIX := dyn_$(ARCH)
- BIN := ../$(FULLNAME).kmd.$(ARCH)
- CFLAGS += $(DYNMOD_CFLAGS) -fPIC
-else
- _SUFFIX := st_$(ARCH)
- CFLAGS += $(KERNEL_CFLAGS)
- BIN := ../$(NAME).xo.$(ARCH)
-endif
-
-OBJ := $(addprefix obj-$(_SUFFIX)/,$(OBJ))
-#OBJ := $(addsuffix .$(_SUFFIX),$(OBJ))
-
-DEPFILES := $(filter %.o,$(OBJ))
-DEPFILES := $(DEPFILES:%.o=%.d)
-
-.PHONY: all clean
-
-all: $(BIN)
-
-clean:
- $(RM) $(BIN) $(BIN).dsm $(KOBJ) $(OBJ) $(DEPFILES) $(EXTRA)
- $(RM) -r obj-$(_SUFFIX)
-
-install: $(BIN)
-ifneq ($(BUILDTYPE),static)
- @$(xMKDIR) $(DISTROOT)/Modules/$(ARCH); true
- $(xCP) $(BIN) $(DISTROOT)/Modules/$(ARCH)/$(NAME).kmd
-else
-endif
-
-
-ifneq ($(BUILDTYPE),static)
-$(BIN): %.kmd.$(ARCH): $(OBJ)
- @echo --- $(LD) -o $@
- @$(LD) --allow-shlib-undefined -shared -nostdlib -o $@ $(OBJ) -defsym=DriverInfo=_DriverInfo_$(FULLNAME) $(LDFLAGS)
- @$(DISASM) $(BIN) > $(BIN).dsm
-else
-$(BIN): %.xo.$(ARCH): $(OBJ)
- @echo --- $(LD) -o $@
- @$(LD) -r -o $@ $(OBJ) $(LDFLAGS)
-endif
-
-obj-$(_SUFFIX)/%.o: %.c Makefile $(CFGFILES)
- @echo --- $(CC) -o $@
- @mkdir -p $(dir $@)
- @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
- @$(CC) -M $(CPPFLAGS) -MT $@ -o obj-$(_SUFFIX)/$*.d $<
-
--include $(DEPFILES)
+++ /dev/null
-CATEGORY = Network
-
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-OBJ = ne2000.o
-NAME = NE2000
-
--include ../Makefile.tpl
+++ /dev/null
-/* Acess2
- * NE2000 Driver
- *
- * See: ~/Sources/bochs/bochs.../iodev/ne2k.cc
- */
-#define DEBUG 1
-#define VERSION ((0<<8)|50)
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <drv_pci.h>
-#include <api_drv_network.h>
-#include <semaphore.h>
-
-// === CONSTANTS ===
-#define MEM_START 0x40
-#define MEM_END 0xC0
-#define RX_FIRST (MEM_START)
-#define RX_LAST (MEM_START+RX_BUF_SIZE-1)
-#define RX_BUF_SIZE 0x40
-#define TX_FIRST (MEM_START+RX_BUF_SIZE)
-#define TX_LAST (MEM_END)
-#define TX_BUF_SIZE 0x40
-#define MAX_PACKET_QUEUE 10
-
-static const struct {
- Uint16 Vendor;
- Uint16 Device;
-} csaCOMPAT_DEVICES[] = {
- {0x10EC, 0x8029}, // Realtek 8029
- {0x10EC, 0x8129} // Realtek 8129
-};
-#define NUM_COMPAT_DEVICES ((int)(sizeof(csaCOMPAT_DEVICES)/sizeof(csaCOMPAT_DEVICES[0])))
-
-enum eNe2k_Page0Read {
- CMD = 0, //!< the master command register
- CLDA0, //!< Current Local DMA Address 0
- CLDA1, //!< Current Local DMA Address 1
- BNRY, //!< Boundary Pointer (for ringbuffer)
- TSR, //!< Transmit Status Register
- NCR, //!< collisions counter
- FIFO, //!< (for what purpose ??)
- ISR, //!< Interrupt Status Register
- CRDA0, //!< Current Remote DMA Address 0
- CRDA1, //!< Current Remote DMA Address 1
- RSR = 0xC //!< Receive Status Register
-};
-
-enum eNe2k_Page0Write {
- PSTART = 1, //!< page start (init only)
- PSTOP, //!< page stop (init only)
- TPSR = 4, //!< transmit page start address
- TBCR0, //!< transmit byte count (low)
- TBCR1, //!< transmit byte count (high)
- RSAR0 = 8, //!< remote start address (lo)
- RSAR1, //!< remote start address (hi)
- RBCR0, //!< remote byte count (lo)
- RBCR1, //!< remote byte count (hi)
- RCR, //!< receive config register
- TCR, //!< transmit config register
- DCR, //!< data config register (init)
- IMR //!< interrupt mask register (init)
-};
-
-enum eNe2k_Page1Read {
- CURR = 7 //!< current page
-};
-
-// === TYPES ===
-typedef struct sNe2k_Card {
- Uint16 IOBase; //!< IO Port Address from PCI
- Uint8 IRQ; //!< IRQ Assigned from PCI
-
- tSemaphore Semaphore; //!< Semaphore for incoming packets
- int NextRXPage; //!< Next expected RX page
-
- int NextMemPage; //!< Next Card Memory page to use
-
- char Name[2]; // "0"
- tVFS_Node Node; //!< VFS Node
- Uint8 MacAddr[6]; //!< Cached MAC address
-} tCard;
-
-// === PROTOTYPES ===
- int Ne2k_Install(char **Arguments);
-char *Ne2k_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name);
- int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data);
-Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
-Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-
- int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer);
-Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length);
-void Ne2k_IRQHandler(int IntNum, void *Ptr);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, Ne2k, Ne2k_Install, NULL, NULL);
-tVFS_NodeType gNe2K_RootNodeType = {
- .ReadDir = Ne2k_ReadDir,
- .FindDir = Ne2k_FindDir,
- .IOCtl = Ne2k_IOCtl
- };
-tVFS_NodeType gNe2K_DevNodeType = {
- .Write = Ne2k_Write,
- .Read = Ne2k_Read,
- .IOCtl = Ne2k_IOCtl
- };
-tDevFS_Driver gNe2k_DriverInfo = {
- NULL, "ne2k",
- {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gNe2K_RootNodeType
- }
-};
-Uint16 gNe2k_BaseAddress;
- int giNe2k_CardCount = 0;
-tCard *gpNe2k_Cards = NULL;
-
-// === CODE ===
-/**
- * \fn int Ne2k_Install(char **Options)
- * \brief Installs the NE2000 Driver
- */
-int Ne2k_Install(char **Options)
-{
- int i, j, k;
- int count, base;
- tPCIDev id;
-
- // --- Scan PCI Bus ---
- // Count Cards
- giNe2k_CardCount = 0;
- for( i = 0; i < NUM_COMPAT_DEVICES; i ++ )
- {
- giNe2k_CardCount += PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device );
- }
-
- if( giNe2k_CardCount == 0 ) return MODULE_ERR_NOTNEEDED;
-
- // Enumerate Cards
- k = 0;
- gpNe2k_Cards = calloc( giNe2k_CardCount, sizeof(tCard) );
-
- for( i = 0; i < NUM_COMPAT_DEVICES; i ++ )
- {
- count = PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device );
- for( j = 0; j < count; j ++,k ++ )
- {
- id = PCI_GetDevice( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device, j );
- // Create Structure
- base = PCI_GetBAR( id, 0 );
- gpNe2k_Cards[ k ].IOBase = base;
- gpNe2k_Cards[ k ].IRQ = PCI_GetIRQ( id );
- gpNe2k_Cards[ k ].NextMemPage = 64;
- gpNe2k_Cards[ k ].NextRXPage = RX_FIRST;
-
- // Install IRQ Handler
- IRQ_AddHandler(gpNe2k_Cards[ k ].IRQ, Ne2k_IRQHandler, &gpNe2k_Cards[k]);
-
- // Reset Card
- outb( base + 0x1F, inb(base + 0x1F) );
- while( (inb( base+ISR ) & 0x80) == 0 );
- outb( base + ISR, 0x80 );
-
- // Initialise Card
- outb( base + CMD, 0x40|0x21 ); // Page 1, No DMA, Stop
- outb( base + CURR, RX_FIRST ); // Current RX page
- outb( base + CMD, 0x21 ); // No DMA and Stop
- outb( base + DCR, 0x49 ); // Set WORD mode
- outb( base + IMR, 0x00 ); // Interrupt Mask Register
- outb( base + ISR, 0xFF );
- outb( base + RCR, 0x20 ); // Reciever to Monitor
- outb( base + TCR, 0x02 ); // Transmitter OFF (TCR.LB = 1, Internal Loopback)
-
- // Read MAC Address
- outb( base + RBCR0, 6*4 ); // Remote Byte Count
- outb( base + RBCR1, 0 );
- outb( base + RSAR0, 0 ); // Clear Source Address
- outb( base + RSAR1, 0 );
- outb( base + CMD, 0x0A ); // Remote Read, Start
- gpNe2k_Cards[ k ].MacAddr[0] = inb(base+0x10);// inb(base+0x10);
- gpNe2k_Cards[ k ].MacAddr[1] = inb(base+0x10);// inb(base+0x10);
- gpNe2k_Cards[ k ].MacAddr[2] = inb(base+0x10);// inb(base+0x10);
- gpNe2k_Cards[ k ].MacAddr[3] = inb(base+0x10);// inb(base+0x10);
- gpNe2k_Cards[ k ].MacAddr[4] = inb(base+0x10);// inb(base+0x10);
- gpNe2k_Cards[ k ].MacAddr[5] = inb(base+0x10);// inb(base+0x10);
-
- outb( base+PSTART, RX_FIRST); // Set Receive Start
- outb( base+BNRY, RX_LAST-1); // Set Boundary Page
- outb( base+PSTOP, RX_LAST); // Set Stop Page
- outb( base+ISR, 0xFF ); // Clear all ints
- outb( base+CMD, 0x22 ); // No DMA, Start
- outb( base+IMR, 0x3F ); // Set Interupt Mask
- outb( base+RCR, 0x0F ); // Set WRAP and allow all packet matches
- outb( base+TCR, 0x00 ); // Set Normal Transmitter mode
- outb( base+TPSR, 0x40); // Set Transmit Start
-
- Log_Log("Ne2k", "Card %i 0x%04x IRQ%i %02x:%02x:%02x:%02x:%02x:%02x",
- k, base, gpNe2k_Cards[ k ].IRQ,
- gpNe2k_Cards[k].MacAddr[0], gpNe2k_Cards[k].MacAddr[1],
- gpNe2k_Cards[k].MacAddr[2], gpNe2k_Cards[k].MacAddr[3],
- gpNe2k_Cards[k].MacAddr[4], gpNe2k_Cards[k].MacAddr[5]
- );
-
- // Set VFS Node
- gpNe2k_Cards[ k ].Name[0] = '0'+k;
- gpNe2k_Cards[ k ].Name[1] = '\0';
- gpNe2k_Cards[ k ].Node.ImplPtr = &gpNe2k_Cards[ k ];
- gpNe2k_Cards[ k ].Node.NumACLs = 0; // Root Only
- gpNe2k_Cards[ k ].Node.CTime = now();
- gpNe2k_Cards[ k ].Node.Type = &gNe2K_DevNodeType;
-
- // Initialise packet semaphore
- // - Start at zero, no max
- Semaphore_Init( &gpNe2k_Cards[k].Semaphore, 0, 0, "NE2000", gpNe2k_Cards[ k ].Name );
- }
- }
-
- gNe2k_DriverInfo.RootNode.Size = giNe2k_CardCount;
- DevFS_AddDevice( &gNe2k_DriverInfo );
- return MODULE_ERR_OK;
-}
-
-/**
- * \fn char *Ne2k_ReadDir(tVFS_Node *Node, int Pos)
- */
-char *Ne2k_ReadDir(tVFS_Node *Node, int Pos)
-{
- char ret[2];
- if(Pos < 0 || Pos >= giNe2k_CardCount) return NULL;
- ret[0] = '0'+Pos;
- ret[1] = '\0';
- return strdup(ret);
-}
-
-/**
- * \fn tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name)
- */
-tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name)
-{
- if(Name[0] == '\0' || Name[1] != '\0') return NULL;
-
- return &gpNe2k_Cards[ Name[0]-'0' ].Node;
-}
-
-static const char *casIOCtls[] = { DRV_IOCTLNAMES, DRV_NETWORK_IOCTLNAMES, NULL };
-/**
- * \fn int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data)
- * \brief IOCtl calls for a network device
- */
-int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- ENTER("pNode iID pData", Node, ID, Data);
- switch( ID )
- {
- BASE_IOCTLS(DRV_TYPE_NETWORK, "NE2000", VERSION, casIOCtls);
- }
-
- // If this is the root, return
- if( Node == &gNe2k_DriverInfo.RootNode ) {
- LEAVE('i', 0);
- return 0;
- }
-
- // Device specific settings
- switch( ID )
- {
- case NET_IOCTL_GETMAC:
- if( !CheckMem(Data, 6) ) {
- LEAVE('i', -1);
- return -1;
- }
- memcpy( Data, ((tCard*)Node->ImplPtr)->MacAddr, 6 );
- LEAVE('i', 1);
- return 1;
- }
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \fn Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
- * \brief Send a packet from the network card
- */
-Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- tCard *Card = (tCard*)Node->ImplPtr;
- const Uint16 *buf = Buffer;
- int rem = Length;
- int page;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- // TODO: Lock
-
- // Sanity Check Length
- if(Length > TX_BUF_SIZE*256) {
- Log_Warning(
- "Ne2k",
- "Ne2k_Write - Attempting to send over TX_BUF_SIZE*256 (%i) bytes (%i)",
- TX_BUF_SIZE*256, Length
- );
- LEAVE('i', 0);
- return 0;
- }
-
- // Make sure that the card is in page 0
- outb(Card->IOBase + CMD, 0|0x22); // Page 0, Start, NoDMA
-
- // Clear Remote DMA Flag
- outb(Card->IOBase + ISR, 0x40); // Bit 6
-
- // Send Size - Transfer Byte Count Register
- outb(Card->IOBase + TBCR0, Length & 0xFF);
- outb(Card->IOBase + TBCR1, Length >> 8);
-
- // Send Size - Remote Byte Count Register
- outb(Card->IOBase + RBCR0, Length & 0xFF);
- outb(Card->IOBase + RBCR1, Length >> 8);
-
- // Set up transfer
- outb(Card->IOBase + RSAR0, 0x00); // Page Offset
- page = Ne2k_int_GetWritePage(Card, Length);
- outb(Card->IOBase + RSAR1, page); // Page Offset
- // Start
- outb(Card->IOBase + CMD, 0|0x10|0x2); // Page 0, Remote Write, Start
-
- // Send Data
- for(rem = Length; rem > 0; rem -= 2) {
- outw(Card->IOBase + 0x10, *buf++);
- }
-
- while( !(inb(Card->IOBase + ISR) & 0x40) ) // Wait for Remote DMA Complete
- ; //Proc_Yield();
-
- outb( Card->IOBase + ISR, 0x40 ); // ACK Interrupt
-
- // Send Packet
- outb(Card->IOBase + TPSR, page);
- outb(Card->IOBase + CMD, 0|0x10|0x4|0x2);
-
- // Complete DMA
- //outb(Card->IOBase + CMD, 0|0x20);
-
- LEAVE('i', Length);
- return Length;
-}
-
-/**
- * \fn Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Wait for and read a packet from the network card
- */
-Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tCard *Card = (tCard*)Node->ImplPtr;
- Uint8 page;
- Uint8 data[256];
- struct {
- Uint8 Status;
- Uint8 NextPacketPage;
- Uint16 Length; // Little Endian
- } *pktHdr;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- // Wait for packets
- if( Semaphore_Wait( &Card->Semaphore, 1 ) != 1 )
- {
- // Error or interrupted
- LEAVE_RET('i', 0);
- }
-
- outb(Card->IOBase, 0x22 | (1 << 6)); // Page 6
- LOG("CURR : 0x%02x", inb(Card->IOBase + CURR));
-
- // Get current read page
- page = Card->NextRXPage;
- LOG("page = %i", page);
-
- Ne2k_int_ReadDMA(Card, page, 1, data);
-
- pktHdr = (void*)data;
-
- LOG("pktHdr->Status = 0x%x", pktHdr->Status);
- LOG("pktHdr->NextPacketPage = %i", pktHdr->NextPacketPage);
- LOG("pktHdr->Length = 0x%03x", pktHdr->Length);
-
- // Have we read all the required bytes yet?
- if(pktHdr->Length < 256 - 4)
- {
- if(Length > pktHdr->Length)
- Length = pktHdr->Length;
- memcpy(Buffer, &data[4], Length);
- }
- // No? oh damn, now we need to allocate a buffer
- else
- {
- int pages = pktHdr->NextPacketPage - page;
- char *buf = malloc( pages*256 );
-
- LOG("pktHdr->Length (%i) > 256 - 4, allocated buffer %p", pktHdr->Length, buf);
-
- if(!buf) LEAVE_RET('i', -1);
-
- // Copy the already read data
- memcpy(buf, data, 256);
-
- // Read all the needed pages
- page ++;
- if(page == RX_LAST+1) page = RX_FIRST;
-
- if( page + pages > RX_LAST )
- {
- int first_count = RX_LAST+1 - page;
- int tmp = 0;
- tmp += Ne2k_int_ReadDMA(Card, page, first_count, buf+256);
- tmp += Ne2k_int_ReadDMA(Card, RX_FIRST, pages-1-first_count, buf+(first_count+1)*256);
- LOG("composite return count = %i", tmp);
- }
- else
- Ne2k_int_ReadDMA(Card, page, pages-1, buf+256);
-
- // Wrap length to the packet length
- if(Length > pktHdr->Length)
- Length = pktHdr->Length;
- else if( Length < pktHdr->Length ) {
- Log_Warning("NE2000", "Packet truncated! (%i bytes truncated to %i)",
- pktHdr->Length, Length);
- }
- memcpy(Buffer, &buf[4], Length);
-
- free(buf);
- }
-
- // Write BNRY (maximum page for incoming packets)
- if(pktHdr->NextPacketPage == RX_FIRST)
- outb( Card->IOBase + BNRY, RX_LAST-1 );
- else
- outb( Card->IOBase + BNRY, pktHdr->NextPacketPage-1 );
- // Set next RX Page and decrement the waiting list
- Card->NextRXPage = pktHdr->NextPacketPage;
-
- LEAVE('i', Length);
- return Length;
-}
-
-int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer)
-{
- int i;
-
- // Sanity check
- if( !(0 <= FirstPage && FirstPage < 256) ) {
- Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - FirstPage(%i) not 8-bit", FirstPage);
- return -1;
- }
- if( !(0 <= NumPages && NumPages < 256) ) {
- Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - NumPages(%i) not 8-bit", NumPages);
- return -1;
- }
-
- ENTER("pCard iFirstPage iNumPages pBuffer", Card, FirstPage, NumPages, Buffer);
-
- // Make sure that the card is in bank 0
- outb(Card->IOBase + CMD, 0|0x22); // Bank 0, Start, NoDMA
- outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag
-
- // Set up transfer
- outb(Card->IOBase + RBCR0, 0);
- outb(Card->IOBase + RBCR1, NumPages); // page count
- outb(Card->IOBase + RSAR0, 0x00); // Page Offset
- outb(Card->IOBase + RSAR1, FirstPage); // Page Number
- outb(Card->IOBase + CMD, 0|0x08|0x2); // Bank 0, Remote Read, Start
-
- // TODO: Less expensive
- //while( !(inb(Card->IOBase + ISR) & 0x40) ) {
- // HALT();
- // LOG("inb(ISR) = 0x%02x", inb(Card->IOBase + ISR));
- //}
- HALT(); // Small delay?
-
- // Read data
- for(i = 0; i < 128*NumPages; i ++)
- ((Uint16*)Buffer)[i] = inw(Card->IOBase + 0x10);
-
-
- outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag
-
- LEAVE('i', NumPages);
- return NumPages;
-}
-
-/**
- * \fn Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length)
- */
-Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length)
-{
- Uint8 ret = Card->NextMemPage;
-
- Card->NextMemPage += (Length + 0xFF) >> 8;
- if(Card->NextMemPage >= TX_LAST) {
- Card->NextMemPage -= TX_BUF_SIZE;
- }
-
- return ret;
-}
-
-/**
- * \fn void Ne2k_IRQHandler(int IntNum)
- */
-void Ne2k_IRQHandler(int IntNum, void *Ptr)
-{
- Uint8 byte;
- tCard *card = Ptr;
-
- if(card->IRQ != IntNum) return;
-
- byte = inb( card->IOBase + ISR );
-
- LOG("byte = 0x%02x", byte);
-
-
- // Reset All (save for RDMA), that's polled
- outb( card->IOBase + ISR, 0xFF&(~0x40) );
-
- // 0: Packet recieved (no error)
- if( byte & 1 )
- {
- //if( card->NumWaitingPackets > MAX_PACKET_QUEUE )
- // card->NumWaitingPackets = MAX_PACKET_QUEUE;
- if( Semaphore_Signal( &card->Semaphore, 1 ) != 1 ) {
- // Oops?
- }
- }
- // 1: Packet sent (no error)
- // 2: Recieved with error
- // 3: Transmission Halted (Excessive Collisions)
- // 4: Recieve Buffer Exhausted
- // 5:
- // 6: Remote DMA Complete
- // 7: Reset
-}
+++ /dev/null
-#
-#
-
-OBJ = rtl8139.o
-NAME = RTL8139
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 PCnet-FAST III Driver
- * - By John Hodge (thePowersGang)
- */
-#define DEBUG 0
-#define VERSION ((0<<8)|10)
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <drv_pci.h>
-#include <api_drv_network.h>
-#include <semaphore.h>
-
-// === CONSTANTS ===
-#define VENDOR_ID 0x1022
-#define DEVICE_ID 0x2000
-
-enum eRegs
-{
- REG_APROM0 = 0x00,
- REG_APROM4 = 0x04,
- REG_APROM8 = 0x08,
- REG_APROMC = 0x0C,
- REG_RDP = 0x10, // 16 bit
- REG_RAP = 0x14, // 8-bit
- REG_RESET = 0x18, // 16-bit
- REG_BDP = 0x1C, // 16-bit
-};
-
-enum eCSR_Regs
-{
- CSR_STATUS, // CSR0 - Am79C973/Am79C975 Controller Status
- CSR_IBA0, // CSR1 - Initialization Block Address[15:0]
- CSR_IBA1, // CSR2 - Initialization Block Address[31:16]
- CSR_INTMASK, // CSR3 - Interrupt Masks and Deferral Control
-
- CSR_MAC0 = 12, // CSR12 - Physical Address[15:0]
- CSR_MAC1 = 13, // CSR13 - Physical Address[31:16]
- CSR_MAC2 = 14, // CSR14 - Physical Address[47:32]
- CSR_MODE = 15, // CSR15 - Mode
-
- CSR_RXBASE0 = 24, // CSR24 - Base Address of Receive Ring Lower
- CSR_RXBASE1 = 25, // CSR25 - Base Address of Receive Ring Upper
- CSR_TXBASE0 = 30, // CSR26 - Base Address of Transmit Ring Lower
- CSR_TXBASE1 = 31, // CSR27 - Base Address of Transmit Ring Upper
-
- CSR_RXLENGTH = 76, // CSR76 - Receive Ring Length
- CSR_TXLENGTH = 78, // CSR78 - Transmit Ring Length
-};
-
-enum eBCR_Regs
-{
- BCR_PHYCS = 32, // BCR32 - Internal PHY Control and Status
- BCR_PHYADDR, // BCR33 - Internal PHY Address
- BCR_PHYMGMT, // BCR34 - Internal PHY Management Data
-};
-
-// === TYPES ===
-typedef struct sCard
-{
- Uint16 IOBase;
- Uint8 IRQ;
-
- int NumWaitingPackets;
-
- char Name[2];
- tVFS_Node Node;
- Uint8 MacAddr[6];
-} tCard;
-
-// === PROTOTYPES ===
- int PCnet3_Install(char **Options);
-char *PCnet3_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename);
- int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Arg);
-Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Arg);
-void PCnet3_IRQHandler(int Num);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, PCnet3, PCnet3_Install, NULL, NULL);
-tDevFS_Driver gPCnet3_DriverInfo = {
- NULL, "PCnet3",
- {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .ReadDir = PCnet3_ReadDir,
- .FindDir = PCnet3_FindDir,
- .IOCtl = PCnet3_RootIOCtl
- }
-};
- int giPCnet3_CardCount;
-tCard *gaPCnet3_Cards;
-
-// === CODE ===
-/**
- * \brief Installs the PCnet3 Driver
- */
-int PCnet3_Install(char **Options)
-{
- int id = -1;
- int i = 0;
- Uint16 base;
- tCard *card;
-
- giPCnet3_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID);
- Log_Debug("PCnet3", "%i cards", giPCnet3_CardCount);
-
- if( giPCnet3_CardCount == 0 ) return MODULE_ERR_NOTNEEDED;
-
- gaPCnet3_Cards = calloc( giPCnet3_CardCount, sizeof(tCard) );
-
- while( (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1 )
- {
- card = &gaPCnet3_Cards[i];
- base = PCI_GetBAR( id, 0 );
- if( !(base & 1) ) {
- Log_Warning("PCnet3", "Driver does not support MMIO, skipping card");
- card->IOBase = 0;
- card->IRQ = 0;
- continue ;
- }
- base &= ~1;
- card->IOBase = base;
- card->IRQ = PCI_GetIRQ( id );
-
- // Install IRQ Handler
- IRQ_AddHandler(card->IRQ, PCnet3_IRQHandler);
-
-
-
- Log_Log("PCnet3", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x",
- i, card->IOBase, card->IRQ,
- card->MacAddr[0], card->MacAddr[1], card->MacAddr[2],
- card->MacAddr[3], card->MacAddr[4], card->MacAddr[5]
- );
-
- i ++;
- }
-
- gPCnet3_DriverInfo.RootNode.Size = giPCnet3_CardCount;
- DevFS_AddDevice( &gPCnet3_DriverInfo );
-
- return MODULE_ERR_OK;
-}
-
-// --- Root Functions ---
-char *PCnet3_ReadDir(tVFS_Node *Node, int Pos)
-{
- if( Pos < 0 || Pos >= giPCnet3_CardCount ) return NULL;
-
- return strdup( gaPCnet3_Cards[Pos].Name );
-}
-
-tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename)
-{
- //TODO: It might be an idea to supprt >10 cards
- if(Filename[0] == '\0' || Filename[1] != '\0') return NULL;
- if(Filename[0] < '0' || Filename[0] > '9') return NULL;
- return &gaPCnet3_Cards[ Filename[0]-'0' ].Node;
-}
-
-const char *csaPCnet3_RootIOCtls[] = {DRV_IOCTLNAMES, NULL};
-int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_RootIOCtls);
- }
- LEAVE('i', 0);
- return 0;
-}
-
-// --- File Functions ---
-Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tCard *card = Node->ImplPtr;
- Uint16 read_ofs, pkt_length;
- int new_read_ofs;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
-retry:
- if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 )
- {
- LEAVE_RET('i', 0);
- }
-
- Mutex_Acquire( &card->ReadMutex );
-
- read_ofs = inw( card->IOBase + CAPR );
- LOG("raw read_ofs = %i", read_ofs);
- read_ofs = (read_ofs + 0x10) & 0xFFFF;
- LOG("read_ofs = %i", read_ofs);
-
- pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
-
- // Calculate new read offset
- new_read_ofs = read_ofs + pkt_length + 4;
- new_read_ofs = (new_read_ofs + 3) & ~3; // Align
- if(new_read_ofs > card->ReceiveBufferLength) {
- LOG("wrapping read_ofs");
- new_read_ofs -= card->ReceiveBufferLength;
- }
- new_read_ofs -= 0x10; // I dunno
- LOG("new_read_ofs = %i", new_read_ofs);
-
- // Check for errors
- if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) {
- // Update CAPR
- outw(card->IOBase + CAPR, new_read_ofs);
- Mutex_Release( &card->ReadMutex );
- goto retry; // I feel evil
- }
-
- // Get packet
- if( Length > pkt_length ) Length = pkt_length;
- memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length);
-
- // Update CAPR
- outw(card->IOBase + CAPR, new_read_ofs);
-
- Mutex_Release( &card->ReadMutex );
-
- LEAVE('i', Length);
-
- return Length;
-}
-
-Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- int td;
- Uint32 status;
- tCard *card = Node->ImplPtr;
-
- if( Length > 1500 ) return 0; // MTU exceeded
-
- ENTER("pNode XLength pBuffer", Node, Length, Buffer);
-
- // TODO: Implement a semaphore for avaliable transmit buffers
-
- // Find an avaliable descriptor
- Mutex_Acquire(&card->CurTXProtector);
- td = card->CurTXDescriptor;
- card->CurTXDescriptor ++;
- card->CurTXDescriptor %= 4;
- Mutex_Release(&card->CurTXProtector);
- // - Lock it
- Mutex_Acquire( &card->TransmitInUse[td] );
-
- LOG("td = %i", td);
-
- // Transmit using descriptor `td`
- LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]);
- outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]);
- LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]);
- // Copy to buffer
- memcpy(card->TransmitBuffers[td], Buffer, Length);
- // Start
- status = 0;
- status |= Length & 0x1FFF; // 0-12: Length
- status |= 0 << 13; // 13: OWN bit
- status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check)
- LOG("status = 0x%08x", status);
- outd(card->IOBase + TSD0 + td*4, status);
-
- LEAVE('i', (int)Length);
-
- return Length;
-}
-
-const char *csaPCnet3_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL};
-int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tCard *card = Node->ImplPtr;
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_NodeIOCtls);
- case NET_IOCTL_GETMAC:
- if( !CheckMem(Data, 6) ) {
- LEAVE('i', -1);
- return -1;
- }
- memcpy( Data, card->MacAddr, 6 );
- LEAVE('i', 1);
- return 1;
- }
- LEAVE('i', 0);
- return 0;
-}
-
-void PCnet3_IRQHandler(int Num)
-{
- int i, j;
- tCard *card;
- Uint16 status;
-
- LOG("Num = %i", Num);
-
- for( i = 0; i < giPCnet3_CardCount; i ++ )
- {
- card = &gaPCnet3_Cards[i];
- if( Num != card->IRQ ) break;
-
- status = inw(card->IOBase + ISR);
- LOG("status = 0x%02x", status);
-
- // Transmit OK, a transmit descriptor is now free
- if( status & FLAG_ISR_TOK )
- {
- for( j = 0; j < 4; j ++ )
- {
- if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK
- Mutex_Release( &card->TransmitInUse[j] );
- // TODO: Update semaphore once implemented
- }
- }
- outw(card->IOBase + ISR, FLAG_ISR_TOK);
- }
-
- // Recieve OK, inform read
- if( status & FLAG_ISR_ROK )
- {
- int read_ofs, end_ofs;
- int packet_count = 0;
- int len;
-
- // Scan recieve buffer for packets
- end_ofs = inw(card->IOBase + CBA);
- read_ofs = card->SeenOfs;
- LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs);
- if( read_ofs > end_ofs )
- {
- while( read_ofs < card->ReceiveBufferLength )
- {
- packet_count ++;
- len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
- LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
- packet_count, read_ofs,
- *(Uint16*)&card->ReceiveBuffer[read_ofs],
- len
- );
- if(len > 2000) {
- Log_Warning("PCnet3", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len);
- }
- read_ofs += len + 4;
- read_ofs = (read_ofs + 3) & ~3; // Align
- }
- read_ofs -= card->ReceiveBufferLength;
- LOG("wrapped read_ofs");
- }
- while( read_ofs < end_ofs )
- {
- packet_count ++;
- LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
- packet_count, read_ofs,
- *(Uint16*)&card->ReceiveBuffer[read_ofs],
- *(Uint16*)&card->ReceiveBuffer[read_ofs+2]
- );
- read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4;
- read_ofs = (read_ofs + 3) & ~3; // Align
- }
- if( read_ofs != end_ofs ) {
- Log_Warning("PCnet3", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs);
- read_ofs = end_ofs;
- }
- card->SeenOfs = read_ofs;
-
- LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs);
-
- if( packet_count )
- {
- if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) {
- // Oops?
- }
- VFS_MarkAvaliable( &card->Node, 1 );
- }
-
- outw(card->IOBase + ISR, FLAG_ISR_ROK);
- }
- }
-}
+++ /dev/null
-#
-#
-
-OBJ = rtl8139.o
-NAME = RTL8139
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 RTL8139 Driver
- * - By John Hodge (thePowersGang)
- */
-#define DEBUG 0
-#define VERSION ((0<<8)|20)
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <drv_pci.h>
-#include <api_drv_network.h>
-#include <semaphore.h>
-
-// === CONSTANTS ===
-#define VENDOR_ID 0x10EC
-#define DEVICE_ID 0x8139
-
-enum eRTL8139_Regs
-{
- // MAC Address
- MAC0, MAC1, MAC2,
- MAC3, MAC4, MAC5,
-
- // Multicast Registers
- MAR0 = 0x08, MAR1, MAR2, MAR3,
- MAR4, MAR5, MAR6, MAR7,
-
- // Transmit status of descriptors 0 - 3
- TSD0 = 0x10, TSD1 = 0x14,
- TSD2 = 0x18, TSD3 = 0x1C,
- // Transmit start addresses
- TSAD0 = 0x20, TSAD1 = 0x24,
- TSAD2 = 0x28, TSAD3 = 0x2C,
-
- RBSTART = 0x30, //!< Recieve Buffer Start (DWord)
- // Early Recieve Byte Count
- ERBCR = 0x34, // 16-bits
- // Early RX Status Register
- ERSR = 0x36,
-
- // ??, ??, ??, RST, RE, TE, ??, ??
- CMD = 0x37,
-
- CAPR = 0x38, // Current address of packet read
- CBA = 0x3A, // Current Buffer Address - Total byte count in RX buffer
-
- IMR = 0x3C, // Interrupt mask register
- ISR = 0x3E, // Interrupt status register
-
- TCR = 0x40, // Transmit Configuration Register
- RCR = 0x44, // Recieve Configuration Register
- TCTR = 0x48, // 32-bit timer (count)
- MPC = 0x4C, // Missed packet count (due to RX overflow)
-
- CR_9346 = 0x50,
- CONFIG0 = 0x51,
- CONFIG1 = 0x52,
- // 0x53 resvd
- TIMERINT = 0x54, // Fires a timeout when TCTR equals this value
-
-};
-
-#define FLAG_ISR_TOK 0x04
-#define FLAG_ISR_ROK 0x01
-
-// === TYPES ===
-typedef struct sCard
-{
- Uint16 IOBase;
- Uint8 IRQ;
-
- int NumWaitingPackets;
-
- char *ReceiveBuffer;
- tPAddr PhysReceiveBuffer;
- int ReceiveBufferLength;
- int SeenOfs; //!< End of the most recently seen packet (by IRQ)
- tMutex ReadMutex;
- tSemaphore ReadSemaphore;
-
- char *TransmitBuffers[4];
- tPAddr PhysTransmitBuffers[4];
- tMutex TransmitInUse[4];
- tMutex CurTXProtector; //!< Protects \a .CurTXDescriptor
- int CurTXDescriptor;
-
- char Name[2];
- tVFS_Node Node;
- Uint8 MacAddr[6];
-} tCard;
-
-// === PROTOTYPES ===
- int RTL8139_Install(char **Options);
-char *RTL8139_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename);
- int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Arg);
-Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
- int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Arg);
-void RTL8139_IRQHandler(int Num, void *Ptr);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, RTL8139, RTL8139_Install, NULL, NULL);
-tVFS_NodeType gRTL8139_RootNodeType = {
- .ReadDir = RTL8139_ReadDir,
- .FindDir = RTL8139_FindDir,
- .IOCtl = RTL8139_IOCtl
- };
-tVFS_NodeType gRTL8139_DevNodeType = {
- .Write = RTL8139_Write,
- .Read = RTL8139_Read,
- .IOCtl = RTL8139_IOCtl
- };
-tDevFS_Driver gRTL8139_DriverInfo = {
- NULL, "RTL8139",
- {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gRTL8139_RootNodeType
- }
-};
- int giRTL8139_CardCount;
-tCard *gaRTL8139_Cards;
-
-// === CODE ===
-/**
- * \brief Installs the RTL8139 Driver
- */
-int RTL8139_Install(char **Options)
-{
- int id = -1;
- int i = 0;
- Uint16 base;
- tCard *card;
-
- giRTL8139_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID);
-
- if( giRTL8139_CardCount == 0 ) return MODULE_ERR_NOTNEEDED;
-
- Log_Debug("RTL8139", "%i cards", giRTL8139_CardCount);
- gaRTL8139_Cards = calloc( giRTL8139_CardCount, sizeof(tCard) );
-
- for( i = 0 ; (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1; i ++ )
- {
- card = &gaRTL8139_Cards[i];
- base = PCI_GetBAR( id, 0 );
- if( !(base & 1) ) {
- Log_Warning("RTL8139", "Driver does not support MMIO, skipping card (addr %x)",
- base);
- card->IOBase = 0;
- card->IRQ = 0;
- continue ;
- }
- base &= ~1;
- card->IOBase = base;
- card->IRQ = PCI_GetIRQ( id );
-
- // Install IRQ Handler
- IRQ_AddHandler(card->IRQ, RTL8139_IRQHandler, card);
-
- // Power on
- outb( base + CONFIG1, 0x00 );
-
- // Reset (0x10 to CMD)
- outb( base + CMD, 0x10 );
- while( inb(base + CMD) & 0x10 ) ;
-
- // Set up recieve buffer
- // - Allocate 3 pages below 4GiB for the recieve buffer (Allows 8k+16+1500)
- card->ReceiveBuffer = (void*)MM_AllocDMA( 3, 32, &card->PhysReceiveBuffer );
- card->ReceiveBufferLength = 8*1024;
- outd(base + RBSTART, (Uint32)card->PhysReceiveBuffer);
- outd(base + CBA, 0);
- outd(base + CAPR, 0);
- // Set IMR to Transmit OK and Receive OK
- outw(base + IMR, 0x5);
-
- // Set up transmit buffers
- // - 2 non-contiguous pages (each page can fit 2 1500 byte packets)
- card->TransmitBuffers[0] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[0] );
- card->TransmitBuffers[1] = card->TransmitBuffers[0] + 0x800;
- card->PhysTransmitBuffers[1] = card->PhysTransmitBuffers[0] + 0x800;
-
- card->TransmitBuffers[2] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[2] );
- card->TransmitBuffers[3] = card->TransmitBuffers[2] + 0x800;
- card->PhysTransmitBuffers[3] = card->PhysTransmitBuffers[2] + 0x800;
-
- outd(base + TSAD0, card->PhysTransmitBuffers[0]);
- outd(base + TSAD1, card->PhysTransmitBuffers[1]);
- outd(base + TSAD2, card->PhysTransmitBuffers[2]);
- outd(base + TSAD3, card->PhysTransmitBuffers[3]);
-
- // Set recieve buffer size and recieve mask
- // - Bit 7 being set tells the card to overflow the recieve buffer if needed
- // (i.e. when the packet starts at the end of the bufffer, it overflows up
- // to 1500 bytes)
- outd(base + RCR, 0x8F);
-
- // Recive Enable and Transmit Enable
- outb(base + CMD, 0x0C);
-
- // Get the card's MAC address
- card->MacAddr[0] = inb(base+MAC0);
- card->MacAddr[1] = inb(base+MAC1);
- card->MacAddr[2] = inb(base+MAC2);
- card->MacAddr[3] = inb(base+MAC3);
- card->MacAddr[4] = inb(base+MAC4);
- card->MacAddr[5] = inb(base+MAC5);
-
- // Set VFS Node
- card->Name[0] = '0'+i;
- card->Name[1] = '\0';
- card->Node.ImplPtr = card;
- card->Node.NumACLs = 0;
- card->Node.CTime = now();
- card->Node.Type = &gRTL8139_DevNodeType;
-
- Log_Log("RTL8139", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x",
- i, card->IOBase, card->IRQ,
- card->MacAddr[0], card->MacAddr[1], card->MacAddr[2],
- card->MacAddr[3], card->MacAddr[4], card->MacAddr[5]
- );
- }
-
- gRTL8139_DriverInfo.RootNode.Size = giRTL8139_CardCount;
- DevFS_AddDevice( &gRTL8139_DriverInfo );
-
- return MODULE_ERR_OK;
-}
-
-// --- Root Functions ---
-char *RTL8139_ReadDir(tVFS_Node *Node, int Pos)
-{
- if( Pos < 0 || Pos >= giRTL8139_CardCount ) return NULL;
-
- return strdup( gaRTL8139_Cards[Pos].Name );
-}
-
-tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename)
-{
- //TODO: It might be an idea to supprt >10 cards
- if(Filename[0] == '\0' || Filename[1] != '\0') return NULL;
- if(Filename[0] < '0' || Filename[0] > '9') return NULL;
- return &gaRTL8139_Cards[ Filename[0]-'0' ].Node;
-}
-
-const char *csaRTL8139_RootIOCtls[] = {DRV_IOCTLNAMES, NULL};
-int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_RootIOCtls);
- }
- LEAVE('i', 0);
- return 0;
-}
-
-// --- File Functions ---
-Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- tCard *card = Node->ImplPtr;
- Uint16 read_ofs, pkt_length;
- int new_read_ofs;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
-retry:
- if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 )
- {
- LEAVE_RET('i', 0);
- }
-
- Mutex_Acquire( &card->ReadMutex );
-
- read_ofs = inw( card->IOBase + CAPR );
- LOG("raw read_ofs = %i", read_ofs);
- read_ofs = (read_ofs + 0x10) & 0xFFFF;
- LOG("read_ofs = %i", read_ofs);
-
- pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
-
- // Calculate new read offset
- new_read_ofs = read_ofs + pkt_length + 4;
- new_read_ofs = (new_read_ofs + 3) & ~3; // Align
- if(new_read_ofs > card->ReceiveBufferLength) {
- LOG("wrapping read_ofs");
- new_read_ofs -= card->ReceiveBufferLength;
- }
- new_read_ofs -= 0x10; // I dunno
- LOG("new_read_ofs = %i", new_read_ofs);
-
- // Check for errors
- if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) {
- // Update CAPR
- outw(card->IOBase + CAPR, new_read_ofs);
- Mutex_Release( &card->ReadMutex );
- goto retry; // I feel evil
- }
-
- // Get packet
- if( Length > pkt_length ) Length = pkt_length;
- memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length);
-
- // Update CAPR
- outw(card->IOBase + CAPR, new_read_ofs);
-
- Mutex_Release( &card->ReadMutex );
-
- LEAVE('i', Length);
-
- return Length;
-}
-
-Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- int td;
- Uint32 status;
- tCard *card = Node->ImplPtr;
-
- if( Length > 1500 ) return 0; // MTU exceeded
-
- ENTER("pNode XLength pBuffer", Node, Length, Buffer);
-
- // TODO: Implement a semaphore for avaliable transmit buffers
-
- // Find an avaliable descriptor
- Mutex_Acquire(&card->CurTXProtector);
- td = card->CurTXDescriptor;
- card->CurTXDescriptor ++;
- card->CurTXDescriptor %= 4;
- Mutex_Release(&card->CurTXProtector);
- // - Lock it
- Mutex_Acquire( &card->TransmitInUse[td] );
-
- LOG("td = %i", td);
-
- // Transmit using descriptor `td`
- LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]);
- outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]);
- LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]);
- // Copy to buffer
- memcpy(card->TransmitBuffers[td], Buffer, Length);
- // Start
- status = 0;
- status |= Length & 0x1FFF; // 0-12: Length
- status |= 0 << 13; // 13: OWN bit
- status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check)
- LOG("status = 0x%08x", status);
- outd(card->IOBase + TSD0 + td*4, status);
-
- LEAVE('i', (int)Length);
-
- return Length;
-}
-
-const char *csaRTL8139_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL};
-int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- tCard *card = Node->ImplPtr;
- ENTER("pNode iID pData", Node, ID, Data);
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_NodeIOCtls);
- case NET_IOCTL_GETMAC:
- if( !CheckMem(Data, 6) ) {
- LEAVE('i', -1);
- return -1;
- }
- memcpy( Data, card->MacAddr, 6 );
- LEAVE('i', 1);
- return 1;
- }
- LEAVE('i', 0);
- return 0;
-}
-
-void RTL8139_IRQHandler(int Num, void *Ptr)
-{
- int j;
- tCard *card = Ptr;
- Uint16 status;
-
- LOG("Num = %i", Num);
-
- if( Num != card->IRQ ) return;
-
- status = inw(card->IOBase + ISR);
- LOG("status = 0x%02x", status);
-
- // Transmit OK, a transmit descriptor is now free
- if( status & FLAG_ISR_TOK )
- {
- for( j = 0; j < 4; j ++ )
- {
- if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK
- Mutex_Release( &card->TransmitInUse[j] );
- // TODO: Update semaphore once implemented
- }
- }
- outw(card->IOBase + ISR, FLAG_ISR_TOK);
- }
-
- // Recieve OK, inform read
- if( status & FLAG_ISR_ROK )
- {
- int read_ofs, end_ofs;
- int packet_count = 0;
- int len;
-
- // Scan recieve buffer for packets
- end_ofs = inw(card->IOBase + CBA);
- read_ofs = card->SeenOfs;
- LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs);
- if( read_ofs > end_ofs )
- {
- while( read_ofs < card->ReceiveBufferLength )
- {
- packet_count ++;
- len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2];
- LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
- packet_count, read_ofs,
- *(Uint16*)&card->ReceiveBuffer[read_ofs],
- len
- );
- if(len > 2000) {
- Log_Warning("RTL8139", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len);
- }
- read_ofs += len + 4;
- read_ofs = (read_ofs + 3) & ~3; // Align
- }
- read_ofs -= card->ReceiveBufferLength;
- LOG("wrapped read_ofs");
- }
- while( read_ofs < end_ofs )
- {
- packet_count ++;
- LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x",
- packet_count, read_ofs,
- *(Uint16*)&card->ReceiveBuffer[read_ofs],
- *(Uint16*)&card->ReceiveBuffer[read_ofs+2]
- );
- read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4;
- read_ofs = (read_ofs + 3) & ~3; // Align
- }
- if( read_ofs != end_ofs ) {
- Log_Warning("RTL8139", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs);
- read_ofs = end_ofs;
- }
- card->SeenOfs = read_ofs;
-
- LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs);
-
- if( packet_count )
- {
- if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) {
- // Oops?
- }
- VFS_MarkAvaliable( &card->Node, 1 );
- }
-
- outw(card->IOBase + ISR, FLAG_ISR_ROK);
- }
-}
+++ /dev/null
-#
-#
-
-OBJ = main.o
-NAME = SoundBlaster16
-
--include ../Makefile.tpl
+++ /dev/null
-/*\r
- * Acess2 SoundBlaster16 Driver\r
- */\r
-#define DEBUG 0\r
-#include <acess.h>\r
-#include <errno.h>\r
-#include <modules.h>\r
-#include <vfs.h>\r
-#include <fs_devfs.h>\r
-#include <drv_pci.h>\r
-#include <api_drv_sound.h>\r
-\r
-#define INT\r
-\r
-// === TYPES ===\r
-typedef struct sSB16\r
-{\r
- Uint16 Base;\r
-} tSB16;\r
-\r
-// === CONSTANTS ===\r
-enum {\r
- SB16_PORT_RESET = 0x6,\r
- SB16_PORT_READ = 0xA,\r
- SB16_PORT_WRITE = 0xC,\r
- SB16_PORT_AVAIL = 0xE\r
-};\r
-#define SB16_BASE_PORT 0x200\r
-enum {\r
- SB16_CMD_RAW8 = 0x10, // 8-bit DAC value follows\r
- SB16_CMD_DMAFREQ = 0x40, // followed by TIME_CONSTANT = 256 - 1000000 / frequency\r
- SB16_CMD_DMASTOP = 0xD0,\r
- SB16_CMD_SPKRON = 0xD1,\r
- SB16_CMD_SPKROFF = 0xD3,\r
- SB16_CMD_DMACONT = 0xD4,\r
- \r
- // DMA Types (uses channel 1)\r
- // - Followed by 16-bit length (well, length - 1)\r
- SB16_CMD_DMA_8BIT = 0x14,\r
-};\r
-\r
-\r
-// === PROTOTYPES ===\r
-// Driver\r
- int SB16_Install(char **Arguments);\r
-void SB16_Uninstall();\r
-// Filesystem\r
-Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
- int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data);\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, 0x0032, SoundBlaster16, SB16_Install, SB16_Uninstall, "PCI", NULL);\r
-tDevFS_Driver gBGA_DriverStruct = {\r
- NULL, "SoundBlaster16",\r
- {\r
- .Write = SB16_Write,\r
- .IOCtl = SB16_IOCtl\r
- }\r
-};\r
-\r
-// === CODE ===\r
-int SB16_Install(char **Arguments)\r
-{\r
- int jumper_port_setting = 1; // 1-6 incl\r
- \r
- // Reset\r
- outb(card->Base+SB16_PORT_RESET, 1);\r
- // - Wait 3us\r
- outb(card->Base+SB16_PORT_RESET, 0);\r
- \r
- SB16_ReadDSP(card);\r
- \r
- return MODULE_ERR_OK;\r
-}\r
-\r
-void SB16_Uninstall()\r
-{\r
-}\r
-\r
-/**\r
- * \fn Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
- * \brief Write to the framebuffer\r
- */\r
-Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
-{ \r
- return 0;\r
-}\r
-\r
-/**\r
- * \fn int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
- * \brief Handle messages to the device\r
- */\r
-int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
-{\r
- return -1;\r
-}\r
-\r
-//\r
-int SB16_WriteDSP(tSB16 *Card, Uint8 Value)\r
-{\r
- // Wait for the card to be ready\r
- while( inb(Card->Base+SB16_PORT_WRITE) & 0x80 )\r
- ;\r
- \r
- outb(Card->Base+SB16_PORT_WRITE, Value);\r
- \r
- return 0;\r
-}\r
-\r
-Uint8 SB16_ReadDSP(tSB16 *Card)\r
-{\r
- // Wait for bit 7 of AVAIL\r
- while( !(inb(card->Base+SB16_PORT_AVAIL) & 0x80) )\r
- ;\r
- return inb(card->Base+SB16_PORT_READ);\r
-}\r
+++ /dev/null
-
- ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
- ³ Programming the SoundBlaster DSP ³
- ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
- Written for the PC-GPE by Mark Feldman
-
- ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
- ³ THIS FILE MAY NOT BE DISTRIBUTED ³
- ³ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. ³
- ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Disclaimer ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-I assume no responsibility whatsoever for any effect that this file, the
-information contained therein or the use thereof has on you, your sanity,
-computer, spouse, children, pets or anything else related to you or your
-existance. No warranty is provided nor implied with this information.
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Introduction ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-The SoundBlaster is capable of both FM and digitised sounds. The FM wave
-is fully Adlib compatible, so check the ADLIB.TXT file for info
-on how to program it. This file will concentrate on recording and playback
-of digital samples through the SoundBlaster CT-DSP 1321 chip.
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ The SoundBlaster DSP I/O Ports ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-The DSP (Digital Sound Processor) chip is programmed through 4 ports which
-are determined by the SoundBlaster base address jumper setting:
-
- RESET 2x6h
-
- READ DATA 2xAh
-
-WRITE COMMAND/DATA output
-WRITE BUFFER STATUS input 2xCh
-
-
- DATA AVAILABLE 2xEh
-
-where x = 1 for base address jumper setting 210h
- x = 2 for base address jumper setting 220h
- .
- .
- x = 6 for base address jumper setting 260h
-
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Resetting the DSP ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-You have to reset the DSP before you program it. This is done with the
-following procedure :
-
-1) Write a 1 to the SoundBlaster RESET port (2x6h)
-2) Wait for 3 micro-seconds
-3) Write a 0 to the SoundBlaster RESET port (2x6h)
-4) Read the byte from the DATA AVAILABLE (2xEh) port until bit 7 = 1
-5) Poll for a ready byte (AAh) from the READ DATA port (2xAh). Before
- reading the READ DATA port it is avdvisable.
-
-The DSP usually takes somewhere around 100 micro-seconds to reset itself.
-If it fails to do within a reasonable time (say 200 micro-seconds) then
-an error has occurred, possibly an incorrect I/O address is being used.
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Writing to the DSP ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-A value can be written to the DSP with the following procedure :
-
-1) Read the DSP's WRITE BUFFER STATUS port (2xCh) until bit 7 = 0
-2) Write the value to the WRITE COMMAND/DATA port (2xCh)
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Reading the DSP ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-A value can be read from the DSP with the following procedure :
-
-1) Read the DSP's DATA AVAILABLE port (2xEh) until bit 7 = 1
-2) Read the data from the READ DATA port (2xAh)
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Turning the speaker on and controlling DMA ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-Speaker and DMA control are handled by writing one of the following bytes
-to the DSP:
-
- ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
- ³ Value Description ³
- ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
- ³ D0h DMA Stop ³
- ³ D1h Turn speaker on ³
- ³ D3h Turn speaker off ³
- ³ D4h DMA Continue ³
- ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-DMA is discussed below. The DMA commands shown here can be used to pause
-the sample during DMA playback playback.
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Writing to the DAC ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-The DAC (Digital to Analog Converter) is the part of the card which converts
-a sample number (ie 0 -> 255) to a sound level. To generate a square sound
-wave at maximum volume (for example) you could alternate writing 0's and
-255's to the DAC.
-
-Programming the DAC in direct mode involves the main program setting the
-DAC to a desired value. Only 8 bit DAC is available in direct mode. To set
-the DAC level you write the value 10h to the DSP followed by the sample
-number (0 -> 255). Note that no sound will be heard unless the speaker has
-been turned on. In direct mode the main program is responsible for the
-timing between samples, the DAC can output sound samples as fast as the
-calling program can change it. Typically the timer interrupt is reprogrammed
-and used to generate the timing required for a sample playback. Info on
-programming the PIT chip can be found in the PIT.TXT file.
-
-The DAC can also be programmed to accept values sent to it via the DMA
-chip. Draeden has written an excellent article on programming the DMA chip
-(see DMA_VLA.TXT) so only a brief example of it's use will be given here.
-The important thing to remember is that the DMA chip cannot transfer data
-which crosses between page breaks. If the data does cross page breaks then
-it will have to be split up into several transfers, with one page per
-transfer.
-
-Setting the playback frequency for the DMA transfer is done by writing
-the value 40h to the DSP followed by TIME_CONSTANT, where
-TIME_CONSTANT = 256 - 1000000 / frequency
-
-There are several types of DMA transfers available. The following table
-lists them:
-
- ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
- ³DMA_TYPE_VALUE Description Frequency Range ³
- ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
- ³ 14h 8 bit 4KHz -> 23 KHz ³
- ³ 74h 4 bit ADPCM 4KHz -> 12 KHz ³
- ³ 75h 4 bit ADPCM with 4KHz -> 12 KHz ³
- ³ reference byte ³
- ³ 76h 2.6 bit ADPCM 4KHz -> 13 KHz ³
- ³ 77h 2.6 bit ADPCM with 4KHz -> 13 KHz ³
- ³ reference byte ³
- ³ 16h 2 bit ADPCM 4KHz -> 11 KHz ³
- ³ 17h 2 bit ADPCM with 4KHz -> 11 KHz ³
- ³ reference byte ³
- ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-ADPCM stands for Adaptive Pulse Code Modulation, a sound compression
-technique where the difference between successive samples is stored rather
-than their actual values. In the modes with reference bytes, the first
-byte is the actual starting value. Having modes with and without reference
-bytes means you can output successive blocks without the need for a
-reference byte at the start of each one.
-
-The procedure for doing a DMA transfer is as follows:
-
-1) Load the sound data into memory
-2) Set up the DMA chip for the tranfer
-3) Set the DSP TIME_CONSTANT to the sampling rate
-4) Write DMA_TYPE_VALUE value to the DSP
-5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where
- DATA_LENGTH = number of bytes to send - 1
-
-Note that the DMA chip must be programmed before the BSP.
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Reading from the ADC ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-Reading samples from the ADC (Analog to Digital Converter) can also be
-done in either direct or DMA mode.
-
-To read a sample in direct mode write the value 20h to the DSP and then
-read the value from the DSP. Simple as that!
-
-To set up the DSP for a DMA transfer, follow this procedure :
-
-1) Get a memory buffer ready to hold the sample
-2) Set up the DMA chip for the transfer
-3) Set the DSP TIME_CONSTANT to the sampling rate
-4) Write the value 24h to the DSP
-5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where
- DATA_LENGTH = number of bytes to read - 1
-
-Note that the DMA chip must be programmed before the BSP.
-
-DMA reads only support 8 bit mode, compressed modes are done by software and
-stored in the voc file. I haven't tried to figure out how the compression is
-done. If someone does figure it out I'd like to know about it!
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Programming the DMA Chip ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-As mentioned before, Draeden has written a very good article on the dma
-chip, but here is a brief run down on what you would need to do to program
-the DMA channel 1 for the DSP in real mode:
-
-1) Calculate the 20 bit address of the memory buffer you are using
- where Base Address = Segment * 16 + Offset
- eg 1234h:5678h = 179B8h
-2) Send the value 05h to port 0Ah (mask off channel 1)
-3) Send the value 00h to port 0Ch (clear the internal DMA flip/flop)
-4) Send the value 49h to port 0Bh (for playback) or
- 45h to port 0Bh (for recording)
-5) Write the LSB (bits 0 -> 7) of the 20 bit memory address to port 02h
-6) Write the MSB (bits 8 -> 15) of the 20 bit memory address to ort 02h
-7) Write the Page (bits 16 -> 19) of the 20 bit memory address to port 83h
-8) Send the LSB of DATA_LENGTH to port 03h
-9) Send the MSB of DATA_LENGTH to port 03h
-10) Send the value 01h to port 0Ah (enable channel 1)
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ End of DMA Interrupt ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-When a DMA transfer is complete an interrupt is generated. The actual
-interrupt number depends on the SoundBlaster card's IRQ jumper setting:
-
- ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
- ³ IRQ Jumper ³
- ³ Setting Interrupt ³
- ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
- ³ 2 0Ah ³
- ³ 3 0Bh ³
- ³ 5 0Dh ³
- ³ 7 0Fh ³
- ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-To service one of these interrupts you must perform these 3 tasks:
-
-1) Acknowledge the DSP interrupt by reading the DATA AVAILABLE port (2xEh)
- once.
-2) If there are more blocks to transfer then set them up
-3) Output value 20h (EOI) to the interrupt controller port 20h
-
-Of course, as with any hardware interrupt you must also leave the
-state of the system (registers etc..) the way it was when the interrupt
-was called.
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ A Simple DSP Pascal Unit ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-{
-
- DSP.PAS - A demo SoundBlaster DSP unit for real mode
-
- By Mark Feldman
-}
-
-Unit DSP;
-
-Interface
-
-{ ResetDSP returns true if reset was successful
- base should be 1 for base address 210h, 2 for 220h etc... }
-function ResetDSP(base : word) : boolean;
-
-{ Write DAC sets the speaker output level }
-procedure WriteDAC(level : byte);
-
-{ ReadDAC reads the microphone input level }
-function ReadDAC : byte;
-
-{ SpeakerOn connects the DAC to the speaker }
-function SpeakerOn: byte;
-
-{ SpeakerOff disconnects the DAC from the speaker,
- but does not affect the DAC operation }
-function SpeakerOff: byte;
-
-{ Functions to pause DMA playback }
-procedure DMAStop;
-procedure DMAContinue;
-
-{ Playback plays a sample of a given size back at a given frequency using
- DMA channel 1. The sample must not cross a page boundry }
-procedure Playback(sound : Pointer; size : word; frequency : word);
-
-Implementation
-
-Uses Crt;
-
-var DSP_RESET : word;
- DSP_READ_DATA : word;
- DSP_WRITE_DATA : word;
- DSP_WRITE_STATUS : word;
- DSP_DATA_AVAIL : word;
-
-function ResetDSP(base : word) : boolean;
-begin
-
- base := base * $10;
-
- { Calculate the port addresses }
- DSP_RESET := base + $206;
- DSP_READ_DATA := base + $20A;
- DSP_WRITE_DATA := base + $20C;
- DSP_WRITE_STATUS := base + $20C;
- DSP_DATA_AVAIL := base + $20E;
-
- { Reset the DSP, and give some nice long delays just to be safe }
- Port[DSP_RESET] := 1;
- Delay(10);
- Port[DSP_RESET] := 0;
- Delay(10);
- if (Port[DSP_DATA_AVAIL] And $80 = $80) And
- (Port[DSP_READ_DATA] = $AA) then
- ResetDSP := true
- else
- ResetDSP := false;
-end;
-
-procedure WriteDSP(value : byte);
-begin
- while Port[DSP_WRITE_STATUS] And $80 <> 0 do;
- Port[DSP_WRITE_DATA] := value;
-end;
-
-function ReadDSP : byte;
-begin
- while Port[DSP_DATA_AVAIL] and $80 = 0 do;
- ReadDSP := Port[DSP_READ_DATA];
-end;
-
-procedure WriteDAC(level : byte);
-begin
- WriteDSP($10);
- WriteDSP(level);
-end;
-
-function ReadDAC : byte;
-begin
- WriteDSP($20);
- ReadDAC := ReadDSP;
-end;
-
-function SpeakerOn: byte;
-begin
- WriteDSP($D1);
-end;
-
-function SpeakerOff: byte;
-begin
- WriteDSP($D3);
-end;
-
-procedure DMAContinue;
-begin
- WriteDSP($D4);
-end;
-
-procedure DMAStop;
-begin
- WriteDSP($D0);
-end;
-
-procedure Playback(sound : Pointer; size : word; frequency : word);
-var time_constant : word;
- page, offset : word;
-begin
-
- SpeakerOn;
-
- size := size - 1;
-
- { Set up the DMA chip }
- offset := Seg(sound^) Shl 4 + Ofs(sound^);
- page := (Seg(sound^) + Ofs(sound^) shr 4) shr 12;
- Port[$0A] := 5;
- Port[$0C] := 0;
- Port[$0B] := $49;
- Port[$02] := Lo(offset);
- Port[$02] := Hi(offset);
- Port[$83] := page;
- Port[$03] := Lo(size);
- Port[$03] := Hi(size);
- Port[$0A] := 1;
-
- { Set the playback frequency }
- time_constant := 256 - 1000000 div frequency;
- WriteDSP($40);
- WriteDSP(time_constant);
-
- { Set the playback type (8-bit) }
- WriteDSP($14);
- WriteDSP(Lo(size));
- WriteDSP(Hi(size));
-end;
-
-end.
-
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ References ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-Title : The SoundBlaster Developpers Kit
-Publishers : Creative Labs Inc
- Creative Technology PTE LTD
-
-Title : Sound Blaster - The Official Book
-Authors : Richard Heimlich, David M. Golden, Ivan Luk, Peter M. Ridge
-Publishers : Osborne/McGraw Hill
-ISBN : 0-07-881907-5
-
-Some of the information in this file was either obtained from or verified
-by the source code in a public domain library called SOUNDX by Peter
-Sprenger. I haven't tried using his library yet (I don't have a C compiler
-at the moment) but it looks very well done and contains numerous sound card
-detection routines. Says Peter : "It would be nice, that when you make
-something commercial with my routines, that you send me a copy of your
-project or send me some bucks, just enough for pizza and coke to support my
-night programming sessions. If you send me nothing, ok. But USE the stuff,
-if you can need it!". Heh...a REAL programmer!
-
-ftpsite: ftp.uwp.edu
-directory: /pub/msdos/demos/programming/game-dev/source
-filename: soundx.zip
-
-ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
-³ Sound Familiar? ³
-ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
-
-What the...why is there a faint glimmer of sunlight outside? HOLY $#!^!! It's
-5:30am! I'm goin' to bed!
-
+++ /dev/null
-#
-#
-
-OBJ = main.o io.o mbr.o
-NAME = ATA
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 IDE Harddisk Driver
- * - main.c
- */
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-#include <acess.h>
-#include <vfs.h>
-
-// === CONSTANTS ===
-#define MAX_ATA_DISKS 4
-#define SECTOR_SIZE 512
-#define ATA_TIMEOUT 2000 // 2s timeout
-// Needed out of io.c because it's the max for Read/WriteDMA
-#define MAX_DMA_SECTORS (0x1000 / SECTOR_SIZE)
-
-// === STRUCTURES ===
-typedef struct
-{
- Uint8 BootCode[0x1BE];
- struct {
- Uint8 Boot;
- Uint8 Unused1; // Also CHS Start
- Uint16 StartHi; // Also CHS Start
- Uint8 SystemID;
- Uint8 Unused2; // Also CHS Length
- Uint16 LengthHi; // Also CHS Length
- Uint32 LBAStart;
- Uint32 LBALength;
- } __attribute__ ((packed)) Parts[4];
- Uint16 BootFlag; // = 0xAA 55
-} __attribute__ ((packed)) tMBR;
-
-typedef struct
-{
- Uint64 Start;
- Uint64 Length;
- char Name[4];
- tVFS_Node Node;
-} tATA_Partition;
-
-typedef struct
-{
- Uint64 Sectors;
- char Name[2];
- tVFS_Node Node;
- int NumPartitions;
- tATA_Partition *Partitions;
-} tATA_Disk;
-
-// === GLOBALS ===
-extern tATA_Disk gATA_Disks[];
-
-// === FUNCTIONS ===
-// --- Common ---
-extern void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length);
-
-// --- MBR Parsing ---
-extern void ATA_ParseMBR(int Disk, tMBR *MBR);
-
-// --- IO Functions ---
-extern int ATA_SetupIO(void);
-extern Uint64 ATA_GetDiskSize(int Disk);
-extern int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer);
-extern int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer);
-
-#endif
+++ /dev/null
-/*
- * Acess2 IDE Harddisk Driver
- * - io.c
- *
- * Disk Input/Output control
- */
-#define DEBUG 0
-#include <acess.h>
-#include <modules.h> // Needed for error codes
-#include <drv_pci.h>
-#include "common.h"
-
-// === MACROS ===
-#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
-
-// === Constants ===
-#define IDE_PRI_BASE 0x1F0
-#define IDE_PRI_CTRL 0x3F6
-#define IDE_SEC_BASE 0x170
-#define IDE_SEC_CTRL 0x376
-
-#define IDE_PRDT_LAST 0x8000
-/**
- \enum HddControls
- \brief Commands to be sent to HDD_CMD
-*/
-enum HddControls {
- HDD_PIO_R28 = 0x20,
- HDD_PIO_R48 = 0x24,
- HDD_DMA_R48 = 0x25,
- HDD_PIO_W28 = 0x30,
- HDD_PIO_W48 = 0x34,
- HDD_DMA_W48 = 0x35,
- HDD_DMA_R28 = 0xC8,
- HDD_DMA_W28 = 0xCA,
- HDD_IDENTIFY = 0xEC
-};
-
-// === TYPES ===
-/**
- * \brief PRDT Entry
- */
-typedef struct
-{
- Uint32 PBufAddr; // Physical Buffer Address
- Uint16 Bytes; // Size of transfer entry
- Uint16 Flags; // Flags
-} __attribute__ ((packed)) tPRDT_Ent;
-
-/**
- * \brief Structure returned by the ATA IDENTIFY command
- */
-typedef struct
-{
- Uint16 Flags; // 1
- Uint16 Usused1[9]; // 10
- char SerialNum[20]; // 20
- Uint16 Usused2[3]; // 23
- char FirmwareVer[8]; // 27
- char ModelNumber[40]; // 47
- Uint16 SectPerInt; // 48 - Low byte only
- Uint16 Unused3; // 49
- Uint16 Capabilities[2]; // 51
- Uint16 Unused4[2]; // 53
- Uint16 ValidExtData; // 54
- Uint16 Unused5[5]; // 59
- Uint16 SizeOfRWMultiple; // 60
- Uint32 Sectors28; // LBA 28 Sector Count
- Uint16 Unused6[100-62];
- Uint64 Sectors48; // LBA 48 Sector Count
- Uint16 Unused7[256-104];
-} __attribute__ ((packed)) tIdentify;
-
-// === PROTOTYPES ===
- int ATA_SetupIO(void);
-Uint64 ATA_GetDiskSize(int Disk);
-Uint16 ATA_GetBasePort(int Disk);
-// Read/Write DMA
- int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer);
- int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer);
-// IRQs
-void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr));
-void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr));
-// Controller IO
-Uint8 ATA_int_BusMasterReadByte(int Ofs);
-Uint32 ATA_int_BusMasterReadDWord(int Ofs);
-void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value);
-void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value);
-
-// === GLOBALS ===
-// - BusMaster IO Addresses
-Uint32 gATA_BusMasterBase; //!< True Address (IO/MMIO)
-Uint8 *gATA_BusMasterBasePtr; //!< Paging Mapped MMIO (If needed)
-// - IRQs
- int gATA_IRQPri = 14;
- int gATA_IRQSec = 15;
-volatile int gaATA_IRQs[2] = {0};
-// - Locks to avoid tripping
-tMutex glaATA_ControllerLock[2];
-// - Buffers!
-Uint8 gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata")));
-// - PRDTs
-tPRDT_Ent gATA_PRDTs[2] = {
- {0, 512, IDE_PRDT_LAST},
- {0, 512, IDE_PRDT_LAST}
-};
-tPAddr gaATA_PRDT_PAddrs[2];
-
-// === CODE ===
-/**
- * \brief Sets up the ATA controller's DMA mode
- */
-int ATA_SetupIO(void)
-{
- int ent;
-
- ENTER("");
-
- // Get IDE Controller's PCI Entry
- ent = PCI_GetDeviceByClass(0x010100, 0xFFFF00, -1);
- LOG("ent = %i", ent);
- gATA_BusMasterBase = PCI_GetBAR(ent, 4);
- if( gATA_BusMasterBase == 0 ) {
- Log_Warning("ATA", "It seems that there is no Bus Master Controller on this machine. Get one");
- // TODO: Use PIO mode instead
- LEAVE('i', MODULE_ERR_NOTNEEDED);
- return MODULE_ERR_NOTNEEDED;
- }
-
- LOG("BAR5 = 0x%x", PCI_GetBAR(ent, 5));
- LOG("IRQ = %i", PCI_GetIRQ(ent));
-
- // Map memory
- if( !(gATA_BusMasterBase & 1) )
- {
- if( gATA_BusMasterBase < 0x100000 )
- gATA_BusMasterBasePtr = (void*)(KERNEL_BASE | (tVAddr)gATA_BusMasterBase);
- else
- gATA_BusMasterBasePtr = (void*)( MM_MapHWPages( gATA_BusMasterBase, 1 ) + (gATA_BusMasterBase&0xFFF) );
- LOG("gATA_BusMasterBasePtr = %p", gATA_BusMasterBasePtr);
- }
- else {
- // Bit 0 is left set as a flag to other functions
- LOG("gATA_BusMasterBase = IO 0x%x", gATA_BusMasterBase & ~1);
- }
-
- // Register IRQs and get Buffers
- IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri, NULL );
- IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec, NULL );
-
- gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[0] );
- gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[1] );
-
- LOG("gATA_PRDTs = {PBufAddr: 0x%x, PBufAddr: 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr);
-
- gaATA_PRDT_PAddrs[0] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[0] );
- LOG("gaATA_PRDT_PAddrs[0] = 0x%x", gaATA_PRDT_PAddrs[0]);
- ATA_int_BusMasterWriteDWord(4, gaATA_PRDT_PAddrs[0]);
-
- gaATA_PRDT_PAddrs[1] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[1] );
- LOG("gaATA_PRDT_PAddrs[1] = 0x%x", gaATA_PRDT_PAddrs[1]);
- ATA_int_BusMasterWriteDWord(12, gaATA_PRDT_PAddrs[1]);
-
- // Enable controllers
- outb(IDE_PRI_BASE+1, 1);
- outb(IDE_SEC_BASE+1, 1);
- outb(IDE_PRI_CTRL, 0);
- outb(IDE_SEC_CTRL, 0);
-
- // Make sure interrupts are ACKed
- ATA_int_BusMasterWriteByte(2, 0x4);
- ATA_int_BusMasterWriteByte(10, 0x4);
-
- // return
- LEAVE('i', MODULE_ERR_OK);
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Get the size (in sectors) of a disk
- * \param Disk Disk to get size of
- * \return Number of sectors reported
- *
- * Does an ATA IDENTIFY
- */
-Uint64 ATA_GetDiskSize(int Disk)
-{
- union {
- Uint16 buf[256];
- tIdentify identify;
- } data;
- Uint16 base;
- Uint8 val;
- int i;
- ENTER("iDisk", Disk);
-
- base = ATA_GetBasePort( Disk );
-
- // Send Disk Selector
- if(Disk & 1) // Slave
- outb(base+6, 0xB0);
- else // Master
- outb(base+6, 0xA0);
- IO_DELAY();
-
- // Check for a floating bus
- if( 0xFF == inb(base+7) ) {
- LOG("Floating bus");
- LEAVE('i', 0);
- return 0;
- }
-
- // Check for the controller
- // - Write to two RW ports and attempt to read back
- outb(base+0x02, 0x66);
- outb(base+0x03, 0xFF);
- if(inb(base+0x02) != 0x66 || inb(base+0x03) != 0xFF) {
- LOG("No controller");
- LEAVE('i', 0);
- return 0;
- }
-
- // Send ATA IDENTIFY
- outb(base+7, HDD_IDENTIFY);
- IO_DELAY();
- val = inb(base+7); // Read status
- LOG("val = 0x%02x", val);
- if(val == 0) {
- LEAVE('i', 0);
- return 0; // Disk does not exist
- }
-
- // Poll until BSY clears or ERR is set
- // TODO: Timeout?
- while( (val & 0x80) && !(val & 1) )
- val = inb(base+7);
- LOG("BSY unset (0x%x)", val);
- // and, wait for DRQ to set
- while( !(val & 0x08) && !(val & 1))
- val = inb(base+7);
- LOG("DRQ set (0x%x)", val);
-
- // Check for an error
- if(val & 1) {
- LEAVE('i', 0);
- return 0; // Error occured, so return false
- }
-
- // Read Data
- for( i = 0; i < 256; i++ )
- data.buf[i] = inw(base);
-
- // Return the disk size
- if(data.identify.Sectors48 != 0) {
- LEAVE('X', data.identify.Sectors48);
- return data.identify.Sectors48;
- }
- else {
- LEAVE('x', data.identify.Sectors28);
- return data.identify.Sectors28;
- }
-}
-
-/**
- * \fn Uint16 ATA_GetPortBase(int Disk)
- * \brief Returns the base port for a given disk
- */
-Uint16 ATA_GetBasePort(int Disk)
-{
- switch(Disk)
- {
- case 0: case 1: return IDE_PRI_BASE;
- case 2: case 3: return IDE_SEC_BASE;
- }
- return 0;
-}
-
-/**
- * \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
- * \return Boolean Failure
- */
-int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
-{
- int cont = (Disk>>1)&1; // Controller ID
- int disk = Disk & 1;
- Uint16 base;
- Sint64 timeoutTime;
-
- ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer);
-
- // Check if the count is small enough
- if(Count > MAX_DMA_SECTORS) {
- Log_Warning("ATA", "Passed too many sectors for a bulk DMA read (%i > %i)",
- Count, MAX_DMA_SECTORS);
- LEAVE('i');
- return 1;
- }
-
- // Hack to make debug hexdump noticable
- #if 1
- memset(Buffer, 0xFF, Count*SECTOR_SIZE);
- #endif
-
- // Get exclusive access to the disk controller
- Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
-
- // Set Size
- gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
-
- // Get Port Base
- base = ATA_GetBasePort(Disk);
-
- // Reset IRQ Flag
- gaATA_IRQs[cont] = 0;
-
- #if 1
- if( cont == 0 ) {
- outb(IDE_PRI_CTRL, 4);
- IO_DELAY();
- outb(IDE_PRI_CTRL, 0);
- }
- else {
- outb(IDE_SEC_CTRL, 4);
- IO_DELAY();
- outb(IDE_SEC_CTRL, 0);
- }
- #endif
-
- // Set up transfer
- if( Address > 0x0FFFFFFF ) // Use LBA48
- {
- outb(base+0x6, 0x40 | (disk << 4));
- IO_DELAY();
- outb(base+0x2, 0 >> 8); // Upper Sector Count
- outb(base+0x3, Address >> 24); // Low 2 Addr
- outb(base+0x4, Address >> 28); // Mid 2 Addr
- outb(base+0x5, Address >> 32); // High 2 Addr
- }
- else
- {
- // Magic, Disk, High Address nibble
- outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
- //outb(base+0x06, 0xA0 | (disk << 4) | ((Address >> 24) & 0x0F));
- IO_DELAY();
- }
-
- //outb(base+0x01, 0x01); //?
- outb(base+0x02, Count & 0xFF); // Sector Count
- outb(base+0x03, Address & 0xFF); // Low Addr
- outb(base+0x04, (Address >> 8) & 0xFF); // Middle Addr
- outb(base+0x05, (Address >> 16) & 0xFF); // High Addr
-
- LOG("Starting Transfer");
-
- // HACK: Ensure the PRDT is reset
- ATA_int_BusMasterWriteDWord(cont*8+4, gaATA_PRDT_PAddrs[cont]);
- ATA_int_BusMasterWriteByte(cont*8, 4); // Reset IRQ
-
- LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes);
- if( Address > 0x0FFFFFFF )
- outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48)
- else
- outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28)
-
- // Start transfer
- ATA_int_BusMasterWriteByte( cont * 8, 9 ); // Read and start
-
- // Wait for transfer to complete
- timeoutTime = now() + ATA_TIMEOUT;
- while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
- {
- HALT();
- }
-
- // Complete Transfer
- ATA_int_BusMasterWriteByte( cont * 8, 8 ); // Read and stop
-
- #if DEBUG
- {
- Uint8 val = inb(base+0x7);
- LOG("Status byte = 0x%02x, Controller Status = 0x%02x",
- val, ATA_int_BusMasterReadByte(cont * 8 + 2));
- }
- #else
- inb(base+0x7);
- #endif
-
- if( gaATA_IRQs[cont] == 0 )
- {
- if( ATA_int_BusMasterReadByte(cont * 8 + 2) & 0x4 ) {
- Log_Error("ATA", "BM Status reports an interrupt, but none recieved");
- ATA_int_BusMasterWriteByte(cont*8 + 2, 4); // Clear interrupt
- memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
- Mutex_Release( &glaATA_ControllerLock[ cont ] );
- LEAVE('i', 0);
- return 0;
- }
-
- #if 1
- Debug_HexDump("ATA", Buffer, 512);
- #endif
-
- // Release controller lock
- Mutex_Release( &glaATA_ControllerLock[ cont ] );
- Log_Warning("ATA",
- "Read timeout on disk %i (Reading sector 0x%llx)",
- Disk, Address);
- // Return error
- LEAVE('i', 1);
- return 1;
- }
- else {
- LOG("Transfer Completed & Acknowledged");
- // Copy to destination buffer
- memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE );
- // Release controller lock
- Mutex_Release( &glaATA_ControllerLock[ cont ] );
-
- LEAVE('i', 0);
- return 0;
- }
-}
-
-/**
- * \fn int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer)
- * \brief Write up to \a MAX_DMA_SECTORS to a disk
- * \param Disk Disk ID to write to
- * \param Address LBA of first sector
- * \param Count Number of sectors to write (must be >= \a MAX_DMA_SECTORS)
- * \param Buffer Source buffer for data
- * \return Boolean Failure
- */
-int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer)
-{
- int cont = (Disk>>1)&1; // Controller ID
- int disk = Disk & 1;
- Uint16 base;
- Sint64 timeoutTime;
-
- // Check if the count is small enough
- if(Count > MAX_DMA_SECTORS) return 1;
-
- // Get exclusive access to the disk controller
- Mutex_Acquire( &glaATA_ControllerLock[ cont ] );
-
- // Set Size
- gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE;
-
- // Get Port Base
- base = ATA_GetBasePort(Disk);
-
- // Reset IRQ Flag
- gaATA_IRQs[cont] = 0;
-
- // Set up transfer
- outb(base+0x01, 0x00);
- if( Address > 0x0FFFFFFF ) // Use LBA48
- {
- outb(base+0x6, 0x40 | (disk << 4));
- outb(base+0x2, 0 >> 8); // Upper Sector Count
- outb(base+0x3, Address >> 24); // Low 2 Addr
- outb(base+0x3, Address >> 28); // Mid 2 Addr
- outb(base+0x3, Address >> 32); // High 2 Addr
- }
- else
- {
- // Magic, Disk, High Address nibble
- outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F));
- }
-
- outb(base+0x02, (Uint8) Count); // Sector Count
- outb(base+0x03, (Uint8) Address); // Low Addr
- outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr
- outb(base+0x05, (Uint8) (Address >> 16)); // High Addr
- if( Address > 0x0FFFFFFF )
- outb(base+0x07, HDD_DMA_W48); // Write Command (LBA48)
- else
- outb(base+0x07, HDD_DMA_W28); // Write Command (LBA28)
-
- // Copy to output buffer
- memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE );
-
- // Start transfer
- ATA_int_BusMasterWriteByte( cont << 3, 1 ); // Write and start
-
- // Wait for transfer to complete
- timeoutTime = now() + ATA_TIMEOUT;
- while( gaATA_IRQs[cont] == 0 && now() < timeoutTime)
- {
- HALT();
- }
-
- // Complete Transfer
- ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop
-
- // If the IRQ is unset, return error
- if( gaATA_IRQs[cont] == 0 ) {
- // Release controller lock
- Mutex_Release( &glaATA_ControllerLock[ cont ] );
- return 1; // Error
- }
- else {
- Mutex_Release( &glaATA_ControllerLock[ cont ] );
- return 0;
- }
-}
-
-/**
- * \brief Primary ATA Channel IRQ handler
- */
-void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr))
-{
- Uint8 val;
-
- // IRQ bit set for Primary Controller
- val = ATA_int_BusMasterReadByte( 0x2 );
- LOG("IRQ val = 0x%x", val);
- if(val & 4) {
- LOG("IRQ hit (val = 0x%x)", val);
- ATA_int_BusMasterWriteByte( 0x2, 4 );
- gaATA_IRQs[0] = 1;
- return ;
- }
-}
-
-/**
- * \brief Second ATA Channel IRQ handler
- */
-void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr))
-{
- Uint8 val;
- // IRQ bit set for Secondary Controller
- val = ATA_int_BusMasterReadByte( 0xA );
- LOG("IRQ val = 0x%x", val);
- if(val & 4) {
- LOG("IRQ hit (val = 0x%x)", val);
- ATA_int_BusMasterWriteByte( 0xA, 4 );
- gaATA_IRQs[1] = 1;
- return ;
- }
-}
-
-/**
- * \brief Read an 8-bit value from a Bus Master register
- * \param Ofs Register offset
- */
-Uint8 ATA_int_BusMasterReadByte(int Ofs)
-{
- if( gATA_BusMasterBase & 1 )
- return inb( (gATA_BusMasterBase & ~1) + Ofs );
- else
- return *(Uint8*)(gATA_BusMasterBasePtr + Ofs);
-}
-
-/**
- * \brief Read an 32-bit value from a Bus Master register
- * \param Ofs Register offset
- */
-Uint32 ATA_int_BusMasterReadDWord(int Ofs)
-{
- if( gATA_BusMasterBase & 1 )
- return ind( (gATA_BusMasterBase & ~1) + Ofs );
- else
- return *(Uint32*)(gATA_BusMasterBasePtr + Ofs);
-}
-
-/**
- * \brief Writes a byte to a Bus Master Register
- * \param Ofs Register Offset
- * \param Value Value to write
- */
-void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value)
-{
- if( gATA_BusMasterBase & 1 )
- outb( (gATA_BusMasterBase & ~1) + Ofs, Value );
- else
- *(Uint8*)(gATA_BusMasterBasePtr + Ofs) = Value;
-}
-
-/**
- * \brief Writes a 32-bit value to a Bus Master Register
- * \param Ofs Register offset
- * \param Value Value to write
- */
-void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value)
-{
- if( gATA_BusMasterBase & 1 )
- outd( (gATA_BusMasterBase & ~1) + Ofs, Value );
- else
- *(Uint32*)(gATA_BusMasterBasePtr + Ofs) = Value;
-}
+++ /dev/null
-/*
- * Acess2 IDE Harddisk Driver
- * - main.c
- */
-#define DEBUG 0
-#define VERSION 0x0032
-#include <acess.h>
-#include <modules.h>
-#include <vfs.h>
-#include <fs_devfs.h>
-#include <api_drv_common.h>
-#include <api_drv_disk.h>
-#include "common.h"
-
-// === MACROS ===
-#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0)
-
-// === PROTOTYPES ===
- int ATA_Install(char **Arguments);
-void ATA_SetupPartitions(void);
-void ATA_SetupVFS(void);
- int ATA_ScanDisk(int Disk);
-void ATA_ParseGPT(int Disk);
-void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length);
-Uint16 ATA_GetBasePort(int Disk);
-// Filesystem Interface
-char *ATA_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name);
-Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
-Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
- int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data);
-// Read/Write Interface/Quantiser
-Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk);
-Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, i386ATA, ATA_Install, NULL, "PCI", NULL);
-tVFS_NodeType gATA_RootNodeType = {
- .TypeName = "ATA Root Node",
- .ReadDir = ATA_ReadDir,
- .FindDir = ATA_FindDir
- };
-tVFS_NodeType gATA_DiskNodeType = {
- .TypeName = "ATA Volume",
- .Read = ATA_ReadFS,
- .Write = ATA_WriteFS,
- .IOCtl = ATA_IOCtl
- };
-tDevFS_Driver gATA_DriverInfo = {
- NULL, "ata",
- {
- .NumACLs = 1,
- .Size = -1,
- .Flags = VFS_FFLAG_DIRECTORY,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Type = &gATA_RootNodeType
- }
-};
-tATA_Disk gATA_Disks[MAX_ATA_DISKS];
- int giATA_NumNodes;
-tVFS_Node **gATA_Nodes;
-
-// === CODE ===
-/**
- * \brief Initialise the ATA driver
- */
-int ATA_Install(char **Arguments)
-{
- int ret;
-
- ret = ATA_SetupIO();
- if(ret) return ret;
-
- ATA_SetupPartitions();
-
- ATA_SetupVFS();
-
- if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 )
- return MODULE_ERR_MISC;
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Scan all disks, looking for partitions
- */
-void ATA_SetupPartitions(void)
-{
- int i;
- for( i = 0; i < MAX_ATA_DISKS; i ++ )
- {
- if( !ATA_ScanDisk(i) ) {
- gATA_Disks[i].Name[0] = '\0'; // Mark as unused
- continue;
- }
- }
-}
-
-/**
- * \brief Sets up the ATA drivers VFS information and registers with DevFS
- */
-void ATA_SetupVFS(void)
-{
- int i, j, k;
-
- // Count number of nodes needed
- giATA_NumNodes = 0;
- for( i = 0; i < MAX_ATA_DISKS; i++ )
- {
- if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore
- giATA_NumNodes ++;
- giATA_NumNodes += gATA_Disks[i].NumPartitions;
- }
-
- // Allocate Node space
- gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) );
-
- // Set nodes
- k = 0;
- for( i = 0; i < MAX_ATA_DISKS; i++ )
- {
- if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore
- gATA_Nodes[ k++ ] = &gATA_Disks[i].Node;
- for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ )
- gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node;
- }
-
- gATA_DriverInfo.RootNode.Size = giATA_NumNodes;
-}
-
-/**
- * \brief Scan a disk, getting the size and any paritions
- * \param Disk Disk ID to scan
- */
-int ATA_ScanDisk(int Disk)
-{
- tVFS_Node *node;
- tMBR mbr;
-
- ENTER("iDisk", Disk);
-
- // Get the disk size
- gATA_Disks[ Disk ].Sectors = ATA_GetDiskSize(Disk);
- if(gATA_Disks[ Disk ].Sectors == 0)
- {
- LEAVE('i', 0);
- return 0;
- }
-
- LOG("gATA_Disks[ %i ].Sectors = 0x%x", Disk, gATA_Disks[ Disk ].Sectors);
-
- // Create Name
- gATA_Disks[ Disk ].Name[0] = 'A'+Disk;
- gATA_Disks[ Disk ].Name[1] = '\0';
-
- #if 1
- {
- Uint64 val = gATA_Disks[ Disk ].Sectors / 2;
- char *units = "KiB";
- if( val > 4*1024 ) {
- val /= 1024;
- units = "MiB";
- }
- else if( val > 4*1024 ) {
- val /= 1024;
- units = "GiB";
- }
- else if( val > 4*1024 ) {
- val /= 1024;
- units = "TiB";
- }
- Log_Notice("ATA", "Disk %s: 0x%llx Sectors (%lli %s)",
- gATA_Disks[ Disk ].Name, gATA_Disks[ Disk ].Sectors, val, units);
- }
- #endif
-
- // Get pointer to vfs node and populate it
- node = &gATA_Disks[ Disk ].Node;
- node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE;
- node->NumACLs = 0; // Means Superuser only can access it
- node->Inode = (Disk << 8) | 0xFF;
- node->ImplPtr = gATA_Disks[ Disk ].Name;
-
- node->ATime = node->MTime = node->CTime = now();
-
- node->Type = &gATA_DiskNodeType;
-
- // --- Scan Partitions ---
- LOG("Reading MBR");
- // Read Boot Sector
- if( ATA_ReadDMA( Disk, 0, 1, &mbr ) != 0 ) {
- Log_Warning("ATA", "Error in reading MBR on %i", Disk);
- LEAVE('i', 0);
- return 0;
- }
-
- // Check for a GPT table
- if(mbr.Parts[0].SystemID == 0xEE)
- ATA_ParseGPT(Disk);
- else // No? Just parse the MBR
- ATA_ParseMBR(Disk, &mbr);
-
- #if DEBUG >= 2
- ATA_ReadDMA( Disk, 1, 1, &mbr );
- Debug_HexDump("ATA_ScanDisk", &mbr, 512);
- #endif
-
- LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
- * \brief Fills a parition's information structure
- */
-void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length)
-{
- ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length);
- Part->Start = Start;
- Part->Length = Length;
- Part->Name[0] = 'A'+Disk;
- if(Num >= 10) {
- Part->Name[1] = '1'+Num/10;
- Part->Name[2] = '1'+Num%10;
- Part->Name[3] = '\0';
- } else {
- Part->Name[1] = '1'+Num;
- Part->Name[2] = '\0';
- }
- Part->Node.NumACLs = 0; // Only root can read/write raw block devices
- Part->Node.Inode = (Disk << 8) | Num;
- Part->Node.ImplPtr = Part->Name;
-
- Part->Node.Type = &gATA_DiskNodeType;
- Log_Notice("ATA", "Partition %s at 0x%llx+0x%llx", Part->Name, Part->Start, Part->Length);
- LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node);
- LEAVE('-');
-}
-
-/**
- * \fn void ATA_ParseGPT(int Disk)
- * \brief Parses the GUID Partition Table
- */
-void ATA_ParseGPT(int Disk)
-{
- ///\todo Support GPT Disks
- Warning("GPT Disks are currently unsupported (Disk %i)", Disk);
-}
-
-/**
- * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos)
- */
-char *ATA_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
-{
- if(Pos >= giATA_NumNodes || Pos < 0) return NULL;
- return strdup( gATA_Nodes[Pos]->ImplPtr );
-}
-
-/**
- * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name)
- */
-tVFS_Node *ATA_FindDir(tVFS_Node *UNUSED(Node), const char *Name)
-{
- int part;
- tATA_Disk *disk;
-
- // Check first character
- if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS)
- return NULL;
- disk = &gATA_Disks[Name[0]-'A'];
- // Raw Disk
- if(Name[1] == '\0') {
- if( disk->Sectors == 0 && disk->Name[0] == '\0')
- return NULL;
- return &disk->Node;
- }
-
- // Partitions
- if(Name[1] < '0' || '9' < Name[1]) return NULL;
- if(Name[2] == '\0') { // <= 9
- part = Name[1] - '0';
- part --;
- return &disk->Partitions[part].Node;
- }
- // > 9
- if('0' > Name[2] || '9' < Name[2]) return NULL;
- if(Name[3] != '\0') return NULL;
-
- part = (Name[1] - '0') * 10;
- part += Name[2] - '0';
- part --;
- return &disk->Partitions[part].Node;
-
-}
-
-/**
- * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- */
-Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- int disk = Node->Inode >> 8;
- int part = Node->Inode & 0xFF;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- // Raw Disk Access
- if(part == 0xFF)
- {
- if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) {
- LEAVE('i', 0);
- return 0;
- }
- if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
- Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
- }
- // Partition
- else
- {
- if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) {
- LEAVE('i', 0);
- return 0;
- }
- if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
- Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
- Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
- }
-
- {
- int ret = DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk);
- //Log("ATA_ReadFS: disk=%i, Offset=%lli, Length=%lli", disk, Offset, Length);
- //Debug_HexDump("ATA_ReadFS", Buffer, Length);
- LEAVE('i', ret);
- return ret;
- }
-}
-
-/**
- * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
- */
-Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- int disk = Node->Inode >> 8;
- int part = Node->Inode & 0xFF;
-
- // Raw Disk Access
- if(part == 0xFF)
- {
- if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE )
- return 0;
- if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE )
- Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset;
- }
- // Partition
- else
- {
- if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
- return 0;
- if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE )
- Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset;
- Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE;
- }
-
- Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer);
- Debug_HexDump("ATA_WriteFS", Buffer, Length);
- return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk);
-}
-
-const char *csaATA_IOCtls[] = {DRV_IOCTLNAMES, DRV_DISK_IOCTLNAMES, NULL};
-/**
- * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief IO Control Funtion
- */
-int ATA_IOCtl(tVFS_Node *UNUSED(Node), int Id, void *Data)
-{
- switch(Id)
- {
- BASE_IOCTLS(DRV_TYPE_DISK, "i386ATA", VERSION, csaATA_IOCtls);
-
- case DISK_IOCTL_GETBLOCKSIZE:
- return 512;
-
- default:
- return 0;
- }
- return 0;
-}
-
-// --- Disk Access ---
-/**
- * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
- */
-Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk)
-{
- int ret;
- Uint offset;
- Uint done = 0;
-
- // Pass straight on to ATA_ReadDMAPage if we can
- if(Count <= MAX_DMA_SECTORS)
- {
- ret = ATA_ReadDMA(Disk, Address, Count, Buffer);
- if(ret == 0) return 0;
- return Count;
- }
-
- // Else we will have to break up the transfer
- offset = 0;
- while(Count > MAX_DMA_SECTORS)
- {
- ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
- // Check for errors
- if(ret != 1) return done;
- // Change Position
- done += MAX_DMA_SECTORS;
- Count -= MAX_DMA_SECTORS;
- offset += MAX_DMA_SECTORS*SECTOR_SIZE;
- }
-
- ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset);
- if(ret != 1) return 0;
- return done+Count;
-}
-
-/**
- * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk)
- */
-Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk)
-{
- int ret;
- Uint offset;
- Uint done = 0;
-
- // Pass straight on to ATA_WriteDMA, if we can
- if(Count <= MAX_DMA_SECTORS)
- {
- ret = ATA_WriteDMA(Disk, Address, Count, Buffer);
- if(ret == 0) return 0;
- return Count;
- }
-
- // Else we will have to break up the transfer
- offset = 0;
- while(Count > MAX_DMA_SECTORS)
- {
- ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset);
- // Check for errors
- if(ret != 1) return done;
- // Change Position
- done += MAX_DMA_SECTORS;
- Count -= MAX_DMA_SECTORS;
- offset += MAX_DMA_SECTORS*SECTOR_SIZE;
- }
-
- ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset);
- if(ret != 1) return 0;
- return done+Count;
-}
+++ /dev/null
-/*
- * Acess2 IDE Harddisk Driver
- * - MBR Parsing Code
- * mbr.c
- */
-#define DEBUG 0
-#include <acess.h>
-#include "common.h"
-
-// === PROTOTYPES ===
-void ATA_ParseMBR(int Disk, tMBR *MBR);
-Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length);
-
-// === GLOBALS ===
-
-// === CODE ===
-/**
- * \fn void ATA_ParseMBR(int Disk, tMBR *MBR)
- */
-void ATA_ParseMBR(int Disk, tMBR *MBR)
-{
- int i, j = 0, k = 4;
- Uint64 extendedLBA;
- Uint64 base, len;
-
- ENTER("iDisk", Disk);
-
- // Count Partitions
- gATA_Disks[Disk].NumPartitions = 0;
- extendedLBA = 0;
- for( i = 0; i < 4; i ++ )
- {
- if( MBR->Parts[i].SystemID == 0 ) continue;
- if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 // LBA 28
- || MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 // LBA 48
- )
- {
- if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) {
- LOG("Extended Partition at 0x%llx", MBR->Parts[i].LBAStart);
- if(extendedLBA != 0) {
- Warning("Disk %i has multiple extended partitions, ignoring rest", Disk);
- continue;
- }
- extendedLBA = MBR->Parts[i].LBAStart;
- continue;
- }
- LOG("Primary Partition at 0x%llx", MBR->Parts[i].LBAStart);
-
- gATA_Disks[Disk].NumPartitions ++;
- continue;
- }
- // Invalid Partition, so don't count it
- }
- while(extendedLBA != 0)
- {
- extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len);
- if( extendedLBA == -1 ) break;
- gATA_Disks[Disk].NumPartitions ++;
- }
- LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions);
-
- // Create patition array
- gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) );
-
- // --- Fill Partition Info ---
- extendedLBA = 0;
- for( j = 0, i = 0; i < 4; i ++ )
- {
- LOG("MBR->Parts[%i].SystemID = 0x%02x", i, MBR->Parts[i].SystemID);
- if( MBR->Parts[i].SystemID == 0 ) continue;
- if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 ) // LBA 28
- {
- base = MBR->Parts[i].LBAStart;
- len = MBR->Parts[i].LBALength;
- }
- else if( MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 ) // LBA 58
- {
- base = (MBR->Parts[i].StartHi << 16) | MBR->Parts[i].LBAStart;
- len = (MBR->Parts[i].LengthHi << 16) | MBR->Parts[i].LBALength;
- }
- else
- continue;
-
- if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) {
- if(extendedLBA != 0) {
- Log_Warning("ATA", "Disk %i has multiple extended partitions, ignoring rest", Disk);
- continue;
- }
- extendedLBA = base;
- continue;
- }
- // Create Partition
- ATA_int_MakePartition(
- &gATA_Disks[Disk].Partitions[j], Disk, j,
- base, len
- );
- j ++;
-
- }
- // Scan extended partitions
- while(extendedLBA != 0)
- {
- extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len);
- if(extendedLBA == -1) break;
- ATA_int_MakePartition(
- &gATA_Disks[Disk].Partitions[j], Disk, k, base, len
- );
- }
-
- LEAVE('-');
-}
-
-/**
- * \brief Reads an extended partition
- * \return LBA of next Extended, -1 on error, 0 for last
- */
-Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length)
-{
- Uint64 link = 0;
- int bFoundPart = 0;;
- int i;
- tMBR mbr;
- Uint64 base, len;
-
- if( ATA_ReadDMA( Disk, Addr, 1, &mbr ) != 0 )
- return -1; // Stop on Errors
-
- for( i = 0; i < 4; i ++ )
- {
- if( mbr.Parts[i].SystemID == 0 ) continue;
-
- // LBA 24
- if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) {
- base = mbr.Parts[i].LBAStart;
- len = mbr.Parts[i].LBALength;
- }
- // LBA 48
- else if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) {
- base = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart;
- len = (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength;
- }
- else {
- Log_Warning("ATA MBR",
- "Unknown partition type 0x%x, Disk %i Ext 0x%llx Part %i",
- mbr.Parts[i].Boot, Disk, Addr, i
- );
- return -1;
- }
-
- switch(mbr.Parts[i].SystemID)
- {
- case 0xF:
- case 0x5:
- if(link != 0) {
- Log_Warning("ATA MBR",
- "Disk %i has two forward links in the extended partition",
- Disk
- );
- return -1;
- }
- link = base;
- break;
- default:
- if(bFoundPart) {
- Warning("ATA MBR",
- "Disk %i has more than one partition in the extended partition at 0x%llx",
- Disk, Addr
- );
- return -1;
- }
- bFoundPart = 1;
- *Base = base;
- *Length = len;
- break;
- }
- }
-
- if(!bFoundPart) {
- Log_Warning("ATA MBR",
- "No partition in extended partiton, Disk %i 0x%llx",
- Disk, Addr);
- return -1;
- }
-
- return link;
-}
+++ /dev/null
-#
-#
-
-OBJ = fdd.o
-NAME = FDD
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * AcessOS 0.1
- * Floppy Disk Access Code
- */
-#define DEBUG 0
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include <api_drv_disk.h>
-#include <dma.h>
-#include <iocache.h>
-
-#define WARN 0
-
-// === CONSTANTS ===
-// --- Current Version
-#define FDD_VERSION ((0<<8)|(75))
-
-// --- Options
-#define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation
-#define MOTOR_ON_DELAY 500 // Miliseconds
-#define MOTOR_OFF_DELAY 2000 // Miliseconds
-#define FDD_MAX_READWRITE_ATTEMPTS 16
-
-// === TYPEDEFS ===
-/**
- * \brief Representation of a floppy drive
- */
-typedef struct sFloppyDrive
-{
- int type;
- volatile int motorState; //2 - On, 1 - Spinup, 0 - Off
- int track[2];
- int timer;
- tVFS_Node Node;
- #if !USE_CACHE
- tIOCache *CacheHandle;
- #endif
-} t_floppyDevice;
-
-/**
- * \brief Cached Sector
- */
-typedef struct {
- Uint64 timestamp;
- Uint16 disk;
- Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD)
- Uint8 data[512];
-} t_floppySector;
-
-// === CONSTANTS ===
-static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" };
-static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 };
-static const short cPORTBASE[] = { 0x3F0, 0x370 };
-#if DEBUG
-static const char *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"};
-#endif
-
-enum FloppyPorts {
- PORT_STATUSA = 0x0,
- PORT_STATUSB = 0x1,
- PORT_DIGOUTPUT = 0x2,
- PORT_MAINSTATUS = 0x4,
- PORT_DATARATE = 0x4,
- PORT_DATA = 0x5,
- PORT_DIGINPUT = 0x7,
- PORT_CONFIGCTRL = 0x7
-};
-
-#define CMD_FLAG_MULTI_TRACK 0x80 //!< Multitrack
-#define CMD_FLAG_MFM_ENCODING 0x40 //!< MFM Encoding Mode (Always set for read/write/format/verify)
-#define CMD_FLAG_SKIP_MODE 0x20 //!< Skip Mode (don't use)
-enum FloppyCommands {
- CMD_READ_TRACK = 0x02,
- CMD_SPECIFY = 0x03,
- CMD_SENSE_STATUS = 0x04,
- CMD_WRITE_DATA = 0x05,
- CMD_READ_DATA = 0x06,
- CMD_RECALIBRATE = 0x07,
- CMD_SENSE_INTERRUPT = 0x08,
- CMD_WRITE_DEL_DATA = 0x09,
- CMD_READ_SECTOR_ID = 0x0A,
- // 0x0B - ?
- CMD_READ_DEL_DATA = 0x0C,
- CMD_FORMAT_TRACK = 0x0D,
- // 0x0E - ?
- CMD_SEEK_TRACK = 0x0F,
- CMD_VERSION = 0x10,
-
- CMD_LOCK = 0x14, //!< Save controller parameters
-
- CMD_CONFIGURE = 0x13
-};
-
-// === PROTOTYPES ===
-// --- Filesystem
- int FDD_Install(char **Arguments);
-void FDD_UnloadModule();
-// --- VFS Methods
-char *FDD_ReadDir(tVFS_Node *Node, int pos);
-tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name);
- int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
-Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
-// --- Functions for IOCache/DrvUtil
-Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk);
-// --- Raw Disk Access
- int FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer);
- int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer);
-// --- Helpers
- int FDD_WaitIRQ();
-void FDD_IRQHandler(int Num, void *Ptr);
-void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl);
-
- int FDD_Reset(int id);
-void FDD_Recalibrate(int disk);
- int FDD_Reconfigure(int ID);
-
- int FDD_int_SeekTrack(int disk, int head, int track);
-void FDD_int_TimerCallback(void *Arg);
-void FDD_int_StopMotor(int Disk);
-void FDD_int_StopMotorCallback(void *Arg);
-void FDD_int_StartMotor(int Disk);
- int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt);
-
- int FDD_int_SendByte(int base, Uint8 Byte);
- int FDD_int_GetByte(int base, Uint8 *Byte);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "x86_ISADMA", NULL);
-t_floppyDevice gFDD_Devices[2];
-tMutex glFDD;
-volatile int gbFDD_IrqFired = 0;
-tDevFS_Driver gFDD_DriverInfo = {
- NULL, "fdd",
- {
- .Size = -1,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .ReadDir = FDD_ReadDir,
- .FindDir = FDD_FindDir,
- .IOCtl = FDD_IOCtl
- }
-};
-
-// === CODE ===
-/**
- * \fn int FDD_Install(char **Arguments)
- * \brief Installs floppy driver
- */
-int FDD_Install(char **Arguments)
-{
- Uint8 data;
- char **args = Arguments;
-
- // Determine Floppy Types (From CMOS)
- outb(0x70, 0x10);
- data = inb(0x71);
- gFDD_Devices[0].type = data >> 4;
- gFDD_Devices[1].type = data & 0xF;
- gFDD_Devices[0].track[0] = -1;
- gFDD_Devices[1].track[1] = -1;
-
- Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]);
-
- if( data == 0 ) {
- return MODULE_ERR_NOTNEEDED;
- }
-
- // Handle arguments
- if(args) {
- for(;*args;args++)
- {
- if(strcmp(*args, "disable")==0)
- return MODULE_ERR_NOTNEEDED;
- }
- }
-
- // Install IRQ6 Handler
- IRQ_AddHandler(6, FDD_IRQHandler, NULL);
-
- // Ensure the FDD version is 0x90
- {
- Uint8 tmp = 0;
- FDD_int_SendByte(cPORTBASE[0], CMD_VERSION);
- FDD_int_GetByte(cPORTBASE[0], &tmp);
- if( tmp != 0x90 ) {
- Log_Error("FDD", "Version(0x%2x) != 0x90", tmp);
- return MODULE_ERR_NOTNEEDED;
- }
- }
-
- // Configure
- FDD_Reconfigure(0);
-
- // Reset Primary FDD Controller
- if( FDD_Reset(0) != 0 ) {
- return MODULE_ERR_MISC;
- }
-
- #if 0
- {
- int retries;
- // Recalibrate disks
- LOG("Recalibrate disks (16x seek)");
- retries = 16;
- while(FDD_int_SeekTrack(0, 0, 1) == 0 && retries --)
- Threads_Yield(); // set track
- if(retries < 0) LEAVE_RET('i', -1);
-
- retries = 16;
- while(FDD_int_SeekTrack(0, 1, 1) == 0 && retries --)
- Threads_Yield(); // set track
- if(retries < 0) LEAVE_RET('i', -1);
- }
- #endif
-
-
- // Initialise Root Node
- gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
- = gFDD_DriverInfo.RootNode.ATime = now();
-
- // Initialise Child Nodes
- gFDD_Devices[0].Node.Inode = 0;
- gFDD_Devices[0].Node.Flags = 0;
- gFDD_Devices[0].Node.NumACLs = 0;
- gFDD_Devices[0].Node.Read = FDD_ReadFS;
- gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS;
- memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node));
-
- gFDD_Devices[1].Node.Inode = 1;
-
- // Set Lengths
- gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4];
- gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF];
-
- // Create Sector Cache
- if( cFDD_SIZES[data >> 4] )
- {
- gFDD_Devices[0].CacheHandle = IOCache_Create(
- FDD_WriteSector, 0, 512,
- gFDD_Devices[0].Node.Size / (512*4)
- ); // Cache is 1/4 the size of the disk
- }
- if( cFDD_SIZES[data & 15] )
- {
- gFDD_Devices[1].CacheHandle = IOCache_Create(
- FDD_WriteSector, 0, 512,
- gFDD_Devices[1].Node.Size / (512*4)
- ); // Cache is 1/4 the size of the disk
- }
-
- // Register with devfs
- DevFS_AddDevice(&gFDD_DriverInfo);
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Prepare the module for removal
- */
-void FDD_UnloadModule()
-{
- int i;
- DevFS_DelDevice( &gFDD_DriverInfo );
- Mutex_Acquire(&glFDD);
- for(i=0;i<4;i++) {
- Time_RemoveTimer(gFDD_Devices[i].timer);
- FDD_int_StopMotor(i);
- }
- Mutex_Release(&glFDD);
- //IRQ_Clear(6);
-}
-
-/**
- * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos)
- * \brief Read Directory
- */
-char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos)
-{
- char name[2] = "0\0";
-
- if(Pos >= 2 || Pos < 0) return NULL;
-
- if(gFDD_Devices[Pos].type == 0) return VFS_SKIP;
-
- name[0] += Pos;
-
- return strdup(name);
-}
-
-/**
- * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename);
- * \brief Find File Routine (for vfs_node)
- */
-tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename)
-{
- int i;
-
- ENTER("sFilename", Filename);
-
- // Sanity check string
- if(Filename == NULL) {
- LEAVE('n');
- return NULL;
- }
-
- // Check string length (should be 1)
- if(Filename[0] == '\0' || Filename[1] != '\0') {
- LEAVE('n');
- return NULL;
- }
-
- // Get First character
- i = Filename[0] - '0';
-
- // Check for 1st disk and if it is present return
- if(i == 0 && gFDD_Devices[0].type != 0) {
- LEAVE('p', &gFDD_Devices[0].Node);
- return &gFDD_Devices[0].Node;
- }
-
- // Check for 2nd disk and if it is present return
- if(i == 1 && gFDD_Devices[1].type != 0) {
- LEAVE('p', &gFDD_Devices[1].Node);
- return &gFDD_Devices[1].Node;
- }
-
- // Else return null
- LEAVE('n');
- return NULL;
-}
-
-static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
-/**
- * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data)
- * \brief Stub ioctl function
- */
-int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data)
-{
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS);
-
- case DISK_IOCTL_GETBLOCKSIZE: return 512;
-
- default:
- return 0;
- }
-}
-
-/**
- * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read Data from a disk
-*/
-Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- int ret;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- if(Node == NULL) {
- LEAVE('i', -1);
- return -1;
- }
-
- if(Node->Inode != 0 && Node->Inode != 1) {
- LEAVE('i', -1);
- return -1;
- }
-
- ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode);
- LEAVE('i', ret);
- return ret;
-}
-
-/**
- * \brief Reads \a Count contiguous sectors from a disk
- * \param SectorAddr Address of the first sector
- * \param Count Number of sectors to read
- * \param Buffer Destination Buffer
- * \param Disk Disk Number
- * \return Number of sectors read
- * \note Used as a ::DrvUtil_ReadBlock helper
- */
-Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk)
-{
- Uint ret = 0;
- while(Count --)
- {
- if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 )
- return ret;
-
- Buffer = (void*)( (tVAddr)Buffer + 512 );
- SectorAddr ++;
- ret ++;
- }
- return ret;
-}
-
-int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer)
-{
- int cyl, head, sec;
- int spt, base;
- int i;
- int lba = SectorAddr;
- Uint8 st0=0, st1=0, st2=0, bps=0; // Status Values
-
- ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
-
- base = cPORTBASE[Disk >> 1];
-
- LOG("Calculating Disk Dimensions");
- // Get CHS position
- if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1)
- {
- LEAVE('i', -1);
- return -1;
- }
- LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec);
-
- // Start the motor
- Mutex_Acquire(&glFDD); // Lock to stop the motor stopping on us
- Time_RemoveTimer(gFDD_Devices[Disk].timer); // Remove Old Timer
- // Start motor if needed
- if(gFDD_Devices[Disk].motorState != 2) FDD_int_StartMotor(Disk);
- Mutex_Release(&glFDD);
-
- // Wait for spinup
- LOG("Wait for the motor to spin up");
- while(gFDD_Devices[Disk].motorState == 1) Threads_Yield();
-
- LOG("Acquire Spinlock");
- Mutex_Acquire(&glFDD);
-
- // Read Data from DMA
- LOG("Setting DMA for read");
- DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2
-
- LOG("Sending command");
-
- #define SENDB(__data) if(FDD_int_SendByte(base, __data)) { FDD_Reset(Disk >> 1); continue; }
-
- for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
- {
- FDD_int_SeekTrack(Disk, head, cyl);
- if( Write ) {
- SENDB(CMD_WRITE_DATA|CMD_FLAG_MFM_ENCODING);
- }
- else {
- SENDB(CMD_READ_DATA|CMD_FLAG_MFM_ENCODING);
- }
- SENDB( (head << 2) | (Disk&1) );
- SENDB(cyl & 0xFF);
- SENDB(head & 0xFF);
- SENDB(sec & 0xFF);
- SENDB(0x02); // Bytes Per Sector (Real BPS=128*2^{val})
- SENDB(spt); // SPT
- SENDB(0x1B); // Gap Length (27 is default)
- SENDB(0xFF); // Data Length
-
- // Wait for IRQ
- if( Write ) {
- LOG("Writing Data");
- DMA_WriteData(2, 512, Buffer);
- LOG("Waiting for Data to be written");
- if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; }
- }
- else {
- LOG("Waiting for data to be read");
- if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; }
- LOG("Reading Data");
- DMA_ReadData(2, 512, Buffer);
- }
-
- // Clear Input Buffer
- LOG("Clearing Input Buffer");
- // Status Values
- FDD_int_GetByte(base, &st0);
- FDD_int_GetByte(base, &st1);
- FDD_int_GetByte(base, &st2);
-
- // Cylinder, Head and Sector (mutilated in some way)
- FDD_int_GetByte(base, NULL); // Cylinder
- FDD_int_GetByte(base, NULL); // Head
- FDD_int_GetByte(base, NULL); // Sector
- // Should be the BPS set above (0x02)
- FDD_int_GetByte(base, &bps);
-
- // Check Status
- // - Error Code
- if(st0 & 0xC0) {
- LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]);
- continue;
- }
- // - Status Flags
- if(st0 & 0x08) { LOG("Drive not ready"); continue; }
- if(st1 & 0x80) { LOG("End of Cylinder"); continue; }
- if(st1 & 0x20) { LOG("CRC Error"); continue; }
- if(st1 & 0x10) { LOG("Controller Timeout"); continue; }
- if(st1 & 0x04) { LOG("No Data Found"); continue; }
- if(st1 & 0x01 || st2 & 0x01) {
- LOG("No Address mark found");
- continue;
- }
- if(st2 & 0x40) { LOG("Deleted address mark"); continue; }
- if(st2 & 0x20) { LOG("CRC error in data"); continue; }
- if(st2 & 0x10) { LOG("Wrong Cylinder"); continue; }
- if(st2 & 0x04) { LOG("uPD765 sector not found"); continue; }
- if(st2 & 0x02) { LOG("Bad Cylinder"); continue; }
-
- if(bps != 0x2) {
- LOG("Returned BPS = 0x%02x, not 0x02", bps);
- continue;
- }
-
- if(st1 & 0x02) {
- LOG("Floppy not writable");
- // Return error without triggering the attempt count check
- i = FDD_MAX_READWRITE_ATTEMPTS+1;
- break;
- }
-
- // Success!
- break;
- }
- #undef SENDB
-
- // Release Spinlock
- LOG("Realeasing Spinlock and setting motor to stop");
- Mutex_Release(&glFDD);
-
- if(i == FDD_MAX_READWRITE_ATTEMPTS) {
- Log_Warning("FDD", "Exceeded %i attempts in %s the disk",
- FDD_MAX_READWRITE_ATTEMPTS,
- (Write ? "writing to" : "reading from")
- );
- }
-
- // Don't turn the motor off now, wait for a while
- FDD_int_StopMotor(Disk);
-
- // Error check
- if( i < FDD_MAX_READWRITE_ATTEMPTS ) {
- LEAVE('i', 0);
- return 0;
- }
- else {
- LEAVE('i', 1);
- return 1;
- }
-}
-
-/**
- * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
- * \brief Read a sector from disk
- * \todo Make real-hardware safe (account for read errors)
- */
-int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer)
-{
- int ret;
-
- ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer);
-
- if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) {
- LEAVE('i', 1);
- return 1;
- }
-
- // Pass to general function
- ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer);
-
- if( ret == 0 ) {
- IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer );
- LEAVE('i', 1);
- return 1;
- }
- else {
- LOG("Reading failed");
- LEAVE('i', 0);
- return 0;
- }
-}
-
-/**
- * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
- * \brief Write a sector to the floppy disk
- * \note Not Implemented
- */
-int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer)
-{
- Log_Warning("FDD", "Read Only at the moment");
- return -1;
-}
-
-/**
- * \brief Seek disk to selected track
- */
-int FDD_int_SeekTrack(int disk, int head, int track)
-{
- Uint8 sr0=0, cyl=0;
- int base, i, bUnclean;
-
- base = cPORTBASE[disk>>1];
-
- // Check if seeking is needed
- if(gFDD_Devices[disk].track[head] == track)
- return 1;
-
- // - Seek Head 0
- for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ )
- {
- if(i && bUnclean) FDD_Reset(disk >> 1);
- bUnclean = 1;
- if(FDD_int_SendByte(base, CMD_SEEK_TRACK)) continue;
- if(FDD_int_SendByte(base, (head<<2)|(disk&1))) continue;
- if(FDD_int_SendByte(base, track)) continue;
- FDD_WaitIRQ();
- FDD_SenseInt(base, &sr0, &cyl); // Wait for IRQ
-
- bUnclean = 0;
- if( cyl != track )
- continue; // Try again
- if( (sr0 & 0xF0) != 0x20 ) {
- LOG("sr0 = 0x%x", sr0);
- continue ;
- }
-
- break;
- }
-
- if( i == FDD_MAX_READWRITE_ATTEMPTS ) {
- Log_Warning("FDD", "Unable to seek to track %i on disk %i",
- track, disk);
- return 0;
- }
-
- // Set Track in structure
- gFDD_Devices[disk].track[head] = track;
-
- LOG("Time_Delay(100)");
- // Wait for Head to settle
- Time_Delay(100);
-
- return 1;
-}
-
-/**
- * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
- * \brief Get Dimensions of a disk
- */
-int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt)
-{
- switch(type) {
- case 0:
- return 0;
-
- // 360Kb 5.25"
- case 1:
- *spt = 9;
- *s = (lba % 9) + 1;
- *c = lba / 18;
- *h = (lba / 9) & 1;
- break;
-
- // 1220Kb 5.25"
- case 2:
- *spt = 15;
- *s = (lba % 15) + 1;
- *c = lba / 30;
- *h = (lba / 15) & 1;
- break;
-
- // 720Kb 3.5"
- case 3:
- *spt = 9;
- *s = (lba % 9) + 1;
- *c = lba / 18;
- *h = (lba / 9) & 1;
- break;
-
- // 1440Kb 3.5"
- case 4:
- *spt = 18;
- *s = (lba % 18) + 1;
- *c = lba / 36;
- *h = (lba / 18) & 1;
- //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h);
- break;
-
- // 2880Kb 3.5"
- case 5:
- *spt = 36;
- *s = (lba % 36) + 1;
- *c = lba / 72;
- *h = (lba / 32) & 1;
- break;
-
- default:
- return -2;
- }
- return 1;
-}
-
-/**
- * \fn void FDD_IRQHandler(int Num)
- * \brief Handles IRQ6
- */
-void FDD_IRQHandler(int Num, void *Ptr)
-{
- gbFDD_IrqFired = 1;
-}
-
-/**
- * \brief Wait for the FDD IRQ to fire
- * \return Boolean failure (1 for timeout)
- */
-inline int FDD_WaitIRQ()
-{
- tTime end = now() + 2000;
-
- // Wait for IRQ
- while(!gbFDD_IrqFired && now() < end)
- Threads_Yield();
-
- if( !gbFDD_IrqFired ) {
- Log_Warning("FDD", "FDD_WaitIRQ - Timeout");
- return 1;
- }
-
- gbFDD_IrqFired = 0;
- return 0;
-}
-
-void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl)
-{
- FDD_int_SendByte(base, CMD_SENSE_INTERRUPT);
- FDD_int_GetByte(base, sr0);
- FDD_int_GetByte(base, cyl);
-}
-
-/**
- * void FDD_int_SendByte(int base, char byte)
- * \brief Sends a command to the controller
- */
-int FDD_int_SendByte(int base, Uint8 byte)
-{
- tTime end = now() + 1000; // 1s
-
- while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end )
- Threads_Yield(); //Delay
-// Time_Delay(10); //Delay
-
- if( inb(base + PORT_MAINSTATUS) & 0x40 ) {
- Log_Warning("FDD", "FDD_int_SendByte: DIO set, is this ok?");
- return -2;
- }
-
- if( now() > end )
- {
- Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x", byte, base);
- return 1;
- }
- outb(base + PORT_DATA, byte);
-// Log_Debug("FDD", "FDD_int_SendByte: Sent 0x%02x to 0x%x", byte, base);
- return 0;
-}
-
-/**
- * int FDD_int_GetByte(int base, char byte)
- * \brief Receive data from fdd controller
- */
-int FDD_int_GetByte(int base, Uint8 *value)
-{
- tTime end = now() + 1000; // 1s
-
- while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end )
- Time_Delay(10);
-
- if( !(inb(base + PORT_MAINSTATUS) & 0x40) ) {
- Log_Warning("FDD", "FDD_int_GetByte: DIO unset, is this ok?");
- return -2;
- }
-
- if( now() > end )
- {
- Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x", base);
- return -1;
- }
-
- if(value)
- *value = inb(base + PORT_DATA);
- else
- inb(base + PORT_DATA);
- return 0;
-}
-
-/**
- * \brief Recalibrate the specified disk
- */
-void FDD_Recalibrate(int disk)
-{
- ENTER("idisk", disk);
-
- LOG("Starting Motor");
- FDD_int_StartMotor(disk);
- // Wait for Spinup
- while(gFDD_Devices[disk].motorState <= 1) Threads_Yield();
-
- LOG("Sending Calibrate Command");
- FDD_int_SendByte(cPORTBASE[disk>>1], CMD_RECALIBRATE);
- FDD_int_SendByte(cPORTBASE[disk>>1], disk&1);
-
- LOG("Waiting for IRQ");
- FDD_WaitIRQ();
- FDD_SenseInt(cPORTBASE[disk>>1], NULL, NULL);
-
- LOG("Stopping Motor");
- FDD_int_StopMotor(disk);
- LEAVE('-');
-}
-
-/**
- * \brief Reconfigure the controller
- */
-int FDD_Reconfigure(int ID)
-{
- Uint16 base = cPORTBASE[ID];
-
- ENTER("iID", ID);
-
- FDD_int_SendByte(base, CMD_CONFIGURE);
- FDD_int_SendByte(base, 0);
- // Implied seek enabled, FIFO Enabled, Drive Polling Disabled, data buffer threshold 8 bytes
- FDD_int_SendByte(base, (1 << 6) | (0 << 5) | (0 << 4) | 7);
- FDD_int_SendByte(base, 0); // Precompensation - use default
-
- // Commit
- FDD_int_SendByte(base, CMD_LOCK|CMD_FLAG_MULTI_TRACK);
- FDD_int_GetByte(base, NULL);
-
- LEAVE('i', 0);
- return 0;
-}
-
-/**
- * \brief Reset the specified FDD controller
- */
-int FDD_Reset(int id)
-{
- Uint16 base = cPORTBASE[id];
- Uint8 motor_state;
-
- ENTER("iID", id);
-
- // Reset the card
- motor_state = inb(base + PORT_DIGOUTPUT) & 0xF0;
- outb(base + PORT_DIGOUTPUT, motor_state|0); // Disable FDC
- Time_Delay(1);
- outb(base + PORT_DIGOUTPUT, motor_state|8|4); // Re-enable FDC (DMA and Enable)
-
- // Set the data rate
- outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s
-
- // Wait for IRQ
- LOG("Awaiting IRQ");
-
- FDD_WaitIRQ();
-
- FDD_SenseInt(base, NULL, NULL);
-
- // Specify
- FDD_int_SendByte(base, CMD_SPECIFY); // Step and Head Load Times
- FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
- FDD_int_SendByte(base, 0x02); // Head Load Time >> 1
-
- LOG("Recalibrating Disk");
- FDD_Recalibrate((id<<1)|0);
- FDD_Recalibrate((id<<1)|1);
-
- LEAVE_RET('i', 0);
-}
-
-/**
- * \fn void FDD_int_TimerCallback()
- * \brief Called by timer
- */
-void FDD_int_TimerCallback(void *Arg)
-{
- int disk = (Uint)Arg;
- ENTER("iarg", disk);
- if(gFDD_Devices[disk].motorState == 1)
- gFDD_Devices[disk].motorState = 2;
- Time_RemoveTimer(gFDD_Devices[disk].timer);
- gFDD_Devices[disk].timer = -1;
- LEAVE('-');
-}
-
-/**
- * \fn void FDD_int_StartMotor(char disk)
- * \brief Starts FDD Motor
- */
-void FDD_int_StartMotor(int disk)
-{
- Uint8 state;
- if( gFDD_Devices[disk].motorState != 0 ) return ;
- // Set motor ON bit
- state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
- state |= 1 << (4+disk);
- outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
- // Mark as spinning up
- gFDD_Devices[disk].motorState = 1;
- // Schedule a timer for when it's up to speed
- gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk);
-}
-
-/**
- * \brief Schedule the drive motor to stop
- * \param Disk Disk number to stop
- */
-void FDD_int_StopMotor(int Disk)
-{
- // Ignore if the motor is aready off
- if( gFDD_Devices[Disk].motorState == 0 ) return ;
-
- // Don't double-schedule timer
- if( gFDD_Devices[Disk].timer != -1 )
- {
- Time_RemoveTimer( gFDD_Devices[Disk].timer );
- }
-
- gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(Uint)Disk);
-}
-
-/**
- * \brief Stops FDD Motor
- */
-void FDD_int_StopMotorCallback(void *Arg)
-{
- Uint8 state, disk = (Uint)Arg;
-
- // Mutex is only locked if disk is in use
- if( Mutex_IsLocked(&glFDD) ) return ;
-
- ENTER("iDisk", disk);
-
- // Clear motor on bit
- state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT );
- state &= ~( 1 << (4+disk) );
- outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state );
-
- // Mark as off
- gFDD_Devices[disk].motorState = 0;
-
- LEAVE('-');
-}
-
+++ /dev/null
-#
-#
-
-DEPS = x86/ISADMA
-OBJ = main.o fdc.o
-NAME = FDDv2
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 82077AA FDC
- * - By John Hodge (thePowersGang)
- *
- * common.h
- * - Common definitions
- */
-#ifndef _FDC_COMMON_H_
-#define _FDC_COMMON_H_
-
-#include <mutex.h>
-#include <timers.h>
-
-// === CONSTANTS ===
-#define MAX_DISKS 8 // 4 per controller, 2 controllers
-#define TRACKS_PER_DISK (1440*2/18)
-#define BYTES_PER_TRACK (18*512)
-
-// === TYPEDEFS ===
-typedef struct sFDD_Drive tDrive;
-
-// === STRUCTURES ===
-struct sFDD_Drive
-{
- int bValid;
- int bInserted;
- int MotorState;
- tTimer *Timer;
-
- tMutex Mutex;
-
- void *TrackData[TRACKS_PER_DISK]; // Whole tracks are read
-};
-
-// === FUNCTIONS ===
-extern int FDD_SetupIO(void);
-extern int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
-
-// === GLOBALS ===
-extern tDrive gaFDD_Disks[MAX_DISKS];
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 82077AA FDC
- * - By John Hodge (thePowersGang)
- *
- * fdc.c
- * - FDC IO Functions
- */
-#define DEBUG 0
-#include <acess.h>
-#include "common.h"
-#include <dma.h>
-#include <timers.h>
-
-// === CONSTANTS ===
-#define MOTOR_ON_DELAY 500
-#define MOTOR_OFF_DELAY 2000
-
-enum eMotorState
-{
- MOTOR_OFF,
- MOTOR_ATSPEED,
-};
-
-enum eFDC_Registers
-{
- FDC_DOR = 0x02, // Digital Output Register
- FDC_MSR = 0x04, // Master Status Register (Read Only)
- FDC_FIFO = 0x05, // FIFO port
- FDC_CCR = 0x07 // Configuration Control Register (write only)
-};
-
-enum eFDC_Commands
-{
- CMD_SPECIFY = 3, // Specify parameters
- CMD_WRITE_DATA = 5, // Write Data
- CMD_READ_DATA = 6, // Read Data
- CMD_RECALIBRATE = 7, // Recalibrate a drive
- CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt
- CMD_SEEK = 15, // Seek to a track
-};
-
-// === PROTOTYPES ===
- int FDD_SetupIO(void);
- int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
- int FDD_int_SeekToTrack(int Disk, int Track);
- int FDD_int_Calibrate(int Disk);
- int FDD_int_Reset(int Disk);
-// --- FIFO
- int FDD_int_WriteData(Uint16 Base, Uint8 Data);
- int FDD_int_ReadData(Uint16 Base, Uint8 *Data);
-void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl);
-// --- Motor Control
- int FDD_int_StartMotor(int Disk);
- int FDD_int_StopMotor(int Disk);
-void FDD_int_StopMotorCallback(void *Ptr);
-// --- Helpers
- int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0);
-Uint16 FDD_int_GetBase(int Disk, int *Drive);
-// --- Interrupt
-void FDD_int_ClearIRQ(void);
- int FDD_int_WaitIRQ(void);
-void FDD_int_IRQHandler(int IRQ, void *Ptr);
-
-// === GLOBALS ===
-/**
- * \brief Marker for IRQ6
- * \todo Convert into a semaphore?
- */
- int gbFDD_IRQ6Fired;
-/**
- * \brief Protector for DMA and IRQ6
- */
-tMutex gFDD_IOMutex;
-
-// === CODE ===
-/**
- * \brief Set up FDC IO
- * \return Boolean failure
- *
- * Registers the IRQ handler and resets the controller
- */
-int FDD_SetupIO(void)
-{
- // Install IRQ6 Handler
- IRQ_AddHandler(6, FDD_int_IRQHandler, NULL);
-
- // Reset controller
- FDD_int_Reset(0);
- // TODO: All controllers
-
- return 0;
-}
-
-/**
- * \brief Read/Write data from/to a disk
- * \param Disk Global disk number
- * \param Track Track number (Cyl*2+Head)
- * \param bWrite Toggle write mode
- * \param Buffer Destination/Source buffer
- * \return Boolean failure
- */
-int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer)
-{
- Uint8 cmd;
- int i, _disk;
- Uint16 base = FDD_int_GetBase(Disk, &_disk);
- int cyl = Track >> 1, head = Track & 1;
-
- ENTER("iDisk iTrack ibWrite pBuffer", Disk, Track, bWrite, Buffer);
-
- Mutex_Acquire( &gFDD_IOMutex );
-
- // Initialise DMA for read/write
- // TODO: Support non 1.44MiB FDs
- DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite);
-
- // Select command
- if( bWrite )
- cmd = CMD_WRITE_DATA | 0xC0;
- else
- cmd = CMD_READ_DATA | 0xC0;
-
- LOG("cmd = 0x%x", cmd);
-
- // Seek
- if( FDD_int_SeekToTrack(Disk, Track) ) {
- Mutex_Release( &gFDD_IOMutex );
- LEAVE('i', -1);
- return -1;
- }
- LOG("Track seek done");
-
- for( i = 0; i < 20; i ++ )
- {
- LOG("Starting motor");
- FDD_int_StartMotor(Disk);
-
- // Write data
- if( bWrite )
- DMA_WriteData(2, BYTES_PER_TRACK, Buffer);
-
- LOG("Sending command stream");
- FDD_int_WriteData(base, cmd);
- FDD_int_WriteData(base, (head << 2) | _disk);
- FDD_int_WriteData(base, cyl);
- FDD_int_WriteData(base, head);
- FDD_int_WriteData(base, 1); // First Sector
- FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n)
- FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44
- FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again
- FDD_int_WriteData(base, 0xFF); // Data length - ?
-
- LOG("Waiting for IRQ");
- FDD_int_WaitIRQ();
-
- // No Sense Interrupt
-
- LOG("Reading result");
- Uint8 st0=0, st1=0, st2=0, bps=0;
- FDD_int_ReadData(base, &st0);
- FDD_int_ReadData(base, &st1); // st1
- FDD_int_ReadData(base, &st2); // st2
- FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl
- FDD_int_ReadData(base, NULL); // rhe - Mutilated Head
- FDD_int_ReadData(base, NULL); // rse - Mutilated sector
- FDD_int_ReadData(base, &bps); // bps - Should be the same as above
-
- if( st0 & 0xc0 ) {
- FDD_int_HandleST0Error(__func__, Disk, st0);
- continue ;
- }
-
- if( st2 & 0x02 ) {
- Log_Debug("FDD", "Disk %i is not writable", Disk);
- Mutex_Release( &gFDD_IOMutex );
- LEAVE('i', 2);
- return 2;
- }
-
- if( st0 & 0x08 ) {
- Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready");
- continue ;
- }
-
-
- if( st1 & 0x80 ) {
- Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder");
- continue ;
- }
-
- if( st1 & (0x20|0x10|0x04|0x01) ) {
- Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1);
- continue;
- }
-
- if( st2 & (0x40|0x20|0x10|0x04|0x01) ) {
- Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2);
- continue ;
- }
-
- if( bps != 0x2 ) {
- Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps);
- continue ;
- }
-
- // Read back data
- if( !bWrite )
- DMA_ReadData(2, BYTES_PER_TRACK, Buffer);
-
- LOG("All data done");
- FDD_int_StopMotor(Disk);
- Mutex_Release( &gFDD_IOMutex );
- LEAVE('i', 0);
- return 0;
- }
-
- Log_Debug("FDD", "%i retries exhausted", i);
- FDD_int_StopMotor(Disk);
- Mutex_Release( &gFDD_IOMutex );
- LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \brief Seek to a specific track
- * \param Disk Global disk number
- * \param Track Track number (Cyl*2+Head)
- * \return Boolean failure
- */
-int FDD_int_SeekToTrack(int Disk, int Track)
-{
- Uint8 st0=0, res_cyl=0;
- int cyl, head;
- int _disk;
- Uint16 base = FDD_int_GetBase(Disk, &_disk);;
-
- ENTER("iDisk iTrack", Disk, Track);
-
- cyl = Track / 2;
- head = Track % 2;
-
- LOG("cyl = %i, head = %i", cyl, head);
-
- FDD_int_StartMotor(Disk);
-
- for( int i = 0; i < 10; i ++ )
- {
- LOG("Sending command");
- FDD_int_ClearIRQ();
- FDD_int_WriteData(base, CMD_SEEK);
- FDD_int_WriteData(base, (head << 2) + _disk);
- FDD_int_WriteData(base, cyl);
-
- LOG("Waiting for IRQ");
- FDD_int_WaitIRQ();
- FDD_int_SenseInterrupt(base, &st0, &res_cyl);
-
- if( st0 & 0xC0 )
- {
- FDD_int_HandleST0Error(__func__, Disk, st0);
- continue ;
- }
-
- if( res_cyl == cyl ) {
- FDD_int_StopMotor(Disk);
- LEAVE('i', 0);
- return 0;
- }
- }
-
- Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n");
- FDD_int_StopMotor(Disk);
- LEAVE('i', 1);
- return 1;
-}
-
-/**
- * \brief Calibrate a drive
- * \param Disk Global disk number
- */
-int FDD_int_Calibrate(int Disk)
-{
- int _disk;
- Uint16 base = FDD_int_GetBase(Disk, &_disk);
- FDD_int_StartMotor(Disk);
-
- for( int i = 0; i < 10; i ++ )
- {
- Uint8 st0=0, cyl = -1;
-
- FDD_int_ClearIRQ();
- FDD_int_WriteData(base, CMD_RECALIBRATE);
- FDD_int_WriteData(base, _disk);
-
- FDD_int_WaitIRQ();
-
- FDD_int_SenseInterrupt(base, &st0, &cyl);
-
- if( st0 & 0xC0 ) {
- FDD_int_HandleST0Error(__func__, Disk, st0);
- continue ;
- }
-
- if( cyl == 0 )
- {
- FDD_int_StopMotor(Disk);
- return 0;
- }
- }
-
- Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted");
-
- return 1;
-}
-
-/**
- * \brief Reset a controller
- * \param Base Controller base address
- */
-int FDD_int_Reset(int Disk)
-{
- Uint8 tmp;
- int _disk;
- Uint16 base = FDD_int_GetBase(Disk, &_disk);
-
- tmp = inb(base + FDC_DOR) & 0xF0;
- outb( base + FDC_DOR, 0x00 );
- Time_Delay(1);
- outb( base + FDC_DOR, tmp | 0x0C );
-
- FDD_int_SenseInterrupt(base, NULL, NULL);
-
- outb(base + FDC_CCR, 0x00); // 500KB/s
-
- FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times
- FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each)
- FDD_int_WriteData(base, 0x02); // Head Load Time >> 1
-
- // TODO: Recalibrate all present disks
- FDD_int_Calibrate(Disk);
- return 0;
-}
-
-/**
- * \brief Write a byte to the FIFO
- */
-int FDD_int_WriteData(Uint16 Base, Uint8 Data)
-{
- for( int i = 0; i < 100; i ++ )
- {
- if( inb(Base + FDC_MSR) & 0x80 )
- {
- outb(Base + FDC_FIFO, Data);
- return 0;
- }
- Time_Delay(10);
- }
- Log_Error("FDD", "Write timeout");
- return 1;
-}
-
-/**
- * \brief Read a byte from the FIFO
- */
-int FDD_int_ReadData(Uint16 Base, Uint8 *Data)
-{
- for( int i = 0; i < 100; i ++ )
- {
- if( inb(Base + FDC_MSR) & 0x80 )
- {
- Uint8 tmp = inb(Base + FDC_FIFO);
- if(Data) *Data = tmp;
- return 0;
- }
- Time_Delay(10);
- }
- Log_Error("FDD", "Read timeout");
- return 1;
-}
-
-/**
- * \brief Acknowledge an interrupt
- * \param Base Controller base address
- * \param ST0 Location to store the ST0 value
- * \param Cyl Current cylinder
- */
-void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl)
-{
- FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT);
- FDD_int_ReadData(Base, ST0);
- FDD_int_ReadData(Base, Cyl);
-}
-
-/**
- * \brief Start the motor on a disk
- */
-int FDD_int_StartMotor(int Disk)
-{
- int _disk;
- Uint16 base = FDD_int_GetBase(Disk, &_disk);
-
- // Clear the motor off timer
- Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
- gaFDD_Disks[Disk].Timer = NULL;
-
- // Check if the motor is already on
- if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
- return 0;
-
- // Turn motor on
- outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4)));
-
- // Wait for it to reach speed
- Time_Delay(MOTOR_ON_DELAY);
-
- gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED;
-
- return 0;
-}
-
-/**
- * \brief Schedule the motor to stop
- */
-int FDD_int_StopMotor(int Disk)
-{
- if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
- return 0;
- if( gaFDD_Disks[Disk].Timer != NULL )
- return 0;
-
- gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk);
-
- return 0;
-}
-
-/**
- * \brief Actually stop the motor
- * \param Ptr Actaully the global disk number
- */
-void FDD_int_StopMotorCallback(void *Ptr)
-{
- int Disk = (tVAddr)Ptr;
- int _disk;
- Uint16 base = FDD_int_GetBase(Disk, &_disk);
-
- gaFDD_Disks[Disk].Timer = NULL;
- gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
-
- outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
-
- return ;
-}
-
-/**
- * \brief Converts a global disk number into a controller and drive
- * \param Disk Global disk number
- * \param Drive Destination for controller disk number
- * \return Controller base address
- */
-Uint16 FDD_int_GetBase(int Disk, int *Drive)
-{
- if(Drive) *Drive = Disk & 3;
- switch(Disk >> 2)
- {
- case 0: return 0x3F0;
- case 1: return 0x370;
- default:
- return 0;
- }
-}
-
-/**
- * \brief Convert a ST0 error value into a message
- * \param Fcn Calling function name
- * \parma Disk Global disk number
- * \param ST0 ST0 Value
- * \return Boolean failure
- */
-int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0)
-{
- static const char *status_type[] = {
- 0, "Error", "Invalid", "Drive Error"
- };
-
- Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)",
- Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0
- );
- return 0;
-}
-
-/**
- * \brief Clear the IRQ fired flag
- */
-void FDD_int_ClearIRQ(void)
-{
- gbFDD_IRQ6Fired = 0;
-}
-
-/**
- * \brief Wait for an IRQ to fire
- */
-int FDD_int_WaitIRQ(void)
-{
- while(gbFDD_IRQ6Fired == 0)
- Threads_Yield();
- return 0;
-}
-
-/**
- * \brief IRQ Handler
- * \param IRQ IRQ Number (unused)
- * \param Ptr Data Pointer (unused)
- */
-void FDD_int_IRQHandler(int IRQ, void *Ptr)
-{
- gbFDD_IRQ6Fired = 1;
-}
-
+++ /dev/null
-/*
- * Acess2 82077AA FDC
- * - By John Hodge (thePowersGang)
- *
- * fdc.c
- * - Core file
- */
-#define DEBUG 0
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#include "common.h"
-#include <api_drv_disk.h>
-
-// === CONSTANTS ===
-#define FDD_VERSION VER2(1,10)
-
-// === STRUCTURES ===
-
-// === PROTOTYPES ===
- int FDD_Install(char **Arguments);
- int FDD_RegisterFS(void);
-// --- VFS
-char *FDD_ReadDir(tVFS_Node *Node, int pos);
-tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name);
- int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data);
-Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
-// --- Helpers
- int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, FDD_VERSION, Storage_FDDv2, FDD_Install, NULL, "x86_ISADMA", NULL);
-tDrive gaFDD_Disks[MAX_DISKS];
-tVFS_Node gaFDD_DiskNodes[MAX_DISKS];
-tVFS_NodeType gFDD_RootNodeType = {
- .TypeName = "FDD Root Node",
- .ReadDir = FDD_ReadDir,
- .FindDir = FDD_FindDir,
- .IOCtl = FDD_IOCtl
- };
-tVFS_NodeType gFDD_DevNodeType = {
- .TypeName = "FDD Device",
- .Read = FDD_ReadFS,
- .Write = NULL // FDD_WriteFS?
- };
-tDevFS_Driver gFDD_DriverInfo = {
- NULL, "fdd",
- {
- .Size = -1,
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gFDD_RootNodeType
- }
- };
-
-// === CODE ===
-int FDD_Install(char **Arguments)
-{
- // Query CMOS memory
- {
- Uint8 data;
- outb(0x70, 0x10);
- data = inb(0x71);
-
- // NOTE: CMOS only reports 2 disks
- if( (data & 0xF0) == 0x40 )
- gaFDD_Disks[0].bValid = gaFDD_Disks[0].bInserted = 1;
- if( (data & 0x0F) == 0x04 )
- gaFDD_Disks[1].bValid = gaFDD_Disks[1].bInserted = 1;
-
- if( gaFDD_Disks[0].bValid == 0 && gaFDD_Disks[1].bValid == 0 )
- return MODULE_ERR_NOTNEEDED;
- }
-
- // Initialise controller
- FDD_SetupIO();
-
- FDD_RegisterFS();
-
- return 0;
-}
-
-/**
- * \brief Register the FDD driver with DevFS
- */
-int FDD_RegisterFS(void)
-{
- gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime
- = gFDD_DriverInfo.RootNode.ATime = now();
-
- for( int i = 0; i < MAX_DISKS; i ++ )
- {
- if( !gaFDD_Disks[i].bValid ) continue ;
-
- // Initialise Child Nodes
- gaFDD_DiskNodes[i].Inode = i;
- gaFDD_DiskNodes[i].Flags = 0;
- gaFDD_DiskNodes[i].NumACLs = 0;
- gaFDD_DiskNodes[i].Type = &gFDD_DevNodeType;
- gaFDD_DiskNodes[i].Size = 1440*1024; // TODO: Non 1.44 disks
- }
-
- DevFS_AddDevice( &gFDD_DriverInfo );
- return 0;
-}
-
-/**
- * \brief Get the name of the \a Pos th item in the driver root
- * \param Node Root node (unused)
- * \param Pos Position
- * \return Heap string of node name
- */
-char *FDD_ReadDir(tVFS_Node *Node, int Pos)
-{
- char ret_tpl[2];
- if(Pos < 0 || Pos > MAX_DISKS )
- return NULL;
- if(gaFDD_Disks[Pos].bValid)
- return VFS_SKIP;
-
- ret_tpl[0] = '0' + Pos;
- ret_tpl[1] = '\0';
- return strdup(ret_tpl);
-}
-
-/**
- * \brief Get a node by name
- * \param Node Root node (unused)
- * \param Name Drive name
- * \return Pointer to node structure
- */
-tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *Name)
-{
- int pos;
- if( '0' > Name[0] || Name[0] > '9' ) return NULL;
- if( Name[1] != '\0' ) return NULL;
-
- pos = Name[0] - '0';
-
- return &gaFDD_DiskNodes[pos];
-}
-
-static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL};
-/**
- * \brief Driver root IOCtl Handler
- * \param Node Root node (unused)
- * \param ID IOCtl ID
- * \param Data IOCtl specific data pointer
- */
-int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- switch(ID)
- {
- BASE_IOCTLS(DRV_TYPE_DISK, "FDDv2", FDD_VERSION, casIOCTLS);
-
- case DISK_IOCTL_GETBLOCKSIZE: return 512;
-
- default:
- return -1;
- }
-}
-
-/**
- * \brief Read from a disk
- * \param Node Disk node
- * \param Offset Byte offset in disk
- * \param Length Number of bytes to read
- * \param Buffer Destination buffer
- * \return Number of bytes read
- */
-Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
- int disk = Node->Inode;
- int track;
- int rem_len;
- char *dest = Buffer;
-
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- if( Offset > Node->Size ) LEAVE_RET('i', 0);
- if( Length > Node->Size ) Length = Node->Size;
- if( Offset + Length > Node->Size )
- Length = Node->Size - Offset;
- if( Length == 0 ) return 0;
-
- rem_len = Length;
-
- track = Offset / BYTES_PER_TRACK;
- Offset %= BYTES_PER_TRACK;
-
- if( Offset )
- {
- int len;
- if(rem_len > BYTES_PER_TRACK - Offset)
- len = BYTES_PER_TRACK - Offset;
- else
- len = rem_len;
- FDD_int_ReadWriteWithinTrack(disk, track, 0, Offset, len, dest);
- dest += len;
- rem_len -= len;
- track ++;
- }
-
- if( rem_len > 0)
- {
- while( rem_len > BYTES_PER_TRACK )
- {
- FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, BYTES_PER_TRACK, dest);
- dest += BYTES_PER_TRACK;
- rem_len -= BYTES_PER_TRACK;
- track ++;
- }
-
- FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, rem_len, dest);
- }
-
- LEAVE('X', Length);
- return Length;
-}
-
-/**
- * \brief Read from a track
- */
-int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer)
-{
- if( Offset > BYTES_PER_TRACK || Length > BYTES_PER_TRACK )
- return 1;
- if( Offset + Length > BYTES_PER_TRACK )
- return 1;
-
- if( Length == 0 )
- return 0;
-
- ENTER("iDisk iTrack bbWrite xOffset xLength pBuffer",
- Disk, Track, bWrite, Offset, Length, Buffer);
-
- Mutex_Acquire( &gaFDD_Disks[Disk].Mutex );
-
- // If the cache doesn't exist, create it
- if( !gaFDD_Disks[Disk].TrackData[Track] )
- {
- gaFDD_Disks[Disk].TrackData[Track] = malloc( BYTES_PER_TRACK );
- // Don't bother reading if this is a whole track write
- if( !(bWrite && Offset == 0 && Length == BYTES_PER_TRACK) )
- {
- LOG("Reading track");
- FDD_int_ReadWriteTrack(Disk, Track, 0, gaFDD_Disks[Disk].TrackData[Track]);
- }
- }
-
- // Read/Write
- if( bWrite )
- {
- // Write to cache then commit cache to disk
- char *dest = gaFDD_Disks[Disk].TrackData[Track];
- LOG("Write to cache");
- memcpy( dest + Offset, Buffer, Length );
- FDD_int_ReadWriteTrack(Disk, Track, 1, gaFDD_Disks[Disk].TrackData[Track]);
- }
- else
- {
- // Read from cache
- char *src = gaFDD_Disks[Disk].TrackData[Track];
- LOG("Read from cache");
- memcpy(Buffer, src + Offset, Length);
- }
-
- Mutex_Release( &gaFDD_Disks[Disk].Mutex );
-
- LEAVE('i', 0);
- return 0;
-}
+++ /dev/null
-CATEGORY = Storage
-
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-OBJ = main.o
-OBJ += usb.o usb_lowlevel.o usb_devinit.o usb_io.o usb_poll.o
-OBJ += hub.o
-CPPFLAGS = -Iinclude
-NAME = Core
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * hub.c
- * - Basic hub driver
- */
-#define DEBUG 1
-#include <usb_hub.h>
-
-#define MAX_PORTS 32 // Not actually a max, but used for DeviceRemovable
-
-
-#define GET_STATUS 0
-#define CLEAR_FEATURE 1
-// resvd
-#define SET_FEATURE 3
-
-#define PORT_CONNECTION 0
-#define PORT_ENABLE 1
-#define PORT_SUSPEND 2
-#define PORT_OVER_CURRENT 3
-#define PORT_RESET 4
-#define PORT_POWER 8
-#define PORT_LOW_SPEED 9
-#define C_PORT_CONNECTION 16
-#define C_PORT_ENABLE 17
-#define C_PORT_SUSPEND 18
-#define C_PORT_OVER_CURRENT 19
-#define C_PORT_RESET 20
-#define PORT_TEST 21
-#define PORT_INDICATOR 21
-
-struct sHubDescriptor
-{
- Uint8 DescLength;
- Uint8 DescType; // = 0x29
- Uint8 NbrPorts;
- Uint16 HubCharacteristics;
- Uint8 PwrOn2PwrGood; // 2 ms intervals
- Uint8 HubContrCurrent; // Max internal current (mA)
- Uint8 DeviceRemovable[MAX_PORTS];
-};
-
-struct sHubInfo
-{
- tUSBHub *HubPtr;
- int PowerOnDelay; // in ms
- int nPorts;
- Uint8 DeviceRemovable[];
-};
-
-// === PROTOTYPES ===
-void Hub_Connected(tUSBInterface *Dev);
-void Hub_Disconnected(tUSBInterface *Dev);
-void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
-void Hub_int_HandleChange(tUSBInterface *Dev, int Port);
-
-// === GLOBALS ===
-tUSBDriver gUSBHub_Driver = {
- .Name = "Hub",
- .Match = {.Class = {0x090000, 0xFF0000}},
- .Connected = Hub_Connected,
- .Disconnected = Hub_Disconnected,
- .MaxEndpoints = 1,
- .Endpoints = {
- {0x83, Hub_PortStatusChange}
- }
-};
-
-// === CODE ===
-#if 0
-int Hub_DriverInitialise(char **Arguments)
-{
- USB_RegisterDriver( &gUSBHub_Driver );
- return 0;
-}
-#endif
-
-void Hub_Connected(tUSBInterface *Dev)
-{
- struct sHubDescriptor hub_desc;
- struct sHubInfo *info;
-
- // Read hub descriptor (Class descriptor 0x29)
- USB_ReadDescriptor(Dev, 0x129, 0, sizeof(hub_desc), &hub_desc);
-
- LOG("%i Ports", hub_desc.NbrPorts);
- LOG("Takes %i ms for power to stabilise", hub_desc.PwrOn2PwrGood*2);
-
- // Allocate infomation structure
- info = malloc(sizeof(*info) + (hub_desc.NbrPorts+7)/8);
- if(!info) {
- Log_Error("USBHub", "malloc() failed");
- return ;
- }
- USB_SetDeviceDataPtr(Dev, info);
-
- // Fill data
- info->nPorts = hub_desc.NbrPorts;
- info->PowerOnDelay = hub_desc.PwrOn2PwrGood * 2;
- memcpy(info->DeviceRemovable, hub_desc.DeviceRemovable, (hub_desc.NbrPorts+7)/8);
- // Register
- info->HubPtr = USB_RegisterHub(Dev, info->nPorts);
-
- // Register poll on endpoint
- USB_StartPollingEndpoint(Dev, 1);
-}
-
-void Hub_Disconnected(tUSBInterface *Dev)
-{
- struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
- USB_RemoveHub(info->HubPtr);
- free(info);
-}
-
-void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
-{
- Uint8 *status = Data;
- struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
- int i;
- for( i = 0; i < info->nPorts; i += 8, status ++ )
- {
- if( i/8 >= Length ) break;
- if( *status == 0 ) continue;
-
- for( int j = 0; j < 8; j ++ )
- if( *status & (1 << j) )
- Hub_int_HandleChange(Dev, i+j);
- }
-}
-
-void Hub_int_HandleChange(tUSBInterface *Dev, int Port)
-{
- struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
- Uint16 status[2]; // Status, Change
-
- // Get port status
- USB_Request(Dev, 0, 0xA3, GET_STATUS, 0, Port, 4, status);
-
- LOG("Port %i: status = {0b%b, 0b%b}", Port, status[0], status[1]);
-
- // Handle connections / disconnections
- if( status[1] & 0x0001 )
- {
- if( status[0] & 0x0001 ) {
- // Connected
- // - Power on port
- USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_POWER, Port, 0, NULL);
- Time_Delay(info->PowerOnDelay);
- // - Reset
- USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_RESET, Port, 0, NULL);
- Time_Delay(20); // Spec says 10ms after reset, but how long is reset?
- // - Enable
- USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_ENABLE, Port, 0, NULL);
- // - Poke USB Stack
- USB_DeviceConnected(info->HubPtr, Port);
- }
- else {
- // Disconnected
- USB_DeviceDisconnected(info->HubPtr, Port);
- }
-
- USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, Port, 0, NULL);
- }
-
- // Reset change
- if( status[1] & 0x0010 )
- {
- if( status[0] & 0x0010 ) {
- // Reset complete
- }
- else {
- // Useful?
- }
- // ACK
- USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_RESET, Port, 0, NULL);
- }
-}
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_core.h
- * - Core USB IO Header
- */
-#ifndef _USB_CORE_H_
-#define _USB_CORE_H_
-
-#include <acess.h>
-
-typedef struct sUSBInterface tUSBInterface;
-typedef struct sUSBDriver tUSBDriver;
-
-typedef void (*tUSB_DataCallback)(tUSBInterface *Dev, int EndPt, int Length, void *Data);
-
-/**
- */
-struct sUSBDriver
-{
- tUSBDriver *Next;
-
- const char *Name;
-
- int MatchType; // 0: Interface, 1: Device, 2: Vendor
- union {
- struct {
- // 23:16 - Interface Class
- // 15:8 - Interface Sub Class
- // 7:0 - Interface Protocol
- Uint32 ClassCode;
- Uint32 ClassMask;
- } Class;
- struct {
- Uint16 VendorID;
- Uint16 DeviceID;
- } VendorDev;
- } Match;
-
- void (*Connected)(tUSBInterface *Dev);
- void (*Disconnected)(tUSBInterface *Dev);
-
- int MaxEndpoints;
- struct {
- // USB Attrbute byte
- // NOTE: Top bit indicates the direction (1=Input)
- Uint8 Attributes;
- // Data availiable Callback
- tUSB_DataCallback DataAvail;
- } Endpoints[];
-};
-
-extern void *USB_GetDeviceDataPtr(tUSBInterface *Dev);
-extern void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr);
-
-extern void USB_StartPollingEndpoint(tUSBInterface *Dev, int Endpoint);
-extern void USB_ReadDescriptor(tUSBInterface *Dev, int Type, int Index, int Length, void *Data);
-extern void USB_Request(tUSBInterface *Dev, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data);
-// TODO: Async
-extern void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
-extern void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
-extern void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_host.h
- * - USB Host Controller Interface
- */
-#ifndef _USB_HOST_H_
-#define _USB_HOST_H_
-
-#include "usb_core.h"
-#include "usb_hub.h"
-
-typedef struct sUSBHostDef tUSBHostDef;
-
-typedef void (*tUSBHostCb)(void *DataPtr, void *Data, int Length);
-
-typedef void *(*tUSBHostOp)(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length);
-
-/**
- * \brief Defines a USB Host Controller type
- */
-struct sUSBHostDef
-{
- tUSBHostOp SendIN;
- tUSBHostOp SendOUT;
- tUSBHostOp SendSETUP;
-
- int (*IsOpComplete)(void *Ptr, void *OpPtr);
-
- void (*CheckPorts)(void *Ptr);
-};
-
-extern tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_hub.h
- * - Core Hub Definitions
- */
-#ifndef _USB_HUB_H_
-#define _USB_HUB_H_
-
-#include "usb_core.h"
-
-typedef struct sUSBHub tUSBHub;
-
-/**
- * \brief Register a device as a hub
- *
- * Used by the hub class initialisation routine.
- */
-extern tUSBHub *USB_RegisterHub(tUSBInterface *Device, int nPorts);
-extern void USB_RemoveHub(tUSBHub *Hub);
-
-extern void USB_DeviceConnected(tUSBHub *Hub, int Port);
-extern void USB_DeviceDisconnected(tUSBHub *Hub, int Port);
-
-#endif
-
+++ /dev/null
-/*
- * Acess2
- * USB Stack
- */
-#define VERSION ( (0<<8)| 5 )
-#define DEBUG 1
-#include <acess.h>
-#include <vfs.h>
-#include <fs_devfs.h>
-#include <modules.h>
-#include "usb.h"
-
-// === IMPORTS ===
-extern void USB_PollThread(void *unused);
-extern void USB_AsyncThread(void *Unused);
-
-// === PROTOTYPES ===
- int USB_Install(char **Arguments);
-void USB_Cleanup(void);
-char *USB_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name);
- int USB_IOCtl(tVFS_Node *Node, int Id, void *Data);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, USB_Core, USB_Install, NULL, NULL);
-tVFS_NodeType gUSB_RootNodeType = {
- .ReadDir = USB_ReadDir,
- .FindDir = USB_FindDir,
- .IOCtl = USB_IOCtl
-};
-tDevFS_Driver gUSB_DrvInfo = {
- NULL, "usb", {
- .NumACLs = 1,
- .ACLs = &gVFS_ACL_EveryoneRX,
- .Flags = VFS_FFLAG_DIRECTORY,
- .Type = &gUSB_RootNodeType
- }
-};
-tUSBHost *gUSB_Hosts = NULL;
-
-// === CODE ===
-/**
- * \brief Called once module is loaded
- */
-int USB_Install(char **Arguments)
-{
- Log_Warning("USB", "Not Complete - Devel Only");
-
- Proc_SpawnWorker(USB_PollThread, NULL);
- Proc_SpawnWorker(USB_AsyncThread, NULL);
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Called just before module is unloaded
- */
-void USB_Cleanup()
-{
-}
-
-/**
- * \fn char *USB_ReadDir(tVFS_Node *Node, int Pos)
- * \brief Read from the USB root
- */
-char *USB_ReadDir(tVFS_Node *Node, int Pos)
-{
- return NULL;
-}
-
-/**
- * \fn tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name)
- * \brief Locate an entry in the USB root
- */
-tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name)
-{
- return NULL;
-}
-
-/**
- * \brief Handles IOCtl Calls to the USB driver
- */
-int USB_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
- return 0;
-}
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb.c
- * - USB Structure
- */
-#define DEBUG 1
-#include <acess.h>
-#include <vfs.h>
-#include <drv_pci.h>
-#include "usb.h"
-
-// === IMPORTS ===
-extern tUSBHost *gUSB_Hosts;
-extern tUSBDriver gUSBHub_Driver;
-
-// === STRUCTURES ===
-
-// === PROTOTYPES ===
-tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);
-
-// === GLOBALS ===
-tUSBDriver *gpUSB_InterfaceDrivers = &gUSBHub_Driver;
-
-// === CODE ===
-tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts)
-{
- tUSBHost *host;
-
- host = malloc(sizeof(tUSBHost) + nPorts*sizeof(void*));
- if(!host) {
- // Oh, bugger.
- return NULL;
- }
- host->HostDef = HostDef;
- host->Ptr = ControllerPtr;
- memset(host->AddressBitmap, 0, sizeof(host->AddressBitmap));
-
- host->RootHubDev.ParentHub = NULL;
- host->RootHubDev.Host = host;
- host->RootHubDev.Address = 0;
-
-// host->RootHubIf.Next = NULL;
- host->RootHubIf.Dev = &host->RootHubDev;
- host->RootHubIf.Driver = NULL;
- host->RootHubIf.Data = NULL;
- host->RootHubIf.nEndpoints = 0;
-
- host->RootHub.Interface = &host->RootHubIf;
- host->RootHub.nPorts = nPorts;
- memset(host->RootHub.Devices, 0, sizeof(void*)*nPorts);
-
- // TODO: Lock
- host->Next = gUSB_Hosts;
- gUSB_Hosts = host;
-
- return &host->RootHub;
-}
-
-// --- Drivers ---
-void USB_RegisterDriver(tUSBDriver *Driver)
-{
- Log_Warning("USB", "TODO: Implement USB_RegisterDriver");
-}
-
-tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode)
-{
- ENTER("xClassCode", ClassCode);
- for( tUSBDriver *ret = gpUSB_InterfaceDrivers; ret; ret = ret->Next )
- {
- LOG(" 0x%x & 0x%x == 0x%x?", ClassCode, ret->Match.Class.ClassMask, ret->Match.Class.ClassCode);
- if( (ClassCode & ret->Match.Class.ClassMask) == ret->Match.Class.ClassCode )
- {
- LOG("Found '%s'", ret->Name);
- LEAVE('p', ret);
- return ret;
- }
- }
- LEAVE('n');
- return NULL;
-}
-
-// --- Hub Registration ---
-// NOTE: Doesn't do much nowdays
-tUSBHub *USB_RegisterHub(tUSBInterface *Device, int PortCount)
-{
- tUSBHub *ret;
-
- ret = malloc(sizeof(tUSBHub) + sizeof(ret->Devices[0])*PortCount);
- ret->Interface = Device;
- ret->nPorts = PortCount;
- memset(ret->Devices, 0, sizeof(ret->Devices[0])*PortCount);
- return ret;
-}
-
-void USB_RemoveHub(tUSBHub *Hub)
-{
- for( int i = 0; i < Hub->nPorts; i ++ )
- {
- if( Hub->Devices[i] )
- {
- USB_DeviceDisconnected( Hub, i );
- }
- }
- free(Hub);
-}
-
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb.h
- * - USB Internal definitions
- */
-#ifndef _USB_H_
-#define _USB_H_
-
-#include <usb_core.h>
-#include <usb_hub.h>
-#include <usb_host.h>
-
-typedef struct sUSBHost tUSBHost;
-typedef struct sUSBDevice tUSBDevice;
-typedef struct sUSBEndpoint tUSBEndpoint;
-
-// === STRUCTURES ===
-/**
- * \brief USB Hub data
- */
-struct sUSBHub
-{
- tUSBInterface *Interface;
-
- int nPorts;
- tUSBDevice *Devices[];
-};
-
-struct sUSBEndpoint
-{
- tUSBEndpoint *Next; // (usb_poll.c) Clock list
- tUSBInterface *Interface;
- int EndpointIdx; // Interface endpoint index
- int EndpointNum; // Device endpoint num
-
- int PollingPeriod; // In 1ms intervals
- int MaxPacketSize; // In bytes
- Uint8 Type; // Same as sUSBDriver.Endpoints.Type
-
- int PollingAtoms; // (usb_poll.c) Period in clock list
- void *InputData;
-};
-
-/**
- * \brief Structure for a device's interface
- */
-struct sUSBInterface
-{
-// tUSBInterface *Next;
- tUSBDevice *Dev;
-
- tUSBDriver *Driver;
- void *Data;
-
- int nEndpoints;
- tUSBEndpoint Endpoints[];
-};
-
-/**
- * \brief Defines a single device on the USB Bus
- */
-struct sUSBDevice
-{
- tUSBHub *ParentHub;
-
- /**
- * \brief Host controller used
- */
- tUSBHost *Host;
- int Address;
-
- int nInterfaces;
- tUSBInterface *Interfaces[];
-};
-
-struct sUSBHost
-{
- struct sUSBHost *Next;
-
- tUSBHostDef *HostDef;
- void *Ptr;
-
- Uint8 AddressBitmap[128/8];
-
- tUSBDevice RootHubDev;
- tUSBInterface RootHubIf;
- tUSBHub RootHub;
-};
-
-extern tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode);
-
-#endif
+++ /dev/null
-/*
- * Acess 2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_devinit.c
- * - USB Device Initialisation
- */
-#define DEBUG 1
-
-#include <acess.h>
-#include <vfs.h>
-#include <drv_pci.h>
-#include "usb.h"
-#include "usb_proto.h"
-#include "usb_lowlevel.h"
-
-// === PROTOTYPES ===
-void USB_DeviceConnected(tUSBHub *Hub, int Port);
-void USB_DeviceDisconnected(tUSBHub *Hub, int Port);
-void *USB_GetDeviceDataPtr(tUSBInterface *Dev);
-void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr);
- int USB_int_AllocateAddress(tUSBHost *Host);
-
-// === CODE ===
-void USB_DeviceConnected(tUSBHub *Hub, int Port)
-{
- tUSBDevice tmpdev;
- tUSBDevice *dev = &tmpdev;
- if( Port >= Hub->nPorts ) return ;
- if( Hub->Devices[Port] ) return ;
-
- ENTER("pHub iPort", Hub, Port);
-
- // Device should be in 'Default' state
-
- // Create structure
- dev->ParentHub = Hub;
- dev->Host = Hub->Interface->Dev->Host;
- dev->Address = 0;
-
- // 1. Assign an address
- dev->Address = USB_int_AllocateAddress(dev->Host);
- if(dev->Address == 0) {
- Log_Error("USB", "No addresses avaliable on host %p", dev->Host);
- free(dev);
- LEAVE('-');
- return ;
- }
- USB_int_SendSetupSetAddress(dev->Host, dev->Address);
- LOG("Assigned address %i", dev->Address);
-
- // 2. Get device information
- {
- struct sDescriptor_Device desc;
- LOG("Getting device descriptor");
- // Endpoint 0, Desc Type 1, Index 0
- USB_int_ReadDescriptor(dev, 0, 1, 0, sizeof(desc), &desc);
-
- LOG("Device Descriptor = {");
- LOG(" .Length = %i", desc.Length);
- LOG(" .Type = %i", desc.Type);
- LOG(" .USBVersion = 0x%04x", desc.USBVersion);
- LOG(" .DeviceClass = 0x%02x", desc.DeviceClass);
- LOG(" .DeviceSubClass = 0x%02x", desc.DeviceSubClass);
- LOG(" .DeviceProtocol = 0x%02x", desc.DeviceProtocol);
- LOG(" .MaxPacketSize = 0x%02x", desc.MaxPacketSize);
- LOG(" .VendorID = 0x%04x", desc.VendorID);
- LOG(" .ProductID = 0x%04x", desc.ProductID);
- LOG(" .DeviceID = 0x%04x", desc.DeviceID);
- LOG(" .ManufacturerStr = Str %i", desc.ManufacturerStr);
- LOG(" .ProductStr = Str %i", desc.ProductStr);
- LOG(" .SerialNumberStr = Str %i", desc.SerialNumberStr);
- LOG(" .NumConfigurations = %i", desc.SerialNumberStr);
- LOG("}");
-
- if( desc.ManufacturerStr )
- {
- char *tmp = USB_int_GetDeviceString(dev, 0, desc.ManufacturerStr);
- LOG("ManufacturerStr = '%s'", tmp);
- free(tmp);
- }
- if( desc.ProductStr )
- {
- char *tmp = USB_int_GetDeviceString(dev, 0, desc.ProductStr);
- LOG("ProductStr = '%s'", tmp);
- free(tmp);
- }
- if( desc.SerialNumberStr )
- {
- char *tmp = USB_int_GetDeviceString(dev, 0, desc.SerialNumberStr);
- LOG("SerialNumbertStr = '%s'", tmp);
- free(tmp);
- }
- }
-
- // TODO: Support alternate configurations
-
- // 3. Get configurations
- for( int i = 0; i < 1; i ++ )
- {
- struct sDescriptor_Configuration desc;
- void *full_buf;
- char *cur_ptr;
-
- USB_int_ReadDescriptor(dev, 0, 2, i, sizeof(desc), &desc);
- LOG("Configuration Descriptor %i = {", i);
- LOG(" .Length = %i", desc.Length);
- LOG(" .Type = %i", desc.Type);
- LOG(" .TotalLength = 0x%x", LittleEndian16(desc.TotalLength));
- LOG(" .NumInterfaces = %i", desc.NumInterfaces);
- LOG(" .ConfigurationValue = %i", desc.ConfigurationValue);
- LOG(" .ConfigurationStr = %i", desc.ConfigurationStr);
- LOG(" .AttributesBmp = 0b%b", desc.AttributesBmp);
- LOG(" .MaxPower = %i (*2mA)", desc.MaxPower);
- LOG("}");
- if( desc.ConfigurationStr ) {
- char *tmp = USB_int_GetDeviceString(dev, 0, desc.ConfigurationStr);
- LOG("ConfigurationStr = '%s'", tmp);
- free(tmp);
- }
-
- // TODO: Split here and allow some method of selection
-
- // Allocate device now that we have the configuration
- dev = malloc(sizeof(tUSBDevice) + desc.NumInterfaces * sizeof(void*));
- memcpy(dev, &tmpdev, sizeof(tUSBDevice));
- dev->nInterfaces = desc.NumInterfaces;
-
- // Allocate a temp buffer for config info
- cur_ptr = full_buf = malloc( LittleEndian16(desc.TotalLength) );
- USB_int_ReadDescriptor(dev, 0, 2, i, desc.TotalLength, full_buf);
-
- cur_ptr += desc.Length;
-
- // TODO: Interfaces
- for( int j = 0; j < desc.NumInterfaces; j ++ )
- {
- struct sDescriptor_Interface *iface;
- tUSBInterface *dev_if;
- iface = (void*)cur_ptr;
- // TODO: Sanity check with remaining space
- cur_ptr += sizeof(*iface);
-
- LOG("Interface %i/%i = {", i, j);
- LOG(" .InterfaceNum = %i", iface->InterfaceNum);
- LOG(" .NumEndpoints = %i", iface->NumEndpoints);
- LOG(" .InterfaceClass = 0x%x", iface->InterfaceClass);
- LOG(" .InterfaceSubClass = 0x%x", iface->InterfaceSubClass);
- LOG(" .InterfaceProcol = 0x%x", iface->InterfaceProtocol);
-
- if( iface->InterfaceStr ) {
- char *tmp = USB_int_GetDeviceString(dev, 0, iface->InterfaceStr);
- LOG(" .InterfaceStr = %i '%s'", iface->InterfaceStr, tmp);
- free(tmp);
- }
- LOG("}");
-
- dev_if = malloc(sizeof(tUSBInterface) + iface->NumEndpoints*sizeof(dev_if->Endpoints[0]));
- dev_if->Dev = dev;
- dev_if->Driver = NULL;
- dev_if->Data = NULL;
- dev_if->nEndpoints = iface->NumEndpoints;
- dev->Interfaces[j] = dev_if;
-
- // Copy interface data
- for( int k = 0; k < iface->NumEndpoints; k ++ )
- {
- struct sDescriptor_Endpoint *endpt;
- endpt = (void*)cur_ptr;
- // TODO: Sanity check with remaining space
- cur_ptr += sizeof(*endpt);
-
- LOG("Endpoint %i/%i/%i = {", i, j, k);
- LOG(" .Address = 0x%2x", endpt->Address);
- LOG(" .Attributes = 0b%8b", endpt->Attributes);
- LOG(" .MaxPacketSize = %i", LittleEndian16(endpt->MaxPacketSize));
- LOG(" .PollingInterval = %i", endpt->PollingInterval);
- LOG("}");
-
- dev_if->Endpoints[k].Next = NULL;
- dev_if->Endpoints[k].Interface = dev_if;
- dev_if->Endpoints[k].EndpointIdx = k;
- dev_if->Endpoints[k].EndpointNum = endpt->Address & 0x7F;
- dev_if->Endpoints[k].PollingPeriod = endpt->PollingInterval;
- dev_if->Endpoints[k].MaxPacketSize = LittleEndian16(endpt->MaxPacketSize);
- dev_if->Endpoints[k].Type = endpt->Attributes | (endpt->Address & 0x80);
- dev_if->Endpoints[k].PollingAtoms = 0;
- dev_if->Endpoints[k].InputData = NULL;
- }
-
- // Initialise driver
- dev_if->Driver = USB_int_FindDriverByClass(
- ((int)iface->InterfaceClass << 16)
- |((int)iface->InterfaceSubClass << 8)
- |((int)iface->InterfaceProtocol << 0)
- );
- if(!dev_if->Driver) {
- Log_Notice("USB", "No driver for Class %02x:%02x:%02x",
- iface->InterfaceClass, iface->InterfaceSubClass, iface->InterfaceProtocol
- );
- }
- else {
- dev_if->Driver->Connected( dev_if );
- }
- }
-
- free(full_buf);
- }
-
- // Done.
- LEAVE('-');
-}
-
-void USB_DeviceDisconnected(tUSBHub *Hub, int Port)
-{
-
-}
-
-void *USB_GetDeviceDataPtr(tUSBInterface *Dev) { return Dev->Data; }
-void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr) { Dev->Data = Ptr; }
-
-int USB_int_AllocateAddress(tUSBHost *Host)
-{
- int i;
- for( i = 1; i < 128; i ++ )
- {
- if(Host->AddressBitmap[i/8] & (1 << (i%8)))
- continue ;
- Host->AddressBitmap[i/8] |= 1 << (i%8);
- return i;
- }
- return 0;
-}
-
-void USB_int_DeallocateAddress(tUSBHost *Host, int Address)
-{
- Host->AddressBitmap[Address/8] &= ~(1 << (Address%8));
-}
-
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_io.c
- * - High-level IO
- */
-#define DEBUG 0
-
-#include <usb_core.h>
-#include "usb.h"
-#include "usb_lowlevel.h"
-#include <workqueue.h>
-
-typedef struct sAsyncOp tAsyncOp;
-
-struct sAsyncOp
-{
- tAsyncOp *Next;
- tUSBEndpoint *Endpt;
- int Length;
- void *Data;
-};
-
-// === PROTOTYPES ===
-void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data);
-void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data);
-void USB_AsyncCallback(void *Ptr, void *Buf, int Length);
-void USB_AsyncThread(void *unused);
-
-// === GLOBALS ===
-tWorkqueue gUSB_AsyncQueue;
-
-// === CODE ===
-void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data)
-{
- USB_int_ReadDescriptor(Iface->Dev, 0, Type, Index, Length, Data);
-}
-
-void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data)
-{
- int endpt;
-
- // Sanity check
- if(Endpoint < 0 || Endpoint >= Iface->nEndpoints)
- return ;
-
- // Get endpoint number
- if(Endpoint)
- endpt = Iface->Endpoints[Endpoint-1].EndpointNum;
- else
- endpt = 0;
-
- USB_int_Request(Iface->Dev->Host, Iface->Dev->Address, endpt, Type, Req, Value, Index, Len, Data);
-}
-
-
-void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
-{
- Log_Warning("USB", "TODO: Implement USB_SendData");
-}
-
-void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
-{
- Log_Warning("USB", "TODO: Implement USB_RecvData");
-}
-
-void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback)
-{
- tAsyncOp *op;
- tUSBHost *host;
-
- ENTER("pDev iEndpoint iLength pDataBuf", Dev, Endpoint, Length, DataBuf);
-
- op = malloc(sizeof(*op));
- op->Next = NULL;
- op->Endpt = &Dev->Endpoints[Endpoint-1];
- op->Length = Length;
- op->Data = DataBuf;
-
- // TODO: Handle transfers that are larger than one packet
-
- host = Dev->Dev->Host;
- LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum);
- host->HostDef->SendIN(
- host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum,
- 0, USB_AsyncCallback, op,
- DataBuf, Length
- );
-
- LEAVE('-');
-
-// Log_Warning("USB", "TODO: Implement USB_RecvDataA");
-}
-
-void USB_AsyncCallback(void *Ptr, void *Buf, int Length)
-{
- tAsyncOp *op = Ptr;
- op->Length = Length;
- LOG("adding %p to work queue", op);
- Workqueue_AddWork(&gUSB_AsyncQueue, op);
-}
-
-void USB_AsyncThread(void *Unused)
-{
- Threads_SetName("USB Async IO Thread");
- for(;;)
- {
- tAsyncOp *op = Workqueue_GetWork(&gUSB_AsyncQueue);
- tUSBInterface *iface = op->Endpt->Interface;
-
- LOG("op = %p", op);
-
- iface->Driver->Endpoints[op->Endpt->EndpointIdx].DataAvail(
- iface, op->Endpt->EndpointIdx,
- op->Length, op->Data);
- }
-}
-
+++ /dev/null
-/*
- * Acess 2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_lowlevel.c
- * - Low Level IO
- */
-#define DEBUG 1
-#include <acess.h>
-#include "usb.h"
-#include "usb_proto.h"
-#include "usb_lowlevel.h"
-
-// === PROTOTYPES ===
-void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data);
- int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address);
- int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest);
-char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index);
- int _UTF16to8(Uint16 *Input, int InputLen, char *Dest);
-
-// === CODE ===
-void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data)
-{
- void *hdl;
- // TODO: Sanity check (and check that Type is valid)
- struct sDeviceRequest req;
- req.ReqType = Type;
- req.Request = Req;
- req.Value = LittleEndian16( Val );
- req.Index = LittleEndian16( Indx );
- req.Length = LittleEndian16( Len );
-
- hdl = Host->HostDef->SendSETUP(Host->Ptr, Addr, EndPt, 0, NULL, NULL, &req, sizeof(req));
-
- // TODO: Data toggle?
- // TODO: Multi-packet transfers
- if( Type & 0x80 )
- {
- void *hdl2;
-
- hdl = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len);
-
- hdl2 = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, NULL, 0);
- while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 )
- Time_Delay(1);
- }
- else
- {
- void *hdl2;
-
- if( Len > 0 )
- hdl = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len);
- else
- hdl = NULL;
-
- // Status phase (DataToggle=1)
- hdl2 = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 1, NULL, NULL, NULL, 0);
- while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 )
- Time_Delay(1);
- }
- return hdl;
-}
-
-int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address)
-{
- USB_int_Request(Host, 0, 0, 0x00, 5, Address & 0x7F, 0, 0, NULL);
- return 0;
-}
-
-int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest)
-{
- const int ciMaxPacketSize = 0x400;
- struct sDeviceRequest req;
- int bToggle = 0;
- void *final;
-
- req.ReqType = 0x80;
- switch( Type & 0xF00 )
- {
- case 0x000: req.ReqType |= (0 << 5); break; // Standard
- case 0x100: req.ReqType |= (1 << 5); break; // Class
- case 0x200: req.ReqType |= (2 << 5); break; // Vendor
- }
-
- req.Request = 6; // GET_DESCRIPTOR
- req.Value = LittleEndian16( ((Type & 0xFF) << 8) | (Index & 0xFF) );
- req.Index = LittleEndian16( 0 ); // TODO: Language ID
- req.Length = LittleEndian16( Length );
-
- Dev->Host->HostDef->SendSETUP(
- Dev->Host->Ptr, Dev->Address, Endpoint,
- 0, NULL, NULL,
- &req, sizeof(req)
- );
-
- bToggle = 1;
- while( Length > ciMaxPacketSize )
- {
- Dev->Host->HostDef->SendIN(
- Dev->Host->Ptr, Dev->Address, Endpoint,
- bToggle, NULL, NULL,
- Dest, ciMaxPacketSize
- );
- bToggle = !bToggle;
- Length -= ciMaxPacketSize;
- }
-
- final = Dev->Host->HostDef->SendIN(
- Dev->Host->Ptr, Dev->Address, Endpoint,
- bToggle, INVLPTR, NULL,
- Dest, Length
- );
-
- while( Dev->Host->HostDef->IsOpComplete(Dev->Host->Ptr, final) == 0 )
- Time_Delay(1);
-
- return 0;
-}
-
-char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index)
-{
- struct sDescriptor_String str;
- int src_len, new_len;
- char *ret;
-
- if(Index == 0) return strdup("");
-
- USB_int_ReadDescriptor(Dev, Endpoint, 3, Index, sizeof(str), &str);
- if(str.Length < 2) {
- Log_Error("USB", "String %p:%i:%i:%i descriptor is undersized (%i)",
- Dev->Host, Dev->Address, Endpoint, Index, str.Length);
- return NULL;
- }
-// if(str.Length > sizeof(str)) {
-// // IMPOSSIBLE!
-// Log_Error("USB", "String is %i bytes, which is over prealloc size (%i)",
-// str.Length, sizeof(str)
-// );
-// }
- src_len = (str.Length - 2) / sizeof(str.Data[0]);
-
- LOG("&str = %p, src_len = %i", &str, src_len);
-
- new_len = _UTF16to8(str.Data, src_len, NULL);
- ret = malloc( new_len + 1 );
- _UTF16to8(str.Data, src_len, ret);
- ret[new_len] = 0;
- return ret;
-}
-
-int _UTF16to8(Uint16 *Input, int InputLen, char *Dest)
-{
- int str_len, cp_len;
- Uint32 saved_bits = 0;
- str_len = 0;
- for( int i = 0; i < InputLen; i ++)
- {
- Uint32 cp;
- Uint16 val = Input[i];
- if( val >= 0xD800 && val <= 0xDBFF )
- {
- // Multibyte - Leading
- if(i + 1 > InputLen) {
- cp = '?';
- }
- else {
- saved_bits = (val - 0xD800) << 10;
- saved_bits += 0x10000;
- continue ;
- }
- }
- else if( val >= 0xDC00 && val <= 0xDFFF )
- {
- if( !saved_bits ) {
- cp = '?';
- }
- else {
- saved_bits |= (val - 0xDC00);
- cp = saved_bits;
- }
- }
- else
- cp = val;
-
- cp_len = WriteUTF8((Uint8*)Dest, cp);
- if(Dest)
- Dest += cp_len;
- str_len += cp_len;
-
- saved_bits = 0;
- }
-
- return str_len;
-}
-
+++ /dev/null
-/**
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_lowlevel.h
- * - Low-Level USB IO Functions
- */
-#ifndef _USB_LOWLEVEL_H_
-#define _USB_LOWLEVEL_H_
-
-extern void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data);
-extern int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address);
-extern int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest);
-extern char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index);
-
-#endif
+++ /dev/null
-/*
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_poll.c
- * - Endpoint polling
- */
-#define DEBUG 1
-#include <usb_core.h>
-#include "usb.h"
-
-#define POLL_ATOM 25 // 25ms atom
-#define POLL_MAX 256 // Max period that can be nominated
-#define POLL_SLOTS ((int)(POLL_MAX/POLL_ATOM))
-
-// === IMPORTS ===
-extern tUSBHost *gUSB_Hosts;
-
-// === PROTOTYPES ===
-void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint);
-
-// === GLOBALS ===
-tUSBEndpoint *gUSB_PollQueues[POLL_MAX/POLL_ATOM];
- int giUSB_PollPosition; // Index into gUSB_PollQueues
-
-// === CODE ===
-void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint)
-{
- tUSBEndpoint *endpt;
-
- // Some sanity checks
- if(Endpoint <= 0 || Endpoint > Iface->nEndpoints) return ;
- endpt = &Iface->Endpoints[Endpoint-1];
- if(endpt->PollingPeriod > POLL_MAX || endpt->PollingPeriod <= 0)
- return ;
-
- // TODO: Check that this endpoint isn't already on the queue
-
- endpt->InputData = malloc(endpt->MaxPacketSize);
-
- // Determine polling period in atoms
- endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM;
- if(endpt->PollingAtoms > POLL_SLOTS) endpt->PollingAtoms = POLL_SLOTS;
- // Add to poll queue
- // TODO: Locking
- {
- int idx = giUSB_PollPosition + 1;
- if(idx >= POLL_SLOTS) idx -= POLL_SLOTS;
- endpt->Next = gUSB_PollQueues[idx];
- gUSB_PollQueues[idx] = endpt;
- }
-}
-
-/**
- * \brief USB polling thread
- */
-int USB_PollThread(void *unused)
-{
- Threads_SetName("USB Polling Thread");
- for(;;)
- {
- tUSBEndpoint *ep, *prev;
-
- if(giUSB_PollPosition == 0)
- {
- // Check hosts
- for( tUSBHost *host = gUSB_Hosts; host; host = host->Next )
- {
- host->HostDef->CheckPorts(host->Ptr);
- }
- }
-
-// Log_Debug("USBPoll", "giUSB_PollPosition = %i", giUSB_PollPosition);
-
- // A little evil for neater code
- prev = (void*)( (tVAddr)&gUSB_PollQueues[giUSB_PollPosition] - offsetof(tUSBEndpoint, Next) );
-
- // Process queue
-// LOG("giUSB_PollPosition = %i", giUSB_PollPosition);
- for( ep = gUSB_PollQueues[giUSB_PollPosition]; ep; prev = ep, ep = ep->Next )
- {
- int period_in_atoms = ep->PollingAtoms;
-// LOG("%i: ep = %p", giUSB_PollPosition, ep);
-
- // Check for invalid entries
- if(period_in_atoms < 0 || period_in_atoms > POLL_ATOM)
- {
- Log_Warning("USB", "Endpoint on polling queue with invalid period");
- continue ;
- }
- // Check for entries to delete
- if(period_in_atoms == 0)
- {
- // Remove
- prev->Next = ep->Next;
- ep->PollingAtoms = -1; // Mark as removed
- ep = prev; // Make sure prev is kept valid
- continue ;
- }
-
- // Read data
- // TODO: Check the endpoint
- // TODO: Async checking?
- // - Send the read request on all of them then wait for the first to complete
- USB_RecvDataA(
- ep->Interface, ep->EndpointIdx+1,
- ep->MaxPacketSize, ep->InputData,
- ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail
- );
-
- // Call callback
-
- // Reschedule
- if( period_in_atoms != POLL_SLOTS )
- {
- int newqueue_id = (giUSB_PollPosition + period_in_atoms) % POLL_SLOTS;
- tUSBEndpoint **newqueue = &gUSB_PollQueues[newqueue_id];
-
- prev->Next = ep->Next;
-
- ep->Next = *newqueue;
- *newqueue = ep;
- ep = prev;
- }
- }
- giUSB_PollPosition ++;
- if(giUSB_PollPosition == POLL_SLOTS)
- giUSB_PollPosition = 0;
- // TODO: Check for a longer delay
- Time_Delay(POLL_ATOM);
- }
-}
-
+++ /dev/null
-/**
- * Acess2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * usb_proto.h
- * - USB Core Protocol Definitions
- */
-#ifndef _USB_PROTO_H_
-#define _USB_PROTO_H_
-
-struct sDeviceRequest
-{
- Uint8 ReqType;
- Uint8 Request;
- Uint16 Value;
- Uint16 Index;
- Uint16 Length;
-};
-
-/*
- */
-struct sDescriptor_Device
-{
- Uint8 Length;
- Uint8 Type; // = 1
- Uint16 USBVersion; // BCD, 0x210 = 2.10
- Uint8 DeviceClass;
- Uint8 DeviceSubClass;
- Uint8 DeviceProtocol;
- Uint8 MaxPacketSize;
-
- Uint16 VendorID;
- Uint16 ProductID;
- Uint16 DeviceID; // BCD
-
- Uint8 ManufacturerStr;
- Uint8 ProductStr;
- Uint8 SerialNumberStr;
-
- Uint8 NumConfigurations;
-} PACKED;
-
-struct sDescriptor_Configuration
-{
- Uint8 Length;
- Uint8 Type; // = 2
-
- Uint16 TotalLength;
- Uint8 NumInterfaces;
- Uint8 ConfigurationValue;
- Uint8 ConfigurationStr;
- Uint8 AttributesBmp;
- Uint8 MaxPower; // in units of 2 mA
-} PACKED;
-
-struct sDescriptor_String
-{
- Uint8 Length;
- Uint8 Type; // = 3
-
- Uint16 Data[128-1]; // (256 bytes - 2 bytes) / Uint16
-} PACKED;
-
-struct sDescriptor_Interface
-{
- Uint8 Length;
- Uint8 Type; // = 4
-
- Uint8 InterfaceNum;
- Uint8 AlternateSetting;
- Uint8 NumEndpoints; // Excludes endpoint 0
-
- Uint8 InterfaceClass; //
- Uint8 InterfaceSubClass;
- Uint8 InterfaceProtocol;
-
- Uint8 InterfaceStr;
-} PACKED;
-
-struct sDescriptor_Endpoint
-{
- Uint8 Length;
- Uint8 Type; // = 5
- Uint8 Address; // 3:0 Endpoint Num, 7: Direction (1=IN)
- /**
- * 1:0 - Transfer Type
- * - 00 = Control
- * - 01 = Isochronous
- * - 10 = Bulk
- * - 11 = Interrupt
- * 3:2 - Synchronisation type (Isonchronous only)
- * - 00 = No Synchronisation
- * - 01 = Asynchronous
- * - 10 = Adaptive
- * - 11 = Synchronous
- * 5:4 - Usage type (Isonchronous only)
- * - 00 = Data endpoint
- * - 01 = Feedback endpoint
- */
- Uint8 Attributes;
-
- Uint16 MaxPacketSize;
-
- /**
- *
- */
- Uint8 PollingInterval;
-} PACKED;
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 USB Stack HID Driver
- * - By John Hodge (thePowersGang)
- *
- * hid.h
- * - Core header
- */
-#ifndef _HID_H_
-#define _HID_H_
-
-// Report Descriptor Types
-// - 0: Main
-// > 8: Input - Axis/Button etc
-// > 9: Output - LED/Position
-// > B: Feature -
-// > A: Collection - Group of Input/Output/Feature
-// > C: End collection
-// - 1: Global (Not restored on main)
-// - 2: Local
-// >
-// - 3: Reserved/Unused
-
-// I/O/F Data Values
-#define HID_IOF_CONSTANT 0x001 //!< Host Read-only
-#define HID_IOF_VARIABLE 0x002 //!<
-
-struct sLongItem
-{
- Uint8 Header; // 0xFE (Tag 15, Type 3, Size 2)
- Uint8 DataSize;
- Uint8 Tag;
-};
-
-struct sDescriptor_HID
-{
- Uint8 Length;
- Uint8 Type; //
- Uint16 Version; // 0x0111 = 1.11
- Uint8 CountryCode;
- Uint8 NumDescriptors; // >= 1
- struct {
- Uint8 DescType;
- Uint16 DescLen;
- } Descriptors[];
-}
-
-#endif
+++ /dev/null
-/*
- * Acess2 USB Stack HID Driver
- * - By John Hodge (thePowersGang)
- *
- * usb_keysyms.h
- * - USB HID Keyboard Symbols
- */
-#ifndef _USB_KEYSYMS_H_
-#define _USB_KEYSYMS_H_
-
-enum eUSB_Keysyms
-{
- KEYSYM_NONE,
- KEYSYM_ERRORROLLOVER,
- KEYSYM_POSTFAIL,
- KEYSYM_ERRORUNDEFINED,
- // 0x04 / 4
- KEYSYM_a, KEYSYM_b, KEYSYM_c,
- KEYSYM_d, KEYSYM_e, KEYSYM_f,
- KEYSYM_g, KEYSYM_h, KEYSYM_i,
- KEYSYM_j, KEYSYM_k, KEYSYM_l,
- KEYSYM_m, KEYSYM_n, KEYSYM_o,
- KEYSYM_p, KEYSYM_q, KEYSYM_r,
- KEYSYM_s, KEYSYM_t, KEYSYM_u,
- KEYSYM_v, KEYSYM_w, KEYSYM_x,
- KEYSYM_y, KEYSYM_z,
-
- // 0x1E / 30
- KEYSYM_1, KEYSYM_2,
- KEYSYM_3, KEYSYM_4,
- KEYSYM_5, KEYSYM_6,
- KEYSYM_7, KEYSYM_8,
- KEYSYM_9, KEYSYM_0,
-
- KEYSYM_RETURN, // Enter
- KEYSYM_ESC, // Esc.
- KEYSYM_BACKSP, // Backspace
- KEYSYM_TAB, // Tab
- KEYSYM_SPACE, // Spacebar
- KEYSYM_MINUS, // - _
- KEYSYM_EQUALS, // = +
- KEYSYM_SQUARE_OPEN, // [ {
- KEYSYM_SQUARE_CLOSE, // ] }
- KEYSYM_BACKSLASH, // \ |
- KEYSYM_HASH_TILDE, // # ~ (Non-US)
- KEYSYM_SEMICOLON, // ; :
- KEYSYM_QUOTE, // ' "
- KEYSYM_GRAVE_TILDE, // Grave Accent, Tilde
- KEYSYM_COMMA, // , <
- KEYSYM_PERIOD, // . >
- KEYSYM_SLASH, // / ?
- KEYSYM_CAPS, // Caps Lock
- KEYSYM_F1, KEYSYM_F2,
- KEYSYM_F3, KEYSYM_F4,
- KEYSYM_F5, KEYSYM_F6,
- KEYSYM_F7, KEYSYM_F8,
- KEYSYM_F9, KEYSYM_F10,
- KEYSYM_F11, KEYSYM_F12,
- KEYSYM_PRINTSCREEN,
- KEYSYM_SCROLLLOCK,
- KEYSYM_PAUSE,
- KEYSYM_INSERT,
- KEYSYM_HOME,
- KEYSYM_PGUP,
- KEYSYM_DELETE,
- KEYSYM_END,
- KEYSYM_PGDN,
- KEYSYM_RIGHTARROW,
- KEYSYM_LEFTARROW,
- KEYSYM_DOWNARROW,
- KEYSYM_UPARROW,
-
- KEYSYM_NUMLOCK,
- KEYSYM_KPSLASH,
- KEYSYM_KPSTAR,
- KEYSYM_KPMINUS,
- KEYSYM_KPPLUS,
- KEYSYM_KPENTER,
- KEYSYM_KP1,
- KEYSYM_KP2,
- KEYSYM_KP3,
- KEYSYM_KP4,
- KEYSYM_KP5,
- KEYSYM_KP7,
- KEYSYM_KP8,
- KEYSYM_KP9
- KEYSYM_KP0,
- KEYSYM_KPPERIOD,
-
- KEYSYM_NONUS_BACKSLASH,
- KEYSYM_APPLICATION, // Windows Key
- KEYSYM_POWER,
- KEYSYM_KPEQUALS,
-
- KEYSYM_F13, KEYSYM_F14,
- KEYSYM_F15, KEYSYM_F16,
- KEYSYM_F17, KEYSYM_F18,
- KEYSYM_F19, KEYSYM_F20,
- KEYSYM_F21, KEYSYM_F22,
- KEYSYM_F23, KEYSYM_F24,
- KEYSYM_EXECUTE,
- KEYSYM_HELP,
- KEYSYM_MENU,
- KEYSYM_SELECT,
- KEYSYM_STOP,
- KEYSYM_AGAIN,
- KEYSYM_UNDO,
- KEYSYM_CUT,
- KEYSYM_COPY,
- KEYSYM_PASTE,
- KEYSYM_FIND,
- KEYSYM_MUTE,
- KEYSYM_VOLUP,
- KEYSYM_VOLDN,
- KEYSYM_LOCKING_CAPS, // Physically toggles
- KEYSYM_LOGKING_NUM,
- KEYSYM_LOGKING_SCROLL,
- KEYSYM_KPCOMMA
-
- // TODO: Define the rest
-};
-
-#endif
-
+++ /dev/null
-/*
- * Acess2 USB Stack HID Driver
- * - By John Hodge (thePowersGang)
- *
- * main.c
- * - Driver Core
- */
-#define DEBUG 0
-#define VERSION VER2(0,1)
-#include <acess.h>
-#include <usb_core.h>
-
-// === PROTOTYPES ===
- int HID_Initialise(const char **Arguments);
-void HID_DeviceConnected(tUSBInterface *Dev);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL);
-tUSBDriver gHID_Driver = {
- .Name = "HID",
- .Match = {.Class = {0x030000, 0xFF0000}},
- .Connected = HID_DeviceConnected,
-};
-
-// === CODE ===
-int HID_Initialise(const char **Arguments)
-{
- USB_RegisterDriver( &gHID_Driver );
- return 0;
-}
-
-void HID_DeviceConnected(tUSBInterface *Dev)
-{
-
-}
-
+++ /dev/null
-CATEGORY = USB
-
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-OBJ = uhci.o
-CPPFLAGS = -I../Core/include
-NAME = UHCI
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess 2 USB Stack
- * - By John Hodge (thePowersGang)
- *
- * Universal Host Controller Interface
- */
-#define DEBUG 0
-#define VERSION VER2(0,5)
-#include <acess.h>
-#include <vfs.h>
-#include <drv_pci.h>
-#include <modules.h>
-#include <usb_host.h>
-#include "uhci.h"
-
-// === CONSTANTS ===
-#define MAX_CONTROLLERS 4
-#define NUM_TDs 1024
-
-// === PROTOTYPES ===
- int UHCI_Initialise(char **Arguments);
-void UHCI_Cleanup();
-tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
-void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD);
-void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
- int UHCI_IsTransferComplete(void *Ptr, void *Handle);
- int UHCI_Int_InitHost(tUHCI_Controller *Host);
-void UHCI_CheckPortUpdate(void *Ptr);
-void UHCI_InterruptHandler(int IRQ, void *Ptr);
-//
-static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value);
-static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value);
-static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value);
-static Uint16 _InWord(tUHCI_Controller *Host, int Reg);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
-tUHCI_TD gaUHCI_TDPool[NUM_TDs];
-tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS];
-tUSBHostDef gUHCI_HostDef = {
- .SendIN = UHCI_DataIN,
- .SendOUT = UHCI_DataOUT,
- .SendSETUP = UHCI_SendSetup,
- .CheckPorts = UHCI_CheckPortUpdate,
- .IsOpComplete = UHCI_IsTransferComplete
- };
-
-// === CODE ===
-/**
- * \fn int UHCI_Initialise()
- * \brief Called to initialise the UHCI Driver
- */
-int UHCI_Initialise(char **Arguments)
-{
- int i=0, id=-1;
- int ret;
-
- ENTER("");
-
- // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
- while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
- {
- tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
- Uint32 base_addr;
- // NOTE: Check "protocol" from PCI?
-
- cinfo->PciId = id;
- base_addr = PCI_GetBAR(id, 4);
-
- if( base_addr & 1 )
- {
- cinfo->IOBase = base_addr & ~1;
- cinfo->MemIOMap = NULL;
- }
- else
- {
- cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
- }
- cinfo->IRQNum = PCI_GetIRQ(id);
-
- Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
- id, base_addr, cinfo->IRQNum);
-
- IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
-
- // Initialise Host
- ret = UHCI_Int_InitHost(&gUHCI_Controllers[i]);
- // Detect an error
- if(ret != 0) {
- LEAVE('i', ret);
- return ret;
- }
-
- cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
- LOG("cinfo->RootHub = %p", cinfo->RootHub);
-
- i ++;
- }
-
- if(i == 0) {
- LEAVE('i', MODULE_ERR_NOTNEEDED);
- return MODULE_ERR_NOTNEEDED;
- }
-
- if(i == MAX_CONTROLLERS) {
- Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
- }
- LEAVE('i', MODULE_ERR_OK);
- return MODULE_ERR_OK;
-}
-
-/**
- * \fn void UHCI_Cleanup()
- * \brief Called just before module is unloaded
- */
-void UHCI_Cleanup()
-{
-}
-
-tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
-{
- int i;
- for(i = 0; i < NUM_TDs; i ++)
- {
- if(gaUHCI_TDPool[i].Link == 0) {
- gaUHCI_TDPool[i].Link = 1;
- gaUHCI_TDPool[i].Control = 1 << 23;
- return &gaUHCI_TDPool[i];
- }
- // Still in use? Skip
- if( gaUHCI_TDPool[i].Control & (1 << 23) )
- continue ;
- // Is there a callback on it? Skip
- if( gaUHCI_TDPool[i]._info.Callback )
- continue ;
- // TODO: Garbage collect, but that means removing from the list too
- #if 0
- // Ok, this is actually unused
- gaUHCI_TDPool[i].Link = 1;
- gaUHCI_TDPool[i].Control = 1 << 23;
- return &gaUHCI_TDPool[i];
- #endif
- }
- return NULL;
-}
-
-tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr)
-{
- // TODO: Fix this to work with a non-contiguous pool
- static tPAddr td_pool_base;
- const int pool_size = NUM_TDs;
- int offset;
- if(!td_pool_base) td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
- offset = (PAddr - td_pool_base) / sizeof(gaUHCI_TDPool[0]);
- if( offset < 0 || offset >= pool_size )
- {
- Log_Error("UHCI", "TD PAddr %P not from pool", PAddr);
- return NULL;
- }
- return gaUHCI_TDPool + offset;
-}
-
-void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD)
-{
- int next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1);
- tUHCI_TD *prev_td;
- Uint32 link;
-
- // TODO: How to handle FRNUM incrementing while we are in this function?
-
- // Empty list
- if( Cont->FrameList[next_frame] & 1 )
- {
- // TODO: Ensure 32-bit paddr
- Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD );
- TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame
- LOG("next_frame = %i", next_frame);
- return;
- }
-
- // Find the end of the list
- link = Cont->FrameList[next_frame];
- do {
- prev_td = UHCI_int_GetTDFromPhys(link);
- link = prev_td->Link;
- } while( !(link & 1) );
-
- // Append
- prev_td->Link = MM_GetPhysAddr( (tVAddr)TD );
-
- LOG("next_frame = %i, prev_td = %p", next_frame, prev_td);
-}
-
-/**
- * \brief Send a transaction to the USB bus
- * \param Cont Controller pointer
- * \param Addr Function Address * 16 + Endpoint
- * \param bTgl Data toggle value
- */
-void *UHCI_int_SendTransaction(
- tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
- tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
-{
- tUHCI_TD *td;
-
- if( Length > 0x400 ) return NULL; // Controller allows up to 0x500, but USB doesn't
-
- td = UHCI_int_AllocateTD(Cont);
-
- if( !td ) {
- // TODO: Wait for one to free?
- Log_Error("UHCI", "No avaliable TDs, transaction dropped");
- return NULL;
- }
-
- td->Link = 1;
- td->Control = (Length - 1) & 0x7FF;
- td->Control |= (1 << 23);
- td->Token = ((Length - 1) & 0x7FF) << 21;
- td->Token |= (bTgl & 1) << 19;
- td->Token |= (Addr & 0xF) << 15;
- td->Token |= ((Addr/16) & 0xFF) << 8;
- td->Token |= Type;
-
- // TODO: Ensure 32-bit paddr
- if( ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE ) {
- Log_Warning("UHCI", "TODO: Support non single page transfers (%x + %x > %x)",
- (tVAddr)Data & (PAGE_SIZE-1), Length, PAGE_SIZE
- );
- // TODO: Need to enable IOC to copy the data back
-// td->BufferPointer =
- td->_info.bCopyData = 1;
- return NULL;
- }
- else {
- td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
- td->_info.bCopyData = 0;
- }
-
- // Interrupt on completion
- if( Cb ) {
- td->Control |= (1 << 24);
- LOG("IOC Cb=%p CbData=%p", Cb, CbData);
- td->_info.Callback = Cb; // NOTE: if ERRPTR then the TD is kept allocated until checked
- td->_info.CallbackPtr = CbData;
- }
-
- td->_info.DataPtr = Data;
-
- UHCI_int_AppendTD(Cont, td);
-
- return td;
-}
-
-void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
-{
- return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x69, DataTgl, Cb, CbData, Buf, Length);
-}
-
-void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
-{
- return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0xE1, DataTgl, Cb, CbData, Buf, Length);
-}
-
-void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
-{
- return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x2D, DataTgl, Cb, CbData, Buf, Length);
-}
-
-int UHCI_IsTransferComplete(void *Ptr, void *Handle)
-{
- tUHCI_TD *td = Handle;
- int ret;
- ret = !(td->Control & (1 << 23));
- if(ret) {
- td->_info.Callback = NULL;
- td->Link = 0;
- }
- return ret;
-}
-
-// === INTERNAL FUNCTIONS ===
-/**
- * \fn int UHCI_Int_InitHost(tUCHI_Controller *Host)
- * \brief Initialises a UHCI host controller
- * \param Host Pointer - Host to initialise
- */
-int UHCI_Int_InitHost(tUHCI_Controller *Host)
-{
- ENTER("pHost", Host);
-
- _OutWord( Host, USBCMD, 4 ); // GRESET
- // TODO: Wait for at least 10ms
- _OutWord( Host, USBCMD, 0 ); // GRESET
-
- // Allocate Frame List
- // - 1 Page, 32-bit address
- // - 1 page = 1024 4 byte entries
- Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
- if( !Host->FrameList ) {
- Log_Warning("UHCI", "Unable to allocate frame list, aborting");
- LEAVE('i', -1);
- return -1;
- }
- LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList);
- memsetd( Host->FrameList, 1, 1024 ); // Clear List (Disabling all entries)
-
- //! \todo Properly fill frame list
-
- // Set frame length to 1 ms
- _OutByte( Host, SOFMOD, 64 );
-
- // Set Frame List
- _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
- _OutWord( Host, FRNUM, 0 );
-
- // Enable Interrupts
- _OutWord( Host, USBINTR, 0x000F );
- PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
-
- // Enable processing
- _OutWord( Host, USBCMD, 0x0001 );
-
- LEAVE('i', 0);
- return 0;
-}
-
-void UHCI_CheckPortUpdate(void *Ptr)
-{
- tUHCI_Controller *Host = Ptr;
- // Enable ports
- for( int i = 0; i < 2; i ++ )
- {
- int port = PORTSC1 + i*2;
- Uint16 status;
-
- status = _InWord(Host, port);
- // Check for port change
- if( !(status & 0x0002) ) continue;
- _OutWord(Host, port, 0x0002);
-
- // Check if the port is connected
- if( !(status & 1) )
- {
- // Tell the USB code it's gone.
- USB_DeviceDisconnected(Host->RootHub, i);
- continue;
- }
- else
- {
- LOG("Port %i has something", i);
- // Reset port (set bit 9)
- LOG("Reset");
- _OutWord(Host, port, 0x0200);
- Time_Delay(50); // 50ms delay
- _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
- // Enable port
- LOG("Enable");
- Time_Delay(50); // 50ms delay
- _OutWord(Host, port, _InWord(Host, port) | 0x0004);
- // Tell USB there's a new device
- USB_DeviceConnected(Host->RootHub, i);
- }
- }
-}
-
-void UHCI_InterruptHandler(int IRQ, void *Ptr)
-{
- tUHCI_Controller *Host = Ptr;
- int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
- Uint16 status = _InWord(Host, USBSTS);
-// Log_Debug("UHCI", "UHIC Interrupt, status = 0x%x, frame = %i", status, frame);
-
- // Interrupt-on-completion
- if( status & 1 )
- {
- tPAddr link;
-
- for( int i = 0; i < 10; i ++ )
- {
- link = Host->FrameList[frame];
- Host->FrameList[frame] = 1;
- while( link && !(link & 1) )
- {
- tUHCI_TD *td = UHCI_int_GetTDFromPhys(link);
- int byte_count = (td->Control&0x7FF)+1;
- LOG("link = 0x%x, td = %p, byte_count = %i", link, td, byte_count);
- // Handle non-page aligned destination
- // TODO: This will break if the destination is not in global memory
- if(td->_info.bCopyData)
- {
- void *ptr = (void*)MM_MapTemp(td->BufferPointer);
- Log_Debug("UHCI", "td->_info.DataPtr = %p", td->_info.DataPtr);
- memcpy(td->_info.DataPtr, ptr, byte_count);
- MM_FreeTemp((tVAddr)ptr);
- }
- // Callback
- if(td->_info.Callback && td->_info.Callback != INVLPTR)
- {
- LOG("Calling cb %p", td->_info.Callback);
- td->_info.Callback(td->_info.CallbackPtr, td->_info.DataPtr, byte_count);
- td->_info.Callback = NULL;
- }
- link = td->Link;
- if( td->_info.Callback != INVLPTR )
- td->Link = 0;
- }
-
- if(frame == 0)
- frame = 0x3ff;
- else
- frame --;
- }
-
-// Host->LastCleanedFrame = frame;
- }
-
- LOG("status = 0x%02x", status);
- _OutWord(Host, USBSTS, status);
-}
-
-void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
-{
- if( Host->MemIOMap )
- ((Uint8*)Host->MemIOMap)[Reg] = Value;
- else
- outb(Host->IOBase + Reg, Value);
-}
-
-void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
-{
- if( Host->MemIOMap )
- Host->MemIOMap[Reg/2] = Value;
- else
- outw(Host->IOBase + Reg, Value);
-}
-
-void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
-{
- if( Host->MemIOMap )
- ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
- else
- outd(Host->IOBase + Reg, Value);
-}
-
-Uint16 _InWord(tUHCI_Controller *Host, int Reg)
-{
- if( Host->MemIOMap )
- return Host->MemIOMap[Reg/2];
- else
- return inw(Host->IOBase + Reg);
-}
-
+++ /dev/null
-/*
- * AcessOS Version 1
- * USB Stack
- * - Universal Host Controller Interface
- */
-#ifndef _UHCI_H_
-#define _UHCI_H_
-
-// === TYPES ===
-typedef struct sUHCI_Controller tUHCI_Controller;
-typedef struct sUHCI_TD tUHCI_TD;
-typedef struct sUHCI_QH tUHCI_QH;
-
-// === STRUCTURES ===
-struct sUHCI_Controller
-{
- /**
- * \brief PCI Device ID
- */
- Uint16 PciId;
-
- /**
- * \brief IO Base Address
- */
- Uint16 IOBase;
-
- /**
- * \brief Memory Mapped-IO base address
- */
- Uint16 *MemIOMap;
-
- /**
- * \brief IRQ Number assigned to the device
- */
- int IRQNum;
-
- /**
- * \brief Number of the last frame to be cleaned
- */
- int LastCleanedFrame;
-
- /**
- * \brief Frame list
- *
- * 31:4 - Frame Pointer
- * 3:2 - Reserved
- * 1 - QH/TD Selector
- * 0 - Terminate (Empty Pointer)
- */
- Uint32 *FrameList;
-
- /**
- * \brief Physical Address of the Frame List
- */
- tPAddr PhysFrameList;
-
- tUSBHub *RootHub;
-};
-
-struct sUHCI_TD
-{
- /**
- * \brief Next Entry in list
- *
- * 31:4 - Address
- * 3 - Reserved
- * 2 - Depth/Breadth Select
- * 1 - QH/TD Select
- * 0 - Terminate (Last in List)
- */
- Uint32 Link;
-
- /**
- * \brief Control and Status Field
- *
- * 31:30 - Reserved
- * 29 - Short Packet Detect (Input Only)
- * 28:27 - Number of Errors Allowed
- * 26 - Low Speed Device (Communicating with a low speed device)
- * 25 - Isynchonious Select
- * 24 - Interrupt on Completion (IOC)
- * 23:16 - Status
- * 23 - Active
- * 22 - Stalled
- * 21 - Data Buffer Error
- * 20 - Babble Detected
- * 19 - NAK Detected
- * 18 - CRC/Timout Error
- * 17 - Bitstuff Error
- * 16 - Reserved
- * 15:11 - Reserved
- * 10:0 - Actual Length (Number of bytes transfered)
- */
- Uint32 Control;
-
- /**
- * \brief Packet Header
- *
- * 31:21 - Maximum Length (0=1, Max 0x4FF, 0x7FF=0)
- * 20 - Reserved
- * 19 - Data Toggle
- * 18:15 - Endpoint
- * 14:8 - Device Address
- * 7:0 - PID (Packet Identifcation) - Only 96, E1, 2D allowed
- *
- * 0x96 = Data IN
- * 0xE1 = Data Out
- * 0x2D = Setup
- */
- Uint32 Token;
-
- /**
- * \brief Pointer to the data to send
- */
- Uint32 BufferPointer;
-
- struct
- {
- tUSBHostCb Callback;
- void *CallbackPtr;
- void *DataPtr;
- int bCopyData;
- } _info;
-} __attribute__((aligned(16)));
-
-struct sUHCI_QH
-{
- /**
- * \brief Next Entry in list
- *
- * 31:4 - Address
- * 3:2 - Reserved
- * 1 - QH/TD Select
- * 0 - Terminate (Last in List)
- */
- Uint32 Next;
-
-
- /**
- * \brief Next Entry in list
- *
- * 31:4 - Address
- * 3:2 - Reserved
- * 1 - QH/TD Select
- * 0 - Terminate (Last in List)
- */
- Uint32 Child;
-};
-
-// === ENUMERATIONS ===
-enum eUHCI_IOPorts {
- /**
- * \brief USB Command Register
- *
- * 15:8 - Reserved
- * 7 - Maximum Packet Size selector (1: 64 bytes, 0: 32 bytes)
- * 6 - Configure Flag (No Hardware Effect)
- * 5 - Software Debug (Don't think it will be needed)
- * 4 - Force Global Resume
- * 3 - Enter Global Suspend Mode
- * 2 - Global Reset (Resets all devices on the bus)
- * 1 - Host Controller Reset (Reset just the controller)
- * 0 - Run/Stop
- */
- USBCMD = 0x00,
- /**
- * \brief USB Status Register
- *
- * 15:6 - Reserved
- * 5 - HC Halted, set to 1 when USBCMD:RS is set to 0
- * 4 - Host Controller Process Error (Errors related to the bus)
- * 3 - Host System Error (Errors related to the OS/PCI Bus)
- * 2 - Resume Detect (Set if a RESUME command is sent to the Controller)
- * 1 - USB Error Interrupt
- * 0 - USB Interrupts (Set if a transaction with the IOC bit set is completed)
- */
- USBSTS = 0x02,
- /**
- * \brief USB Interrupt Enable Register
- *
- * 15:4 - Reserved
- * 3 - Short Packet Interrupt Enable
- * 2 - Interrupt on Complete (IOC) Enable
- * 1 - Resume Interrupt Enable
- * 0 - Timout / CRC Error Interrupt Enable
- */
- USBINTR = 0x04,
- /**
- * \brief Frame Number (Index into the Frame List)
- *
- * 15:11 - Reserved
- * 10:0 - Index (Incremented each approx 1ms)
- */
- FRNUM = 0x06,
- /**
- * \brief Frame List Base Address
- *
- * 31:12 - Pysical Address >> 12
- * 11:0 - Reserved (Set to Zero)
- */
- FLBASEADD = 0x08, // 32-bit
- /**
- * \brief Start-of-frame Modify Register
- * \note 8-bits only
- *
- * Sets the size of a frame
- * Frequency = (11936+n)/12000 kHz
- *
- * 7 - Reserved
- * 6:0 -
- */
- SOFMOD = 0x0C, // 8bit
- /**
- * \brief Port Status and Controll Register (Port 1)
- *
- * 15:13 - Reserved
- * 12 - Suspend
- * 11:10 - Reserved
- * 9 - Port Reset
- * 8 - Low Speed Device Attached
- * 5:4 - Line Status
- * 3 - Port Enable/Disable Change - Used for detecting device removal
- * 2 - Port Enable/Disable
- * 1 - Connect Status Change
- * 0 - Current Connect Status
- */
- PORTSC1 = 0x10,
- /**
- * \brief Port Status and Controll Register (Port 2)
- *
- * See ::PORTSC1
- */
- PORTSC2 = 0x12
-};
-
-#endif
+++ /dev/null
-#
-#
-
-OBJ := gic.o
-NAME := GIC
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * ARMv7 GIC Support
- * - By John Hodge (thePowersGang)
- *
- * gic.c
- * - GIC Core
- */
-#define DEBUG 1
-
-#include <acess.h>
-#include <modules.h>
-#include "gic.h"
-#include <options.h>
-
-#define N_IRQS 1024
-
-// === IMPORTS ===
-extern void *gpIRQHandler;
-
-// === TYPES ===
-typedef void (*tIRQ_Handler)(int, void*);
-
-// === PROTOTYPES ===
- int GIC_Install(char **Arguments);
-void GIC_IRQHandler(void);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x100, armv7_GIC, GIC_Install, NULL, NULL);
-Uint32 *gpGIC_DistributorBase;
-Uint32 *gpGIC_InterfaceBase;
-tPAddr gGIC_DistributorAddr;
-tPAddr gGIC_InterfaceAddr;
-tIRQ_Handler gaIRQ_Handlers[N_IRQS];
-void *gaIRQ_HandlerData[N_IRQS];
-
-// === CODE ===
-int GIC_Install(char **Arguments)
-{
- // Realview PB
- gGIC_InterfaceAddr = 0x1e000000;
- gGIC_DistributorAddr = 0x1e001000;
-
- // Initialise
- gpGIC_InterfaceBase = (void*)MM_MapHWPages(gGIC_InterfaceAddr, 1);
- LOG("gpGIC_InterfaceBase = %p", gpGIC_InterfaceBase);
- gpGIC_DistributorBase = (void*)MM_MapHWPages(gGIC_DistributorAddr, 1);
- LOG("gpGIC_DistributorBase = %p", gpGIC_DistributorBase);
-
- gpGIC_InterfaceBase[GICC_PMR] = 0xFF;
- gpGIC_InterfaceBase[GICC_CTLR] = 1; // Enable CPU
- gpGIC_DistributorBase[GICD_CTLR] = 1; // Enable Distributor
-
- gpIRQHandler = GIC_IRQHandler;
-
- __asm__ __volatile__ ("cpsie if"); // Enable IRQs and FIQs
-
- return MODULE_ERR_OK;
-}
-
-void GIC_IRQHandler(void)
-{
- Uint32 num = gpGIC_InterfaceBase[GICC_IAR];
-// Log_Debug("GIC", "IRQ 0x%x", num);
- gaIRQ_Handlers[num]( num, gaIRQ_HandlerData[num] );
- gpGIC_InterfaceBase[GICC_EOIR] = num;
-}
-
-int IRQ_AddHandler(int IRQ, tIRQ_Handler Handler, void *Ptr)
-{
- if( IRQ < 0 || IRQ >= N_IRQS-32 ) {
- return 1;
- }
-
- LOG("IRQ = %i", IRQ);
- IRQ += 32; // 32 internal IRQs
- LOG("IRQ = %i (after adjust)", IRQ);
- LOG("mask = 0x%x", 1 << (IRQ & (31-1)));
- gpGIC_DistributorBase[GICD_ISENABLER0+IRQ/32] = 1 << (IRQ & (32-1));
- ((Uint8*)&gpGIC_DistributorBase[GICD_ITARGETSR0])[IRQ] = 1;
-
-// Log_Warning("GIC", "TODO: Implement IRQ_AddHandler");
-
- if( gaIRQ_Handlers[IRQ] )
- return 2;
-
- gaIRQ_Handlers[IRQ] = Handler;
- gaIRQ_HandlerData[IRQ] = Ptr;
-
- return 0;
-}
-
+++ /dev/null
-/*
- * ARMv7 GIC Support
- * - By John Hodge (thePowersGang)
- *
- * gic.h
- * - GIC Core Definitions
- */
-#ifndef _ARM7_GIC_H_
-#define _ARM7_GIC_H_
-
-enum eGICD_Registers
-{
- GICD_CTLR = 0x000/4, // Distributor Control Register
- GICD_TYPER = 0x004/4, // Interrupt Controller Type
- GICD_IIDR = 0x008/4, // Distributor Implementer Identifcation
-
- GICD_IGROUPR0 = 0x080/4, // Interrupt Group Register (#0)
- GICD_ISENABLER0 = 0x100/4, // Interrupt Set-Enable Register #0 (128*8=1024)
- GICD_ICENABLER0 = 0x180/4, // Interrupt Clear-Enable Register #0
- GICD_ISPENDR0 = 0x200/4, // Interrupt Set-Pending Register #0
- GICD_ICPENDR0 = 0x280/4, // Interrupt Clear-Pending Register #0
- GICD_ISACTIVER0 = 0x300/4, // Interrupt Set-Active Register (GICv2)
- GICD_ICACTIVER0 = 0x380/4, // Interrupt Clear-Active Register (GICv2)
-
- GICD_IPRIORITYR0 = 0x400/4, // Interrupt priority registers (254*4 = )
-
- GICD_ITARGETSR0 = 0x800/4, // Interrupt Processor Targets Register (8*4)
-
- GICD_ICFGR0 = 0xC00/4, // Interrupt Configuration Register (64*4)
- GICD_NSACR0 = 0xE00/4, // Non-secure Access Control Register (64*4)
- GICD_SIGR = 0xF00/4, // Software Generated Interrupt Register (Write Only)
- GICD_CPENDSGIR0 = 0xF10/4, // SGI Clear-Pending Registers (4*4)
- GICD_SPENDSGIR0 = 0xF20/4, // SGI Set-Pending Registers (4*4)
-};
-
-enum eGICC_Registers
-{
- GICC_CTLR = 0x000/4, // CPU Interface Control Register
- GICC_PMR = 0x004/4, // Interrupt Priority Mask Register
- GICC_BPR = 0x008/4, // Binary Point Register
- GICC_IAR = 0x00C/4, // Interrupt Acknowledge Register
- GICC_EOIR = 0x010/4, // End of Interrupt Register
- GICC_RPR = 0x014/4, // Running Priority Register
- GICC_HPPIR = 0x018/4, // Highest Priority Pending Interrupt Register
- GICC_ABPR = 0x01C/4, // Aliased Binary Point Register
- GICC_AIAR = 0x020/4, // Aliased Interrupt Acknowledge Register,
- GICC_AEOIR = 0x024/4, // Aliased End of Interrupt Register
- GICC_AHPPIR = 0x028/4, // Aliased Highest Priority Pending Interrupt Register
-
- GICC_APR0 = 0x0D0/4, // Active Priorities Registers (4*4)
- GICC_NSAPR0 = 0x0E0/4, // Non-secure Active Priorities Registers (4*4)
-
- GICC_IIDR = 0x0FC/4, // CPU Interface Identifcation Register
- GICC_DIR = 0x0FC/4, // Deactivate Interrupt Register (Write Only)
-};
-
-#endif
+++ /dev/null
-CATEGORY = armv7
-
--include ../../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 Kernel Modules
- * Linker Script
- */
-
-ENTRY(ModuleEntry)
-OUTPUT_FORMAT(elf32-i386)
-
-SECTIONS
-{
- . = 0 + SIZEOF_HEADERS;
-
- .text : AT(ADDR(.text)) {
- textzero = .;
- *(.text)
- }
-
- .rodata ALIGN(0x1000): AT(ADDR(.rodata)) {
- *(.rodata)
- *(.rdata)
- DriverInfo = .;
- *(KMODULES)
- }
-
- .data ALIGN (0x1000) : AT(ADDR(.data)) {
- *(.data)
- }
-
- .bss : AT(ADDR(.bss)) {
- _sbss = .;
- *(COMMON)
- *(.bss)
- _ebss = .;
- }
-}
+++ /dev/null
-#
-#
-
-OBJ := dma.o
-NAME := ISADMA
-
--include ../Makefile.tpl
+++ /dev/null
-/*\r
- * AcessOS 1.0\r
- * DMA Driver\r
- */\r
-#include <acess.h>\r
-#include <modules.h>\r
-\r
-#define DMA_SIZE (0x2400)\r
-#define DMA_ADDRESS(c) ((c)*DMA_SIZE+0x500) //Save Space for IDT and BDA\r
-\r
-#define LOWB(x) ((x)&0xFF)\r
-#define HIB(x) (((x)>>8)&0xFF)\r
-#define HIW(x) (((x)>>16)&0xFFFF)\r
-\r
-// === TYPES ===\r
-typedef struct\r
-{\r
- int mode;\r
- char *address;\r
-} t_dmaChannel;\r
-\r
-// === PROTOTYPES ===\r
- int DMA_Install(char **Arguments);\r
-void DMA_SetChannel(int Channel, int length, int read);\r
- int DMA_ReadData(int channel, int count, void *buffer);\r
- int DMA_WriteData(int channel, int count, const void *buffer);\r
-\r
-// === CONSTANTS ===\r
-const Uint8 cMASKPORT [8] = { 0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4 };\r
-const Uint8 cMODEPORT [8] = { 0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6 };\r
-const Uint8 cCLEARPORT[8] = { 0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8 };\r
-const Uint8 cPAGEPORT [8] = { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A };\r
-const Uint8 cADDRPORT [8] = { 0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC };\r
-const Uint8 cCOUNTPORT[8] = { 0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE };\r
-\r
-// === GLOBALS ===\r
-MODULE_DEFINE(0, 0x0100, x86_ISADMA, DMA_Install, NULL, NULL);\r
-char *dma_addresses[8];\r
-t_dmaChannel dma_channels[8];\r
-\r
-// === CODE ===\r
-/**\r
- * \brief Initialise DMA channels\r
- * \param Arguments Arguments passed at boot time\r
- */\r
-int DMA_Install(char **Arguments)\r
-{\r
- Uint i;\r
- for(i=8;i--;)\r
- {\r
- outb( cMASKPORT[i], 0x04 | (i & 0x3) ); // mask channel\r
- outb( cCLEARPORT[i], 0x00 );\r
- outb( cMODEPORT[i], 0x48 | (i & 0x3) ); //Read Flag\r
- outb( 0xd8, 0xff); //Reset Flip-Flop\r
- outb( cADDRPORT[i], LOWB(DMA_ADDRESS(i)) ); // send address\r
- outb( cADDRPORT[i], HIB(DMA_ADDRESS(i)) ); // send address\r
- outb( 0xd8, 0xff); //Reset Flip-Flop\r
- outb( cCOUNTPORT[i], LOWB(DMA_SIZE) ); // send size\r
- outb( cCOUNTPORT[i], HIB(DMA_SIZE) ); // send size\r
- outb( cPAGEPORT[i], LOWB(HIW(DMA_ADDRESS(i))) ); // send page\r
- outb( cMASKPORT[i], i & 0x3 ); // unmask channel\r
- \r
- dma_channels[i].mode = 0;\r
- dma_addresses[i] = (char*)DMA_ADDRESS(i);\r
- dma_addresses[i] += KERNEL_BASE;\r
- }\r
- return MODULE_ERR_OK;\r
-}\r
-\r
-/**\r
- * \fn void DMA_SetChannel(int Channel, int length, int read)\r
- * \brief Set DMA Channel Length and RW\r
- */\r
-void DMA_SetChannel(int Channel, int length, int read)\r
-{\r
- Uint chan = Channel & 7;\r
- read = !!read;\r
- if(length > DMA_SIZE) length = DMA_SIZE;\r
- length --; //Adjust for DMA\r
- outb( cMASKPORT[chan], 0x04 | (chan & 0x3) ); // mask channel\r
- outb( cCLEARPORT[chan], 0x00 );\r
- outb( cMODEPORT[chan], (0x44 + (!read)*4) | (chan & 0x3) );\r
- outb( cADDRPORT[chan], LOWB(DMA_ADDRESS(chan)) ); // send address\r
- outb( cADDRPORT[chan], HIB(DMA_ADDRESS(chan)) ); // send address\r
- outb( cPAGEPORT[chan], HIW(DMA_ADDRESS(chan)) ); // send page\r
- outb( cCOUNTPORT[chan], LOWB(length) ); // send size\r
- outb( cCOUNTPORT[chan], HIB(length) ); // send size\r
- outb( cMASKPORT[chan], chan & 0x3 ); // unmask channel\r
- dma_addresses[chan] = (char*)DMA_ADDRESS(chan);\r
- dma_addresses[chan] += KERNEL_BASE;\r
-}\r
-\r
-/**\r
- * \fn void DMA_ReadData(int channel, int count, void *buffer)\r
- * \brief Read data from a DMA buffer\r
- */\r
-int DMA_ReadData(int channel, int count, void *buffer)\r
-{\r
- if(channel < 0 || channel > 7)\r
- return -1;\r
- if(count < 0 || count > DMA_SIZE)\r
- return -2;\r
- //LogF("memcpy(*0x%x, dma_channels[channel].address, count)\n", buffer\r
- memcpy(buffer, dma_addresses[channel], count);\r
- return 0;\r
-}\r
-\r
-/**\r
- * \fn void DMA_WriteData(int channel, int count, void *buffer)\r
- * \brief Write data to a DMA buffer\r
- */\r
-int DMA_WriteData(int channel, int count, const void *buffer)\r
-{\r
- if(channel < 0 || channel > 7)\r
- return -1;\r
- if(count < 0 || count > DMA_SIZE)\r
- return -2;\r
- \r
- memcpy(dma_addresses[channel], buffer, count);\r
- \r
- return 0;\r
-}\r
+++ /dev/null
-/*
- * Acess2 DMA Driver
- */
-#ifndef _DMA_H_
-#define _DMA_H_
-
-extern void DMA_SetChannel(int channel, int length, int read);
-extern int DMA_ReadData(int channel, int count, void *buffer);
-extern int DMA_WriteData(int channel, int count, void *buffer);
-
-#endif
+++ /dev/null
-CATEGORY = x86
-
--include ../../Makefile.tpl
+++ /dev/null
-#
-#
-
-OBJ := vga.o
-NAME := VGAText
-
--include ../Makefile.tpl
+++ /dev/null
-/*
- * Acess2 VGA Controller Driver
- */
-#define DEBUG 0
-#include <acess.h>
-#include <fs_devfs.h>
-#include <api_drv_video.h>
-#include <modules.h>
-
-// === CONSTANTS ===
-#define VGA_WIDTH 80
-#define VGA_HEIGHT 25
-
-// === PROTOTYPES ===
- int VGA_Install(char **Arguments);
-Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
- int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data);
-Uint8 VGA_int_GetColourNibble(Uint16 col);
-Uint16 VGA_int_GetWord(const tVT_Char *Char);
-void VGA_int_SetCursor(Sint16 x, Sint16 y);
-// --- 2D Acceleration Functions --
-void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
-void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x000A, x86_VGAText, VGA_Install, NULL, NULL);
-tVFS_NodeType gVGA_NodeType = {
- //.Read = VGA_Read,
- .Write = VGA_Write,
- .IOCtl = VGA_IOCtl
- };
-tDevFS_Driver gVGA_DevInfo = {
- NULL, "x86_VGAText",
- {
- .NumACLs = 0,
- .Size = VGA_WIDTH*VGA_HEIGHT*sizeof(tVT_Char),
- .Type = &gVGA_NodeType
- }
-};
-Uint16 *gVGA_Framebuffer = (void*)( KERNEL_BASE|0xB8000 );
- int giVGA_BufferFormat = VIDEO_BUFFMT_TEXT;
-tDrvUtil_Video_2DHandlers gVGA_2DFunctions = {
- NULL,
- VGA_2D_Fill,
- VGA_2D_Blit
-};
-
-// === CODE ===
-/**
- * \fn int VGA_Install(char **Arguments)
- */
-int VGA_Install(char **Arguments)
-{
- Uint8 byte;
-
- // Enable Bright Backgrounds
- inb(0x3DA); // Reset flipflop
- outb(0x3C0, 0x30); // Index 0x10, PAS
- byte = inb(0x3C1);
- byte &= ~8; // Disable Blink
- outb(0x3C0, byte); // Write value
-
-
- // Install DevFS
- DevFS_AddDevice( &gVGA_DevInfo );
-
- return MODULE_ERR_OK;
-}
-
-/**
- * \brief Writes a string of bytes to the VGA controller
- */
-Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
-{
- if( giVGA_BufferFormat == VIDEO_BUFFMT_TEXT )
- {
- int num = Length / sizeof(tVT_Char);
- int ofs = Offset / sizeof(tVT_Char);
- int i = 0;
- const tVT_Char *chars = Buffer;
- Uint16 word;
-
- //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-
- for( ; num--; i ++, ofs ++)
- {
- word = VGA_int_GetWord( &chars[i] );
- gVGA_Framebuffer[ ofs ] = word;
- }
-
- //LEAVE('X', Length);
- return Length;
- }
- else if( giVGA_BufferFormat == VIDEO_BUFFMT_2DSTREAM )
- {
- return DrvUtil_Video_2DStream(NULL, Buffer, Length, &gVGA_2DFunctions, sizeof(gVGA_2DFunctions));
- }
- else
- {
- return 0;
- }
-}
-
-/**
- * \fn int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief IO Control Call
- */
-int VGA_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
- int rv;
- switch(ID)
- {
- case DRV_IOCTL_TYPE: return DRV_TYPE_VIDEO;
- case DRV_IOCTL_IDENT: memcpy(Data, "VGA\0", 4); return 1;
- case DRV_IOCTL_VERSION: *(int*)Data = 50; return 1;
- case DRV_IOCTL_LOOKUP: return 0;
-
- case VIDEO_IOCTL_GETSETMODE: return 0; // Mode 0 only
- case VIDEO_IOCTL_FINDMODE:
- if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1;
- ((tVideo_IOCtl_Mode*)Data)->id = 0; // Text Only!
- case VIDEO_IOCTL_MODEINFO:
- if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1;
- if( ((tVideo_IOCtl_Mode*)Data)->id != 0) return 0;
- ((tVideo_IOCtl_Mode*)Data)->width = VGA_WIDTH*giVT_CharWidth;
- ((tVideo_IOCtl_Mode*)Data)->height = VGA_HEIGHT*giVT_CharHeight;
- ((tVideo_IOCtl_Mode*)Data)->bpp = 4;
- return 1;
-
- case VIDEO_IOCTL_SETBUFFORMAT:
- if( !CheckMem(Data, sizeof(int)) ) return -1;
- switch( *(int*)Data )
- {
- case VIDEO_BUFFMT_TEXT:
- case VIDEO_BUFFMT_2DSTREAM:
- rv = giVGA_BufferFormat;
- giVGA_BufferFormat = *(int*)Data;
-// Log_Debug("VGA", "Buffer format set to %i", giVGA_BufferFormat);
- return rv;
- default:
- break;
- }
- return -1;
-
- case VIDEO_IOCTL_SETCURSOR:
- VGA_int_SetCursor( ((tVideo_IOCtl_Pos*)Data)->x, ((tVideo_IOCtl_Pos*)Data)->y );
- return 1;
- }
- return 0;
-}
-
-/**
- * \fn Uint8 VGA_int_GetColourNibble(Uint16 col)
- * \brief Converts a 12-bit colour into a VGA 4-bit colour
- */
-Uint8 VGA_int_GetColourNibble(Uint16 col)
-{
- Uint8 ret = 0;
- int bright = 0;
-
- col = col & 0xCCC;
- col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30);
- bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2;
-
- switch(col)
- {
- // Black
- case 0x00: ret = 0x0; break;
- // Dark Grey
- case 0x15: ret = 0x8; break;
- // Blues
- case 0x01:
- case 0x02: ret = 0x1; break;
- case 0x03: ret = 0x9; break;
- // Green
- case 0x04:
- case 0x08: ret = 0x2; break;
- case 0x0C: ret = 0xA; break;
- // Reds
- case 0x10:
- case 0x20: ret = 0x4; break;
- case 0x30: ret = 0xC; break;
- // Light Grey
- case 0x2A: ret = 0x7; break;
- // White
- case 0x3F: ret = 0xF; break;
-
- default:
- ret |= (col & 0x03 ? 1 : 0);
- ret |= (col & 0x0C ? 2 : 0);
- ret |= (col & 0x30 ? 4 : 0);
- ret |= (bright ? 8 : 0);
- break;
- }
- return ret;
-}
-
-/**
- * \fn Uint16 VGA_int_GetWord(tVT_Char *Char)
- * \brief Convers a character structure to a VGA character word
- */
-Uint16 VGA_int_GetWord(const tVT_Char *Char)
-{
- Uint16 ret;
- Uint16 col;
-
- // Get Character
- if(Char->Ch < 128)
- ret = Char->Ch;
- else {
- switch(Char->Ch)
- {
- default: ret = 0; break;
- }
- }
-
- col = VGA_int_GetColourNibble(Char->BGCol);
- ret |= col << 12;
-
- col = VGA_int_GetColourNibble(Char->FGCol);
- ret |= col << 8;
-
- return ret;
-}
-
-/**
- * \fn void VGA_int_SetCursor(Sint16 x, Sint16 y)
- * \brief Updates the cursor position
- */
-void VGA_int_SetCursor(Sint16 x, Sint16 y)
-{
- int pos = x+y*VGA_WIDTH;
- if(x == -1 || y == -1)
- pos = -1;
- outb(0x3D4, 14);
- outb(0x3D5, pos >> 8);
- outb(0x3D4, 15);
- outb(0x3D5, pos);
-}
-
-void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
-{
- tVT_Char ch;
- Uint16 word, *buf;
-
- X /= giVT_CharWidth;
- W /= giVT_CharWidth;
- Y /= giVT_CharHeight;
- H /= giVT_CharHeight;
-
- ch.Ch = 0x20;
- ch.BGCol = (Colour & 0x0F0000) >> (16-8);
- ch.BGCol |= (Colour & 0x000F00) >> (8-4);
- ch.BGCol |= (Colour & 0x00000F);
- word = VGA_int_GetWord(&ch);
-
- Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word);
-
- if( X > VGA_WIDTH || Y > VGA_HEIGHT ) return ;
- if( X + W > VGA_WIDTH ) W = VGA_WIDTH - X;
- if( Y + H > VGA_HEIGHT ) H = VGA_HEIGHT - Y;
-
- buf = gVGA_Framebuffer + Y*VGA_WIDTH + X;
-
-
- while( H -- ) {
- int i;
- for( i = 0; i < W; i ++ )
- *buf++ = word;
- buf += VGA_WIDTH - W;
- }
-}
-
-void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
-{
- Uint16 *src, *dst;
-
- DstX /= giVT_CharWidth;
- SrcX /= giVT_CharWidth;
- W /= giVT_CharWidth;
-
- DstY /= giVT_CharHeight;
- SrcY /= giVT_CharHeight;
- H /= giVT_CharHeight;
-
-// Log("(%i,%i) from (%i,%i) %ix%i", DstX, DstY, SrcX, SrcY, W, H);
-
- if( SrcX > VGA_WIDTH || SrcY > VGA_HEIGHT ) return ;
- if( SrcX + W > VGA_WIDTH ) W = VGA_WIDTH - SrcX;
- if( SrcY + H > VGA_HEIGHT ) H = VGA_HEIGHT - SrcY;
- if( DstX > VGA_WIDTH || DstY > VGA_HEIGHT ) return ;
- if( DstX + W > VGA_WIDTH ) W = VGA_WIDTH - DstX;
- if( DstY + H > VGA_HEIGHT ) H = VGA_HEIGHT - DstY;
-
-
- src = gVGA_Framebuffer + SrcY*VGA_WIDTH + SrcX;
- dst = gVGA_Framebuffer + DstY*VGA_WIDTH + DstX;
-
- if( src > dst )
- {
- // Simple copy
- while( H-- ) {
- memcpy(dst, src, W*2);
- dst += VGA_WIDTH;
- src += VGA_WIDTH;
- }
- }
- else
- {
- dst += H*VGA_WIDTH;
- src += H*VGA_WIDTH;
- while( H -- ) {
- int i;
- dst -= VGA_WIDTH-W;
- src -= VGA_WIDTH-W;
- for( i = W; i --; ) *--dst = *--src;
- }
- }
-}
--- /dev/null
+Acess File System
+- Database Design
+
+== Data Strutures ==
+Blocks are of a size specified in the superblock
+- Superblock
+ > Fixed offset: 1024 bytes
+- Field Table
+ > Offset set in superblock
+- Index Table
+- Inode Table
+
+=== Superblock ===
+struct sSuperblock {
+ Uint8 Magic[4]; // == '\xACFS'+Version
+ Uint8 BlockSize; // TrueSize = 2^(7+BlockSize)
+};
+
+=== Field Table ===
+struct sFieldTableEntry {
+ Uint16 Ident;
+ Uint8 Type;
+ Uint8 Length;
+ char Text[];
+} FieldTable[SuperBlock.NFields];
+
+=== Index Table ==
+struct sIndexTableEntry {
+ Uint16 Field;
+ Uint16 CheckSum;
+ Uint32 Block;
+} IndexTable[SuperBlock.NFields];
+
+=== Index Table entry ==
+struct {
+ Uint32 NumEntries;
+ Uint32 Links[];
+};
+
+=== Inode Table ===
+struct sInodeTable {
+
+};
+
+=== Inode ===
+struct sInodeEntry {
+ Uint16 Name;
+ Uint8 Size;
+ Uint8 Checksum;
+ Uint8 data[];
+};
+
+Each `sInodeEntry` defines an entry in a "database"
+
--- /dev/null
+SHORTLOCK()
+ cli; lock cmpxchg
+SHORTREL()
+ lock and ; sti
+
+
+LONGLOCK()
+ mov eax, 1
+ lock cmpxchg lock.lock, eax
+ if(eax) {
+ SHORTLOCK(lock.listLock)
+ // add to list (linked list, 4 static entries)
+ SHORTREL(lock.listLock)
+ for(;;)
+ {
+ check owner
+ mov eax, 1
+ lock cmpxchg lock.lock, eax
+ if(!eax) break; // got lock
+ Threads_Sleep();
+ }
+ }
+
+LONGREL()
+ lock and lock.lock, 0
+ pop off front of list, free entry, wake thread
--- /dev/null
+select()
+- Implemented using a wait queue for every file descriptor
+- That requires waiting on sockets to be centeralised
+
+All wait tasks (reads on VTerm, Pipes, PTYs, network sockets) use the kernel
+version of select with an inifinite timeout.
+They then signal the VFS using their VFS node pointer as a reference
+
+The VFS function select()
+- Maintains a list of processes on each node (if select has been called on that node)
+- Each process maybe has a semaphore on it (to use the semaphore code to maintain the process list)
+ > Could maybe use a mutex instead
+
+
+VFS_Select(int, fd_set* read, fd_set* write, fd_set* except)
+
+read is the set of sockets that we are waiting to read from
+write " " " be able to write to
+except " " " for possible errors on
+
+
+Hence, each VFS_Node has three listener lists (or just pointers)
+- One for when data is avaliable
+- One for when space is avaliable for writing
+- One for when an error occurs (closed pipe, etc ...)
--- /dev/null
+
+== Read ==
+- UTF-8 / UCS-4 Character Stream
+ > Selected with mode call
+
+== Write ==
+UTF-8 Emulation Text Mode:
+- Emuates a character device
+ > VT-100/ANSI Control Codes
+ > Characters/Symbols are sent as UTF-8
+
+/*
+Native Text Mode:
+- NxM 64-bit entries
+ > UCS-32 Codepoint (if a diacritic is encountered, the previous character is modified)
+ > 12-bit (16 encoded) Foreground
+ > 12-bit (16 encoded) Background
+*/
+
+Framebuffer Graphics:
+- WxH 32-bit (3x 8-bit channels) framebuffer
+- Write to entire framebuffer
+
+Accellerated Graphics:
+- Command Stream
+ > Each Node.Write call is a single command
+ + NOP (-)
+ + Direct (Uint16 X, Y, W, H, Uint32 Data[])
+ + Blit (Uint16 W, H, SrcX, SrxY, DstX, DstY, Uint32 Data[])
+ + Fill (Uint16 X, Y, W, H)
+ + Rect (Uint16 X, Y, W, H)
+ + Line (Uint16 X, Y, W, H)
+ + Text (Uint16 X, Y, Size, Font)
+ + ShowTile (Uint16 ID, Uint16 X, Y)
+ + DelTile (Uint16 ID)
+- Extra IOCtls
+ + int LoadFont(char *Path)
+ + UnloadFont(int *ID)
+ + int MakeTile(struct {Uint16 W, H, Uint32 Data[]} *Img)
+ + DelTile(int *ID)
+- Allow fast switch between Accel/Framebuffer?
+- Min Reqd Tile Size 32x32
+ > Tiles should be driver emulated if unavaliable by hardware
+
+3D Graphics: (Can be emulated if not avaliable, or just denied)
+- Command Stream
+ >
+ + NOP (-)
+ + FlipBuffer (-)
+ + LoadTexture(Uint16 ID, W, H, Uint32 Data[])
+ + UnloadTexture(Uint16 ID)
+ + SetTexture(Uint16 ID)
+ + Triangle (Uint16 Texture, Uint32[3+3+2][3])