+++ /dev/null
-# Doxyfile 1.8.7
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed in
-# front of the TAG it is preceding.
-#
-# All text after a single 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.
-# The default value is: UTF-8.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
-# double-quotes, unless you are using Doxywizard) that should identify the
-# project for which the documentation is generated. This name is used in the
-# title of most generated pages and in a few other places.
-# The default value is: My Project.
-
-PROJECT_NAME = "SWAP Modules"
-
-# 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) path
-# into which the generated documentation will be written. 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 =
-
-# 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 causes
-# performance problems for the file system.
-# The default value is: NO.
-
-CREATE_SUBDIRS = NO
-
-# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
-# characters to appear in the names of generated files. If set to NO, non-ASCII
-# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
-# U+3044.
-# The default value is: NO.
-
-ALLOW_UNICODE_NAMES = 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.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
-# The default value is: English.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES 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.
-# The default value is: YES.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES 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.
-# The default value is: YES.
-
-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 and 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.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES 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
-# The default value is: YES.
-
-FULL_PATH_NAMES = YES
-
-# 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.
-#
-# Note that you can specify absolute paths here, but also relative paths, which
-# will be relative from the directory where doxygen is started.
-# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-
-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 list of 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.
-# The default value is: NO.
-
-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-style will behave just like regular Qt-
-# style comments (thus requiring an explicit @brief command for a brief
-# description.)
-# The default value is: NO.
-
-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 Qt-style will behave just like regular Qt-style comments (thus
-# requiring an explicit \brief command for a brief description.)
-# The default value is: NO.
-
-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 behavior. 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 behavior instead.
-#
-# Note that setting this tag to YES also means that rational rose comments are
-# not recognized any more.
-# The default value is: NO.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
-# documentation from any documented member that it re-implements.
-# The default value is: YES.
-
-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.
-# The default value is: NO.
-
-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.
-# Minimum value: 1, maximum value: 16, default value: 4.
-
-TAB_SIZE = 4
-
-# This tag can be used to specify a number of aliases that act 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 =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
-# 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.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_FOR_C = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
-# Python sources only. Doxygen will then generate output that is more tailored
-# for that language. For instance, namespaces will be presented as packages,
-# qualified scopes will look different, etc.
-# The default value is: NO.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources. Doxygen will then generate output that is tailored for Fortran.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-
-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 (fixed format Fortran:
-# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
-# Fortran. In the later case the parser tries to guess whether the code is fixed
-# or free formatted code, this is the default for Fortran type files), VHDL. 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 For files without extension you can use no_extension as a placeholder.
-#
-# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
-
-EXTENSION_MAPPING =
-
-# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
-# according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you can
-# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
-# case of backward compatibilities issues.
-# The default value is: YES.
-
-MARKDOWN_SUPPORT = YES
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by by putting a % sign in front of the word
-# or globally by setting AUTOLINK_SUPPORT to NO.
-# The default value is: YES.
-
-AUTOLINK_SUPPORT = YES
-
-# 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);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-# The default value is: NO.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-# The default value is: NO.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) 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.
-# The default value is: NO.
-
-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 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.
-# The default value is: YES.
-
-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.
-# The default value is: NO.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES 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.
-# The default value is: YES.
-
-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).
-#
-# Note that this feature does not work in combination with
-# SEPARATE_MEMBER_PAGES.
-# The default value is: NO.
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
-# with only public data fields or simple typedef 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, structs, classes, and unions are shown on a separate page (for HTML and
-# Man pages) or section (for LaTeX and RTF).
-# The default value is: NO.
-
-INLINE_SIMPLE_STRUCTS = NO
-
-# When TYPEDEF_HIDES_STRUCT tag 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.
-# The default value is: NO.
-
-TYPEDEF_HIDES_STRUCT = NO
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can be
-# an expensive process and often the same symbol appears multiple times in the
-# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
-# doxygen will become slower. If the cache is too large, memory is wasted. The
-# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
-# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
-# symbols. At the end of a run doxygen will report the cache usage and suggest
-# the optimal cache size from a speed point of view.
-# Minimum value: 0, maximum value: 9, default value: 0.
-
-LOOKUP_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 respectively EXTRACT_STATIC tags are set to YES.
-# Note: This will also disable the warnings about undocumented members that are
-# normally produced when WARNINGS is set to YES.
-# The default value is: NO.
-
-EXTRACT_ALL = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
-# be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
-# The default value is: NO.
-
-EXTRACT_PACKAGE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
-# included in the documentation.
-# The default value is: NO.
-
-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. Does not have any effect
-# for Java sources.
-# The default value is: YES.
-
-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 only methods in the interface are
-# included.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
-# undocumented members inside documented classes or files. If set to NO 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.
-# The default value is: NO.
-
-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 these classes will be included in the various overviews. This option has
-# no effect if EXTRACT_ALL is enabled.
-# The default value is: NO.
-
-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 these declarations will be
-# included in the documentation.
-# The default value is: NO.
-
-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 these
-# blocks will be appended to the function's detailed documentation block.
-# The default value is: NO.
-
-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 then the documentation
-# will be excluded. Set it to YES to include the internal documentation.
-# The default value is: NO.
-
-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.
-# The default value is: system dependent.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
-# their full class and namespace scopes in the documentation. If set to YES the
-# scope will be hidden.
-# The default value is: NO.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
-# the files that are included by a file in the documentation of that file.
-# The default value is: YES.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
-# grouped member an include statement to the documentation, telling the reader
-# which file to include in order to use the member.
-# The default value is: NO.
-
-SHOW_GROUPED_MEMB_INC = NO
-
-# 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.
-# The default value is: NO.
-
-FORCE_LOCAL_INCLUDES = NO
-
-# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
-# documentation for inline members.
-# The default value is: YES.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES 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.
-# The default value is: YES.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
-# descriptions of file, namespace and class members alphabetically by member
-# name. If set to NO the members will appear in declaration order. Note that
-# this will also influence the order of the classes in the class list.
-# The default value is: NO.
-
-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 constructors will appear in the
-# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
-# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
-# member documentation.
-# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
-# detailed member documentation.
-# The default value is: 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 group names will
-# appear in their defined order.
-# The default value is: NO.
-
-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 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.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-
-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.
-# The default value is: YES.
-
-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.
-# The default value is: YES.
-
-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.
-# The default value is: YES.
-
-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.
-# The default value is: YES.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional documentation
-# sections, marked by \if <section_label> ... \endif and \cond <section_label>
-# ... \endcond blocks.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
-# initial value of a variable or macro / define can have 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 value of individual variables and macros / defines can be
-# controlled using \showinitializer or \hideinitializer command in the
-# documentation regardless of this setting.
-# Minimum value: 0, maximum value: 10000, default value: 30.
-
-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.
-# The default value is: YES.
-
-SHOW_USED_FILES = YES
-
-# 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 value 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 value 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. For an example see the documentation.
-
-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. To 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.
-#
-# Note that if you run doxygen from a directory containing a file called
-# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
-# tag is left empty.
-
-LAYOUT_FILE =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
-# the reference definitions. This must be a list of .bib files. The .bib
-# extension is automatically appended if omitted. This 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. To use this feature you need bibtex and perl available in the
-# search path. Do not use file names with spaces, bibtex cannot handle them. See
-# also \cite for info how to create references.
-
-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 to
-# standard output by doxygen. If QUIET is set to YES this implies that the
-# messages are off.
-# The default value is: NO.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
-# this implies that the warnings are on.
-#
-# Tip: Turn warnings on while writing the documentation.
-# The default value is: YES.
-
-WARNINGS = YES
-
-# If the WARN_IF_UNDOCUMENTED tag 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.
-# The default value is: YES.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If the WARN_IF_DOC_ERROR tag 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.
-# The default value is: YES.
-
-WARN_IF_DOC_ERROR = YES
-
-# This 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 doxygen will only warn about wrong or incomplete parameter
-# documentation, but not about the absence of documentation.
-# The default value is: NO.
-
-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)
-# The default value is: $file:$line: $text.
-
-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 standard
-# error (stderr).
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag is 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.
-# Note: If this tag is empty the current directory is searched.
-
-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. Doxygen uses
-# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
-# possible encodings.
-# The default value is: UTF-8.
-
-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 patterns (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, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
-# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
-# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
-# *.qsf, *.as and *.js.
-
-FILE_PATTERNS =
-
-# The RECURSIVE tag can be used to specify whether or not subdirectories should
-# be searched for input files as well.
-# The default value is: NO.
-
-RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# 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 the directory from which doxygen is
-# run.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-# The default value is: NO.
-
-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
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */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.
-# The default value is: NO.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or directories
-# that contain images that are to be 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.
-#
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-
-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 information on how
-# filters are used. If the FILTER_PATTERNS tag is empty or if none 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 also be used to filter the input files that are used for
-# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
-# The default value is: NO.
-
-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 tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want to reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE =
-
-#---------------------------------------------------------------------------
-# 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 that
-# also VERBATIM_HEADERS is set to NO.
-# The default value is: NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
-# The default value is: NO.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
-# special comment blocks from generated source code fragments. Normal C, C++ and
-# Fortran comments will always remain visible.
-# The default value is: YES.
-
-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.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES 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.
-# The default value is: YES.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
-# source code will show a tooltip with additional information such as prototype,
-# brief description and links to the definition and documentation. Since this
-# will make the HTML file larger and loading of large files a bit slower, you
-# can opt to disable this feature.
-# The default value is: YES.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-SOURCE_TOOLTIPS = 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.
-#
-# To use it do the following:
-# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
-# - Make sure the INPUT points to the root of the source tree
-# - Run doxygen as normal
-#
-# Doxygen will invoke htags (and that will in turn invoke gtags), so these
-# tools must be available from the command line (i.e. in the search path).
-#
-# The result: instead of the source browser generated by doxygen, the links to
-# source code will now point to the output of htags.
-# The default value is: NO.
-# This tag requires that the tag SOURCE_BROWSER is set to YES.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set the YES 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.
-# See also: Section \class.
-# The default value is: YES.
-
-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.
-# The default value is: YES.
-
-ALPHABETICAL_INDEX = YES
-
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-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 a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
-# The default value is: YES.
-
-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.
-# The default directory is: html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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).
-# The default value is: .html.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
-# each generated HTML page. If the tag is left blank doxygen will generate a
-# standard header.
-#
-# To get valid HTML the header file that includes any scripts and style sheets
-# that doxygen needs, which is dependent on the configuration options used (e.g.
-# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
-# default header using
-# doxygen -w html new_header.html new_footer.html new_stylesheet.css
-# YourConfigFile
-# and then modify the file new_header.html. See also section "Doxygen usage"
-# for information on how to generate the default header that doxygen normally
-# uses.
-# Note: The header is subject to change so you typically have to regenerate the
-# default header when upgrading to a newer version of doxygen. For a description
-# of the possible markers and block names see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
-# generated HTML page. If the tag is left blank doxygen will generate a standard
-# footer. See HTML_HEADER for more information on how to generate a default
-# footer and what special commands can be used inside the footer. See also
-# section "Doxygen usage" for information on how to generate the default footer
-# that doxygen normally uses.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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 left blank doxygen will generate a default style sheet.
-# See also section "Doxygen usage" for information on how to generate the style
-# sheet that doxygen normally uses.
-# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
-# it is more robust and this tag (HTML_STYLESHEET) will in the future become
-# obsolete.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_STYLESHEET =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
-# defined cascading style sheet that is included after the standard style sheets
-# created by doxygen. Using this option one can overrule certain style aspects.
-# This is preferred over using HTML_STYLESHEET since it does not replace the
-# standard style sheet and is therefor more robust against future updates.
-# Doxygen will copy the style sheet file to the output directory. For an example
-# see the documentation.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_EXTRA_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.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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.
-# Minimum value: 0, maximum value: 359, default value: 220.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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.
-# Minimum value: 0, maximum value: 255, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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.
-# Minimum value: 40, maximum value: 240, default value: 80.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP = 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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
-# shown in the various tree structured indices initially; the user can expand
-# and collapse entries dynamically later on. Doxygen will expand the tree to
-# such a level that at most the specified number of entries are visible (unless
-# a fully collapsed tree already exceeds this amount). So setting the number of
-# entries 1 will produce a full collapsed tree by default. 0 is a special value
-# representing an infinite number of entries and will result in a full expanded
-# tree by default.
-# Minimum value: 0, maximum value: 9999, default value: 100.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# 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 (see: http://developer.apple.com/tools/xcode/), 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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_DOCSET = NO
-
-# This tag determines the name of the docset 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.
-# The default value is: Doxygen generated docs.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# 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.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# The DOCSET_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.
-# The default value is: org.doxygen.Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-
-# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
-# The default value is: Publisher.
-# This tag requires that the tag GENERATE_DOCSET is set to YES.
-
-DOCSET_PUBLISHER_NAME = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
-# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
-# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
-#
-# The HTML Help Workshop contains a compiler that can convert all HTML output
-# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
-# files are now used as the Windows 98 help format, and will replace the old
-# Windows help format (.hlp) on all Windows platforms in the future. Compressed
-# HTML files also contain an index, a table of contents, and you can search for
-# words in the documentation. The HTML workshop also contains a viewer for
-# compressed HTML files.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_HTMLHELP = NO
-
-# 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.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_FILE =
-
-# 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.
-# The file has to be specified with full path.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-HHC_LOCATION =
-
-# 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).
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-GENERATE_CHI = NO
-
-# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
-# and project file content.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-CHM_INDEX_ENCODING =
-
-# 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. Furthermore it
-# enables the Previous and Next buttons.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members to
-# the table of contents of the HTML help documentation and to the tree view.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
-# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_NAMESPACE = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
-# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
-# folders).
-# The default value is: doc.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
-# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
-# filters).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHP_SECT_FILTER_ATTRS =
-
-# 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.
-# This tag requires that the tag GENERATE_QHP is set to YES.
-
-QHG_LOCATION =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
-# generated, together with the HTML files, they 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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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. Each documentation set should have its own identifier.
-# The default value is: org.doxygen.Project.
-# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
-
-ECLIPSE_DOC_ID = org.doxygen.Project
-
-# If you want full control over the layout of the generated HTML pages it might
-# be necessary to disable the index and replace it with your own. The
-# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
-# of each HTML page. A value of NO enables the index and the value YES disables
-# it. Since the tabs in the index contain the same information as the navigation
-# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-DISABLE_INDEX = NO
-
-# 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. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-GENERATE_TREEVIEW = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 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.
-# Minimum value: 0, maximum value: 20, default value: 4.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-ENUM_VALUES_PER_LINE = 4
-
-# 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.
-# Minimum value: 0, maximum value: 1500, default value: 250.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-EXT_LINKS_IN_WINDOW = NO
-
-# Use this tag to change the font size of LaTeX formulas included as images in
-# the HTML documentation. 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.
-# Minimum value: 8, maximum value: 50, default value: 10.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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 directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-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 may also need to install MathJax separately and configure the path
-# to it using the MATHJAX_RELPATH option.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-USE_MATHJAX = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
-# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
-# The default value is: HTML-CSS.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_FORMAT = HTML-CSS
-
-# 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
-# Content Delivery Network so you can quickly see the result without installing
-# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
-# extension names that should be enabled during MathJax rendering. For example
-# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_EXTENSIONS =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
-# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
-# example see the documentation.
-# This tag requires that the tag USE_MATHJAX is set to YES.
-
-MATHJAX_CODEFILE =
-
-# 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. It is possible to
-# search using the keyboard; to jump to the search box use <access key> + S
-# (what the <access key> is depends on the OS and browser, but it is typically
-# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
-# key> to jump into the search results window, the results can be navigated
-# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
-# the search. The filter options can be selected when the cursor is inside the
-# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
-# to select a filter and <Enter> or <escape> to activate or cancel the filter
-# option.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-SEARCHENGINE = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
-# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
-# setting. When disabled, doxygen will generate a PHP script for searching and
-# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
-# and searching needs to be provided by external tools. See the section
-# "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SERVER_BASED_SEARCH = NO
-
-# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
-# search results.
-#
-# Doxygen ships with an example indexer ( doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/).
-#
-# See the section "External Indexing and Searching" for details.
-# The default value is: NO.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH = NO
-
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will return the search results when EXTERNAL_SEARCH is enabled.
-#
-# Doxygen ships with an example indexer ( doxyindexer) and search engine
-# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/). See the section "External Indexing and
-# Searching" for details.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHENGINE_URL =
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
-# SEARCHDATA_FILE tag the name of this file can be specified.
-# The default file is: searchdata.xml.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-SEARCHDATA_FILE = searchdata.xml
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
-# projects and redirect the results back to the right project.
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTERNAL_SEARCH_ID =
-
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
-# to a relative location where the documentation can be found. The format is:
-# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
-# This tag requires that the tag SEARCHENGINE is set to YES.
-
-EXTRA_SEARCH_MAPPINGS =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
-# The default value is: YES.
-
-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.
-# The default directory is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked.
-#
-# 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.
-# The default file is: latex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
-# index for LaTeX.
-# The default file is: makeindex.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
-# 14 inches) and executive (7.25 x 10.5 inches).
-# The default value is: a4.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PAPER_TYPE = a4
-
-# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
-# that should be included in the LaTeX output. To get the times font for
-# instance you can specify
-# EXTRA_PACKAGES=times
-# If left blank no extra packages will be included.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
-#
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
-# replace them by respectively the title of the page, the current date and time,
-# only the current date, the version number of doxygen, the project name (see
-# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_FOOTER =
-
-# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the LATEX_OUTPUT output
-# directory. Note that the files will be copied as-is; there are no commands or
-# markers available.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_EXTRA_FILES =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
-# prepared for conversion to PDF (using ps2pdf or pdflatex). 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.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-PDF_HYPERLINKS = YES
-
-# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES to get a
-# higher quality PDF documentation.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_BATCHMODE = NO
-
-# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
-# index chapters (such as File Index, Compound Index, etc.) in the output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_HIDE_INDICES = NO
-
-# If the LATEX_SOURCE_CODE tag 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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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. See
-# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
-# The default value is: plain.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-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 too pretty with other RTF
-# readers/editors.
-# The default value is: NO.
-
-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.
-# The default directory is: rtf.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-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 some other Word compatible readers that support those
-# fields.
-#
-# Note: WordPad (write) and others do not support links.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-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.
-#
-# See also section "Doxygen usage" for information on how to generate the
-# default style sheet that doxygen normally uses.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
-# classes and files.
-# The default value is: NO.
-
-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. A directory man3 will be created inside the directory specified by
-# MAN_OUTPUT.
-# The default directory is: man.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to the generated
-# man pages. In case the manual section does not start with a number, the number
-# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
-# optional.
-# The default value is: .3.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_EXTENSION = .3
-
-# The MAN_SUBDIR tag determines the name of the directory created within
-# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
-# MAN_EXTENSION with the initial . removed.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-MAN_SUBDIR =
-
-# 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 value is: NO.
-# This tag requires that the tag GENERATE_MAN is set to YES.
-
-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.
-# The default value is: NO.
-
-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.
-# The default directory is: xml.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_OUTPUT = xml
-
-# 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.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to the DOCBOOK output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
-# that can be used to generate PDF.
-# The default value is: NO.
-
-GENERATE_DOCBOOK = NO
-
-# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# front of it.
-# The default directory is: docbook.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_OUTPUT = docbook
-
-#---------------------------------------------------------------------------
-# Configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
-# Definitions (see http://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.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-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.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-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.
-# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
-# C-preprocessor directives found in the sources and include files.
-# The default value is: YES.
-
-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 only conditional compilation will be
-# performed. Macro expansion can be done in a controlled way by setting
-# EXPAND_ONLY_PREDEF to YES.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set 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.
-# The default value is: NO.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES the includes files in the
-# INCLUDE_PATH will be searched if a #include is found.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-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.
-# This tag requires that the tag SEARCH_INCLUDES is set to YES.
-
-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.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-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 e.g.
-# 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.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-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.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES 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. Such function macros
-# are typically used for boiler-plate code, and will confuse the parser if not
-# removed.
-# The default value is: YES.
-# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration options related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES tag can be used to specify one or more tag files. For each tag
-# file the location of the external documentation should be added. 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. See the
-# section "Linking to external documentation" for more information about the use
-# of tag files.
-# Note: 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. See section "Linking to
-# external documentation" for more information about the usage of tag files.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
-# class index. If set to NO only the inherited external classes will be listed.
-# The default value is: NO.
-
-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.
-# The default value is: YES.
-
-EXTERNAL_GROUPS = YES
-
-# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
-# the related pages index. If set to NO, only the current project's pages will
-# be listed.
-# The default value is: YES.
-
-EXTERNAL_PAGES = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
-# (in HTML 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.
-# The default value is: YES.
-
-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 =
-
-# You can include diagrams made with dia in doxygen documentation. Doxygen will
-# then run dia to produce the diagram and insert it in the documentation. The
-# DIA_PATH tag allows you to specify the directory where the dia binary resides.
-# If left empty dia is assumed to be found in the default search path.
-
-DIA_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.
-# The default value is: YES.
-
-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 (see:
-# http://www.graphviz.org/), 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 value is: NO.
-
-HAVE_DOT = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
-# to run in parallel. When set to 0 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.
-# Minimum value: 0, maximum value: 32, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_NUM_THREADS = 0
-
-# When you want a differently looking font n the dot files that doxygen
-# generates 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.
-# The default value is: Helvetica.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTNAME = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
-# dot graphs.
-# Minimum value: 4, maximum value: 24, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the default font as specified with
-# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
-# the path where dot can find it using this tag.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH tag is 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 CLASS_DIAGRAMS tag to NO.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH tag is 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.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LOOK = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
-# class node. If there are many fields or methods and many nodes the graph may
-# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
-# number of items for each type to make the size more manageable. Set this to 0
-# for no limit. Note that the threshold may be exceeded by 50% before the limit
-# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
-# but if the number exceeds 15, the total amount of fields shown is limited to
-# 10.
-# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-UML_LIMIT_NUM_FIELDS = 10
-
-# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
-# collaboration graphs will show the relations between templates and their
-# instances.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-TEMPLATE_RELATIONS = NO
-
-# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDE_GRAPH = YES
-
-# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH tag is 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.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALL_GRAPH = YES
-
-# If the CALLER_GRAPH tag is 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.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-CALLER_GRAPH = YES
-
-# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
-# hierarchy of all classes instead of a textual one.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH tag is 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.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot.
-# Note: 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).
-# Possible values are: png, jpg, gif and svg.
-# The default value is: png.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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.
-# Note: 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.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-INTERACTIVE_SVG = NO
-
-# The DOT_PATH tag 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.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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).
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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 DIAFILE_DIRS tag can be used to specify one or more directories that
-# contain dia files that are included in the documentation (see the \diafile
-# command).
-
-DIAFILE_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.
-# Minimum value: 0, maximum value: 10000, default value: 50.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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.
-# Minimum value: 0, maximum value: 1000, default value: 0.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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).
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-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.
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
-# explaining the meaning of the various boxes and arrows in the dot generated
-# graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
-# files that are used to generate the various graphs.
-# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_CLEANUP = YES
+++ /dev/null
-extra_cflags := -I$(M) -I$(M)/kprobe/arch/$(LINKNAME)/ -I$(M)/uprobe/arch/$(LINKNAME)/
-extra_cflags += $(MCFLAGS)
-EXTRA_CFLAGS := $(extra_cflags)
-export extra_cflags
-
-obj-m := master/ \
- buffer/ \
- ksyms/ \
- driver/ \
- writer/ \
- kprobe/ \
- uprobe/ \
- us_manager/ \
- ks_features/ \
- loader/ \
- sampler/ \
- energy/ \
- parser/ \
- retprobe/ \
- preload/ \
- fbiprobe/ \
- wsp/ \
- nsp/ \
- task_ctx/ \
- uihv/ \
- got_patcher/
-
-ifneq ($(CONFIG_SWAP_KERNEL_IMMUTABLE), y)
-obj-m += ks_manager/
-endif # CONFIG_SWAP_KERNEL_IMMUTABLE
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_COMPAT_ARM64_H
-#define _SWAP_ASM_COMPAT_ARM64_H
-
-
-#ifdef CONFIG_ARM64
-
-# define PSR_T_BIT COMPAT_PSR_T_BIT
-
-# define ARM_r0 compat_usr(0)
-# define ARM_r1 compat_usr(1)
-# define ARM_r2 compat_usr(2)
-# define ARM_r3 compat_usr(3)
-# define ARM_r4 compat_usr(4)
-# define ARM_r5 compat_usr(5)
-# define ARM_r6 compat_usr(6)
-# define ARM_r7 compat_usr(7)
-# define ARM_r8 compat_usr(8)
-# define ARM_r9 compat_usr(9)
-# define ARM_r10 compat_usr(10)
-# define ARM_fp compat_fp
-# define ARM_ip compat_usr(12)
-# define ARM_sp compat_sp
-# define ARM_lr compat_lr
-# define ARM_pc pc
-# define ARM_cpsr pstate
-
-# define thumb_mode(regs) compat_thumb_mode(regs)
-
-#endif /* CONFIG_ARM64 */
-
-
-#endif /* _SWAP_ASM_COMPAT_ARM64_H */
-
-
-
-
-
-
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_DECODE_ARM_OLD_H
-#define _SWAP_ASM_DECODE_ARM_OLD_H
-
-
-#define ARM_INSN_MATCH(name, insn) \
- ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name)
-#define ARM_INSN_REG_RN(insn) ((insn & 0x000F0000) >> 16)
-#define ARM_INSN_REG_SET_RN(insn, nreg) { insn &= ~0x000F0000; insn |= nreg << 16; }
-#define ARM_INSN_REG_RD(insn) ((insn & 0x0000F000) >> 12)
-#define ARM_INSN_REG_SET_RD(insn, nreg) { insn &= ~0x0000F000; insn |= nreg << 12; }
-#define ARM_INSN_REG_RS(insn) ((insn & 0x00000F00) >> 8)
-#define ARM_INSN_REG_SET_RS(insn, nreg) { insn &= ~0x00000F00; insn |= nreg << 8; }
-#define ARM_INSN_REG_RM(insn) (insn & 0x0000000F)
-#define ARM_INSN_REG_SET_RM(insn, nreg) { insn &= ~0x0000000F; insn |= nreg; }
-#define ARM_INSN_REG_MR(insn, nreg) (insn & (1 << nreg))
-#define ARM_INSN_REG_SET_MR(insn, nreg) { insn |= (1 << nreg); }
-#define ARM_INSN_REG_CLEAR_MR(insn, nreg) { insn &= ~(1 << nreg); }
-
-
-/* Undefined */
-#define MASK_ARM_INSN_UNDEF 0x0FF00000
-#define PTRN_ARM_INSN_UNDEF 0x03000000
-
-/* Architecturally undefined */
-#define MASK_ARM_INSN_AUNDEF 0x0FF000F0
-#define PTRN_ARM_INSN_AUNDEF 0x07F000F0
-
-/* Branches */
-#define MASK_ARM_INSN_B 0x0F000000
-#define PTRN_ARM_INSN_B 0x0A000000
-
-#define MASK_ARM_INSN_BL 0x0F000000
-#define PTRN_ARM_INSN_BL 0x0B000000
-
-#define MASK_ARM_INSN_BLX1 0xFE000000
-#define PTRN_ARM_INSN_BLX1 0xFA000000
-
-#define MASK_ARM_INSN_BLX2 0x0FF000F0
-#define PTRN_ARM_INSN_BLX2 0x01200030
-
-#define MASK_ARM_INSN_BX 0x0FF000F0
-#define PTRN_ARM_INSN_BX 0x01200010
-
-#define MASK_ARM_INSN_BXJ 0x0FF000F0
-#define PTRN_ARM_INSN_BXJ 0x01200020
-
-/* Software interrupts */
-#define MASK_ARM_INSN_SWI 0x0F000000
-#define PTRN_ARM_INSN_SWI 0x0F000000
-
-/* Break */
-#define MASK_ARM_INSN_BREAK 0xFFF000F0
-#define PTRN_ARM_INSN_BREAK 0xE1200070
-/* A8-56 ARM DDI 046B if cond != ‘1110’ then UNPREDICTABLE; */
-
-/* CLZ */
-#define MASK_ARM_INSN_CLZ 0x0FFF0FF0
-#define PTRN_ARM_INSN_CLZ 0x016F0F10
-
-/* Data processing immediate shift */
-#define MASK_ARM_INSN_DPIS 0x0E000010
-#define PTRN_ARM_INSN_DPIS 0x00000000
-
-/* Data processing register shift */
-#define MASK_ARM_INSN_DPRS 0x0E000090
-#define PTRN_ARM_INSN_DPRS 0x00000010
-
-/* Data processing immediate */
-#define MASK_ARM_INSN_DPI 0x0E000000
-#define PTRN_ARM_INSN_DPI 0x02000000
-
-/* Load immediate offset */
-#define MASK_ARM_INSN_LIO 0x0E100000
-#define PTRN_ARM_INSN_LIO 0x04100000
-
-/* Store immediate offset */
-#define MASK_ARM_INSN_SIO MASK_ARM_INSN_LIO
-#define PTRN_ARM_INSN_SIO 0x04000000
-
-/* Load register offset */
-#define MASK_ARM_INSN_LRO 0x0E100010
-#define PTRN_ARM_INSN_LRO 0x06100000
-
-/* Store register offset */
-#define MASK_ARM_INSN_SRO MASK_ARM_INSN_LRO
-#define PTRN_ARM_INSN_SRO 0x06000000
-
-/* Load multiple */
-#define MASK_ARM_INSN_LM 0x0E100000
-#define PTRN_ARM_INSN_LM 0x08100000
-
-/* Store multiple */
-#define MASK_ARM_INSN_SM MASK_ARM_INSN_LM
-#define PTRN_ARM_INSN_SM 0x08000000
-
-
-/* Coprocessor load/store and double register transfers */
-#define MASK_ARM_INSN_CLS 0x0E000000
-#define PTRN_ARM_INSN_CLS 0x0C000000
-
-/* Coprocessor register transfers */
-#define MASK_ARM_INSN_CRT 0x0F000010
-#define PTRN_ARM_INSN_CRT 0x0E000010
-
-
-#endif /* _SWAP_ASM_DECODE_ARM_OLD_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/ptrace.h>
-#include "decode_thumb.h"
-#include "tramps_thumb.h"
-#include "compat_arm64.h"
-
-
-#define GET_BIT(x, n) ((x >> n) & 0x1)
-#define GET_FIELD(x, s, l) ((x >> s) & ((1 << l) - 1))
-#define SET_FIELD(x, s, l, v) ({ \
- typeof(x) mask = (((1 << l) - 1) << s); \
- (x & ~mask) | ((v << s) & mask); \
-})
-
-
-typedef union thumb_insn {
- u32 val;
- struct {
- u16 hw1;
- u16 hw2;
- } __packed;
-} thumb_insn_t;
-
-typedef int (*decode_handler_t)(thumb_insn_t insn, struct decode_info *info);
-
-
-static void make_def(void *tramp, u32 insn, u32 vaddr, bool t2)
-{
- u32 ret_addr;
- u16 *tr = tramp;
-
- /*
- * thumb - +2
- * thumb2 - +4
- */
- ret_addr = vaddr + (2 << t2);
- tr[4] = insn & 0x0000ffff;
- if (t2)
- tr[5] = insn >> 16;
-
- tr[13] = RET_BREAK_THUMB;
- tr[16] = (ret_addr & 0x0000ffff) | 0x1;
- tr[17] = ret_addr >> 16;
-}
-
-static void tt_make_common(void *tramp, u32 insn, u32 vaddr, bool t2)
-{
- memcpy(tramp, gen_insn_execbuf_thumb, sizeof(gen_insn_execbuf_thumb));
- make_def(tramp, insn, vaddr, t2);
-}
-
-static void tt_make_pc_deps(void *tramp, u32 mod_insn, u32 vaddr, bool t2)
-{
- u32 pc_val = vaddr + 4;
- u16 *tr = tramp;
-
- memcpy(tramp, pc_dep_insn_execbuf_thumb,
- sizeof(pc_dep_insn_execbuf_thumb));
- make_def(tramp, mod_insn, vaddr, t2);
-
- /* save PC value */
- tr[14] = pc_val & 0x0000ffff;
- tr[15] = pc_val >> 16;
-}
-
-
-static bool bad_reg(int n)
-{
- return n == 13 || n == 15;
-}
-
-static int thumb_not_implement(thumb_insn_t insn, struct decode_info *info)
-{
- return -EFAULT;
-}
-
-static int thumb_unpredictable(thumb_insn_t insn, struct decode_info *info)
-{
- return -EINVAL;
-}
-
-/* hw1[1110 100x x1xx ????] */
-static int t32_ldrd_strd(thumb_insn_t insn, struct decode_info *info)
-{
- int w = GET_BIT(insn.hw1, 5);
- int n = GET_FIELD(insn.hw1, 0, 4);
- int t = GET_FIELD(insn.hw2, 12, 4);
- int t2 = GET_FIELD(insn.hw2, 8, 4);
-
- if (bad_reg(t) || bad_reg(t2))
- return thumb_unpredictable(insn, info);
-
- /* check load flag */
- if (GET_BIT(insn.hw1, 4)) {
- /* LDRD */
- if ((w && (n == 15)) || t == t2)
- return thumb_unpredictable(insn, info);
-
- if (n == 15) {
- /* change PC -> SP */
- insn.hw1 = SET_FIELD(insn.hw1, 0, 4, 13);
- tt_make_pc_deps(info->tramp, insn.val,
- info->vaddr, true);
-
- return 0;
- }
- } else {
- /* STRD */
- if ((w && t == n) || (w && t2 == n) || (n == 15))
- return thumb_unpredictable(insn, info);
- }
-
- tt_make_common(info->tramp, insn.val, info->vaddr, true);
-
- return 0;
-}
-
-static int t32_b1110_100x_x1(thumb_insn_t insn, struct decode_info *info)
-{
- /* check PW bits */
- if (insn.hw1 & 0x120)
- return t32_ldrd_strd(insn, info);
-
- return thumb_not_implement(insn, info);
-}
-
-static int t32_b1110_100(thumb_insn_t insn, struct decode_info *info)
-{
- if (GET_BIT(insn.hw1, 6))
- return t32_b1110_100x_x1(insn, info);
-
- return thumb_not_implement(insn, info);
-}
-
-static void t32_simulate_branch(u32 insn, struct arch_insn_arm *ainsn,
- struct pt_regs *regs)
-{
- u32 pc = regs->ARM_pc;
- thumb_insn_t i = { .val = insn };
-
- s32 offset = GET_FIELD(i.hw2, 0, 11); /* imm11 */
- offset += GET_FIELD(i.hw1, 0, 10) << 11; /* imm10 */
- offset += GET_BIT(i.hw2, 13) << 21; /* J1 */
- offset += GET_BIT(i.hw2, 11) << 22; /* J2 */
-
- /* check S bit */
- if (GET_BIT(i.hw1, 10))
- offset -= 0x00800000; /* Apply sign bit */
- else
- offset ^= 0x00600000; /* Invert J1 and J2 */
-
- /* check link */
- if (GET_BIT(i.hw2, 14)) {
- /* BL or BLX */
- regs->ARM_lr = regs->ARM_pc | 1;
- if (!GET_BIT(i.hw2, 12)) {
- /* BLX so switch to ARM mode */
- regs->ARM_cpsr &= ~PSR_T_BIT;
- pc &= ~3;
- }
- }
-
- regs->ARM_pc = pc + (offset * 2);
-}
-
-static int t32_branch(thumb_insn_t insn, struct decode_info *info)
-{
- info->handeler = t32_simulate_branch;
-
- return 0;
-}
-
-static decode_handler_t table_branches[8] = {
- /* hw2[14 12 0] */
- /* Bc 0 0 0 */ thumb_not_implement,
- /* Bc 0 0 1 */ thumb_not_implement,
- /* B 0 1 0 */ t32_branch,
- /* B 0 1 1 */ t32_branch,
- /* BLX 1 0 0 */ t32_branch,
- /* res 1 0 1 */ thumb_unpredictable,
- /* BL 1 1 0 */ t32_branch,
- /* BL 1 1 1 */ t32_branch,
-};
-
-
-
-static int t32_b1111_0xxx_xxxx_xxxx_1(thumb_insn_t insn,
- struct decode_info *info)
-{
- u32 s = GET_BIT(insn.hw2, 14) << 2 |
- GET_BIT(insn.hw2, 12) << 1 |
- GET_BIT(insn.hw2, 0);
-
- return table_branches[s](insn, info);
-}
-
-static int b111(thumb_insn_t insn, struct decode_info *info)
-{
- /* hw1[111x xxx? ???? ????] */
- switch (GET_FIELD(insn.hw1, 9, 4)) {
- case 0b0100:
- return t32_b1110_100(insn, info);
- }
-
- /* [1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx] */
- if (GET_FIELD(insn.hw1, 11, 2) == 0b10 && GET_BIT(insn.hw2, 15) == 1)
- return t32_b1111_0xxx_xxxx_xxxx_1(insn, info);
-
- return thumb_not_implement(insn, info);
-}
-
-
-decode_handler_t table_xxx[8] = {
- /* 000 */ thumb_not_implement,
- /* 001 */ thumb_not_implement,
- /* 010 */ thumb_not_implement,
- /* 011 */ thumb_not_implement,
- /* 100 */ thumb_not_implement,
- /* 101 */ thumb_not_implement,
- /* 110 */ thumb_not_implement,
- /* 111 */ b111,
-};
-
-
-int decode_thumb(u32 insn, struct decode_info *info)
-{
- thumb_insn_t tinsn = { .val = insn };
-
- /* check first 3 bits hw1[xxx? ???? ???? ????] */
- return table_xxx[GET_FIELD(tinsn.hw1, 13, 3)](tinsn, info);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _ARM_DECODE_THUMB_H
-#define _ARM_DECODE_THUMB_H
-
-
-#include "probes.h"
-
-
-struct decode_info {
- u32 vaddr;
- void *tramp;
- probe_handler_arm_t handeler;
-};
-
-
-int decode_thumb(u32 insn, struct decode_info *info);
-
-
-#endif /* _ARM_DECODE_THUMB_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_DECODE_THUMB_OLD_H
-#define _SWAP_ASM_DECODE_THUMB_OLD_H
-
-
-/* == THUMB == */
-#define THUMB_INSN_MATCH(name, insn) \
- (((insn & 0x0000FFFF) & MASK_THUMB_INSN_##name) == \
- PTRN_THUMB_INSN_##name)
-
-
-/* Undefined */
-#define MASK_THUMB_INSN_UNDEF 0xFE00
-#define PTRN_THUMB_INSN_UNDEF 0xDE00
-
-/* Branches */
-#define MASK_THUMB_INSN_B1 0xF000
-#define PTRN_THUMB_INSN_B1 0xD000 /* b<cond> label */
-
-#define MASK_THUMB_INSN_B2 0xF800
-#define PTRN_THUMB_INSN_B2 0xE000 /* b label */
-
-#define MASK_THUMB_INSN_CBZ 0xF500
-#define PTRN_THUMB_INSN_CBZ 0xB100 /* CBZ/CBNZ */
-
-#define MASK_THUMB_INSN_BLX2 0xFF80 /* blx reg */
-#define PTRN_THUMB_INSN_BLX2 0x4780
-
-#define MASK_THUMB_INSN_BX 0xFF80
-#define PTRN_THUMB_INSN_BX 0x4700
-
-/* Software interrupts */
-#define MASK_THUMB_INSN_SWI 0xFF00
-#define PTRN_THUMB_INSN_SWI 0xDF00
-
-/* Break */
-#define MASK_THUMB_INSN_BREAK 0xFF00
-#define PTRN_THUMB_INSN_BREAK 0xBE00
-
-/* Data processing immediate */
-#define MASK_THUMB_INSN_DP 0xFC00
-#define PTRN_THUMB_INSN_DP 0x4000
-
-#define MASK_THUMB_INSN_APC 0xF800
-#define PTRN_THUMB_INSN_APC 0xA000 /* ADD Rd, [PC, #<imm8> * 4] */
-
-#define MASK_THUMB_INSN_MOV3 0xFF00
-#define PTRN_THUMB_INSN_MOV3 0x4600 /* MOV Rd, PC */
-
-/* Load immediate offset */
-#define MASK_THUMB_INSN_LIO1 0xF800
-#define PTRN_THUMB_INSN_LIO1 0x6800 /* LDR */
-
-#define MASK_THUMB_INSN_LIO2 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_LIO2 0x7800 /* LDRB */
-
-#define MASK_THUMB_INSN_LIO3 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_LIO3 0x8800 /* LDRH */
-
-#define MASK_THUMB_INSN_LIO4 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_LIO4 0x9800 /* LDR SP relative */
-
-/* Store immediate offset */
-#define MASK_THUMB_INSN_SIO1 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_SIO1 0x6000 /* STR */
-
-#define MASK_THUMB_INSN_SIO2 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_SIO2 0x7000 /* STRB */
-
-#define MASK_THUMB_INSN_SIO3 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_SIO3 0x8000 /* STRH */
-
-#define MASK_THUMB_INSN_SIO4 MASK_THUMB_INSN_LIO1
-#define PTRN_THUMB_INSN_SIO4 0x9000 /* STR SP relative */
-
-/* Load register offset */
-#define MASK_THUMB_INSN_LRO1 0xFE00
-#define PTRN_THUMB_INSN_LRO1 0x5600 /* LDRSB */
-
-#define MASK_THUMB_INSN_LRO2 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_LRO2 0x5800 /* LDR */
-
-#define MASK_THUMB_INSN_LRO3 0xf800
-#define PTRN_THUMB_INSN_LRO3 0x4800 /* LDR Rd, [PC, #<imm8> * 4] */
-
-#define MASK_THUMB_INSN_LRO4 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_LRO4 0x5A00 /* LDRH */
-
-#define MASK_THUMB_INSN_LRO5 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_LRO5 0x5C00 /* LDRB */
-
-#define MASK_THUMB_INSN_LRO6 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_LRO6 0x5E00 /* LDRSH */
-
-/* Store register offset */
-#define MASK_THUMB_INSN_SRO1 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_SRO1 0x5000 /* STR */
-
-#define MASK_THUMB_INSN_SRO2 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_SRO2 0x5200 /* STRH */
-
-#define MASK_THUMB_INSN_SRO3 MASK_THUMB_INSN_LRO1
-#define PTRN_THUMB_INSN_SRO3 0x5400 /* STRB */
-
-
-/* == THUMB2 == */
-#define THUMB2_INSN_MATCH(name, insn) \
- ((insn & MASK_THUMB2_INSN_##name) == PTRN_THUMB2_INSN_##name)
-
-#define THUMB2_INSN_REG_RT(insn) ((insn & 0xf0000000) >> 28)
-#define THUMB2_INSN_REG_RT2(insn) ((insn & 0x0f000000) >> 24)
-#define THUMB2_INSN_REG_RN(insn) (insn & 0x0000000f)
-#define THUMB2_INSN_REG_RD(insn) ((insn & 0x0f000000) >> 24)
-#define THUMB2_INSN_REG_RM(insn) ((insn & 0x000f0000) >> 16)
-
-
-/* Branches */
-#define MASK_THUMB2_INSN_B1 0xD000F800
-#define PTRN_THUMB2_INSN_B1 0x8000F000
-
-#define MASK_THUMB2_INSN_B2 0xD000F800
-#define PTRN_THUMB2_INSN_B2 0x9000F000
-
-#define MASK_THUMB2_INSN_BL 0xD000F800
-#define PTRN_THUMB2_INSN_BL 0xD000F000 /* bl imm swapped */
-
-#define MASK_THUMB2_INSN_BLX1 0xD001F800
-#define PTRN_THUMB2_INSN_BLX1 0xC000F000
-
-#define MASK_THUMB2_INSN_BXJ 0xD000FFF0
-#define PTRN_THUMB2_INSN_BXJ 0x8000F3C0
-
-/* Data processing register shift */
-#define MASK_THUMB2_INSN_DPRS 0xFFE00000
-#define PTRN_THUMB2_INSN_DPRS 0xEA000000
-
-/* Data processing immediate */
-#define MASK_THUMB2_INSN_DPI 0xFBE08000
-#define PTRN_THUMB2_INSN_DPI 0xF2000000
-
-#define MASK_THUMB2_INSN_RSBW 0x8000fbe0
-#define PTRN_THUMB2_INSN_RSBW 0x0000f1c0 /* RSB{S}.W Rd,Rn,#<const> */
-
-#define MASK_THUMB2_INSN_RORW 0xf0f0ffe0
-#define PTRN_THUMB2_INSN_RORW 0xf000fa60 /* ROR{S}.W Rd, Rn, Rm */
-
-#define MASK_THUMB2_INSN_ROR 0x0030ffef
-#define PTRN_THUMB2_INSN_ROR 0x0030ea4f /* ROR{S} Rd, Rm, #<imm> */
-
-#define MASK_THUMB2_INSN_LSLW1 0xf0f0ffe0
-#define PTRN_THUMB2_INSN_LSLW1 0xf000fa00 /* LSL{S}.W Rd, Rn, Rm */
-
-#define MASK_THUMB2_INSN_LSLW2 0x0030ffef
-#define PTRN_THUMB2_INSN_LSLW2 0x0000ea4f /* LSL{S}.W Rd, Rm, #<imm5>*/
-
-#define MASK_THUMB2_INSN_LSRW1 0xf0f0ffe0
-#define PTRN_THUMB2_INSN_LSRW1 0xf000fa20 /* LSR{S}.W Rd, Rn, Rm */
-
-#define MASK_THUMB2_INSN_LSRW2 0x0030ffef
-#define PTRN_THUMB2_INSN_LSRW2 0x0010ea4f /* LSR{S}.W Rd, Rm, #<imm5> */
-
-#define MASK_THUMB2_INSN_TEQ1 0x8f00fbf0
-#define PTRN_THUMB2_INSN_TEQ1 0x0f00f090 /* TEQ Rn, #<const> */
-
-#define MASK_THUMB2_INSN_TEQ2 0x0f00fff0
-#define PTRN_THUMB2_INSN_TEQ2 0x0f00ea90 /* TEQ Rn, Rm{,<shift>} */
-
-#define MASK_THUMB2_INSN_TST1 0x8f00fbf0
-#define PTRN_THUMB2_INSN_TST1 0x0f00f010 /* TST Rn, #<const> */
-
-#define MASK_THUMB2_INSN_TST2 0x0f00fff0
-#define PTRN_THUMB2_INSN_TST2 0x0f00ea10 /* TST Rn, Rm{,<shift>} */
-
-/* Load immediate offset */
-#define MASK_THUMB2_INSN_LDRW 0x0000fff0
-#define PTRN_THUMB2_INSN_LDRW 0x0000f850 /* LDR.W Rt, [Rn, #-<imm12>] */
-
-#define MASK_THUMB2_INSN_LDRW1 MASK_THUMB2_INSN_LDRW
-#define PTRN_THUMB2_INSN_LDRW1 0x0000f8d0 /* LDR.W Rt, [Rn, #<imm12>] */
-
-#define MASK_THUMB2_INSN_LDRBW MASK_THUMB2_INSN_LDRW
-#define PTRN_THUMB2_INSN_LDRBW 0x0000f810 /* LDRB.W Rt, [Rn, #-<imm8>] */
-
-#define MASK_THUMB2_INSN_LDRBW1 MASK_THUMB2_INSN_LDRW
-#define PTRN_THUMB2_INSN_LDRBW1 0x0000f890 /* LDRB.W Rt, [Rn, #<imm12>] */
-
-#define MASK_THUMB2_INSN_LDRHW MASK_THUMB2_INSN_LDRW
-#define PTRN_THUMB2_INSN_LDRHW 0x0000f830 /* LDRH.W Rt, [Rn, #-<imm8>] */
-
-#define MASK_THUMB2_INSN_LDRHW1 MASK_THUMB2_INSN_LDRW
-#define PTRN_THUMB2_INSN_LDRHW1 0x0000f8b0 /* LDRH.W Rt, [Rn, #<imm12>] */
-
-#define MASK_THUMB2_INSN_LDRD 0x0000fed0
-#define PTRN_THUMB2_INSN_LDRD 0x0000e850 /* LDRD Rt, Rt2, [Rn, #-<imm8>] */
-
-#define MASK_THUMB2_INSN_LDRD1 MASK_THUMB2_INSN_LDRD
-#define PTRN_THUMB2_INSN_LDRD1 0x0000e8d0 /* LDRD Rt, Rt2, [Rn, #<imm8>] */
-
-#define MASK_THUMB2_INSN_LDRWL 0x0fc0fff0
-#define PTRN_THUMB2_INSN_LDRWL 0x0000f850 /* LDR.W Rt, [Rn,Rm,LSL #<imm2>] */
-
-#define MASK_THUMB2_INSN_LDREX 0x0f00ffff
-#define PTRN_THUMB2_INSN_LDREX 0x0f00e85f /* LDREX Rt, [PC, #<imm8>] */
-
-#define MASK_THUMB2_INSN_MUL 0xf0f0fff0
-#define PTRN_THUMB2_INSN_MUL 0xf000fb00 /* MUL Rd, Rn, Rm */
-
-#define MASK_THUMB2_INSN_DP 0x0000ff00
-#define PTRN_THUMB2_INSN_DP 0x0000eb00 /* ADD/SUB/SBC/...Rd,Rn,Rm{,<shift>} */
-
-/* Store immediate offset */
-#define MASK_THUMB2_INSN_STRW 0x0fc0fff0
-#define PTRN_THUMB2_INSN_STRW 0x0000f840 /* STR.W Rt,[Rn,Rm,{LSL #<imm2>}] */
-
-#define MASK_THUMB2_INSN_STRW1 0x0000fff0
-#define PTRN_THUMB2_INSN_STRW1 0x0000f8c0 /* STR.W Rt, [Rn, #imm12]
- * STR.W Rt, [PC, #imm12] shall be
- * skipped, because it hangs
- * on Tegra. WTF */
-
-#define MASK_THUMB2_INSN_STRHW MASK_THUMB2_INSN_STRW
-#define PTRN_THUMB2_INSN_STRHW 0x0000f820 /* STRH.W Rt,[Rn,Rm,{LSL #<imm2>}] */
-
-#define MASK_THUMB2_INSN_STRHW1 0x0000fff0
-#define PTRN_THUMB2_INSN_STRHW1 0x0000f8a0 /* STRH.W Rt, [Rn, #<imm12>] */
-
-#define MASK_THUMB2_INSN_STRHT 0x0f00fff0 /* strht r1, [pc, #imm] illegal
- * instruction on Tegra. WTF */
-#define PTRN_THUMB2_INSN_STRHT 0x0e00f820 /* STRHT Rt, [Rn, #<imm8>] */
-
-#define MASK_THUMB2_INSN_STRT 0x0f00fff0
-#define PTRN_THUMB2_INSN_STRT 0x0e00f840 /* STRT Rt, [Rn, #<imm8>] */
-
-#define MASK_THUMB2_INSN_STRBW MASK_THUMB2_INSN_STRW
-#define PTRN_THUMB2_INSN_STRBW 0x0000f800 /* STRB.W Rt,[Rn,Rm,{LSL #<imm2>}] */
-
-#define MASK_THUMB2_INSN_STRBW1 0x0000fff0
-#define PTRN_THUMB2_INSN_STRBW1 0x0000f880 /* STRB.W Rt, [Rn, #<imm12>]
- * STRB.W Rt, [PC, #imm12] shall be
- * skipped, because it hangs
- * on Tegra. WTF */
-
-#define MASK_THUMB2_INSN_STRBT 0x0f00fff0
-#define PTRN_THUMB2_INSN_STRBT 0x0e00f800 /* STRBT Rt, [Rn, #<imm8>}] */
-
-#define MASK_THUMB2_INSN_STRD 0x0000fe50
-#define PTRN_THUMB2_INSN_STRD 0x0000e840 /* STR{D,EX,EXB,EXH,EXD} Rt, Rt2, [Rn, #<imm8>] */
-
-/* Load register offset */
-#define MASK_THUMB2_INSN_ADR 0x8000fa1f
-#define PTRN_THUMB2_INSN_ADR 0x0000f20f
-
-/* Load multiple */
-#define MASK_THUMB2_INSN_LDMIA 0x8000ffd0
-#define PTRN_THUMB2_INSN_LDMIA 0x8000e890 /* LDMIA(.W) Rn(!),{Rx-PC} */
-
-#define MASK_THUMB2_INSN_LDMDB 0x8000ffd0
-#define PTRN_THUMB2_INSN_LDMDB 0x8000e910 /* LDMDB(.W) Rn(!), {Rx-PC} */
-
-
-#endif /* _SWAP_ASM_DECODE_THUMB_OLD_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include "probes.h"
-#include "probes_arm.h"
-#include "probes_thumb.h"
-
-
-int make_tramp(struct arch_insn_arm *ainsn, u32 vaddr, u32 insn,
- u32 *tramp, u32 tramp_len)
-{
- int ret;
- int thumb_mode = vaddr & 1;
-
- vaddr &= ~1;
- if (thumb_mode)
- ret = make_trampoline_thumb(ainsn, vaddr, insn,
- tramp, tramp_len);
- else
- ret = make_trampoline_arm(vaddr, insn, tramp);
-
- return ret;
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_PROBES_H
-#define _SWAP_ASM_PROBES_H
-
-
-#include <linux/types.h>
-#include <swap-asm/swap_probes.h>
-
-
-struct pt_regs;
-struct arch_insn_arm;
-
-
-typedef void (*probe_handler_arm_t)(u32 insn, struct arch_insn_arm *ainsn,
- struct pt_regs *regs);
-
-
-struct arch_insn_arm {
- probe_handler_arm_t handler;
-};
-
-
-int make_tramp(struct arch_insn_arm *ainsn, u32 vaddr, u32 insn,
- u32 *tramp, u32 tramp_len);
-
-
-#endif /* _SWAP_ASM_PROBES_H */
-
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/ptrace.h> /* nedded for printk.h */
-#include <linux/linkage.h> /* needed for printk.h */
-#include <linux/printk.h>
-#include "probes.h"
-#include "tramps_arm.h"
-#include "decode_arm_old.h"
-
-
-#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
-#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
-
-
-static u32 get_addr_b(u32 insn, u32 addr)
-{
- /* real position less then PC by 8 */
- return ((s32)addr + 8 + branch_displacement(insn));
-}
-
-static int prep_pc_dep_insn_execbuf(u32 *insns, u32 insn, int uregs)
-{
- int i;
-
- if (uregs & 0x10) {
- int reg_mask = 0x1;
- /* search in reg list */
- for (i = 0; i < 13; i++, reg_mask <<= 1) {
- if (!(insn & reg_mask))
- break;
- }
- } else {
- for (i = 0; i < 13; i++) {
- if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i))
- continue;
- if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i))
- continue;
- if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i))
- continue;
- if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i))
- continue;
- break;
- }
- }
-
- if (i == 13) {
- pr_err("there are no free register %x in insn %x!",
- uregs, insn);
- return -EINVAL;
- }
-
- /* set register to save */
- ARM_INSN_REG_SET_RD(insns[0], i);
- /* set register to load address to */
- ARM_INSN_REG_SET_RD(insns[1], i);
- /* set instruction to execute and patch it */
- if (uregs & 0x10) {
- ARM_INSN_REG_CLEAR_MR(insn, 15);
- ARM_INSN_REG_SET_MR(insn, i);
- } else {
- if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15))
- ARM_INSN_REG_SET_RN(insn, i);
- if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15))
- ARM_INSN_REG_SET_RD(insn, i);
- if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15))
- ARM_INSN_REG_SET_RS(insn, i);
- if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15))
- ARM_INSN_REG_SET_RM(insn, i);
- }
-
- insns[PROBES_TRAMP_INSN_IDX] = insn;
- /* set register to restore */
- ARM_INSN_REG_SET_RD(insns[3], i);
-
- return 0;
-}
-
-static int arch_check_insn_arm(u32 insn)
-{
- /* check instructions that can change PC by nature */
- if (
- /* ARM_INSN_MATCH(UNDEF, insn) || */
- ARM_INSN_MATCH(AUNDEF, insn) ||
- ARM_INSN_MATCH(SWI, insn) ||
- ARM_INSN_MATCH(BREAK, insn) ||
- ARM_INSN_MATCH(BXJ, insn)) {
- goto bad_insn;
-#ifndef CONFIG_CPU_V7
- /* check instructions that can write result to PC */
- } else if ((ARM_INSN_MATCH(DPIS, insn) ||
- ARM_INSN_MATCH(DPRS, insn) ||
- ARM_INSN_MATCH(DPI, insn) ||
- ARM_INSN_MATCH(LIO, insn) ||
- ARM_INSN_MATCH(LRO, insn)) &&
- (ARM_INSN_REG_RD(insn) == 15)) {
- goto bad_insn;
-#endif /* CONFIG_CPU_V7 */
- /* check special instruction loads store multiple registers */
- } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) &&
- /* store PC or load to PC */
- (ARM_INSN_REG_MR(insn, 15) ||
- /* store/load with PC update */
- ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) {
- goto bad_insn;
- }
-
- return 0;
-
-bad_insn:
- return -EFAULT;
-}
-
-static int make_branch_tarmpoline(u32 addr, u32 insn, u32 *tramp)
-{
- int ok = 0;
-
- /* B */
- if (ARM_INSN_MATCH(B, insn) &&
- !ARM_INSN_MATCH(BLX1, insn)) {
- /* B check can be false positive on BLX1 instruction */
- memcpy(tramp, b_cond_insn_execbuf, sizeof(b_cond_insn_execbuf));
- tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
- tramp[0] |= insn & 0xf0000000;
- tramp[6] = get_addr_b(insn, addr);
- tramp[7] = addr + 4;
- ok = 1;
- /* BX, BLX (Rm) */
- } else if (ARM_INSN_MATCH(BX, insn) ||
- ARM_INSN_MATCH(BLX2, insn)) {
- memcpy(tramp, b_r_insn_execbuf, sizeof(b_r_insn_execbuf));
- tramp[0] = insn;
- tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
- tramp[7] = addr + 4;
- ok = 1;
- /* BL, BLX (Off) */
- } else if (ARM_INSN_MATCH(BLX1, insn)) {
- memcpy(tramp, blx_off_insn_execbuf, sizeof(blx_off_insn_execbuf));
- tramp[0] |= 0xe0000000;
- tramp[1] |= 0xe0000000;
- tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
- tramp[6] = get_addr_b(insn, addr) +
- 2 * (insn & 01000000) + 1; /* jump to thumb */
- tramp[7] = addr + 4;
- ok = 1;
- /* BL */
- } else if (ARM_INSN_MATCH(BL, insn)) {
- memcpy(tramp, blx_off_insn_execbuf, sizeof(blx_off_insn_execbuf));
- tramp[0] |= insn & 0xf0000000;
- tramp[1] |= insn & 0xf0000000;
- tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
- tramp[6] = get_addr_b(insn, addr);
- tramp[7] = addr + 4;
- ok = 1;
- }
-
- return ok;
-}
-
-/**
- * @brief Creates ARM trampoline.
- *
- * @param addr Probe address.
- * @param insn Instuction at this address.
- * @param tramp Pointer to memory for trampoline.
- * @return 0 on success, error code on error.
- */
-int make_trampoline_arm(u32 addr, u32 insn, u32 *tramp)
-{
- int ret, uregs, pc_dep;
-
- if (addr & 0x03) {
- pr_err("Error in %s at %d: attempt to register probe "
- "at an unaligned address\n", __FILE__, __LINE__);
- return -EINVAL;
- }
-
- ret = arch_check_insn_arm(insn);
- if (ret)
- return ret;
-
- if (make_branch_tarmpoline(addr, insn, tramp))
- return 0;
-
- uregs = pc_dep = 0;
- /* Rm */
- if (ARM_INSN_MATCH(CLZ, insn)) {
- uregs = 0xa;
- if (ARM_INSN_REG_RM(insn) == 15)
- pc_dep = 1;
- /* Rn, Rm ,Rd */
- } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) ||
- ARM_INSN_MATCH(SRO, insn)) {
- uregs = 0xb;
- if ((ARM_INSN_REG_RN(insn) == 15) ||
- (ARM_INSN_REG_RM(insn) == 15) ||
- (ARM_INSN_MATCH(SRO, insn) &&
- (ARM_INSN_REG_RD(insn) == 15))) {
- pc_dep = 1;
- }
- /* Rn ,Rd */
- } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) ||
- ARM_INSN_MATCH(SIO, insn)) {
- uregs = 0x3;
- if ((ARM_INSN_REG_RN(insn) == 15) ||
- (ARM_INSN_MATCH(SIO, insn) &&
- (ARM_INSN_REG_RD(insn) == 15))) {
- pc_dep = 1;
- }
- /* Rn, Rm, Rs */
- } else if (ARM_INSN_MATCH(DPRS, insn)) {
- uregs = 0xd;
- if ((ARM_INSN_REG_RN(insn) == 15) ||
- (ARM_INSN_REG_RM(insn) == 15) ||
- (ARM_INSN_REG_RS(insn) == 15)) {
- pc_dep = 1;
- }
- /* register list */
- } else if (ARM_INSN_MATCH(SM, insn)) {
- uregs = 0x10;
- if (ARM_INSN_REG_MR(insn, 15))
- pc_dep = 1;
- }
-
- /* check instructions that can write result to SP and uses PC */
- if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) {
- pr_err("Error in %s at %d: instruction check failed (arm)\n",
- __FILE__, __LINE__);
- return -EFAULT;
- }
-
- if (unlikely(uregs && pc_dep)) {
- memcpy(tramp, pc_dep_insn_execbuf, sizeof(pc_dep_insn_execbuf));
- if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) {
- pr_err("Error in %s at %d: failed "
- "to prepare exec buffer for insn %x!",
- __FILE__, __LINE__, insn);
- return -EINVAL;
- }
-
- tramp[6] = addr + 8;
- } else {
- memcpy(tramp, gen_insn_execbuf, sizeof(gen_insn_execbuf));
- tramp[PROBES_TRAMP_INSN_IDX] = insn;
- }
-
- /* TODO: remove for probe */
- tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
- tramp[7] = addr + 4;
-
- return 0;
-}
-
-int noret_arm(u32 opcode)
-{
- return !!(ARM_INSN_MATCH(BL, opcode) ||
- ARM_INSN_MATCH(BLX1, opcode) ||
- ARM_INSN_MATCH(BLX2, opcode));
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_PROBES_ARM_H
-#define _SWAP_ASM_PROBES_ARM_H
-
-
-int make_trampoline_arm(u32 addr, u32 insn, u32 *tramp);
-int noret_arm(u32 opcode);
-
-
-#endif /* _SWAP_ASM_PROBES_ARM_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/errno.h>
-#include <linux/string.h>
-#include "tramps_thumb.h"
-#include "decode_thumb.h"
-#include "decode_thumb_old.h"
-
-
-static inline s32 branch_t16_dest(u32 insn, unsigned int insn_addr)
-{
- s32 offset = insn & 0x3ff;
- offset -= insn & 0x400;
- return insn_addr + 4 + offset * 2;
-}
-
-static inline s32 branch_cond_t16_dest(u32 insn, unsigned int insn_addr)
-{
- s32 offset = insn & 0x7f;
- offset -= insn & 0x80;
- return insn_addr + 4 + offset * 2;
-}
-
-static inline s32 branch_t32_dest(u32 insn, unsigned int insn_addr)
-{
- unsigned int poff = insn & 0x3ff;
- unsigned int offset = (insn & 0x07fe0000) >> 17;
-
- poff -= (insn & 0x400);
-
- if (insn & (1 << 12))
- return insn_addr + 4 + (poff << 12) + offset * 4;
-
- return (insn_addr + 4 + (poff << 12) + offset * 4) & ~3;
-}
-
-static inline s32 cbz_t16_dest(u32 insn, unsigned int insn_addr)
-{
- unsigned int i = (insn & 0x200) >> 3;
- unsigned int offset = (insn & 0xf8) >> 2;
- return insn_addr + 4 + i + offset;
-}
-
-/* is instruction Thumb2 and NOT a branch, etc... */
-static int is_thumb2(u32 insn)
-{
- return ((insn & 0xf800) == 0xe800 ||
- (insn & 0xf800) == 0xf000 ||
- (insn & 0xf800) == 0xf800);
-}
-
-static int prep_pc_dep_insn_execbuf_thumb(u32 *insns, u32 insn, int uregs)
-{
- unsigned char mreg = 0;
- unsigned char reg = 0;
-
- if (THUMB_INSN_MATCH(APC, insn) ||
- THUMB_INSN_MATCH(LRO3, insn)) {
- reg = ((insn & 0xffff) & uregs) >> 8;
- } else if (THUMB_INSN_MATCH(MOV3, insn)) {
- if (((((unsigned char)insn) & 0xff) >> 3) == 15)
- reg = (insn & 0xffff) & uregs;
- else
- return 0;
- } else if (THUMB2_INSN_MATCH(ADR, insn)) {
- reg = ((insn >> 16) & uregs) >> 8;
- if (reg == 15)
- return 0;
- } else if (THUMB2_INSN_MATCH(LDRW, insn) ||
- THUMB2_INSN_MATCH(LDRW1, insn) ||
- THUMB2_INSN_MATCH(LDRHW, insn) ||
- THUMB2_INSN_MATCH(LDRHW1, insn) ||
- THUMB2_INSN_MATCH(LDRWL, insn)) {
- reg = ((insn >> 16) & uregs) >> 12;
- if (reg == 15)
- return 0;
- /*
- * LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped
- */
- } else if (THUMB2_INSN_MATCH(LDRBW, insn) ||
- THUMB2_INSN_MATCH(LDRBW1, insn) ||
- THUMB2_INSN_MATCH(LDREX, insn)) {
- reg = ((insn >> 16) & uregs) >> 12;
- } else if (THUMB2_INSN_MATCH(DP, insn)) {
- reg = ((insn >> 16) & uregs) >> 12;
- if (reg == 15)
- return 0;
- } else if (THUMB2_INSN_MATCH(RSBW, insn)) {
- reg = ((insn >> 12) & uregs) >> 8;
- if (reg == 15)
- return 0;
- } else if (THUMB2_INSN_MATCH(RORW, insn)) {
- reg = ((insn >> 12) & uregs) >> 8;
- if (reg == 15)
- return 0;
- } else if (THUMB2_INSN_MATCH(ROR, insn) ||
- THUMB2_INSN_MATCH(LSLW1, insn) ||
- THUMB2_INSN_MATCH(LSLW2, insn) ||
- THUMB2_INSN_MATCH(LSRW1, insn) ||
- THUMB2_INSN_MATCH(LSRW2, insn)) {
- reg = ((insn >> 12) & uregs) >> 8;
- if (reg == 15)
- return 0;
- } else if (THUMB2_INSN_MATCH(TEQ1, insn) ||
- THUMB2_INSN_MATCH(TST1, insn)) {
- reg = 15;
- } else if (THUMB2_INSN_MATCH(TEQ2, insn) ||
- THUMB2_INSN_MATCH(TST2, insn)) {
- reg = THUMB2_INSN_REG_RM(insn);
- }
-
- if ((THUMB2_INSN_MATCH(STRW, insn) ||
- THUMB2_INSN_MATCH(STRBW, insn) ||
- THUMB2_INSN_MATCH(STRD, insn) ||
- THUMB2_INSN_MATCH(STRHT, insn) ||
- THUMB2_INSN_MATCH(STRT, insn) ||
- THUMB2_INSN_MATCH(STRHW1, insn) ||
- THUMB2_INSN_MATCH(STRHW, insn)) &&
- THUMB2_INSN_REG_RT(insn) == 15) {
- reg = THUMB2_INSN_REG_RT(insn);
- }
-
- if (reg == 6 || reg == 7) {
- *((u16 *)insns + 0) =
- (*((u16 *)insns + 0) & 0x00ff) |
- ((1 << mreg) | (1 << (mreg + 1)));
- *((u16 *)insns + 1) =
- (*((u16 *)insns + 1) & 0xf8ff) | (mreg << 8);
- *((u16 *)insns + 2) =
- (*((u16 *)insns + 2) & 0xfff8) | (mreg + 1);
- *((u16 *)insns + 3) =
- (*((u16 *)insns + 3) & 0xffc7) | (mreg << 3);
- *((u16 *)insns + 7) =
- (*((u16 *)insns + 7) & 0xf8ff) | (mreg << 8);
- *((u16 *)insns + 8) =
- (*((u16 *)insns + 8) & 0xffc7) | (mreg << 3);
- *((u16 *)insns + 9) =
- (*((u16 *)insns + 9) & 0xffc7) | ((mreg + 1) << 3);
- *((u16 *)insns + 10) =
- (*((u16 *)insns + 10) & 0x00ff) |
- ((1 << mreg) | (1 << (mreg + 1)));
- }
-
- if (THUMB_INSN_MATCH(APC, insn)) {
- /* ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4 */
- *((u16 *)insns + 4) = ((insn & 0xffff) | 0x800);
- } else if (THUMB_INSN_MATCH(LRO3, insn)) {
- /* LDR Rd, [PC, #immed_8*4] ->
- * LDR Rd, [SP, #immed_8*4] */
- *((u16 *)insns + 4) = ((insn & 0xffff) + 0x5000);
- } else if (THUMB_INSN_MATCH(MOV3, insn)) {
- /* MOV Rd, PC -> MOV Rd, SP */
- *((u16 *)insns + 4) = ((insn & 0xffff) ^ 0x10);
- } else if (THUMB2_INSN_MATCH(ADR, insn)) {
- /* ADDW Rd,PC,#imm -> ADDW Rd,SP,#imm */
- insns[2] = (insn & 0xfffffff0) | 0x0d;
- } else if (THUMB2_INSN_MATCH(LDRW, insn) ||
- THUMB2_INSN_MATCH(LDRBW, insn) ||
- THUMB2_INSN_MATCH(LDRHW, insn)) {
- /* LDR.W Rt, [PC, #-<imm_12>] ->
- * LDR.W Rt, [SP, #-<imm_8>]
- * !!!!!!!!!!!!!!!!!!!!!!!!
- * !!! imm_12 vs. imm_8 !!!
- * !!!!!!!!!!!!!!!!!!!!!!!! */
- insns[2] = (insn & 0xf0fffff0) | 0x0c00000d;
- } else if (THUMB2_INSN_MATCH(LDRW1, insn) ||
- THUMB2_INSN_MATCH(LDRBW1, insn) ||
- THUMB2_INSN_MATCH(LDRHW1, insn) ||
- THUMB2_INSN_MATCH(LDRD, insn) ||
- THUMB2_INSN_MATCH(LDRD1, insn) ||
- THUMB2_INSN_MATCH(LDREX, insn)) {
- /* LDRx.W Rt, [PC, #+<imm_12>] ->
- * LDRx.W Rt, [SP, #+<imm_12>]
- (+/-imm_8 for LDRD Rt, Rt2, [PC, #<imm_8>] */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- } else if (THUMB2_INSN_MATCH(MUL, insn)) {
- /* MUL Rd, Rn, SP */
- insns[2] = (insn & 0xfff0ffff) | 0x000d0000;
- } else if (THUMB2_INSN_MATCH(DP, insn)) {
- if (THUMB2_INSN_REG_RM(insn) == 15)
- /* DP Rd, Rn, PC */
- insns[2] = (insn & 0xfff0ffff) | 0x000d0000;
- else if (THUMB2_INSN_REG_RN(insn) == 15)
- /* DP Rd, PC, Rm */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- } else if (THUMB2_INSN_MATCH(LDRWL, insn)) {
- /* LDRx.W Rt, [PC, #<imm_12>] ->
- * LDRx.W Rt, [SP, #+<imm_12>]
- * (+/-imm_8 for LDRD Rt, Rt2, [PC, #<imm_8>] */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- } else if (THUMB2_INSN_MATCH(RSBW, insn)) {
- /* RSB{S}.W Rd, PC, #<const> -> RSB{S}.W Rd, SP, #<const> */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- } else if (THUMB2_INSN_MATCH(RORW, insn) ||
- THUMB2_INSN_MATCH(LSLW1, insn) ||
- THUMB2_INSN_MATCH(LSRW1, insn)) {
- if ((THUMB2_INSN_REG_RM(insn) == 15) &&
- (THUMB2_INSN_REG_RN(insn) == 15))
- /* ROR.W Rd, PC, PC */
- insns[2] = (insn & 0xfffdfffd);
- else if (THUMB2_INSN_REG_RM(insn) == 15)
- /* ROR.W Rd, Rn, PC */
- insns[2] = (insn & 0xfff0ffff) | 0xd0000;
- else if (THUMB2_INSN_REG_RN(insn) == 15)
- /* ROR.W Rd, PC, Rm */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- } else if (THUMB2_INSN_MATCH(ROR, insn) ||
- THUMB2_INSN_MATCH(LSLW2, insn) ||
- THUMB2_INSN_MATCH(LSRW2, insn)) {
- /* ROR{S} Rd, PC, #<const> -> ROR{S} Rd, SP, #<const> */
- insns[2] = (insn & 0xfff0ffff) | 0xd0000;
- }
-
- if (THUMB2_INSN_MATCH(STRW, insn) ||
- THUMB2_INSN_MATCH(STRBW, insn)) {
- /* STRx.W Rt, [Rn, SP] */
- insns[2] = (insn & 0xfff0ffff) | 0x000d0000;
- } else if (THUMB2_INSN_MATCH(STRD, insn) ||
- THUMB2_INSN_MATCH(STRHT, insn) ||
- THUMB2_INSN_MATCH(STRT, insn) ||
- THUMB2_INSN_MATCH(STRHW1, insn)) {
- if (THUMB2_INSN_REG_RN(insn) == 15)
- /* STRD/T/HT{.W} Rt, [SP, ...] */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- else
- insns[2] = insn;
- } else if (THUMB2_INSN_MATCH(STRHW, insn) &&
- (THUMB2_INSN_REG_RN(insn) == 15)) {
- if (THUMB2_INSN_REG_RN(insn) == 15)
- /* STRH.W Rt, [SP, #-<imm_8>] */
- insns[2] = (insn & 0xf0fffff0) | 0x0c00000d;
- else
- insns[2] = insn;
- }
-
- /* STRx PC, xxx */
- if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn) ||
- THUMB2_INSN_MATCH(STRBW, insn) ||
- THUMB2_INSN_MATCH(STRD, insn) ||
- THUMB2_INSN_MATCH(STRHT, insn) ||
- THUMB2_INSN_MATCH(STRT, insn) ||
- THUMB2_INSN_MATCH(STRHW1, insn) ||
- THUMB2_INSN_MATCH(STRHW, insn))) {
- insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000;
- }
-
- if (THUMB2_INSN_MATCH(TEQ1, insn) ||
- THUMB2_INSN_MATCH(TST1, insn)) {
- /* TEQ SP, #<const> */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- } else if (THUMB2_INSN_MATCH(TEQ2, insn) ||
- THUMB2_INSN_MATCH(TST2, insn)) {
- if ((THUMB2_INSN_REG_RN(insn) == 15) &&
- (THUMB2_INSN_REG_RM(insn) == 15))
- /* TEQ/TST PC, PC */
- insns[2] = (insn & 0xfffdfffd);
- else if (THUMB2_INSN_REG_RM(insn) == 15)
- /* TEQ/TST Rn, PC */
- insns[2] = (insn & 0xfff0ffff) | 0xd0000;
- else if (THUMB2_INSN_REG_RN(insn) == 15)
- /* TEQ/TST PC, Rm */
- insns[2] = (insn & 0xfffffff0) | 0xd;
- }
-
- return 0;
-}
-
-static int arch_check_insn_thumb(u32 insn)
-{
- int ret = 0;
-
- /* check instructions that can change PC */
- if (THUMB_INSN_MATCH(UNDEF, insn) ||
- THUMB2_INSN_MATCH(BLX1, insn) ||
- THUMB2_INSN_MATCH(BL, insn) ||
- THUMB_INSN_MATCH(SWI, insn) ||
- THUMB_INSN_MATCH(BREAK, insn) ||
- THUMB2_INSN_MATCH(B1, insn) ||
- THUMB2_INSN_MATCH(B2, insn) ||
- THUMB2_INSN_MATCH(BXJ, insn) ||
- (THUMB2_INSN_MATCH(ADR, insn) &&
- THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RT(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRW1, insn) &&
- THUMB2_INSN_REG_RT(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRHW, insn) &&
- THUMB2_INSN_REG_RT(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRHW1, insn) &&
- THUMB2_INSN_REG_RT(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRWL, insn) &&
- THUMB2_INSN_REG_RT(insn) == 15) ||
- THUMB2_INSN_MATCH(LDMIA, insn) ||
- THUMB2_INSN_MATCH(LDMDB, insn) ||
- (THUMB2_INSN_MATCH(DP, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(RSBW, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(RORW, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(ROR, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(LSLW1, insn) &&
- THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(LSLW2, insn) &&
- THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(LSRW1, insn) &&
- THUMB2_INSN_REG_RD(insn) == 15) ||
- (THUMB2_INSN_MATCH(LSRW2, insn) &&
- THUMB2_INSN_REG_RD(insn) == 15) ||
- /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */
- (THUMB2_INSN_MATCH(STRW1, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(STRBW1, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(STRHW1, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(STRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(STRHW, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRBW, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_MATCH(LDRHW, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */
- (THUMB2_INSN_MATCH(LDRD, insn) || THUMB2_INSN_MATCH(LDRD1, insn) ||
- THUMB2_INSN_MATCH(STRD, insn))) {
- ret = -EFAULT;
- }
-
- return ret;
-}
-
-static int do_make_trampoline_thumb(u32 vaddr, u32 insn,
- u32 *tramp, size_t tramp_len)
-{
- int ret;
- int uregs = 0;
- int pc_dep = 0;
- unsigned int addr;
-
- ret = arch_check_insn_thumb(insn);
- if (ret)
- return ret;
-
- if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) {
- uregs = 0x0700; /* 8-10 */
- pc_dep = 1;
- } else if (THUMB_INSN_MATCH(MOV3, insn) &&
- (((((unsigned char)insn) & 0xff) >> 3) == 15)) {
- /* MOV Rd, PC */
- uregs = 0x07;
- pc_dep = 1;
- } else if THUMB2_INSN_MATCH(ADR, insn) {
- uregs = 0x0f00; /* Rd 8-11 */
- pc_dep = 1;
- } else if (((THUMB2_INSN_MATCH(LDRW, insn) ||
- THUMB2_INSN_MATCH(LDRW1, insn) ||
- THUMB2_INSN_MATCH(LDRBW, insn) ||
- THUMB2_INSN_MATCH(LDRBW1, insn) ||
- THUMB2_INSN_MATCH(LDRHW, insn) ||
- THUMB2_INSN_MATCH(LDRHW1, insn) ||
- THUMB2_INSN_MATCH(LDRWL, insn)) &&
- THUMB2_INSN_REG_RN(insn) == 15) ||
- THUMB2_INSN_MATCH(LDREX, insn) ||
- ((THUMB2_INSN_MATCH(STRW, insn) ||
- THUMB2_INSN_MATCH(STRBW, insn) ||
- THUMB2_INSN_MATCH(STRHW, insn) ||
- THUMB2_INSN_MATCH(STRHW1, insn)) &&
- (THUMB2_INSN_REG_RN(insn) == 15 ||
- THUMB2_INSN_REG_RT(insn) == 15)) ||
- ((THUMB2_INSN_MATCH(STRT, insn) ||
- THUMB2_INSN_MATCH(STRHT, insn)) &&
- (THUMB2_INSN_REG_RN(insn) == 15 ||
- THUMB2_INSN_REG_RT(insn) == 15))) {
- uregs = 0xf000; /* Rt 12-15 */
- pc_dep = 1;
- } else if ((THUMB2_INSN_MATCH(LDRD, insn) ||
- THUMB2_INSN_MATCH(LDRD1, insn)) &&
- (THUMB2_INSN_REG_RN(insn) == 15)) {
- uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */
- pc_dep = 1;
- } else if (THUMB2_INSN_MATCH(MUL, insn) &&
- THUMB2_INSN_REG_RM(insn) == 15) {
- uregs = 0xf;
- pc_dep = 1;
- } else if (THUMB2_INSN_MATCH(DP, insn) &&
- (THUMB2_INSN_REG_RN(insn) == 15 ||
- THUMB2_INSN_REG_RM(insn) == 15)) {
- uregs = 0xf000; /* Rd 12-15 */
- pc_dep = 1;
- } else if (THUMB2_INSN_MATCH(STRD, insn) &&
- ((THUMB2_INSN_REG_RN(insn) == 15) ||
- (THUMB2_INSN_REG_RT(insn) == 15) ||
- THUMB2_INSN_REG_RT2(insn) == 15)) {
- uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */
- pc_dep = 1;
- } else if (THUMB2_INSN_MATCH(RSBW, insn) &&
- THUMB2_INSN_REG_RN(insn) == 15) {
- uregs = 0x0f00; /* Rd 8-11 */
- pc_dep = 1;
- } else if (THUMB2_INSN_MATCH(RORW, insn) &&
- (THUMB2_INSN_REG_RN(insn) == 15 ||
- THUMB2_INSN_REG_RM(insn) == 15)) {
- uregs = 0x0f00;
- pc_dep = 1;
- } else if ((THUMB2_INSN_MATCH(ROR, insn) ||
- THUMB2_INSN_MATCH(LSLW2, insn) ||
- THUMB2_INSN_MATCH(LSRW2, insn)) &&
- THUMB2_INSN_REG_RM(insn) == 15) {
- uregs = 0x0f00; /* Rd 8-11 */
- pc_dep = 1;
- } else if ((THUMB2_INSN_MATCH(LSLW1, insn) ||
- THUMB2_INSN_MATCH(LSRW1, insn)) &&
- (THUMB2_INSN_REG_RN(insn) == 15 ||
- THUMB2_INSN_REG_RM(insn) == 15)) {
- uregs = 0x0f00; /* Rd 8-11 */
- pc_dep = 1;
- } else if ((THUMB2_INSN_MATCH(TEQ1, insn) ||
- THUMB2_INSN_MATCH(TST1, insn)) &&
- THUMB2_INSN_REG_RN(insn) == 15) {
- uregs = 0xf0000; /* Rn 0-3 (16-19) */
- pc_dep = 1;
- } else if ((THUMB2_INSN_MATCH(TEQ2, insn) ||
- THUMB2_INSN_MATCH(TST2, insn)) &&
- (THUMB2_INSN_REG_RN(insn) == 15 ||
- THUMB2_INSN_REG_RM(insn) == 15)) {
- uregs = 0xf0000; /* Rn 0-3 (16-19) */
- pc_dep = 1;
- }
-
- if (unlikely(uregs && pc_dep)) {
- memcpy(tramp, pc_dep_insn_execbuf_thumb, tramp_len);
- prep_pc_dep_insn_execbuf_thumb(tramp, insn, uregs);
-
- addr = vaddr + 4;
- *((u16 *)tramp + 13) = RET_BREAK_THUMB;
- *((u16 *)tramp + 14) = addr & 0x0000ffff;
- *((u16 *)tramp + 15) = addr >> 16;
- if (!is_thumb2(insn)) {
- addr = vaddr + 2;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
- } else {
- addr = vaddr + 4;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
- }
- } else {
- memcpy(tramp, gen_insn_execbuf_thumb, tramp_len);
- *((u16 *)tramp + 13) = RET_BREAK_THUMB;
- if (!is_thumb2(insn)) {
- addr = vaddr + 2;
- *((u16 *)tramp + 2) = insn;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
- } else {
- addr = vaddr + 4;
- tramp[1] = insn;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
- }
- }
-
- if (THUMB_INSN_MATCH(B2, insn)) {
- memcpy(tramp, b_off_insn_execbuf_thumb, tramp_len);
- *((u16 *)tramp + 13) = RET_BREAK_THUMB;
- addr = branch_t16_dest(insn, vaddr);
- *((u16 *)tramp + 14) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 15) = addr >> 16;
- *((u16 *)tramp + 16) = 0;
- *((u16 *)tramp + 17) = 0;
-
- } else if (THUMB_INSN_MATCH(B1, insn)) {
- memcpy(tramp, b_cond_insn_execbuf_thumb, tramp_len);
- *((u16 *)tramp + 13) = RET_BREAK_THUMB;
- *((u16 *)tramp + 0) |= (insn & 0xf00);
- addr = branch_cond_t16_dest(insn, vaddr);
- *((u16 *)tramp + 14) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 15) = addr >> 16;
- addr = vaddr + 2;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
-
- } else if (THUMB_INSN_MATCH(BLX2, insn) ||
- THUMB_INSN_MATCH(BX, insn)) {
- memcpy(tramp, b_r_insn_execbuf_thumb, tramp_len);
- *((u16 *)tramp + 13) = RET_BREAK_THUMB;
- *((u16 *)tramp + 4) = insn;
- addr = vaddr + 2;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
-
- } else if (THUMB_INSN_MATCH(CBZ, insn)) {
- memcpy(tramp, cbz_insn_execbuf_thumb, tramp_len);
- *((u16 *)tramp + 13) = RET_BREAK_THUMB;
- /* zero out original branch displacement (imm5 = 0; i = 0) */
- *((u16 *)tramp + 0) = insn & (~0x2f8);
- /* replace it with 8 bytes offset in execbuf (imm5 = 0b00010) */
- *((u16 *)tramp + 0) |= 0x20;
- addr = cbz_t16_dest(insn, vaddr);
- *((u16 *)tramp + 14) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 15) = addr >> 16;
- addr = vaddr + 2;
- *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
- *((u16 *)tramp + 17) = addr >> 16;
- }
-
- return 0;
-}
-
-int make_trampoline_thumb(struct arch_insn_arm *ainsn,
- u32 vaddr, u32 insn, u32 *tramp, size_t tramp_len)
-{
- int ret;
-
- ret = do_make_trampoline_thumb(vaddr, insn, tramp, tramp_len);
- if (ret) {
- struct decode_info info = {
- .vaddr = vaddr,
- .tramp = tramp,
- .handeler = NULL,
- };
-
- ret = decode_thumb(insn, &info);
- if (info.handeler) {
- u16 *tr = (u16 *)tramp;
- tr[13] = RET_BREAK_THUMB; /* bp for uretprobe */
- ainsn->handler = info.handeler;
- }
- }
-
- return ret;
-}
-
-int noret_thumb(u32 opcode)
-{
- return !!(THUMB2_INSN_MATCH(BL, opcode) ||
- THUMB2_INSN_MATCH(BLX1, opcode) ||
- THUMB_INSN_MATCH(BLX2, opcode));
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_PROBES_THUMB_H
-#define _SWAP_ASM_PROBES_THUMB_H
-
-
-int make_trampoline_thumb(struct arch_insn_arm *ainsn, u32 vaddr, u32 insn,
- u32 *tramp, size_t tramp_len);
-int noret_thumb(u32 opcode);
-
-
-#endif /* _SWAP_ASM_PROBES_THUMB_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-__asm(
-".text\n"
-".arm\n"
-".global gen_insn_execbuf\n"
-"gen_insn_execbuf:\n"
-" nop\n"
-" nop\n"
-" nop\n" /* original instruction */
-" nop\n"
-" ldr pc, [pc, #4]\n" /* ssbreak */
-" nop\n" /* retbreak */
-" nop\n"
-" nop\n" /* stored PC-4(next insn addr) */
-
-".global pc_dep_insn_execbuf\n"
-"pc_dep_insn_execbuf:\n"
-" str r0, [sp, #-4]\n"
-" ldr r0, [pc, #12]\n"
-" nop\n" /* instruction with replaced PC */
-" ldr r0, [sp, #-4]\n"
-" ldr pc, [pc, #4]\n" /* ssbreak */
-" nop\n" /* retbreak */
-" nop\n" /* stored PC */
-" nop\n" /* stored PC-4 (next insn addr) */
-
-".global b_r_insn_execbuf\n"
-"b_r_insn_execbuf:\n"
-" nop\n" /* bx, blx (Rm) */
-" ldr pc, np1\n"
-" nop\n"
-" nop\n"
-" nop\n"
-" nop\n" /* retbreak */
-" nop\n"
-"np1:\n"
-" nop\n" /* stored PC-4 (next insn addr) */
-
-".global b_cond_insn_execbuf\n"
-"b_cond_insn_execbuf:\n"
-" beq condway\n"
-" ldr pc, np2\n"
-"condway:\n"
-" ldr pc, bd2\n"
-" nop\n"
-" nop\n"
-" nop\n" /* retbreak */
-"bd2:\n"
-" nop\n" /* branch displacement */
-"np2:\n"
-" nop\n" /* stored PC-4 (next insn addr) */
-
-".global blx_off_insn_execbuf\n"
-"blx_off_insn_execbuf:\n"
-" ldreq lr, bd3\n"
-" blxeq lr\n"
-" ldr pc, np3\n"
-" nop\n"
-" nop\n"
-" nop\n" /* retbreak */
-"bd3:\n"
-" nop\n" /* branch displacement */
-"np3:\n"
-" nop\n" /* stored PC-4 (next insn addr) */
-);
+++ /dev/null
-/**
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM/MIPS
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
- * Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>:
- * redesign module for separating core and arch parts
- * @author Alexander Shirshikov <a.shirshikov@samsung.com>:
- * initial implementation for Thumb
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- */
-
-#ifndef _SWAP_ASM_TRAMPS_ARM_H
-#define _SWAP_ASM_TRAMPS_ARM_H
-
-
-#include <linux/types.h>
-
-
-/*
- * These arrays generated from tramps_arm.c
- * using 32 bit compiler:
- * $ gcc tramps_arm.c -c -o tramps_arm.o
- * $ objdump -d tramps_arm.o
- */
-
-static u32 __attribute__((unused)) gen_insn_execbuf[] = {
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe59ff004, // ldr pc, [pc, #4]
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
-};
-
-static u32 __attribute__((unused)) pc_dep_insn_execbuf[] = {
- 0xe50d0004, // str r0, [sp, #-4]
- 0xe59f000c, // ldr r0, [pc, #12]
- 0xe320f000, // nop
- 0xe51d0004, // ldr r0, [sp, #-4]
- 0xe59ff004, // ldr pc, [pc, #4]
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
-};
-
-static u32 __attribute__((unused)) b_r_insn_execbuf[] = {
- 0xe320f000, // nop
- 0xe59ff010, // ldr pc, [pc, #16]
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
-};
-
-static u32 __attribute__((unused)) b_cond_insn_execbuf[] = {
- 0x0a000000, // beq 68 <condway>
- 0xe59ff010, // ldr pc, [pc, #16]
- 0xe59ff008, // ldr pc, [pc, #8]
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
-};
-
-static u32 __attribute__((unused)) blx_off_insn_execbuf[] = {
- 0x059fe010, // ldreq lr, [pc, #16]
- 0x012fff3e, // blxeq lr
- 0xe59ff00c, // ldr pc, [pc, #12]
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
- 0xe320f000, // nop
-};
-
-
-#endif /* _SWAP_ASM_TRAMPS_ARM_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-__asm(
-".text\n"
-".thumb\n"
-
-".global gen_insn_execbuf_thumb\n"
-"gen_insn_execbuf_thumb:\n"
- "nop\n"
- "nop\n"
- "nop\n" /* original instruction */
- "nop\n" /* original instruction */
- "nop\n"
- "nop\n"
- "nop\n"
- "sub sp, sp, #8\n"
- "str r0, [sp, #0]\n"
- "ldr r0, [pc, #12]\n"
- "str r0, [sp, #4]\n"
- "nop\n"
- "pop {r0, pc}\n" /* ssbreak */
- "nop\n" /* retbreak */
- "nop\n"
- "nop\n"
- "nop\n" /* stored PC-4(next insn addr) hi */
- "nop\n" /* stored PC-4(next insn addr) lo */
-
- "nop\n"
-
-".global pc_dep_insn_execbuf_thumb\n"
-".align 4\n"
-"pc_dep_insn_execbuf_thumb:\n"
- "push {r6, r7}\n"
- "ldr r6, i1\n"
- "mov r7, sp\n"
- "mov sp, r6\n"
- "nop\n" /* PC -> SP */
- "nop\n" /* PC -> SP */
- "mov sp, r7\n"
- "pop {r6, r7}\n"
- "push {r0, r1}\n"
- "ldr r0, i2\n"
- "nop\n"
- "str r0, [sp, #4]\n"
- "pop {r0, pc}\n" /* ssbreak */
- "nop\n" /* retbreak */
-"i1:\n"
- "nop\n" /* stored PC hi */
- "nop\n" /* stored PC lo */
-"i2:\n"
- "nop\n" /* stored PC-4(next insn addr) hi */
- "nop\n" /* stored PC-4(next insn addr) lo */
-
-".global b_r_insn_execbuf_thumb\n"
-".align 4\n"
-"b_r_insn_execbuf_thumb:\n"
- "nop\n"
- "nop\n"
- "nop\n"
- "nop\n"
- "nop\n" /* bx,blx (Rm) */
- "nop\n"
- "push {r0,r1}\n"
- "ldr r0, np\n"
- "nop\n"
- "str r0, [sp, #4]\n"
- "pop {r0,pc}\n"
- "nop\n"
- "nop\n" /* ssbreak */
- "nop\n" /* retbreak */
- "nop\n"
- "nop\n"
-"np:\n"
- "nop\n" /* stored PC-4(next insn addr) hi */
- "nop\n" /* stored PC-4(next insn addr) lo */
-
-".global b_off_insn_execbuf_thumb\n"
-".align 4\n"
-"b_off_insn_execbuf_thumb:\n"
- "push {r0,r1}\n"
- "ldr r0, bd\n"
- "str r0, [sp, #4]\n"
- "pop {r0, pc}\n"
- "nop\n"
- "nop\n"
- "push {r0,r1}\n"
- "ldr r0, np2\n"
- "nop\n"
- "str r0, [sp, #4]\n"
- "pop {r0,pc}\n"
- "nop\n"
- "nop\n" /* ssbreak */
- "nop\n" /* retbreak */
-"bd:\n"
- "nop\n" /* branch displacement hi */
- "nop\n" /* branch displacement lo */
-"np2:\n"
- "nop\n" /* stored PC-4(next insn addr) hi */
- "nop\n" /* stored PC-4(next insn addr) lo */
-
-".global b_cond_insn_execbuf_thumb\n"
-".align 4\n"
-"b_cond_insn_execbuf_thumb:\n"
- "beq condway\n"
- "push {r0,r1}\n"
- "ldr r0, np4\n"
- "nop\n"
- "str r0, [sp, #4]\n"
- "pop {r0,pc}\n"
-"condway:\n"
- "push {r0,r1}\n"
- "ldr r0, bd4\n"
- "str r0, [sp, #4]\n"
- "pop {r0,pc}\n"
- "nop\n"
- "nop\n"
- "nop\n" /* ssbreak */
- "nop\n" /* retbreak */
-"bd4:\n"
- "nop\n" /* branch displacement hi */
- "nop\n" /* branch displacement lo */
-"np4:\n"
- "nop\n" /* stored PC-4(next insn addr) hi */
- "nop\n" /* stored PC-4(next insn addr) lo */
-
-".global cbz_insn_execbuf_thumb\n"
-".align 4\n"
-"cbz_insn_execbuf_thumb:\n"
- "nop\n" /* cbz */
- "push {r0,r1}\n"
- "ldr r0, np5\n"
- "nop\n"
- "str r0, [sp, #4]\n"
- "pop {r0,pc}\n"
- "push {r0,r1}\n"
- "ldr r0, bd5\n"
- "str r0, [sp, #4]\n"
- "pop {r0,pc}\n"
- "nop\n"
- "nop\n"
- "nop\n" /* ssbreak */
- "nop\n" /* retbreak */
-"bd5:\n"
- "nop\n" /* branch displacement hi */
- "nop\n" /* branch displacement lo */
-"np5:\n"
- "nop\n" /* stored PC-4(next insn addr) hi */
- "nop\n" /* stored PC-4(next insn addr) lo */
-);
+++ /dev/null
-/**
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- */
-
-
-#ifndef _SWAP_ASM_TRAMPS_THUMB_H
-#define _SWAP_ASM_TRAMPS_THUMB_H
-
-
-#include <linux/types.h>
-
-
-/*
- * These arrays generated from tramps_thumb.c
- * using 32 bit compiler:
- * $ gcc tramps_thumb.c -c -o tramps_thumb.o
- * $ objdump -d tramps_thumb.o
- */
-
-static u16 __attribute__((unused)) gen_insn_execbuf_thumb[] = {
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xb082, // sub sp, #8
- 0x9000, // str r0, [sp, #0]
- 0x4803, // ldr r0, [pc, #12]
- 0x9001, // str r0, [sp, #4]
- 0xbf00, // nop
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
-};
-
-static u16 __attribute__((unused)) pc_dep_insn_execbuf_thumb[] = {
- 0xb4c0, // push {r6, r7}
- 0x4e06, // ldr r6, [pc, #24]
- 0x466f, // mov r7, sp
- 0x46b5, // mov sp, r6
- 0xbf00, // nop
- 0xbf00, // nop
- 0x46bd, // mov sp, r7
- 0xbcc0, // pop {r6, r7}
- 0xb403, // push {r0, r1}
- 0x4803, // ldr r0, [pc, #12]
- 0xbf00, // nop
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
-};
-
-static u16 __attribute__((unused)) b_r_insn_execbuf_thumb[] = {
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xb403, // push {r0, r1}
- 0x4804, // ldr r0, [pc, #16]
- 0xbf00, // nop
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
-};
-
-static u16 __attribute__((unused)) b_off_insn_execbuf_thumb[] = {
- 0xb403, // push {r0, r1}
- 0x4806, // ldr r0, [pc, #24]
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xb403, // push {r0, r1}
- 0x4804, // ldr r0, [pc, #16]
- 0xbf00, // nop
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
-};
-
-static u16 __attribute__((unused)) b_cond_insn_execbuf_thumb[] = {
- 0xf000, 0x8005, // beq.w ce <condway>
- 0xb403, // push {r0, r1}
- 0x4807, // ldr r0, [pc, #28]
- 0xbf00, // nop
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xb403, // push {r0, r1}
- 0xf8df, 0x000c, // ldr.w r0, [pc, #12]
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
-};
-
-static u16 __attribute__((unused)) cbz_insn_execbuf_thumb[] = {
- 0xbf00, // nop
- 0xb403, // push {r0, r1}
- 0x4806, // ldr r0, [pc, #24]
- 0xbf00, // nop
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xb403, // push {r0, r1}
- 0x4803, // ldr r0, [pc, #12]
- 0x9001, // str r0, [sp, #4]
- 0xbd01, // pop {r0, pc}
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
- 0xbf00, // nop
-};
-
-
-#endif /* _SWAP_ASM_TRAMPS_THUMB_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-
-#include <arch/arm/probes/probes.h>
-#include <arch/arm/probes/probes_arm.h>
-#include <arch/arm/probes/probes_thumb.h>
-#include <uprobe/swap_uprobes.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <kprobe/swap_slots.h>
-#include "../probes/compat_arm64.h"
-
-
-#define PTR_TO_U32(x) ((u32)(unsigned long)(x))
-#define U32_TO_PTR(x) ((void *)(unsigned long)(x))
-
-#define flush_insns(addr, size) \
- flush_icache_range((unsigned long)(addr), \
- (unsigned long)(addr) + (size))
-
-
-/**
- * @brief Prepares uprobe for ARM.
- *
- * @param up Pointer to the uprobe.
- * @return 0 on success,\n
- * negative error code on error.
- */
-int arch_prepare_uprobe_arm(struct uprobe *p)
-{
- int ret;
- struct task_struct *task = p->task;
- unsigned long vaddr = (unsigned long)p->addr;
- u32 insn;
- u32 tramp[UPROBES_TRAMP_LEN];
- u32 __user *utramp;
- enum { tramp_len = sizeof(tramp) };
-
- if (!read_proc_vm_atomic(task, vaddr & ~1, &insn, sizeof(insn))) {
- printk(KERN_ERR "failed to read memory %lx!\n", vaddr);
- return -EINVAL;
- }
-
- ret = make_tramp(&p->ainsn.insn, vaddr, insn, tramp, tramp_len);
- if (ret) {
- pr_err("failed to make tramp, addr=%p\n", p->addr);
- return ret;
- }
-
- utramp = swap_slot_alloc(p->sm);
- if (utramp == NULL) {
- printk(KERN_INFO "Error: swap_slot_alloc failed (%08lx)\n",
- vaddr);
- return -ENOMEM;
- }
-
- if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp,
- tramp_len)) {
- pr_err("failed to write memory tramp=%p!\n", utramp);
- swap_slot_free(p->sm, utramp);
- return -EINVAL;
- }
-
- flush_insns(utramp, tramp_len);
- p->insn = utramp;
- p->opcode = insn;
-
- return 0;
-}
-
-int arch_arm_uprobe_arm(struct uprobe *p)
-{
- int ret;
- unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
- int thumb_mode = (unsigned long)p->addr & 1;
- int len = 4 >> thumb_mode; /* if thumb_mode then len = 2 */
- unsigned long insn = thumb_mode ? BREAK_THUMB : BREAK_ARM;
-
- ret = write_proc_vm_atomic(p->task, vaddr, &insn, len);
- if (!ret) {
- pr_err("failed to write memory tgid=%u addr=%08lx len=%d\n",
- p->task->tgid, vaddr, len);
-
- return -EACCES;
- } else {
- flush_insns(vaddr, len);
- }
-
- return 0;
-}
-
-void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task)
-{
- int ret;
-
- unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
- int thumb_mode = (unsigned long)p->addr & 1;
- int len = 4 >> thumb_mode; /* if thumb_mode then len = 2 */
-
- ret = write_proc_vm_atomic(task, vaddr, &p->opcode, len);
- if (!ret) {
- pr_err("Failed to write memory tgid=%u addr=%08lx len=%d\n",
- task->tgid, vaddr, len);
- } else {
- flush_insns(vaddr, len);
- }
-}
-
-/**
- * @brief Prepates uretprobe for ARM.
- *
- * @param ri Pointer to the uretprobe instance.
- * @param regs Pointer to CPU register data.
- * @return Error code.
- */
-int prepare_uretprobe_arm(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- unsigned long thumb, bp_offset;
-
- thumb = ri->preload.use ? ri->preload.thumb : thumb_mode(regs);
- bp_offset = thumb ? 0x1b : 4 * PROBES_TRAMP_RET_BREAK_IDX;
-
- /* save original return address */
- ri->ret_addr = (uprobe_opcode_t *)regs->ARM_lr;
-
- /* replace return address with break point adddress */
- regs->ARM_lr = (unsigned long)(ri->rp->up.insn) + bp_offset;
-
- /* save stack pointer address */
- ri->sp = (uprobe_opcode_t *)regs->ARM_sp;
-
- /* Set flag of current mode */
- ri->sp = (uprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
-
- return 0;
-}
-
-/**
- * @brief Restores return address.
- *
- * @param orig_ret_addr Original return address.
- * @param regs Pointer to CPU register data.
- * @return Void.
- */
-void set_orig_ret_addr_arm(unsigned long orig_ret_addr, struct pt_regs *regs)
-{
- regs->ARM_lr = orig_ret_addr;
- regs->ARM_pc = orig_ret_addr & ~0x1;
-
- if (regs->ARM_lr & 0x1)
- regs->ARM_cpsr |= PSR_T_BIT;
- else
- regs->ARM_cpsr &= ~PSR_T_BIT;
-}
-
-void arch_opcode_analysis_uretprobe_arm(struct uretprobe *rp)
-{
- /* Remove retprobe if first insn overwrites lr */
- rp->thumb_noret = noret_thumb(rp->up.opcode);
- rp->arm_noret = noret_arm(rp->up.opcode);
-}
-
-unsigned long arch_get_trampoline_addr_arm(struct uprobe *p,
- struct pt_regs *regs)
-{
- return thumb_mode(regs) ?
- PTR_TO_U32(p->insn) + 0x1b :
- PTR_TO_U32(p->insn +
- PROBES_TRAMP_RET_BREAK_IDX);
-}
-
-unsigned long arch_tramp_by_ri_arm(struct uretprobe_instance *ri)
-{
- /* Understand function mode */
- return (PTR_TO_U32(ri->sp) & 1) ?
- PTR_TO_U32(ri->rp->up.insn) + 0x1b :
- PTR_TO_U32(ri->rp->up.insn +
- PROBES_TRAMP_RET_BREAK_IDX);
-}
-
-int arch_disarm_urp_inst_arm(struct uretprobe_instance *ri,
- struct task_struct *task)
-{
- struct pt_regs *uregs = task_pt_regs(ri->task);
- u32 ra = uregs->ARM_lr;
- u32 vaddr, tramp, found = 0;
- u32 sp = PTR_TO_U32(ri->sp) & ~1;
- u32 ret_addr = PTR_TO_U32(ri->ret_addr);
- u32 stack = sp - 4 * (URETPROBE_STACK_DEPTH + 1);
- u32 buf[URETPROBE_STACK_DEPTH];
- int i, ret;
-
- vaddr = PTR_TO_U32(ri->rp->up.addr);
- tramp = arch_tramp_by_ri_arm(ri);
-
- /* check stack */
- ret = read_proc_vm_atomic(task, stack, buf, sizeof(buf));
- if (ret != sizeof(buf)) {
- pr_info("---> %s (%d/%d): failed to read stack from %08x\n",
- task->comm, task->tgid, task->pid, stack);
- ret = -EFAULT;
- goto check_lr;
- }
-
- /* search the stack from the bottom */
- for (i = URETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
- if (buf[i] == tramp) {
- found = stack + 4 * i;
- break;
- }
- }
-
- if (!found) {
- ret = -ESRCH;
- goto check_lr;
- }
-
- pr_info("---> %s (%d/%d): trampoline found at "
- "%08x (%08x /%+d) - %x, set ret_addr=%08x\n",
- task->comm, task->tgid, task->pid,
- found, sp,
- found - sp, vaddr, ret_addr);
- ret = write_proc_vm_atomic(task, found, &ret_addr, 4);
- if (ret != 4) {
- pr_info("---> %s (%d/%d): failed to write value to %08x",
- task->comm, task->tgid, task->pid, found);
- ret = -EFAULT;
- } else {
- ret = 0;
- }
-
-check_lr: /* check lr anyway */
- if (ra == tramp) {
- pr_info("---> %s (%d/%d): trampoline found at "
- "lr = %08x - %x, set ret_addr=%08x\n",
- task->comm, task->tgid, task->pid, ra, vaddr,
- ret_addr);
-
- /* set ret_addr */
- uregs->ARM_lr = ret_addr;
- ret = 0;
- } else if (ret) {
- pr_info("---> %s (%d/%d): trampoline NOT found at "
- "sp=%08x, lr=%08x - %x, ret_addr=%08x\n",
- task->comm, task->tgid, task->pid,
- sp, ra, vaddr, ret_addr);
- }
-
- return ret;
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_ARM_UPROBE_H
-#define _SWAP_ASM_ARM_UPROBE_H
-
-
-#include <linux/printk.h>
-#include <linux/uaccess.h>
-#include "../probes/compat_arm64.h"
-
-
-struct pt_regs;
-struct uprobe;
-struct uretprobe;
-struct uretprobe_instance;
-
-
-static inline unsigned long swap_get_upc_arm(struct pt_regs *regs)
-{
- return regs->ARM_pc | !!thumb_mode(regs);
-}
-
-static inline void swap_set_upc_arm(struct pt_regs *regs, unsigned long val)
-{
- if (val & 1) {
- regs->ARM_pc = val & ~1UL;
- regs->ARM_cpsr |= PSR_T_BIT;
- } else {
- regs->ARM_pc = val;
- regs->ARM_cpsr &= ~PSR_T_BIT;
- }
-}
-
-static inline unsigned long swap_get_uarg_arm(struct pt_regs *regs,
- unsigned long n)
-{
- u32 *ptr, val = 0;
-
- switch (n) {
- case 0:
- return regs->ARM_r0;
- case 1:
- return regs->ARM_r1;
- case 2:
- return regs->ARM_r2;
- case 3:
- return regs->ARM_r3;
- default:
- ptr = (u32 *)regs->ARM_sp + n - 4;
- if (get_user(val, ptr))
- pr_err("Failed to dereference a pointer[%p]\n", ptr);
- break;
- }
-
- return val;
-}
-
-static inline void swap_put_uarg_arm(struct pt_regs *regs, unsigned long n,
- unsigned long val)
-{
- u32 *ptr;
-
- switch (n) {
- case 0:
- regs->ARM_r0 = val;
- break;
- case 1:
- regs->ARM_r1 = val;
- break;
- case 2:
- regs->ARM_r2 = val;
- break;
- case 3:
- regs->ARM_r3 = val;
- break;
- default:
- ptr = (u32 *)regs->ARM_sp + n - 4;
- if (put_user(val, ptr))
- pr_err("Failed to dereference a pointer[%p]\n", ptr);
- }
-}
-
-static inline unsigned long swap_get_uret_addr_arm(struct pt_regs *regs)
-{
- return regs->ARM_lr;
-}
-
-static inline void swap_set_uret_addr_arm(struct pt_regs *regs, unsigned long v)
-{
- regs->ARM_lr = v;
-}
-
-int arch_prepare_uprobe_arm(struct uprobe *p);
-int arch_arm_uprobe_arm(struct uprobe *p);
-void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task);
-
-int prepare_uretprobe_arm(struct uretprobe_instance *ri, struct pt_regs *regs);
-void set_orig_ret_addr_arm(unsigned long orig_ret_addr, struct pt_regs *regs);
-void arch_opcode_analysis_uretprobe_arm(struct uretprobe *rp);
-unsigned long arch_get_trampoline_addr_arm(struct uprobe *p,
- struct pt_regs *regs);
-unsigned long arch_tramp_by_ri_arm(struct uretprobe_instance *ri);
-int arch_disarm_urp_inst_arm(struct uretprobe_instance *ri,
- struct task_struct *task);
-
-
-#endif /* _SWAP_ASM_ARM_UPROBE_H */
-
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_buffer.o
-swap_buffer-y := swap_buffer_module.o \
- buffer_queue.o
-
+++ /dev/null
-/**
- * @file buffer/buffer_description.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * swap_subbuffer structure represents one buffers subbufer
- */
-
-#ifndef __BUFFER_DESCRIPTION_H__
-#define __BUFFER_DESCRIPTION_H__
-
-#include "data_types.h"
-
-/**
- * @struct swap_subbuffer
- * @brief This structures are combined in array which represents the SWAP buffer.
- * @var swap_subbuffer::next_in_queue
- * Pointer to the next swap_subbufer in queue
- * @var swap_subbuffer::full_buffer_part
- * Currently occupied subbuffers size
- * @var swap_subbuffer::data_buffer
- * Pointer to subbuffers data itself of type swap_subbuffer_ptr
- * @var swap_subbuffer::buffer_sync
- * Subbuffers sync primitive
- */
-struct swap_subbuffer {
- /* Pointer to the next subbuffer in queue */
- struct swap_subbuffer *next_in_queue;
- /* Size of the filled part of a subbuffer */
- size_t full_buffer_part;
- /* Pointer to data buffer */
- swap_subbuffer_ptr data_buffer;
- /* Buffer rw sync */
- struct sync_t buffer_sync;
-};
-
-#endif /* __BUFFER_DESCRIPTION_H__ */
+++ /dev/null
-/**
- * buffer/buffer_queue.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Implements buffers queues interface
- */
-
-/* For all memory allocation/deallocation operations, except buffer memory
- * allocation/deallocation should be used
- * memory_allocation(size_t memory_size)
- * memory_free(void *ptr)
- * defines.
- * For subbuffer allocation/deallocation operations should be used
- * buffer_allocation(size_t subbuffer_size)
- * buffer_free(void *ptr, size_t subbuffer_size)
- * To get buffer pointer for any usage, EXCEPT ALLOCATION AND DEALLOCATION
- * use the following define:
- * buffer_pointer(void *ptr_to_buffer_element_of_swap_buffer_structure)
- * DO NOT USE SUBBUFFER PTR IN STRUCT SWAP_BUFFER WITHOUT THIS DEFINE!
- * It will be ok for user space, but fail in kernel space.
- *
- * See space_dep_types_and_def.h for details */
-
-
-
-#include "buffer_queue.h"
-#include "swap_buffer_to_buffer_queue.h"
-#include "swap_buffer_errors.h"
-#include "kernel_operations.h"
-
-/**
- * @struct queue_t
- * @brief Queue structure. Consist of pointers to the first and the last
- * elements of queue.
- * @var queue_t::start_ptr
- * Pointer to the first subbuffer in queue
- * @var queue_t::end_ptr
- * Pointer to the last subbuffer in queue
- * @var queue_t::subbuffers_count
- * Subbuffers count in queue
- * @var queue_t::queue_sync
- * Queue access sync primitive
- */
-struct queue_t {
- struct swap_subbuffer *start_ptr;
- struct swap_subbuffer *end_ptr;
- unsigned int subbuffers_count;
- struct sync_t queue_sync;
-};
-
-/**
- * @var write_queue
- * @brief Represents write queue.
- */
-struct queue_t write_queue = {
- .start_ptr = NULL,
- .end_ptr = NULL,
- .subbuffers_count = 0,
- .queue_sync = {
- .flags = 0x0
- }
-};
-
-/**
- * @var read_queue
- * @brief Represents read queue.
- */
-struct queue_t read_queue = {
- .start_ptr = NULL,
- .end_ptr = NULL,
- .subbuffers_count = 0,
- .queue_sync = {
- .flags = 0x0
- }
-};
-
-/* Pointers array. Points to busy buffers */
-static struct swap_subbuffer **queue_busy;
-
-/* Store last busy element */
-static unsigned int queue_busy_last_element;
-
-/* Subbuffers count */
-static unsigned int queue_subbuffer_count;
-
-/* One subbuffer size */
-static size_t queue_subbuffer_size;
-
-/* Busy list sync */
-static struct sync_t buffer_busy_sync = {
- .flags = 0x0
-};
-
-/* Memory pages count in one subbuffer */
-static int pages_order_in_subbuffer;
-
-/**
- * @brief Allocates memory for swap_subbuffer structures and subbuffers.
- * Total allocated memory = subbuffer_size * subbuffers_count.
- *
- * @param subbuffer_size Size of each subbuffer.
- * @param subbuffers_count Count of subbuffers.
- * @return 0 on success, negative error code otherwise.
- */
-int buffer_queue_allocation(size_t subbuffer_size,
- unsigned int subbuffers_count)
-{
- unsigned int i = 0;
- unsigned int j = 0;
- unsigned int allocated_buffers = 0;
- unsigned int allocated_structs = 0;
- struct swap_subbuffer *clean_tmp_struct;
- int result;
-
- /* Static varibles initialization */
- queue_subbuffer_size = subbuffer_size;
- queue_subbuffer_count = subbuffers_count;
- queue_busy_last_element = 0;
-
- /* Set variable pages_in_subbuffer. It is used for allocation and
- * deallocation memory pages and its value is returned from
- * swap_buffer_get() and contains page count in one subbuffer.
- * All this useful only in kernel space. In userspace it is dummy.*/
- set_pages_order_in_subbuffer(queue_subbuffer_size);
- /* Sync primitives initialization */
- sync_init(&read_queue.queue_sync);
- sync_init(&write_queue.queue_sync);
- sync_init(&buffer_busy_sync);
-
- /* Memory allocation for queue_busy */
- queue_busy =
- memory_allocation(sizeof(*queue_busy) * queue_subbuffer_count);
-
- if (!queue_busy) {
- result = -E_SB_NO_MEM_QUEUE_BUSY;
- goto buffer_allocation_error_ret;
- }
-
- /* Memory allocation for swap_subbuffer structures */
-
- /* Allocation for first structure. */
- write_queue.start_ptr =
- memory_allocation(sizeof(*write_queue.start_ptr));
-
- if (!write_queue.start_ptr) {
- result = -E_SB_NO_MEM_BUFFER_STRUCT;
- goto buffer_allocation_queue_busy_free;
- }
- allocated_structs++;
-
-
- write_queue.end_ptr = write_queue.start_ptr;
-
- write_queue.end_ptr->next_in_queue = NULL;
- write_queue.end_ptr->full_buffer_part = 0;
- write_queue.end_ptr->data_buffer =
- buffer_allocation(queue_subbuffer_size);
- if (!write_queue.end_ptr->data_buffer) {
- print_err("Cannot allocate memory for buffer 1\n");
- result = -E_SB_NO_MEM_DATA_BUFFER;
- goto buffer_allocation_error_free;
- }
- allocated_buffers++;
-
- sync_init(&write_queue.end_ptr->buffer_sync);
-
- /* Buffer initialization */
- memset(buffer_address(write_queue.end_ptr->data_buffer), 0,
- queue_subbuffer_size);
-
- /* Allocation for other structures. */
- for (i = 1; i < queue_subbuffer_count; i++) {
- write_queue.end_ptr->next_in_queue =
- memory_allocation(
- sizeof(*write_queue.end_ptr->next_in_queue));
- if (!write_queue.end_ptr->next_in_queue) {
- result = -E_SB_NO_MEM_BUFFER_STRUCT;
- goto buffer_allocation_error_free;
- }
- allocated_structs++;
-
- /* Now next write_queue.end_ptr is next */
- write_queue.end_ptr = write_queue.end_ptr->next_in_queue;
-
- write_queue.end_ptr->next_in_queue = NULL;
- write_queue.end_ptr->full_buffer_part = 0;
- write_queue.end_ptr->data_buffer =
- buffer_allocation(queue_subbuffer_size);
- if (!write_queue.end_ptr->data_buffer) {
- result = -E_SB_NO_MEM_DATA_BUFFER;
- goto buffer_allocation_error_free;
- }
- allocated_buffers++;
-
- sync_init(&write_queue.end_ptr->buffer_sync);
-
- /* Buffer initialization */
- memset(buffer_address(write_queue.end_ptr->data_buffer), 0,
- queue_subbuffer_size);
- }
-
- /* All subbuffers are in write list */
- write_queue.subbuffers_count = subbuffers_count;
-
- return E_SB_SUCCESS;
-
- /* In case of errors, this code is called */
- /* Free all previously allocated memory */
-buffer_allocation_error_free:
- clean_tmp_struct = write_queue.start_ptr;
-
- for (j = 0; j < allocated_structs; j++) {
- clean_tmp_struct = write_queue.start_ptr;
- if (allocated_buffers) {
- buffer_free(clean_tmp_struct->data_buffer,
- queue_subbuffer_size);
- allocated_buffers--;
- }
- if (write_queue.start_ptr != write_queue.end_ptr)
- write_queue.start_ptr =
- write_queue.start_ptr->next_in_queue;
- memory_free(clean_tmp_struct);
- }
- write_queue.end_ptr = NULL;
- write_queue.start_ptr = NULL;
-
-buffer_allocation_queue_busy_free:
- memory_free(queue_busy);
- queue_busy = NULL;
-
-buffer_allocation_error_ret:
- return result;
-}
-
-/**
- * @brief Resets all subbuffers for writing.
- *
- * @return 0 on success, negative error code otherwise.
- */
-int buffer_queue_reset(void)
-{
- struct swap_subbuffer *buffer = read_queue.start_ptr;
-
- /* Check if there are some subbuffers in busy list.
- * If so - return error */
- if (get_busy_buffers_count())
- return -E_SB_UNRELEASED_BUFFERS;
-
- /* Lock read sync primitive */
- sync_lock(&read_queue.queue_sync);
-
- /* Set all subbuffers in read list to write list
- * and reinitialize them */
- while (read_queue.start_ptr) {
-
- /* Lock buffer sync primitive to prevent writing to buffer if it
- * had been selected for writing, but still wasn't wrote. */
- sync_lock(&buffer->buffer_sync);
-
- buffer = read_queue.start_ptr;
-
- /* If we reached end of the list */
- if (read_queue.start_ptr == read_queue.end_ptr)
- read_queue.end_ptr = NULL;
-
- read_queue.start_ptr = read_queue.start_ptr->next_in_queue;
-
- /* Reinit full buffer part */
- buffer->full_buffer_part = 0;
-
- add_to_write_list(buffer);
-
- /* Unlock buffer sync primitive */
- sync_unlock(&buffer->buffer_sync);
- }
-
- /* Unlock read primitive */
- sync_unlock(&read_queue.queue_sync);
-
- return E_SB_SUCCESS;
-}
-
-/**
- * @brief Free all allocated subbuffers.
- *
- * @return Void.
- */
-void buffer_queue_free(void)
-{
- struct swap_subbuffer *tmp = NULL;
-
- /* Lock all sync primitives to prevet accessing free memory */
- sync_lock(&write_queue.queue_sync);
- sync_lock(&read_queue.queue_sync);
- sync_lock(&buffer_busy_sync);
-
- /* Free buffers and structures memory that are in read list */
- while (read_queue.start_ptr) {
- tmp = read_queue.start_ptr;
- read_queue.start_ptr = read_queue.start_ptr->next_in_queue;
- buffer_free(tmp->data_buffer, queue_subbuffer_size);
- memory_free(tmp);
- }
-
- /* Free buffers and structures memory that are in read list */
- while (write_queue.start_ptr) {
- tmp = write_queue.start_ptr;
- write_queue.start_ptr = write_queue.start_ptr->next_in_queue;
- buffer_free(tmp->data_buffer, queue_subbuffer_size);
- memory_free(tmp);
- }
-
- /* Free busy_list */
- memory_free(queue_busy);
- queue_busy = NULL;
-
- queue_subbuffer_size = 0;
- queue_subbuffer_count = 0;
- read_queue.start_ptr = NULL;
- read_queue.end_ptr = NULL;
- write_queue.start_ptr = NULL;
- write_queue.end_ptr = NULL;
-
- /* Unlock all sync primitives */
- sync_unlock(&buffer_busy_sync);
- sync_unlock(&read_queue.queue_sync);
- sync_unlock(&write_queue.queue_sync);
-}
-
-static unsigned int is_buffer_enough(struct swap_subbuffer *subbuffer,
- size_t size)
-{
- /* XXX Think about checking full_buffer_part for correctness
- * (<queue_subbuffer_size). It should be true, but if isn't (due to
- * sources chaning, etc.) this function should be true! */
- return ((queue_subbuffer_size-subbuffer->full_buffer_part) >= size) ?
- 1 : 0;
-}
-
-static void next_queue_element(struct queue_t *queue)
-{
- /* If we reached the last elemenet, end pointer should point to NULL */
- if (queue->start_ptr == queue->end_ptr)
- queue->end_ptr = NULL;
-
- queue->start_ptr = queue->start_ptr->next_in_queue;
- --queue->subbuffers_count;
-}
-
-/**
- * @brief Get first subbuffer from read list.
- *
- * @return Pointer to swap_subbuffer
- */
-struct swap_subbuffer *get_from_read_list(void)
-{
- struct swap_subbuffer *result = NULL;
-
- /* Lock read sync primitive */
- sync_lock(&read_queue.queue_sync);
-
- if (read_queue.start_ptr == NULL) {
- result = NULL;
- goto get_from_read_list_unlock;
- }
-
- result = read_queue.start_ptr;
-
- next_queue_element(&read_queue);
-
-get_from_read_list_unlock:
- /* Unlock read sync primitive */
- sync_unlock(&read_queue.queue_sync);
-
- return result;
-}
-
-/**
- * @brief Add subbuffer to read list.
- *
- * @param subbuffer Pointer to the subbuffer to add.
- * @return Void.
- */
-void add_to_read_list(struct swap_subbuffer *subbuffer)
-{
- /* Lock read sync primitive */
- sync_lock(&read_queue.queue_sync);
-
- if (!read_queue.start_ptr)
- read_queue.start_ptr = subbuffer;
-
- if (read_queue.end_ptr) {
- read_queue.end_ptr->next_in_queue = subbuffer;
-
- read_queue.end_ptr = read_queue.end_ptr->next_in_queue;
- } else {
- read_queue.end_ptr = subbuffer;
- }
- read_queue.end_ptr->next_in_queue = NULL;
- ++read_queue.subbuffers_count;
-
- /* Unlock read sync primitive */
- sync_unlock(&read_queue.queue_sync);
-}
-
-static int add_to_read_list_with_callback(struct swap_subbuffer *subbuffer,
- bool wakeup)
-{
- int result = 0;
-
- add_to_read_list(subbuffer);
- /* TODO Handle ret value */
- result = swap_buffer_callback(subbuffer, wakeup);
-
- return result;
-}
-
-/**
- * @brief Returns subbuffers to read count.
- *
- * @return Count of subbuffers in read_queue.
- */
-unsigned int get_readable_buf_cnt(void)
-{
- return read_queue.subbuffers_count;
-}
-
-
-/**
- * @brief Get first writable subbuffer from write list.
- *
- * @param size Minimum amount of free space in subbuffer.
- * @param[out] ptr_to_write Pointer to the variable where pointer to the
- * beginning of memory for writing should be stored.
- * @return Found swap_subbuffer.
- */
-struct swap_subbuffer *get_from_write_list(size_t size, void **ptr_to_write,
- bool wakeup)
-{
- struct swap_subbuffer *result = NULL;
-
- /* Callbacks are called at the end of the function
- * to prevent deadlocks */
- struct queue_t callback_queue = {
- .start_ptr = NULL,
- .end_ptr = NULL,
- .queue_sync = {
- .flags = 0x0
- }
- };
- struct swap_subbuffer *tmp_buffer = NULL;
-
- /* Init pointer */
- *ptr_to_write = NULL;
-
- /* Lock write list sync primitive */
- sync_lock(&write_queue.queue_sync);
-
- while (write_queue.start_ptr) {
-
- /* We're found subbuffer */
- if (is_buffer_enough(write_queue.start_ptr, size)) {
-
- result = write_queue.start_ptr;
- *ptr_to_write =
- (void *)((unsigned long)
- (buffer_address(result->data_buffer)) +
- result->full_buffer_part);
-
- /* Add data size to full_buffer_part.
- * Very important to do it in
- * write_queue.queue_sync spinlock */
- write_queue.start_ptr->full_buffer_part += size;
-
- /* Lock rw sync.
- * Should be unlocked in swap_buffer_write() */
- sync_lock_no_flags(&result->buffer_sync);
- break;
- /* This subbuffer is not enough => it goes to read list */
- } else {
- result = write_queue.start_ptr;
-
- next_queue_element(&write_queue);
-
- /* Add to callback list */
- if (!callback_queue.start_ptr)
- callback_queue.start_ptr = result;
-
- if (callback_queue.end_ptr)
- callback_queue.end_ptr->next_in_queue = result;
- callback_queue.end_ptr = result;
- callback_queue.end_ptr->next_in_queue = NULL;
- result = NULL;
- }
- }
-
- /* Unlock write list sync primitive */
- sync_unlock(&write_queue.queue_sync);
-
- /* Adding buffers to read list and calling callbacks */
- for (tmp_buffer = NULL; callback_queue.start_ptr; ) {
- if (callback_queue.start_ptr == callback_queue.end_ptr)
- callback_queue.end_ptr = NULL;
-
- tmp_buffer = callback_queue.start_ptr;
- callback_queue.start_ptr =
- callback_queue.start_ptr->next_in_queue;
-
- add_to_read_list_with_callback(tmp_buffer, wakeup);
- }
-
- return result;
-}
-
-/**
- * @brief Add subbuffer to write list.
- *
- * @param subbuffer Pointer to the swap_subbuffer that should be stored.
- * @return Void.
- */
-void add_to_write_list(struct swap_subbuffer *subbuffer)
-{
- sync_lock(&write_queue.queue_sync);
-
- /* Reinitialize */
- subbuffer->full_buffer_part = 0;
-
- if (!write_queue.start_ptr)
- write_queue.start_ptr = subbuffer;
-
- if (write_queue.end_ptr) {
- write_queue.end_ptr->next_in_queue = subbuffer;
- write_queue.end_ptr = write_queue.end_ptr->next_in_queue;
- } else {
- write_queue.end_ptr = subbuffer;
- }
- write_queue.end_ptr->next_in_queue = NULL;
- ++write_queue.subbuffers_count;
-
- sync_unlock(&write_queue.queue_sync);
-}
-
-/**
- * @brief Returns subbuffers to write count.
- *
- * @return Count of subbuffers in write queue.
- */
-unsigned int get_writable_buf_cnt(void)
-{
- return write_queue.subbuffers_count;
-}
-
-
-/**
- * @brief Add subbuffer to busy list when it is read from out of the buffer.
- *
- * @param subbuffer Pointer to the swap_subbuffer that should be added.
- * @return Void.
- */
-void add_to_busy_list(struct swap_subbuffer *subbuffer)
-{
- /* Lock busy sync primitive */
- sync_lock(&buffer_busy_sync);
-
- subbuffer->next_in_queue = NULL;
- queue_busy[queue_busy_last_element] = subbuffer;
- queue_busy_last_element += 1;
-
- /* Unlock busy sync primitive */
- sync_unlock(&buffer_busy_sync);
-}
-
-/**
- * @brief Remove subbuffer from busy list when it is released.
- *
- * @param subbuffer Pointer to the swap_subbuffer that should be removed.
- * @return 0 on success, negative error code otherwise.
- */
-int remove_from_busy_list(struct swap_subbuffer *subbuffer)
-{
- int result = -E_SB_NO_SUBBUFFER_IN_BUSY; /* For sanitization */
- int i;
-
- /* Lock busy list sync primitive */
- sync_lock(&buffer_busy_sync);
-
- /* Sanitization and removing */
- for (i = 0; i < queue_busy_last_element; i++) {
- if (queue_busy[i] == subbuffer) {
- /* Last element goes here and length is down 1 */
- queue_busy[i] = queue_busy[queue_busy_last_element - 1];
- queue_busy_last_element -= 1;
- result = E_SB_SUCCESS;
- break;
- }
- }
-
- /* Unlock busy list sync primitive */
- sync_unlock(&buffer_busy_sync);
-
- return result;
-}
-
-/**
- * @brief Set all subbuffers in write list to read list.
- *
- * @return Void.
- */
-void buffer_queue_flush(void)
-{
- struct swap_subbuffer *buffer = write_queue.start_ptr;
-
- /* Locking write sync primitive */
- sync_lock(&write_queue.queue_sync);
-
- while (write_queue.start_ptr &&
- write_queue.start_ptr->full_buffer_part) {
-
- /* Lock buffer sync primitive to prevent writing to buffer if it
- * had been selected for writing, but still wasn't wrote. */
- sync_lock(&buffer->buffer_sync);
-
- buffer = write_queue.start_ptr;
- next_queue_element(&write_queue);
- add_to_read_list(buffer);
-
- /* Unlock buffer sync primitive */
- sync_unlock(&buffer->buffer_sync);
- }
-
- /* Unlock write primitive */
- sync_unlock(&write_queue.queue_sync);
-}
-
-/**
- * @brief Get subbuffers count in busy list.
- *
- * @return Count of swap_subbuffers in busy list.
- */
-int get_busy_buffers_count(void)
-{
- int result;
-
- sync_lock(&buffer_busy_sync);
- result = queue_busy_last_element;
- sync_unlock(&buffer_busy_sync);
-
- return result;
-}
-
-/**
- * @brief Get memory pages count in subbuffer.
- *
- * @return Pages count in subbuffer.
- */
-int get_pages_count_in_subbuffer(void)
-{
-/* Return 1 if pages order 0,
- * or 2 of power pages_order_in_subbuffer otherwise */
- return (pages_order_in_subbuffer) ?
- 2 << (pages_order_in_subbuffer - 1) : 1;
-}
+++ /dev/null
-/**
- * @file buffer/buffer_queue.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Represents buffers queues interface
- */
-
-/* SWAP Buffer queues interface */
-
-#ifndef __BUFFER_QUEUE_H__
-#define __BUFFER_QUEUE_H__
-
-#include <linux/types.h>
-#include "buffer_description.h"
-
-int buffer_queue_allocation(size_t subbuffer_size,
- unsigned int subbuffers_count);
-void buffer_queue_free(void);
-int buffer_queue_reset(void);
-void buffer_queue_flush(void);
-struct swap_subbuffer *get_from_write_list(size_t size, void **ptr_to_write,
- bool wakeup);
-struct swap_subbuffer *get_from_read_list(void);
-void add_to_write_list(struct swap_subbuffer *subbuffer);
-void add_to_read_list(struct swap_subbuffer *subbuffer);
-void add_to_busy_list(struct swap_subbuffer *subbuffer);
-int remove_from_busy_list(struct swap_subbuffer *subbuffer);
-
-unsigned int get_readable_buf_cnt(void);
-unsigned int get_writable_buf_cnt(void);
-int get_busy_buffers_count(void);
-int get_pages_count_in_subbuffer(void);
-
-#endif /* __BUFFER_QUEUE_H__ */
+++ /dev/null
-/**
- * @file buffer/data_types.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Declares data types for SWAP buffer.
- */
-
-#ifndef __DATA_TYPES_H__
-#define __DATA_TYPES_H__
-
-
-#include <linux/spinlock.h>
-
-
-struct page;
-
-/**
- * @struct sync_t
- * @brief Using spinlocks as sync primitives.
- * @var sync_t::spinlock
- * Spinlock.
- * @var sync_t::flags
- * Flags for spinlock.
- */
-struct sync_t {
- spinlock_t spinlock;
- unsigned long flags;
-};
-
-/**
- * @brief swap_subbuffer_ptr points to the first memory page of the subbuffer.
- */
-typedef struct page *swap_subbuffer_ptr;
-
-#endif /* __DATA_TYPES_H__ */
+++ /dev/null
-/**
- * @file buffer/kernel_operations.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Kernel functions wrap.
- */
-
-#ifndef __KERNEL_OPERATIONS_H__
-#define __KERNEL_OPERATIONS_H__
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/semaphore.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/gfp.h>
-#include <linux/mm.h>
-
-#include "data_types.h"
-
-
-/* MESSAGES */
-/** Prints debug message.*/
-#define print_debug(msg, args...) \
- printk(KERN_DEBUG "SWAP_BUFFER DEBUG : " msg, ##args)
-/** Prints info message.*/
-#define print_msg(msg, args...) \
- printk(KERN_INFO "SWAP_BUFFER : " msg, ##args)
-/** Prints warning message.*/
-#define print_warn(msg, args...) \
- printk(KERN_WARNING "SWAP_BUFFER WARNING : " msg, ##args)
-/** Prints error message.*/
-#define print_err(msg, args...) \
- printk(KERN_ERR "SWAP_BUFFER ERROR : " msg, ##args)
-/** Prints critical error message.*/
-#define print_crit(msg, args...) \
- printk(KERN_CRIT "SWAP_BUFFER CRITICAL : " msg, ##args)
-
-
-/**
- * @brief struct sync_t initialization.
- *
- * @param buffer_sync Target sync primitive.
- * @return Void.
- */
-static inline void sync_init(struct sync_t *buffer_sync)
-{
- spin_lock_init(&buffer_sync->spinlock);
-}
-
-/**
- * @brief Lock sync_t with saving flags.
- *
- * @param buffer_sync Target sync primitive.
- * @return Void.
- */
-static inline void sync_lock(struct sync_t *buffer_sync)
-{
- spin_lock_irqsave(&buffer_sync->spinlock, buffer_sync->flags);
-}
-
-/**
- * @brief Unlock sync_t with restoring flags.
- *
- * @param buffer_sync Target sync primitive.
- * @return Void.
- */
-static inline void sync_unlock(struct sync_t *buffer_sync)
-{
- spin_unlock_irqrestore(&buffer_sync->spinlock, buffer_sync->flags);
-}
-
-/**
- * @brief Lock sync_t without saving flags.
- *
- * @param buffer_sync Target sync primitive.
- * @return Void.
- */
-static inline void sync_lock_no_flags(struct sync_t *buffer_sync)
-{
- spin_lock(&buffer_sync->spinlock);
-}
-
-/**
- * @brief Unlock sync_t without restoring flags.
- *
- * @param buffer_sync Target sync primitive.
- * @return Void.
- */
-static inline void sync_unlock_no_flags(struct sync_t *buffer_sync)
-{
- spin_unlock(&buffer_sync->spinlock);
-}
-
-/**
- * @brief Disable preemption and irqs.
- *
- * @param flags Variable to save flags to.
- * @return Void.
- */
-static inline void swap_irq_disable(unsigned long *flags)
-{
- preempt_disable();
- local_irq_save(*flags);
-}
-
-/**
- * @brief Enable preemption and irqs.
- *
- * @param flags Variable to restore flags from.
- * @return Void.
- */
-static inline void swap_irq_enable(unsigned long *flags)
-{
- local_irq_restore(*flags);
- preempt_enable();
-}
-
-/* SWAP SUBBUFER */
-
-
-/* We alloc memory for swap_subbuffer structures with common kmalloc */
-/** Allocates memory for subbuffer structures.*/
-#define memory_allocation(memory_size) kmalloc(memory_size, GFP_KERNEL)
-/** Free subbuffer structures memory.*/
-#define memory_free(ptr) kfree(ptr)
-
-/** For subbuffers themselves, we allocate memory with alloc_pages, so, we have
- * to evaluate required pages order */
-#define buffer_allocation(memory_size) \
- alloc_pages(GFP_KERNEL, (pages_order_in_subbuffer >= 0) ? \
- pages_order_in_subbuffer : \
- get_order_for_alloc_pages(memory_size))
-
-/** Free buffer's memory.*/
-#define buffer_free(ptr, subbuf_size) \
- __free_pages(ptr, (pages_order_in_subbuffer >= 0) ? \
- pages_order_in_subbuffer : \
- get_order_for_alloc_pages(subbuf_size))
-
-/** Returns buffer address.*/
-#define buffer_address(buffer_ptr) page_address(buffer_ptr)
-/** Sets page order in subbuffer.*/
-#define set_pages_order_in_subbuffer(memory_size) \
- pages_order_in_subbuffer = get_order_for_alloc_pages(memory_size)
-
-/**
- * @brief Functions for pages allocation.
- *
- * @param number Target number.
- * @return Power of two.
- */
-static inline unsigned int nearest_power_of_two(unsigned int number)
-{
- unsigned int result = 0;
- unsigned int two_to_the_power = 1;
-
- /* If aligned_size == PAGE_SIZE we need only one page, so return 0 */
- if (number == 1)
- return result;
-
- while (two_to_the_power < number) {
- two_to_the_power <<= 1;
- result++;
- }
-
- return result;
-}
-
-/**
- * @brief Order for alloc pages.
- *
- * @param memory_size Wishful memory size.
- * @return Pages order.
- */
-static inline unsigned int get_order_for_alloc_pages(size_t memory_size)
-{
- /* First evaluate remainder of the division memory_size by PAGE_SIZE.
- * If memory_size is divisible by PAGE_SIZE, then remainder equals 0. */
- size_t remainder = (memory_size % PAGE_SIZE) ?
- (memory_size % PAGE_SIZE) : PAGE_SIZE;
-
- /* Align memory_size to the PAGE_SIZE. aligned_size >= memory_size */
- size_t aligned_size = memory_size + (PAGE_SIZE - remainder);
-
- return nearest_power_of_two(aligned_size / PAGE_SIZE);
-}
-
-#endif /* __KERNEL_OPERATIONS_H__ */
+++ /dev/null
-/**
- * @file buffer/swap_buffer_errors.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP Buffer error codes enumeration.
- */
-
-#ifndef __SWAP_BUFFER_ERRORS_H__
-#define __SWAP_BUFFER_ERRORS_H__
-
-/**
- * @enum _swap_buffer_errors
- * @brief SWAP buffer errors enumeration.
- */
-enum _swap_buffer_errors {
- /**
- * @brief Success.
- */
- E_SB_SUCCESS = 0,
- /**
- * @brief There are some unreleased buffers.
- * Mainly returned by swap_buffer_uninit.
- */
- E_SB_UNRELEASED_BUFFERS = 1,
- /**
- * @brief No buffers for writing.
- */
- E_SB_NO_WRITABLE_BUFFERS = 2,
- /**
- * @brief Wrong data size: size == 0 or size > subbuffer size.
- */
- E_SB_WRONG_DATA_SIZE = 3,
- /**
- * @brief Trying to write data after SWAP buffer has been stopped.
- */
- E_SB_IS_STOPPED = 4,
- /**
- * @brief Memory areas of data to be written and subbuffer itself
- * are overlap.
- */
- E_SB_OVERLAP = 5,
- /**
- * @brief No buffers for reading.
- */
- E_SB_NO_READABLE_BUFFERS = 6,
- /**
- * @brief Callback function ptr == NULL.
- */
- E_SB_NO_CALLBACK = 7,
- /**
- * @brief Memory for queue_busy wasn't allocated.
- */
- E_SB_NO_MEM_QUEUE_BUSY = 8,
- /**
- * @brief Memory for one of struct swap_buffer wasn't allocated.
- */
- E_SB_NO_MEM_BUFFER_STRUCT = 9,
- /**
- * @brief Memort for data buffer itself wasn't allocated.
- */
- E_SB_NO_MEM_DATA_BUFFER = 10,
- /**
- * @brief No such subbuffer in busy_list.
- */
- E_SB_NO_SUBBUFFER_IN_BUSY = 11,
- /**
- * @brief Subbuffers aren't allocated.
- */
- E_SB_NOT_ALLOC = 12,
- /**
- * @brief Thresholds > 100, top < lower.
- */
- E_SB_WRONG_THRESHOLD = 13
-};
-
-#endif /* __SWAP_BUFFER_ERRORS_H__ */
+++ /dev/null
-/**
- * buffer/swap_buffer_module.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP Buffer interface implementation.
- */
-
-#include "swap_buffer_module.h"
-#include "buffer_queue.h"
-#include "buffer_description.h"
-#include "swap_buffer_errors.h"
-#include "kernel_operations.h"
-
-/**
- * @enum _swap_buffer_status_mask
- * @brief Bitwise mask for buffer status.
- */
-enum _swap_buffer_status_mask {
- BUFFER_FREE = 0, /**< 000 - memory free. */
- BUFFER_ALLOC = 1, /**< 001 - memory allocated. */
- BUFFER_PAUSE = 2, /**< 010 - buffer overflow. */
- BUFFER_WORK = 4 /**< @brief 100 - buffer work. */
-};
-
-static unsigned char swap_buffer_status = BUFFER_FREE;
-
-/**
- * @brief Subbuffer callback type.
- */
-typedef int(*subbuffer_callback_type)(bool wakeup);
-
-/* Callback that is called when full subbuffer appears */
-static subbuffer_callback_type subbuffer_callback;
-
-/* One subbuffer size */
-static size_t subbuffers_size;
-
-/* Subbuffers count */
-static unsigned int subbuffers_num;
-
-static unsigned int enough_writable_bufs;
-static unsigned int min_writable_bufs;
-static int (*low_mem_cb)(void);
-static int (*enough_mem_cb)(void);
-
-
-static inline int areas_overlap(const void *area1,
- const void *area2,
- size_t size)
-{
- int i;
-
- for (i = 0; i < size; i++)
- if ((area1 + i == area2) || (area2 + i == area1))
- return 1;
-
- return 0;
-}
-
-static inline unsigned int percent_to_count(unsigned char percent,
- unsigned int cnt)
-{
- return (percent * cnt) / 100;
-}
-
-/**
- * @brief Initializes SWAP buffer and allocates memory.
- *
- * @param buf_init Pointer to the buffer_init_t structure which contains
- * information about subbuffers count, subbuffers size and subbuffer-full-
- * callback.
- * @return 0 on success, negative error code otherwise.
- */
-int swap_buffer_init(struct buffer_init_t *buf_init)
-{
- int result = -1;
-
- swap_buffer_status &= ~BUFFER_WORK;
- print_debug("status buffer stop = %d\n", swap_buffer_status);
-
- if ((buf_init->top_threshold > 100) ||
- (buf_init->lower_threshold > 100) ||
- (buf_init->top_threshold < buf_init->lower_threshold))
- return -E_SB_WRONG_THRESHOLD;
-
- min_writable_bufs = percent_to_count(buf_init->lower_threshold,
- buf_init->nr_subbuffers);
-
- enough_writable_bufs = percent_to_count(buf_init->top_threshold,
- buf_init->nr_subbuffers);
-
- low_mem_cb = buf_init->low_mem_cb;
- enough_mem_cb = buf_init->enough_mem_cb;
-
- if ((swap_buffer_status & BUFFER_ALLOC) &&
- (subbuffers_size == buf_init->subbuffer_size) &&
- (subbuffers_num == buf_init->nr_subbuffers) &&
- ((subbuffer_callback_type)subbuffer_callback ==
- buf_init->subbuffer_full_cb)) {
- result = buffer_queue_reset();
- goto swap_buffer_init_work;
- }
-
- subbuffer_callback = buf_init->subbuffer_full_cb;
- subbuffers_size = buf_init->subbuffer_size;
- subbuffers_num = buf_init->nr_subbuffers;
-
- result = buffer_queue_allocation(subbuffers_size, subbuffers_num);
- if (result < 0)
- return result;
-
- result = get_pages_count_in_subbuffer();
-
- swap_buffer_status |= BUFFER_ALLOC;
- print_debug("status buffer alloc = %d\n", swap_buffer_status);
-
-swap_buffer_init_work:
- swap_buffer_status |= BUFFER_WORK;
- print_debug("status buffer work = %d\n", swap_buffer_status);
-
- return result;
-}
-EXPORT_SYMBOL_GPL(swap_buffer_init);
-
-/**
- * @brief Uninitializes SWAP buffer, releases allocated memory.
- *
- * @return 0 on success, negative error code otherwise.
- */
-int swap_buffer_uninit(void)
-{
- /* Check whether buffer is allocated */
- if (!(swap_buffer_status & BUFFER_ALLOC))
- return -E_SB_NOT_ALLOC;
-
- /* Stop buffer */
- swap_buffer_status &= ~BUFFER_WORK;
- print_debug("status buffer stop = %d\n", swap_buffer_status);
-
- /* Check whether all buffers are released */
- if (get_busy_buffers_count())
- return -E_SB_UNRELEASED_BUFFERS;
-
- /* Free */
- buffer_queue_free();
-
- subbuffer_callback = NULL;
- subbuffers_size = 0;
- subbuffers_num = 0;
- min_writable_bufs = 0;
- enough_writable_bufs = 0;
- low_mem_cb = NULL;
- enough_mem_cb = NULL;
-
- swap_buffer_status &= ~BUFFER_ALLOC;
- print_debug("status buffer dealloc = %d\n", swap_buffer_status);
-
- return E_SB_SUCCESS;
-}
-EXPORT_SYMBOL_GPL(swap_buffer_uninit);
-
-/**
- * @brief Writes data to SWAP buffer.
- *
- * @param data Pointer to a data for writing.
- * @param size Size of a data for writing.
- * @return Size of written data on success, negative error code otherwise.
- */
-ssize_t swap_buffer_write(void *data, size_t size, bool wakeup)
-{
- int result = E_SB_SUCCESS;
- struct swap_subbuffer *buffer_to_write = NULL;
- void *ptr_to_write = NULL;
- unsigned long flags = 0;
-
- /* Size sanitization */
- if ((size > subbuffers_size) || (size == 0))
- return -E_SB_WRONG_DATA_SIZE;
-
- /* Check buffer status */
- if (!(swap_buffer_status & BUFFER_WORK))
- return -E_SB_IS_STOPPED;
-
- /* We're going to look for writable buffer, so disable irqs */
- swap_irq_disable(&flags);
-
- /* Get next write buffer and occupying semaphore */
- buffer_to_write = get_from_write_list(size, &ptr_to_write, wakeup);
- if (!buffer_to_write) {
- swap_irq_enable(&flags);
- return -E_SB_NO_WRITABLE_BUFFERS;
- }
-
- /* Check for overlapping */
- if (areas_overlap(ptr_to_write, data, size)) {
- result = -E_SB_OVERLAP;
- goto buf_write_sem_post;
- }
-
- /* Copy data to buffer */
- /* XXX Think of using memmove instead - useless, anyway overlapping
- * means that something went wrong. */
- memcpy(ptr_to_write, data, size);
-
- result = size;
-
- if ((get_writable_buf_cnt() < min_writable_bufs) &&
- !(swap_buffer_status & BUFFER_PAUSE)) {
- swap_buffer_status |= BUFFER_PAUSE;
- if (low_mem_cb != NULL)
- low_mem_cb();
- }
-
- /* Unlock sync (Locked in get_from_write_list()) and enable irqs */
-buf_write_sem_post:
- sync_unlock_no_flags(&buffer_to_write->buffer_sync);
- swap_irq_enable(&flags);
-
- return result;
-}
-EXPORT_SYMBOL_GPL(swap_buffer_write);
-
-/**
- * @brief Gets pointer to subbuffer for reading.
- *
- * @param[out] subbuffer Pointer to a variable which points on target subbuffer.
- * @return 0 on success, negative error code otherwise.
- */
-int swap_buffer_get(struct swap_subbuffer **subbuffer)
-{
- int result = 0;
- struct swap_subbuffer *buffer_to_read = NULL;
-
- /* Check buffer status */
- if (!(swap_buffer_status & BUFFER_WORK))
- return -E_SB_IS_STOPPED;
-
- /* Get next read buffer */
- buffer_to_read = get_from_read_list();
- if (!buffer_to_read)
- return -E_SB_NO_READABLE_BUFFERS;
-
- /* Add to busy list */
- buffer_to_read->next_in_queue = NULL;
- add_to_busy_list(buffer_to_read);
-
- *subbuffer = buffer_to_read;
-
- result = get_pages_count_in_subbuffer();
-
- return result;
-}
-EXPORT_SYMBOL_GPL(swap_buffer_get);
-
-/**
- * @brief Releases subbuffer after reading.
- *
- * @param subbuffer Subbuffer that should be released.
- * @return 0 on success, negative error code otherwise.
- */
-int swap_buffer_release(struct swap_subbuffer **subbuffer)
-{
- int result;
-
- /* Remove from busy list (includes sanitization) */
- result = remove_from_busy_list(*subbuffer);
- if (result < 0)
- return result;
-
- /* Add to write list */
- add_to_write_list(*subbuffer);
-
- if ((swap_buffer_status & BUFFER_PAUSE) &&
- (get_writable_buf_cnt() >= enough_writable_bufs)) {
- swap_buffer_status &= ~BUFFER_PAUSE;
- if (enough_mem_cb != NULL)
- enough_mem_cb();
- }
-
- return E_SB_SUCCESS;
-}
-EXPORT_SYMBOL_GPL(swap_buffer_release);
-
-/**
- * @brief Sets all subbuffers for reading.
- *
- * @return Count of subbeffers for reading.
- */
-unsigned int swap_buffer_flush(void)
-{
- unsigned int result;
-
- /* Set all non-empty write buffers to read list */
- buffer_queue_flush();
-
- /* Get count of all full buffers */
- result = get_readable_buf_cnt();
-
- return result;
-}
-EXPORT_SYMBOL_GPL(swap_buffer_flush);
-
-/**
- * @brief Executes subbuffer-full-callback.
- *
- * @param buffer Pointer to the full subbuffer.
- * @return -E_SB_NO_CALLBACK if no callback is registered or callbacks ret
- * value otherwise.
- */
-int swap_buffer_callback(void *buffer, bool wakeup)
-{
- int result;
-
- if (!subbuffer_callback)
- return -E_SB_NO_CALLBACK;
-
- result = subbuffer_callback(wakeup);
- if (result < 0)
- print_err("Callback error! Error code: %d\n", result);
-
- return result;
-}
-
-static int __init swap_buffer_module_init(void)
-{
- printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n");
- return E_SB_SUCCESS;
-}
-
-static void __exit swap_buffer_module_exit(void)
-{
- if (swap_buffer_status & BUFFER_ALLOC)
- swap_buffer_uninit();
- printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n");
-}
-
-module_init(swap_buffer_module_init);
-module_exit(swap_buffer_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP buffer module");
-MODULE_AUTHOR("Aksenov A.S.");
+++ /dev/null
-/**
- * @file buffer/swap_buffer_module.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP Buffer interface declaration.
- */
-
-#ifndef __SWAP_BUFFER_MODULE_H__
-#define __SWAP_BUFFER_MODULE_H__
-
-#include <linux/types.h>
-
-struct swap_subbuffer;
-
-/**
- * @struct buffer_init_t
- * @brief Buffer init structure. Contains information necessary for SWAP buffer
- * initialization.
- * @var buffer_init_t::subbuffer_size
- * Subbuffer size.
- * @var buffer_init_t::nr_subbuffers
- * Subbuffers count.
- * @var buffer_init_t::subbuffer_full_cb
- * Callback. Called when one of subbuffers is full.
- * @var buffer_init_t::lower_threshold
- * Lower threshold in percent. When buffers fall below this limit
- * low_mem_cb is called and swap_buffer is suspended.
- * @var buffer_init_t::low_mem_cb
- * Callback that is called when count of free subbuffers falls below
- * lower_threshold.
- * @var buffer_init_t::top_threshold
- * Top threshold in percent. When buffers exceed this limit
- * enough_mem_cb is called.
- * @var buffer_init_t::enough_mem_cb
- * Callback that is called when count of free subbuffers exceeds top_threshold.
- */
-
-struct buffer_init_t {
- size_t subbuffer_size;
- unsigned int nr_subbuffers;
- int (*subbuffer_full_cb)(bool wakeup);
-
- unsigned char lower_threshold;
- int (*low_mem_cb)(void);
-
- unsigned char top_threshold;
- int (*enough_mem_cb)(void);
-};
-
-/* SWAP Buffer initialization function. Call it before using buffer.
- * Returns memory pages count (>0) in one subbuffer on success, or error code
- * (<0) otherwise. */
-int swap_buffer_init(struct buffer_init_t *buf_init);
-
-/* SWAP Buffer uninitialization function. Call it every time before removing
- * this module.
- * Returns E_SB_SUCCESS (0) on success, otherwise error code. */
-int swap_buffer_uninit(void);
-
-/* SWAP Buffer write function. Pass it size of the data and pointer to the data.
- * On success returns number of bytes written (>=0) or error code (<0)
- * otherwise */
-ssize_t swap_buffer_write(void *data, size_t size, bool wakeup);
-
-/* SWAP Buffer get. Put subbuffer pointer to the variable *subbuffer.
- * Return pages count in subbuffer. */
-int swap_buffer_get(struct swap_subbuffer **subbuffer);
-
-/* SWAP Buffer release. All 'get' buffers must be released with this function.
- * Just pass &subbuffer_ptr to it */
-int swap_buffer_release(struct swap_subbuffer **subbuffer);
-
-/* SWAP Buffer flush. Puts all buffers to read queue and returns their count. */
-unsigned int swap_buffer_flush(void);
-
-#endif /* __SWAP_BUFFER_MODULE_H__ */
+++ /dev/null
-/**
- * @file buffer/swap_buffer_to_buffer_queue.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- * SWAP Buffer interface for buffer queue.
- */
-
-#ifndef __SWAP_BUFFER_TO_BUFFER_QUEUE_H__
-#define __SWAP_BUFFER_TO_BUFFER_QUEUE_H__
-
-#include <linux/types.h>
-
-int swap_buffer_callback(void *buffer, bool wakeup);
-
-#endif /* __SWAP_BUFFER_TO_BUFFER_QUEUE_H__ */
+++ /dev/null
-# Config for build.sh
-
-kernel=<path-to-kernel-sourse>
-toolchain=<path-to-toolchain>
-arch=<architecture>
+++ /dev/null
-#!/usr/bin/env bash
-# NOTE: This requires GNU getopt. On Mac OS X and FreeBSD, you have to install
-# this separately; see below.
-
-IDENT=${0}
-
-show_usage_and_exit () {
- echo -e "Usage: ${IDENT} <options> <compile|check|clean>"
- echo -e "\tOptions:"
- echo -e "\t--verbose"
- echo -e "\t--file <config file>"
-# echo -e "\t--debug"
- echo -e "\t--kernel <kernel path>"
- echo -e "\t--arch <arm|arm64|i386>"
- echo -e "\t[--toolchain <cross compile path>]"
- exit 1
-}
-
-TEMP=`getopt -o vk:t:a:f: --long verbose,kernel:,toolchain:,arch:,file: \
- -n '${IDENT}' -- "$@"`
-
-if [ $? != 0 ] ; then
- show_usage_and_exit
-fi
-
-# Note the quotes around `$TEMP': they are essential!
-eval set -- "$TEMP"
-
-MDIR=`pwd`
-VERBOSE=false
-CONFIG_FILE="build.config"
-KERNELDIR=""
-TOOLCHAIN=""
-ARCH=""
-
-while true; do
- case "$1" in
- -v | --verbose ) VERBOSE=true; shift ;;
- -k | --kernel ) KERNELDIR="$2"; shift 2 ;;
- -t | --toolchain ) TOOLCHAIN="$2"; shift 2;;
- -a | --arch ) ARCH="$2"; shift 2;;
- -f | --file ) CONFIG_FILE="$2"; shift 2;;
- -- ) shift; break ;;
- * ) break ;;
- esac
-done
-
-if [ "${1}" != "compile" -a "${1}" != "clean" -a "${1}" != "check" ] ; then
- ACTION="compile"
- ARCH=${2}
- KERNELDIR=${1}
- if [ "${3}" != "" ] ; then
- TOOLCHAIN=${3}
- fi
-else
- if [ -r ${CONFIG_FILE} ]; then
- . ${CONFIG_FILE}
- KERNELDIR=${kernel}
- TOOLCHAIN=${toolchain}
- ARCH=$arch
- fi
- ACTION="${1}"
-fi
-
-if [ "${KERNELDIR}" = "" ] ; then
- show_usage_and_exit
-fi
-
-if [ "${ARCH}" = "arm" ] ; then
- LINKNAME="arm"
-elif [ "${ARCH}" = "arm64" ] ; then
- LINKNAME="arm64"
-elif [ "${ARCH}" = "i386" ] ; then
- LINKNAME="x86"
-else
- show_usage_and_exit
-fi
-
-MCFLAGS="-Werror"
-
-CMDLINE_ARGS=""
-CMDLINE_ARGS="CROSS_COMPILE=${TOOLCHAIN} ARCH=${ARCH} -C ${KERNELDIR}"
-CMDLINE_ARGS="${CMDLINE_ARGS} M=${MDIR} MCFLAGS=${MCFLAGS} LINKNAME=${LINKNAME}"
-
-if [ "${ACTION}" = "check" ] ; then
- CMDLINE="make C=2 CF=\"-Wsparse-all\" ${CMDLINE_ARGS} modules"
-elif [ "${ACTION}" = "clean" ] ; then
- CMDLINE="make ${CMDLINE_ARGS} clean"
-else
- CMDLINE="make ${CMDLINE_ARGS} modules"
-fi
-
-if [ ${VERBOSE} = "true" ] ; then
- CMDLINE="${CMDLINE} V=1"
-fi
-
-#echo -n "CMDLINE ${CMDLINE}\n"
-
-${CMDLINE} || exit 1
-
-# On arm64 gbs compiler fails on stripping arm64. This workaround is made to
-# make it buildable until the bug is fixed.
-if [ "${ARCH}" = "arm64" ] ; then
- for i in */*.ko; do
- echo "Stripping $i..."
- strip -x -g $i
- done
-fi
-
-exit 0
-
+++ /dev/null
-#!/bin/bash
-
-model=$(sed -n "s|.*tizen.org/system/model_name[^>]*>\([^<]*\)<.*|\1|p" /etc/config/model-config.xml)
-modpath=/opt/swap/sdk
-
-case $model in
-# armv7l
- "TM1")
- mv $modpath/tm1_swap_modules/* $modpath
- rm -r $modpath/tm1_swap_modules
- rm -rf $modpath/odroid_swap_modules
- rm -rf $modpath/tw1_swap_modules
- ;;
- "xu3")
- mv $modpath/odroid_swap_modules/* $modpath
- rm -r $modpath/odroid_swap_modules
- rm -rf $modpath/tm1_swap_modules
- rm -rf $modpath/tw1_swap_modules
- ;;
- "TW1")
- mv $modpath/tw1_swap_modules/* $modpath
- rm -r $modpath/tw1_swap_modules
- rm -rf $modpath/odroid_swap_modules
- rm -rf $modpath/tm1_swap_modules
- ;;
-# x86
- "Emulator")
- mv $modpath/emul_swap_modules/* $modpath
- rm -r $modpath/emul_swap_modules
- ;;
-# aarch64
- "TM2")
- mv $modpath/tm2_swap_modules/* $modpath
- rm -r $modpath/tm2_swap_modules
- rm -rf $modpath/rpi3_swap_modules
- rm -rf $modpath/tw2_swap_modules
- ;;
- "TW2")
- mv $modpath/tw2_swap_modules/* $modpath
- rm -r $modpath/tw2_swap_modules
- rm -rf $modpath/rpi3_swap_modules
- rm -rf $modpath/tm2_swap_modules
- ;;
- "rpi3")
- mv $modpath/rpi3_swap_modules/* $modpath
- rm -r $modpath/rpi3_swap_modules
- rm -rf $modpath/tw2_swap_modules
- rm -rf $modpath/tm2_swap_modules
- ;;
- *)
- echo "Device isn't supported" > /tmp/swap-modules_install.log
- ;;
-esac
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../buffer/Module.symvers
-
-obj-m := swap_driver.o
-swap_driver-y := swap_driver_module.o \
- device_driver.o \
- driver_to_buffer.o \
- driver_debugfs.o
-
-ifeq ($(CONFIG_CONNECTOR),y)
- swap_driver-y += us_interaction.o
-endif
+++ /dev/null
-/**
- * @file driver/app_manage.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * Driver user <-> kernel connect implement.
- */
-
-#ifndef __APP_MANAGE_H__
-#define __APP_MANAGE_H__
-
-#include "us_interaction.h"
-#include "us_interaction_msg.h"
-
-/**
- * @brief Sends pause message to kernel.
- *
- * @return us_interaction_send_msg result.
- */
-static inline int app_manage_pause_apps(void)
-{
- enum us_interaction_k2u_msg_t us_int_msg = US_INT_PAUSE_APPS;
-
- return us_interaction_send_msg(&us_int_msg, sizeof(us_int_msg));
-}
-
-/**
- * @brief Sends continue message to kernel.
- *
- * @return us_interaction_send_msg result.
- */
-static inline int app_manage_cont_apps(void)
-{
- enum us_interaction_k2u_msg_t us_int_msg = US_INT_CONT_APPS;
-
- return us_interaction_send_msg(&us_int_msg, sizeof(us_int_msg));
-}
-
-#endif /* __APP_MANAGE_H__ */
+++ /dev/null
-/**
- * driver/device_driver.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Provides SWAP device.
- */
-
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/cdev.h>
-#include <linux/err.h>
-#include <linux/device.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/splice.h>
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
-#include <linux/uaccess.h>
-#include <linux/version.h>
-
-#include <ksyms/ksyms.h>
-#include <master/swap_initializer.h>
-
-#include "device_driver.h"
-#include "swap_driver_errors.h"
-#include "driver_to_buffer.h"
-#include "swap_ioctl.h"
-#include "driver_defs.h"
-#include "device_driver_to_driver_to_buffer.h"
-#include "driver_to_buffer.h"
-#include "driver_to_msg.h"
-
-/** SWAP device name as it is in /dev/. */
-#define SWAP_DEVICE_NAME "swap_device"
-
-/* swap_device driver routines */
-static ssize_t swap_device_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos);
-static long swap_device_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg);
-static ssize_t swap_device_splice_read(struct file *filp, loff_t *ppos,
- struct pipe_inode_info *pipe, size_t len,
- unsigned int flags);
-
-#ifdef CONFIG_COMPAT
-static long swap_compat_device_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
-{
- switch (cmd & ~IOCSIZE_MASK) {
- case SWAP_DRIVER_BUFFER_INITIALIZE & ~IOCSIZE_MASK:
- case SWAP_DRIVER_MSG & ~IOCSIZE_MASK:
- /* Fix up pointer size (usually 4 -> 8 in 32-on-64-bit case) */
- if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
- cmd &= ~IOCSIZE_MASK;
- cmd |= sizeof(void *) << IOCSIZE_SHIFT;
- }
- break;
- }
-
- return swap_device_ioctl(filp, cmd, arg);
-}
-#else /* CONFIG_COMPAT */
-#define swap_compat_device_ioctl NULL
-#endif /* CONFIG_COMPAT */
-
-
-/**
- * @var swap_device_fops
- * @brief SWAP device file operations.
- */
-const struct file_operations swap_device_fops = {
- .owner = THIS_MODULE,
- .read = swap_device_read,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .unlocked_ioctl = swap_device_ioctl,
- .compat_ioctl = swap_compat_device_ioctl,
- .splice_read = swap_device_splice_read,
-};
-
-/* Typedefs for splice_* funcs. Prototypes are for linux-3.8.6 */
-/** Splice to pipe pointer type. */
-typedef ssize_t(*splice_to_pipe_p_t)(struct pipe_inode_info *pipe,
- struct splice_pipe_desc *spd);
-/** Splice grow spd pointer type. */
-typedef int(*splice_grow_spd_p_t)(const struct pipe_inode_info *pipe,
- struct splice_pipe_desc *spd);
-
-static splice_to_pipe_p_t splice_to_pipe_p;
-static splice_grow_spd_p_t splice_grow_spd_p;
-
-/* Device numbers */
-static dev_t swap_device_no;
-
-/* Device cdev struct */
-static struct cdev swap_device_cdev;
-
-/* Device class struct */
-static struct class *swap_device_class;
-
-/* Device device struct */
-static struct device *swap_device_device;
-
-/* Reading tasks queue */
-static DECLARE_WAIT_QUEUE_HEAD(swap_device_wait);
-
-
-static atomic_t flag_wake_up = ATOMIC_INIT(0);
-
-static void __bottom_wake_up(void)
-{
- if (waitqueue_active(&swap_device_wait))
- wake_up_interruptible(&swap_device_wait);
-}
-
-static void bottom_wake_up(struct work_struct *work)
-{
- if (atomic_read(&flag_wake_up)) {
- atomic_set(&flag_wake_up, 0);
- __bottom_wake_up();
- }
-}
-
-static DECLARE_WORK(w_wake_up, bottom_wake_up);
-
-static void exit_w_wake_up(void)
-{
- flush_scheduled_work();
- __bottom_wake_up();
-}
-
-
-/*
- * Driver message handler
- */
-static DECLARE_RWSEM(dmsg_handler_sem);
-static struct driver_msg_handler *dmsg_handler;
-
-static int driver_msg_handler_call(void __user *data)
-{
- int ret;
-
- down_read(&dmsg_handler_sem);
- if (dmsg_handler) {
- ret = dmsg_handler->handler(data);
- } else {
- print_warn("dmsg_handler() is not register\n");
- ret = -EINVAL;
- }
- up_read(&dmsg_handler_sem);
-
- return ret;
-}
-
-/**
- * @brief Register message handler.
- *
- * @param msg_handler Pointer to message handler.
- * @return Void.
- */
-void driver_msg_handler_set(struct driver_msg_handler *msg_handler)
-{
- down_write(&dmsg_handler_sem);
- /* unregister dmsg_handler */
- if (dmsg_handler) {
- module_put(dmsg_handler->mod);
- dmsg_handler = NULL;
- }
-
- /* register dmsg_handler */
- if (msg_handler) {
- BUG_ON(!try_module_get(msg_handler->mod));
- dmsg_handler = msg_handler;
- }
- up_write(&dmsg_handler_sem);
-}
-EXPORT_SYMBOL_GPL(driver_msg_handler_set);
-
-
-/**
- * @brief We need this realization of splice_shrink_spd() because its desing
- * frequently changes in custom kernels.
- *
- * @param pipe Pointer to the pipe whereto splice data.
- * @param spd Pointer to the splice_pipe_desc structure.
- * @return Void.
- */
-void swap_device_splice_shrink_spd(struct pipe_inode_info *pipe,
- struct splice_pipe_desc *spd)
-{
- if (pipe->buffers <= PIPE_DEF_BUFFERS)
- return;
-
- kfree(spd->pages);
- kfree(spd->partial);
-}
-
-
-/* TODO Think of permanent major */
-
-/**
- * @brief Register device.
- *
- * @return 0 on success, negative error code otherwise.
- */
-int swap_device_init(void)
-{
- int result;
-
- /* Allocating device major and minor nums for swap_device */
- result = alloc_chrdev_region(&swap_device_no, 0, 1, SWAP_DEVICE_NAME);
- if (result < 0) {
- print_crit("Major number allocation has failed\n");
- result = -E_SD_ALLOC_CHRDEV_FAIL;
- goto init_fail;
- }
-
- /* Creating device class. Using IS_ERR, because class_create
- * returns ERR_PTR on error. */
- swap_device_class = class_create(THIS_MODULE, SWAP_DEVICE_NAME);
- if (IS_ERR(swap_device_class)) {
- print_crit("Class creation has failed\n");
- result = -E_SD_CLASS_CREATE_FAIL;
- goto init_fail;
- }
-
- /* Cdev intialization and setting file operations */
- cdev_init(&swap_device_cdev, &swap_device_fops);
-
- /* Adding cdev to system */
- result = cdev_add(&swap_device_cdev, swap_device_no, 1);
- if (result < 0) {
- print_crit("Device adding has failed\n");
- result = -E_SD_CDEV_ADD_FAIL;
- goto init_fail;
- }
-
- /* Create device struct */
- swap_device_device = device_create(swap_device_class, NULL,
- swap_device_no,
- "%s", SWAP_DEVICE_NAME);
- if (IS_ERR(swap_device_device)) {
- print_crit("Device struct creating has failed\n");
- result = -E_SD_DEVICE_CREATE_FAIL;
- goto init_fail;
- }
-
- /* Find splice_* funcs addresses */
- splice_to_pipe_p = (splice_to_pipe_p_t)swap_ksyms("splice_to_pipe");
- if (!splice_to_pipe_p) {
- print_err("splice_to_pipe() not found!\n");
- result = -E_SD_NO_SPLICE_FUNCS;
- goto init_fail;
- }
-
- splice_grow_spd_p = (splice_grow_spd_p_t)swap_ksyms("splice_grow_spd");
- if (!splice_grow_spd_p) {
- print_err("splice_grow_spd() not found!\n");
- result = -E_SD_NO_SPLICE_FUNCS;
- goto init_fail;
- }
-
- return 0;
-
-init_fail:
- cdev_del(&swap_device_cdev);
- if (swap_device_class)
- class_destroy(swap_device_class);
- if (swap_device_no)
- unregister_chrdev_region(swap_device_no, 1);
- return result;
-}
-
-/* TODO Check wether driver is registered */
-
-/**
- * @brief Unregister device.
- *
- * @return Void.
- */
-void swap_device_exit(void)
-{
- exit_w_wake_up();
-
- splice_to_pipe_p = NULL;
- splice_grow_spd_p = NULL;
-
- device_destroy(swap_device_class, swap_device_no);
- cdev_del(&swap_device_cdev);
- class_destroy(swap_device_class);
- unregister_chrdev_region(swap_device_no, 1);
-}
-
-static ssize_t swap_device_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos)
-{
- /* Wait queue item that consists current task. It is used to be added in
- * swap_device_wait queue if there is no data to be read. */
- DEFINE_WAIT(wait);
- int result;
-
- /* TODO : Think about spin_locks to prevent reading race condition. */
- while ((result =
- driver_to_buffer_next_buffer_to_read()) != E_SD_SUCCESS) {
-
- /* Add process to the swap_device_wait queue and set the current
- * task state TASK_INTERRUPTIBLE. If there is any data to be
- * read, then the current task is removed from the
- * swap_device_wait queue and its state is changed to this. */
- prepare_to_wait(&swap_device_wait, &wait, TASK_INTERRUPTIBLE);
-
- if (result < 0) {
- result = 0;
- goto swap_device_read_error;
- } else if (result == E_SD_NO_DATA_TO_READ) {
- /* Yes, E_SD_NO_DATA_TO_READ should be positive,
- * cause it's not really an error */
- if (filp->f_flags & O_NONBLOCK) {
- result = -EAGAIN;
- goto swap_device_read_error;
- }
- if (signal_pending(current)) {
- result = -ERESTARTSYS;
- goto swap_device_read_error;
- }
- schedule();
- finish_wait(&swap_device_wait, &wait);
- }
- }
-
- result = driver_to_buffer_read(buf, count);
- /* If there is an error - return 0 */
- if (result < 0)
- result = 0;
-
-
- return result;
-
-swap_device_read_error:
- finish_wait(&swap_device_wait, &wait);
-
- return result;
-}
-
-static long swap_device_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
-{
- int result;
-
- switch (cmd) {
- case SWAP_DRIVER_BUFFER_INITIALIZE:
- {
- struct buffer_initialize initialize_struct;
-
- result = copy_from_user(&initialize_struct, (void *)arg,
- sizeof(struct buffer_initialize));
- if (result)
- break;
-
- result = driver_to_buffer_set_size(initialize_struct.size);
- if (result < 0) {
- print_err("Wrong subbuffer size=%u\n",
- initialize_struct.size);
- break;
- }
-
- result = driver_to_buffer_set_count(initialize_struct.count);
- if (result < 0) {
- print_err("Wrong subbuffer count=%u\n",
- initialize_struct.count);
- break;
- }
-
- result = driver_to_buffer_initialize();
- if (result < 0) {
- print_err("Buffer initialization failed %d\n", result);
- break;
- }
- result = E_SD_SUCCESS;
-
- break;
- }
- case SWAP_DRIVER_BUFFER_UNINITIALIZE:
- {
- result = driver_to_buffer_uninitialize();
- if (result < 0)
- print_err("Buffer uninitialization failed %d\n",
- result);
- break;
- }
- case SWAP_DRIVER_NEXT_BUFFER_TO_READ:
- {
- /* Use this carefully */
- result = driver_to_buffer_next_buffer_to_read();
- if (result == E_SD_NO_DATA_TO_READ) {
- /* TODO Do what we usually do when there are no
- * subbuffers to read (make daemon sleep ?) */
- }
- break;
- }
- case SWAP_DRIVER_FLUSH_BUFFER:
- {
- result = driver_to_buffer_flush();
- break;
- }
- case SWAP_DRIVER_MSG:
- {
- result = driver_msg_handler_call((void __user *)arg);
- break;
- }
- case SWAP_DRIVER_WAKE_UP:
- {
- swap_device_wake_up_process();
- result = E_SD_SUCCESS;
- break;
- }
- default:
- print_warn("Unknown command %d\n", cmd);
- result = -EINVAL;
- break;
-
- }
- return result;
-}
-
-static void swap_device_pipe_buf_release(struct pipe_inode_info *inode,
- struct pipe_buffer *pipe)
-{
- __free_page(pipe->page);
-}
-
-static void swap_device_page_release(struct splice_pipe_desc *spd,
- unsigned int i)
-{
- __free_page(spd->pages[i]);
-}
-
-static const struct pipe_buf_operations swap_device_pipe_buf_ops = {
- .can_merge = 0,
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0)
- .map = generic_pipe_buf_map,
- .unmap = generic_pipe_buf_unmap,
-#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0) */
- .confirm = generic_pipe_buf_confirm,
- .release = swap_device_pipe_buf_release,
- .steal = generic_pipe_buf_steal,
- .get = generic_pipe_buf_get
-};
-
-static ssize_t swap_device_splice_read(struct file *filp, loff_t *ppos,
- struct pipe_inode_info *pipe,
- size_t len, unsigned int flags)
-{
- /* Wait queue item that consists current task. It is used to be added in
- * swap_device_wait queue if there is no data to be read. */
- DEFINE_WAIT(wait);
-
- int result;
- struct page *pages[PIPE_DEF_BUFFERS];
- struct partial_page partial[PIPE_DEF_BUFFERS];
- struct splice_pipe_desc spd = {
- .pages = pages,
- .partial = partial,
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 5))
- .nr_pages_max = PIPE_DEF_BUFFERS,
-#endif
- .nr_pages = 0,
- .flags = flags,
- .ops = &swap_device_pipe_buf_ops,
- .spd_release = swap_device_page_release,
- };
-
- /* Get next buffer to read */
- /* TODO : Think about spin_locks to prevent reading race condition.*/
- while ((result =
- driver_to_buffer_next_buffer_to_read()) != E_SD_SUCCESS) {
-
- /* Add process to the swap_device_wait queue and set the current
- * task state TASK_INTERRUPTIBLE. If there is any data to be
- * read, then the current task is removed from the
- * swap_device_wait queue and its state is changed. */
- prepare_to_wait(&swap_device_wait, &wait, TASK_INTERRUPTIBLE);
- if (result < 0) {
- print_err("driver_to_buffer_next_buffer_to_read error "
- "%d\n", result);
- /* TODO Error return to OS */
- result = 0;
- goto swap_device_splice_read_error;
- } else if (result == E_SD_NO_DATA_TO_READ) {
- if (filp->f_flags & O_NONBLOCK) {
- result = -EAGAIN;
- goto swap_device_splice_read_error;
- }
- if (signal_pending(current)) {
- result = -ERESTARTSYS;
- goto swap_device_splice_read_error;
- }
- schedule();
- finish_wait(&swap_device_wait, &wait);
- }
- }
-
- if (splice_grow_spd_p(pipe, &spd)) {
- result = -ENOMEM;
- goto swap_device_splice_read_out;
- }
-
- result = driver_to_buffer_fill_spd(&spd);
- if (result != 0) {
- print_err("Cannot fill spd for splice\n");
- goto swap_device_shrink_spd;
- }
-
- result = splice_to_pipe_p(pipe, &spd);
-
-swap_device_shrink_spd:
- swap_device_splice_shrink_spd(pipe, &spd);
-
-swap_device_splice_read_out:
- return result;
-
-swap_device_splice_read_error:
- finish_wait(&swap_device_wait, &wait);
-
- return result;
-}
-
-/**
- * @brief Wakes up daemon that splicing data from driver.
- *
- * @return Void.
- */
-void swap_device_wake_up_process(void)
-{
- if (atomic_read(&flag_wake_up) == 0) {
- atomic_set(&flag_wake_up, 1);
- schedule_work(&w_wake_up);
- }
-}
+++ /dev/null
-/**
- * @file driver/device_driver.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP device driver interface declaration.
- */
-
-#ifndef __SWAP_DRIVER_DEVICE_DRIVER_H__
-#define __SWAP_DRIVER_DEVICE_DRIVER_H__
-
-/* Create and register device */
-int swap_device_init(void);
-
-/* Delete device */
-void swap_device_exit(void);
-
-#endif /* __SWAP_DRIVER_DEVICE_DRIVER_H__ */
+++ /dev/null
-/**
- * @file device_driver_to_driver_to_buffer.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- * SWAP device interface for driver_to_buffer.
- */
-
-
-#ifndef __DEVICE_DRIVER_TO_DRIVER_TO_BUFFER_H__
-#define __DEVICE_DRIVER_TO_DRIVER_TO_BUFFER_H__
-
-void swap_device_wake_up_process(void);
-
-#endif /* __DEVICE_DRIVER_TO_DRIVER_TO_BUFFER_H__ */
+++ /dev/null
-/**
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * @section DESCRIPTION
- *
- * SWAP debugfs interface definition.
- */
-
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/debugfs.h>
-#include <master/swap_debugfs.h>
-#include "driver_to_buffer.h"
-#include "swap_driver_errors.h"
-
-
-#define WRITER_DBG_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
-
-
-static int buf_enabled_set(u64 val)
-{
- int ret = -EINVAL;
-
- switch (val) {
- case 0:
- ret = driver_to_buffer_uninitialize();
- break;
- case 1:
- ret = driver_to_buffer_initialize();
- break;
- }
-
- return ret;
-}
-
-static u64 buf_enabled_get(void)
-{
- return driver_to_buffer_enabled();
-}
-
-static struct dfs_setget_64 dfs_enabled = {
- .set = buf_enabled_set,
- .get = buf_enabled_get,
-};
-
-static int subbuf_size_set(u64 val)
-{
-
- if (driver_to_buffer_set_size(val) != E_SD_SUCCESS)
- return -EINVAL;
-
- return 0;
-}
-
-static u64 subbuf_size_get(void)
-{
- return driver_to_buffer_get_size();
-}
-
-static struct dfs_setget_64 dfs_subbuf_size = {
- .set = subbuf_size_set,
- .get = subbuf_size_get,
-};
-
-static int subbuf_count_set(u64 val)
-{
- if (driver_to_buffer_set_count(val) != E_SD_SUCCESS)
- return -EINVAL;
-
- return 0;
-}
-
-static u64 subbuf_count_get(void)
-{
- return driver_to_buffer_get_count();
-}
-
-static struct dfs_setget_64 dfs_subbuf_count = {
- .set = subbuf_count_set,
- .get = subbuf_count_get,
-};
-
-
-struct dbgfs_data {
- const char *name;
- struct dfs_setget_64 *setget;
-};
-
-static struct dbgfs_data dbgfs[] = {
- {
- .name = "buffer_enabled",
- .setget = &dfs_enabled,
- }, {
- .name = "subbuf_size",
- .setget = &dfs_subbuf_size,
- }, {
- .name = "subbuf_conunt",
- .setget = &dfs_subbuf_count,
- }
-};
-
-
-static struct dentry *driver_dir;
-
-void driver_debugfs_uninit(void)
-{
- debugfs_remove_recursive(driver_dir);
- driver_dir = NULL;
-}
-
-int driver_debugfs_init(void)
-{
- int i;
- struct dentry *swap_dir, *dentry;
-
- swap_dir = swap_debugfs_getdir();
- if (swap_dir == NULL)
- return -ENOENT;
-
- driver_dir = swap_debugfs_create_dir("driver", swap_dir);
- if (driver_dir == NULL)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(dbgfs); ++i) {
- struct dbgfs_data *data = &dbgfs[i];
- dentry = swap_debugfs_create_setget_u64(data->name,
- WRITER_DBG_PERMS,
- driver_dir,
- data->setget);
- if (!dentry)
- goto fail;
- }
-
- return 0;
-fail:
- driver_debugfs_uninit();
- return -ENOMEM;
-}
+++ /dev/null
-/**
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * @section DESCRIPTION
- *
- * SWAP debugfs interface definition.
- */
-
-#ifndef DRIVER_DEBUGFS_H
-#define DRIVER_DEBUGFS_H
-
-
-int driver_debugfs_init(void);
-void driver_debugfs_uninit(void);
-
-#endif // DRIVER_DEBUGFS_H
+++ /dev/null
-/**
- * @file driver/driver_defs.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- * Device driver defs.
- */
-
-#ifndef __SWAP_DRIVER_DEVICE_DEFS_H__
-#define __SWAP_DRIVER_DEVICE_DEFS_H__
-
-#include <linux/kernel.h>
-
-/** Prints debug message.*/
-#define print_debug(msg, args...) \
- printk(KERN_DEBUG "SWAP_DRIVER DEBUG : " msg, ##args)
-/** Prints info message.*/
-#define print_msg(msg, args...) \
- printk(KERN_INFO "SWAP_DRIVER : " msg, ##args)
-/** Prints warning message.*/
-#define print_warn(msg, args...) \
- printk(KERN_WARNING "SWAP_DRIVER WARNING : " msg, ##args)
-/** Prints error message.*/
-#define print_err(msg, args...) \
- printk(KERN_ERR "SWAP_DRIVER ERROR : " msg, ##args)
-/** Prints critical error message.*/
-#define print_crit(msg, args...) \
- printk(KERN_CRIT "SWAP_DRIVER CRITICAL : " msg, ##args)
-
-#endif /* __SWAP_DRIVER_DEVICE_DEFS_H__ */
+++ /dev/null
-/**
- * driver/driver_to_buffer.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Driver and buffer interaction interface implementation.
- */
-
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/splice.h>
-#include <linux/uaccess.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-
-#include <buffer/swap_buffer_module.h>
-#include <buffer/swap_buffer_errors.h>
-#include <buffer/buffer_description.h>
-
-#include "driver_defs.h"
-#include "swap_driver_errors.h"
-#include "device_driver_to_driver_to_buffer.h"
-#include "app_manage.h"
-
-/** Maximum subbuffer size. Used for sanitization checks. */
-#define MAXIMUM_SUBBUFFER_SIZE (64 * 1024)
-
-/* Current busy buffer */
-static struct swap_subbuffer *busy_buffer;
-
-/* Buffers count ready to be read */
-static int buffers_to_read;
-
-/* Pages count in one subbuffer */
-static int pages_per_buffer;
-
-/* Used to sync changes of the buffers_to_read var */
-static spinlock_t buf_to_read;
-
-
-static inline void init_buffers_to_read(void)
-{
- spin_lock_init(&buf_to_read);
- buffers_to_read = 0;
-}
-
-static inline void inc_buffers_to_read(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&buf_to_read, flags);
- buffers_to_read++;
- spin_unlock_irqrestore(&buf_to_read, flags);
-}
-
-static inline void dec_buffers_to_read(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&buf_to_read, flags);
- buffers_to_read--;
- spin_unlock_irqrestore(&buf_to_read, flags);
-}
-
-static inline void set_buffers_to_read(int count)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&buf_to_read, flags);
- buffers_to_read = count;
- spin_unlock_irqrestore(&buf_to_read, flags);
-}
-
-static inline int something_to_read(void)
-{
- unsigned long flags;
- int result;
-
- spin_lock_irqsave(&buf_to_read, flags);
- result = buffers_to_read;
- spin_unlock_irqrestore(&buf_to_read, flags);
-
- return result;
-}
-
-/* TODO Get subbuffer for reading */
-static size_t driver_to_buffer_get(void)
-{
- int result;
-
- /* If there is no readable buffers, return error */
- result = swap_buffer_get(&busy_buffer);
- if (result == -E_SB_NO_READABLE_BUFFERS) {
- busy_buffer = NULL;
- return -E_SD_NO_DATA_TO_READ;
- } else if (result < 0) {
- print_err("swap_buffer_get unhandle error %d\n", result);
- return -E_SD_BUFFER_ERROR;
- }
-
- return busy_buffer->full_buffer_part;
-}
-
-/* TODO Release subbuffer */
-static int driver_to_buffer_release(void)
-{
- int result;
-
- if (!busy_buffer)
- return -E_SD_NO_BUSY_SUBBUFFER;
-
- result = swap_buffer_release(&busy_buffer);
- if (result == -E_SB_NO_SUBBUFFER_IN_BUSY) {
- return -E_SD_WRONG_SUBBUFFER_PTR;
- } else if (result < 0) {
- print_err("swap_buffer_release unhandle error %d\n", result);
- return -E_SD_BUFFER_ERROR;
- }
-
- busy_buffer = NULL;
-
- return E_SD_SUCCESS;
-}
-
-static int driver_to_buffer_callback(bool wakeup)
-{
- /* Increment buffers_to_read counter */
- inc_buffers_to_read();
- if (wakeup)
- swap_device_wake_up_process();
-
- return E_SD_SUCCESS;
-}
-
-/**
- * @brief Copies data from subbuffer to userspace.
- *
- * @param[out] buf Pointer to userspace memory area whereto copy data from
- * subbuffer.
- * @param count Size of data to be read.
- * @return Read data size on success, negative error code on error.
- */
-ssize_t driver_to_buffer_read(char __user *buf, size_t count)
-{
- size_t bytes_to_copy;
- size_t bytes_to_read = 0;
- int page_counter = 0;
-
- /* Reading from swap_device means reading only current busy_buffer.
- * So, if there is no busy_buffer, we don't get next to read, we just
- * read nothing. In this case, or if there is nothing to read from
- * busy_buffer - return -E_SD_NO_DATA_TO_READ. It should be correctly
- * handled in device_driver */
- if (!busy_buffer || !busy_buffer->full_buffer_part)
- return -E_SD_NO_DATA_TO_READ;
-
- /* Bytes count that we're going to copy to user buffer is equal to user
- * buffer size or to subbuffer readable size whichever is less */
- bytes_to_copy = (count > busy_buffer->full_buffer_part) ?
- busy_buffer->full_buffer_part : count;
-
- /* Copy data from each page to buffer */
- while (bytes_to_copy > 0) {
- /* Get size that should be copied from current page */
- size_t read_from_this_page =
- (bytes_to_copy > PAGE_SIZE) ? PAGE_SIZE
- : bytes_to_copy;
-
- /* Copy and add size to copied bytes count */
-
- /* TODO Check with more than one page */
- bytes_to_read += read_from_this_page -
- copy_to_user(
- buf, page_address(busy_buffer->data_buffer) +
- (sizeof(struct page *) *
- page_counter),
- read_from_this_page);
- bytes_to_copy -= read_from_this_page;
- page_counter++;
- }
-
- return bytes_to_read;
-}
-
-/**
- * @brief Flushes SWAP buffer.
- *
- * @return 0.
- */
-int driver_to_buffer_flush(void)
-{
- unsigned int flushed;
-
- flushed = swap_buffer_flush();
- set_buffers_to_read(flushed);
- swap_device_wake_up_process();
-
- return E_SD_SUCCESS;
-}
-
-/**
- * @brief Fills spd structure.
- *
- * @param[out] spd Pointer to the splice_pipe_desc struct that should be filled.
- * @return 0 on success, negative error code on error.
- */
-int driver_to_buffer_fill_spd(struct splice_pipe_desc *spd)
-{
- size_t data_to_splice = busy_buffer->full_buffer_part;
- struct page **pages = spd->pages;
- struct partial_page *partial = spd->partial;
-
- while (data_to_splice) {
- size_t read_from_current_page = min(data_to_splice,
- (size_t)PAGE_SIZE);
-
- pages[spd->nr_pages] = alloc_page(GFP_KERNEL);
- if (!pages[spd->nr_pages]) {
- print_err("Cannot alloc page for splice\n");
- return -ENOMEM;
- }
-
- /* FIXME: maybe there is more efficient way */
- memcpy(page_address(pages[spd->nr_pages]),
- page_address(&busy_buffer->data_buffer[spd->nr_pages]),
- read_from_current_page);
-
- /* Always beginning of the page */
- partial[spd->nr_pages].offset = 0;
- partial[spd->nr_pages].len = read_from_current_page;
-
- /* Private is not used */
- partial[spd->nr_pages].private = 0;
-
- spd->nr_pages++;
- data_to_splice -= read_from_current_page;
-
- /* TODO: add check for pipe->buffers exceeding */
- /* if (spd->nr_pages == pipe->buffers) { */
- /* break; */
- /* } */
- }
- return 0;
-}
-
-/**
- * @brief Check for subbuffer ready to be read.
- *
- * @return 1 if there is subbuffer to be read, 0 - if there isn't.
- */
-int driver_to_buffer_buffer_to_read(void)
-{
- return busy_buffer ? 1 : 0;
-}
-
-static size_t subbuf_size;
-static unsigned int subbuf_count;
-static bool buffer_enabled;
-static DEFINE_MUTEX(buffer_mtx);
-
-bool driver_to_buffer_enabled(void)
-{
- return buffer_enabled;
-}
-
-enum _swap_driver_errors driver_to_buffer_set_size(size_t size)
-{
- enum _swap_driver_errors ret = E_SD_SUCCESS;
-
- if (!size || size > MAXIMUM_SUBBUFFER_SIZE)
- return -E_SD_WRONG_ARGS;
-
- mutex_lock(&buffer_mtx);
- if (buffer_enabled) {
- ret = -E_SD_BUFFER_ENABLED;
- goto unlock;
- }
-
- subbuf_size = size;
-unlock:
- mutex_unlock(&buffer_mtx);
- return ret;
-}
-
-size_t driver_to_buffer_get_size(void)
-{
- return subbuf_size;
-}
-
-enum _swap_driver_errors driver_to_buffer_set_count(unsigned int count)
-{
- enum _swap_driver_errors ret = E_SD_SUCCESS;
-
- if (!count)
- return -E_SD_WRONG_ARGS;
-
- mutex_lock(&buffer_mtx);
- if (buffer_enabled) {
- ret = -E_SD_BUFFER_ENABLED;
- goto unlock;
- }
- subbuf_count = count;
-
-unlock:
- mutex_unlock(&buffer_mtx);
- return ret;
-}
-
-unsigned int driver_to_buffer_get_count(void)
-{
- return subbuf_count;
-}
-
-/**
- * @brief Initializes SWAP buffer.
- *
- * @param size Size of one subbuffer.
- * @param count Count of subbuffers.
- * @return 0 on success, negative error code on error.
- */
-int driver_to_buffer_initialize(void)
-{
- enum _swap_driver_errors result;
- struct buffer_init_t buf_init = {
- .subbuffer_size = subbuf_size,
- .nr_subbuffers = subbuf_count,
- .subbuffer_full_cb = driver_to_buffer_callback,
- .lower_threshold = 20,
- .low_mem_cb = app_manage_pause_apps,
- .top_threshold = 80,
- .enough_mem_cb = app_manage_cont_apps,
- };
-
- mutex_lock(&buffer_mtx);
- if (buffer_enabled) {
- result = -E_SD_BUFFER_ENABLED;
- goto unlock;
- }
-
- result = swap_buffer_init(&buf_init);
- if (result == -E_SB_NO_MEM_QUEUE_BUSY
- || result == -E_SB_NO_MEM_BUFFER_STRUCT) {
- result = -E_SD_NO_MEMORY;
- goto unlock;
- }
-
- /* TODO Race condition: buffer can be used in other thread till */
- /* we're in this func */
- /* Initialize driver_to_buffer variables */
- pages_per_buffer = result;
- busy_buffer = NULL;
- init_buffers_to_read();
- result = E_SD_SUCCESS;
- buffer_enabled = true;
-
-unlock:
- mutex_unlock(&buffer_mtx);
- return result;
-}
-
-/**
- * @brief Uninitializes buffer.
- *
- * @return 0 on success, negative error code on error.
- */
-int driver_to_buffer_uninitialize(void)
-{
- int result;
-
- mutex_lock(&buffer_mtx);
- if (!buffer_enabled) {
- result = -E_SD_BUFFER_DISABLED;
- goto unlock;
- }
-
- /* Release occupied buffer */
- if (busy_buffer) {
- result = driver_to_buffer_release();
- /* TODO Maybe release anyway */
- if (result < 0)
- goto unlock;
- busy_buffer = NULL;
- }
-
- result = swap_buffer_uninit();
- if (result == -E_SB_UNRELEASED_BUFFERS) {
- print_err("Can't uninit buffer! There are busy subbuffers!\n");
- result = -E_SD_BUFFER_ERROR;
- } else if (result < 0) {
- print_err("swap_buffer_uninit error %d\n", result);
- result = -E_SD_BUFFER_ERROR;
- } else {
- result = E_SD_SUCCESS;
- buffer_enabled = false;
- }
-
- /* Reinit driver_to_buffer vars */
- init_buffers_to_read();
- pages_per_buffer = 0;
-
-unlock:
- mutex_unlock(&buffer_mtx);
- return result;
-}
-
-/**
- * @brief Get next buffer to read.
- *
- * @return 0 on success, negative error code on error, E_SD_NO_DATA_TO_READ if
- * there is nothing to be read.
- */
-int driver_to_buffer_next_buffer_to_read(void)
-{
- int result;
-
- /* If there is busy_buffer first release it */
- if (busy_buffer) {
- result = driver_to_buffer_release();
- if (result)
- return result;
- }
-
- /* If there is no buffers to read, return E_SD_NO_DATA_TO_READ.
- * SHOULD BE POSITIVE, cause there is no real error. */
- if (!something_to_read())
- return E_SD_NO_DATA_TO_READ;
-
- /* Get next buffer to read */
- result = driver_to_buffer_get();
- if (result < 0) {
- print_err("buffer_to_reads > 0, but there are no buffers to read\n");
- return result;
- }
-
- /* Decrement buffers_to_read counter */
- dec_buffers_to_read();
-
- return E_SD_SUCCESS;
-}
+++ /dev/null
-/**
- * @file driver/driver_to_buffer.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Driver and buffer interaction interface declaration.
- */
-
-#ifndef __SWAP_DRIVER_DRIVER_TO_BUFFER__
-#define __SWAP_DRIVER_DRIVER_TO_BUFFER__
-
-
-#include <linux/types.h>
-
-struct splice_pipe_desc;
-
-
-bool driver_to_buffer_enabled(void);
-
-enum _swap_driver_errors driver_to_buffer_set_size(size_t size);
-size_t driver_to_buffer_get_size(void);
-
-enum _swap_driver_errors driver_to_buffer_set_count(unsigned int count);
-unsigned int driver_to_buffer_get_count(void);
-
-int driver_to_buffer_initialize(void);
-int driver_to_buffer_uninitialize(void);
-ssize_t driver_to_buffer_read(char __user *buf, size_t count);
-int driver_to_buffer_fill_spd(struct splice_pipe_desc *spd);
-int driver_to_buffer_buffer_to_read(void);
-int driver_to_buffer_next_buffer_to_read(void);
-int driver_to_buffer_flush(void);
-
-
-#endif /* __SWAP_DRIVER_DRIVER_TO_BUFFER__ */
+++ /dev/null
-/**
- * @file driver/driver_to_msg.h
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Driver and parser interaction interface declaration.
- */
-
-#ifndef __SWAP_DRIVER_DRIVER_TO_MSG__
-#define __SWAP_DRIVER_DRIVER_TO_MSG__
-
-#include <linux/compiler.h>
-
-struct module;
-
-struct driver_msg_handler {
- struct module *mod;
- int (*handler)(void __user *data);
-};
-
-/* Set the message handler */
-void driver_msg_handler_set(struct driver_msg_handler *msg_handler);
-
-#endif /* __SWAP_DRIVER_DRIVER_TO_MSG__ */
+++ /dev/null
-/**
- * @file driver/swap_driver_errors.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP driver error codes.
- */
-
-#ifndef __SWAP_DRIVER_ERRORS_H__
-#define __SWAP_DRIVER_ERRORS_H__
-
-
-/**
- * @enum _swap_driver_errors
- * @brief SWAP driver errors enumeration.
- */
-enum _swap_driver_errors {
- /**
- * @brief Success.
- */
- E_SD_SUCCESS = 0,
- /**
- * @brief Alloc_chrdev_region failed.
- */
- E_SD_ALLOC_CHRDEV_FAIL = 1,
- /**
- * @brief cdev_alloc failed.
- */
- E_SD_CDEV_ALLOC_FAIL = 2,
- /**
- * @brief cdev_add failed.
- */
- E_SD_CDEV_ADD_FAIL = 3,
- /**
- * @brief class_create failed.
- */
- E_SD_CLASS_CREATE_FAIL = 4,
- /**
- * @brief device_create failed.
- */
- E_SD_DEVICE_CREATE_FAIL = 5,
- /**
- * @brief splice_* funcs not found.
- */
- E_SD_NO_SPLICE_FUNCS = 6,
- /**
- * @brief swap_buffer_get tells us that there is no readable subbuffers.
- */
- E_SD_NO_DATA_TO_READ = 7,
- /**
- * @brief No busy subbuffer.
- */
- E_SD_NO_BUSY_SUBBUFFER = 8,
- /**
- * @brief Wrong subbuffer pointer passed to swap_buffer module.
- */
- E_SD_WRONG_SUBBUFFER_PTR = 9,
- /**
- * @brief Unhandled swap_buffer error.
- */
- E_SD_BUFFER_ERROR = 10,
- /**
- * @brief Write to subbuffer error.
- */
- E_SD_WRITE_ERROR = 11,
- /**
- * @brief Arguments, been passed to the func, doesn't pass sanity check.
- */
- E_SD_WRONG_ARGS = 12,
- /**
- * @brief No memory to allocate.
- */
- E_SD_NO_MEMORY = 13,
- /**
- * @brief swap_buffer uninitialization error.
- */
- E_SD_UNINIT_ERROR = 14,
- /**
- * @brief Netlink init error.
- */
- E_SD_NL_INIT_ERR = 15,
- /**
- * @brief Netlink message send error.
- */
- E_SD_NL_MSG_ERR = 16,
- /**
- * @brief No daemon pid in us_interaction.
- */
- E_SD_NO_DAEMON_PID = 17,
- /**
- * @brief Buffer already enabled
- */
- E_SD_BUFFER_ENABLED = 18,
- /**
- * @brief Buffer already disabled
- */
- E_SD_BUFFER_DISABLED = 19,
-};
-
-#endif /* __SWAP_DRIVER_ERRORS_H__ */
+++ /dev/null
-/**
- * driver/swap_driver_module.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP drive module interface implementation.
- */
-
-#include <linux/module.h>
-#include <master/swap_initializer.h>
-#include "driver_defs.h"
-#include "device_driver.h"
-#include "us_interaction.h"
-#include "driver_debugfs.h"
-
-static int fs_init(void)
-{
- int ret;
-
- ret = swap_device_init();
- if (ret)
- goto dev_init_fail;
-
- ret = us_interaction_create();
- if (ret)
- print_err("Cannot initialize netlink socket\n");
-
- ret = driver_debugfs_init();
- if (ret)
- goto us_int_destroy;
-
- print_msg("Driver module initialized\n");
-
- return ret;
-
-us_int_destroy:
- us_interaction_destroy();
-dev_init_fail:
- swap_device_exit();
-
- return ret;
-}
-
-static void fs_uninit(void)
-{
- driver_debugfs_uninit();
- us_interaction_destroy();
- swap_device_exit();
- print_msg("Driver module uninitialized\n");
-}
-
-SWAP_LIGHT_INIT_MODULE(NULL, NULL, NULL, fs_init, fs_uninit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP device driver");
-MODULE_AUTHOR("Aksenov A.S.");
+++ /dev/null
-/**
- * @file driver/swap_ioctl.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Provides ioctl commands and recources for SWAP driver.
- */
-
-#ifndef __SWAP_IOCTL_H__
-#define __SWAP_IOCTL_H__
-
-#include <linux/ioctl.h>
-
-/** SWAP device magic number. */
-#define SWAP_DRIVER_IOC_MAGIC 0xAF
-
-/**
- * @struct buffer_initialize
- * @brief SWAP buffer initialization struct.
- * @var buffer_initialize::size
- * Size of one subbuffer.
- * @var buffer_initialize::count
- * Count of subbuffers in the buffer.
- */
-struct buffer_initialize {
- u32 size;
- u32 count;
-} __packed;
-
-/* SWAP Device ioctl commands */
-
-/** Initialize buffer message. */
-#define SWAP_DRIVER_BUFFER_INITIALIZE _IOW(SWAP_DRIVER_IOC_MAGIC, 1, \
- struct buffer_initialize *)
-/** Uninitialize buffer message. */
-#define SWAP_DRIVER_BUFFER_UNINITIALIZE _IO(SWAP_DRIVER_IOC_MAGIC, 2)
-/** Set next buffer to read. */
-#define SWAP_DRIVER_NEXT_BUFFER_TO_READ _IO(SWAP_DRIVER_IOC_MAGIC, 3)
-/** Flush buffers. */
-#define SWAP_DRIVER_FLUSH_BUFFER _IO(SWAP_DRIVER_IOC_MAGIC, 4)
-/** Custom message. */
-#define SWAP_DRIVER_MSG _IOW(SWAP_DRIVER_IOC_MAGIC, 5, \
- void *)
-/** Force wake up daemon. */
-#define SWAP_DRIVER_WAKE_UP _IO(SWAP_DRIVER_IOC_MAGIC, 6)
-
-#endif /* __SWAP_IOCTL_H__ */
+++ /dev/null
-/**
- * driver/us_interaction.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * Kernel-to-user interface implementation.
- */
-
-
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/types.h>
-#include <linux/connector.h>
-#include <linux/slab.h>
-
-#include "us_interaction.h"
-#include "us_interaction_msg.h"
-#include "swap_driver_errors.h"
-#include "driver_defs.h"
-
-
-/* Connector id struct */
-static struct cb_id cn_swap_id = {CN_SWAP_IDX, CN_SWAP_VAL};
-
-/* Swap connector name */
-static const char cn_swap_name[] = "cn_swap";
-
-/* Send messages counter */
-static u32 msg_counter;
-
-/**
- * @brief Sends message to userspace via netlink.
- *
- * @param data Pointer to the data to be send.
- * @param size Size of the data to be send.
- * @return 0 on success, error code on error.
- */
-int us_interaction_send_msg(const void *data, size_t size)
-{
- struct cn_msg *msg;
- int ret;
-
- msg = kzalloc(sizeof(*msg) + size, GFP_ATOMIC);
- if (msg == NULL)
- return -E_SD_NO_MEMORY;
-
- memcpy(&msg->id, &cn_swap_id, sizeof(msg->id));
- msg->seq = msg_counter;
- msg->len = size;
- memcpy(msg->data, data, msg->len);
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 25)
- ret = cn_netlink_send(msg, CN_DAEMON_GROUP, GFP_ATOMIC);
-#else /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0) */
- ret = cn_netlink_send(msg, 0, CN_DAEMON_GROUP, GFP_ATOMIC);
-#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0) */
- if (ret < 0)
- goto fail_send;
- kfree(msg);
-
- msg_counter++;
-
- return E_SD_SUCCESS;
-
-fail_send:
- kfree(msg);
-
- return ret;
-}
-
-static void us_interaction_recv_msg(struct cn_msg *msg,
- struct netlink_skb_parms *nsp)
-{
-}
-
-/**
- * @brief Creates netlink connection.
- *
- * @return 0 on success, error code on error.
- */
-int us_interaction_create(void)
-{
- int res;
-
- res = cn_add_callback(&cn_swap_id,
- cn_swap_name,
- us_interaction_recv_msg);
- if (res)
- return -E_SD_NL_INIT_ERR;
-
- return E_SD_SUCCESS;
-}
-
-/**
- * @brief Destroy netlink connection.
- *
- * @return Void.
- */
-void us_interaction_destroy(void)
-{
- cn_del_callback(&cn_swap_id);
-}
+++ /dev/null
-/**
- * @file driver/us_interaction.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * Kernel-to-user interface definition.
- */
-
-#ifndef __US_INTERACTION_H__
-#define __US_INTERACTION_H__
-
-#include <linux/version.h>
-
-#ifdef CONFIG_CONNECTOR
-
-int us_interaction_create(void);
-void us_interaction_destroy(void);
-int us_interaction_send_msg(const void *data, size_t size);
-
-#else /* CONFIG_CONNECTOR */
-
-static inline int us_interaction_create(void)
-{
- return -EPERM;
-}
-
-static inline void us_interaction_destroy(void)
-{
-}
-
-static inline int us_interaction_send_msg(const void *data, size_t size)
-{
- return -EPERM;
-}
-
-#endif /* CONFIG_CONNECTOR */
-
-#endif /* __US_INTERACTION_H__ */
+++ /dev/null
-/**
- * @file driver/us_interaction_msg.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * Netlink messages declaration.
- */
-
-#ifndef __US_INTERACTION_MSG_H__
-#define __US_INTERACTION_MSG_H__
-
-#define CN_SWAP_IDX 0x22 /**< Should be unique throughout the system */
-#define CN_SWAP_VAL 0x1 /**< Just the same in kernel and user */
-#define CN_DAEMON_GROUP 0x1 /**< Listener group. Connector works a bit
- * faster when using one */
-
-/**
- * @enum us_interaction_k2u_msg_t
- * @brief Kernel-to-user netlink messages headers.
- */
-enum us_interaction_k2u_msg_t {
- /**
- * @brief Make daemon pause apps.
- */
- US_INT_PAUSE_APPS = 1,
- /**
- * @brief Make daemon continue apps.
- */
- US_INT_CONT_APPS = 2
-};
-
-#endif /* __US_INTERACTION_MSG_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers \
- $(src)/../us_manager/Module.symvers \
- $(src)/../driver/Module.symvers
-
-
-
-
-
-###############################################################################
-### swap energy module description ###
-###############################################################################
-obj-m := swap_energy.o
-swap_energy-y := energy_module.o \
- energy.o \
- rational_debugfs.o \
- debugfs_energy.o \
- lcd/lcd_base.o \
- lcd/lcd_debugfs.o
-
-
-
-
-
-###############################################################################
-### math support ###
-###############################################################################
-# S6E8AA0:
-ifeq ($(CONFIG_LCD_S6E8AA0), y)
- swap_energy-y += lcd/s6e8aa0.o
- LCD_FUNC_LIST += s6e8aa0
-endif
-
-
-# PANEL_S6E8AA0:
-ifeq ($(CONFIG_DISPLAY_PANEL_S6E8AA0), y)
- swap_energy-y += lcd/s6e8aa0_panel.o
- LCD_FUNC_LIST += s6e8aa0_panel
-endif
-
-
-# MARU:
-ifeq ($(CONFIG_MARU_BACKLIGHT), y)
- swap_energy-y += lcd/maru.o
- LCD_FUNC_LIST += maru
-endif
-
-
-
-
-
-###############################################################################
-### description functions ###
-###############################################################################
-LCD_FUNC_ARGS := void
-LCD_FUNC_RET := struct lcd_ops *
-
-
-
-
-
-###############################################################################
-### generate defines ###
-###############################################################################
-LCD_PREFIX := lcd_energy_
-
-# add prefix
-TMP := $(foreach it, $(LCD_FUNC_LIST), $(LCD_PREFIX)$(it))
-LCD_FUNC_LIST := $(TMP)
-
-# generate DEFINITION_LCD_FUNC
-TMP := ($(LCD_FUNC_ARGS));
-DEFINITION_LCD_FUNC := DEFINITION_LCD_FUNC=\
-$(foreach it, $(LCD_FUNC_LIST), "extern" $(LCD_FUNC_RET) $(it)$(TMP))
-
-
-# generate DEFINITION_LCD_ARRAY
-COMMA := ,
-AND := &
-DEFINITION_LCD_ARRAY := DEFINITION_LCD_ARRAY=\
-"{" $(foreach it, $(LCD_FUNC_LIST), &$(it),) "}"
-
-
-# generate LCD_MAKE_FNAME
-LCD_MAKE_FNAME := LCD_MAKE_FNAME(name)=$(LCD_PREFIX)\#\#name
-
-
-
-
-
-###############################################################################
-### add generate defines to EXTRA_CFLAGS ###
-###############################################################################
-EXTRA_CFLAGS += -D"$(DEFINITION_LCD_FUNC)" \
- -D"$(DEFINITION_LCD_ARRAY)" \
- -D"$(LCD_MAKE_FNAME)"
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * energy/debugfs_energy.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/math64.h>
-#include <master/swap_debugfs.h>
-#include "energy.h"
-#include "debugfs_energy.h"
-#include "rational_debugfs.h"
-#include "lcd/lcd_debugfs.h"
-#include "lcd/lcd_base.h"
-
-
-/* CPU running */
-static DEFINE_RATIONAL(cpu0_running_coef); /* boot core uses distinct coeff */
-static DEFINE_RATIONAL(cpuN_running_coef);
-
-static u64 __energy_cpu0(enum parameter_energy pe)
-{
- u64 times[NR_CPUS] = { 0 };
- u64 val = 0;
-
- /* TODO: make for only cpu0 */
- if (get_parameter_energy(pe, times, sizeof(times)) == 0) {
- val = div_u64(times[0] * cpu0_running_coef.num,
- cpu0_running_coef.denom);
- }
-
- return val;
-}
-
-static u64 __energy_cpuN(enum parameter_energy pe)
-{
- u64 times[NR_CPUS] = { 0 };
- u64 val = 0;
-
- if (get_parameter_energy(pe, times, sizeof(times)) == 0) {
- int i;
-
- for (i = 1; i < NR_CPUS; i++)
- val += div_u64(times[i] * cpuN_running_coef.num,
- cpuN_running_coef.denom);
- }
-
- return val;
-}
-
-static u64 cpu0_system(void)
-{
- return __energy_cpu0(PE_TIME_SYSTEM);
-}
-
-static u64 cpuN_system(void)
-{
- return __energy_cpuN(PE_TIME_SYSTEM);
-}
-
-static u64 cpu0_apps(void)
-{
- return __energy_cpu0(PE_TIME_APPS);
-}
-
-static u64 cpuN_apps(void)
-{
- return __energy_cpuN(PE_TIME_APPS);
-}
-
-
-/* CPU idle */
-static DEFINE_RATIONAL(cpu_idle_coef);
-
-static u64 cpu_idle_system(void)
-{
- u64 time = 0;
-
- get_parameter_energy(PE_TIME_IDLE, &time, sizeof(time));
- return div_u64(time * cpu_idle_coef.num, cpu_idle_coef.denom);
-}
-
-
-/* flash read */
-static DEFINE_RATIONAL(fr_coef);
-
-static u64 fr_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_READ_SYSTEM, &byte, sizeof(byte));
- return div_u64(byte * fr_coef.num, fr_coef.denom);
-}
-
-static u64 fr_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_READ_APPS, &byte, sizeof(byte));
- return div_u64(byte * fr_coef.num, fr_coef.denom);
-}
-
-
-/* flash write */
-static DEFINE_RATIONAL(fw_coef);
-
-static u64 fw_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_WRITE_SYSTEM, &byte, sizeof(byte));
- return div_u64(byte * fw_coef.num, fw_coef.denom);
-}
-
-static u64 fw_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_WRITE_APPS, &byte, sizeof(byte));
- return div_u64(byte * fw_coef.num, fw_coef.denom);
-}
-
-
-/* wifi recv */
-static DEFINE_RATIONAL(wf_recv_coef);
-
-static u64 wf_recv_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_WF_RECV_SYSTEM, &byte, sizeof(byte));
-
- return div_u64(byte * wf_recv_coef.num, wf_recv_coef.denom);
-}
-
-static u64 wf_recv_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_WF_RECV_APPS, &byte, sizeof(byte));
-
- return div_u64(byte * wf_recv_coef.num, wf_recv_coef.denom);
-}
-
-/* wifi send */
-static DEFINE_RATIONAL(wf_send_coef);
-
-static u64 wf_send_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_WF_SEND_SYSTEM, &byte, sizeof(byte));
-
- return div_u64(byte * wf_send_coef.num, wf_send_coef.denom);
-}
-
-static u64 wf_send_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_WF_SEND_APPS, &byte, sizeof(byte));
-
- return div_u64(byte * wf_send_coef.num, wf_send_coef.denom);
-}
-
-/* l2cap_recv_acldata */
-static DEFINE_RATIONAL(l2cap_recv_acldata_coef);
-
-static u64 l2cap_recv_acldata_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_L2CAP_RECV_SYSTEM, &byte, sizeof(byte));
-
- return div_u64(byte * l2cap_recv_acldata_coef.num,
- l2cap_recv_acldata_coef.denom);
-}
-
-static u64 l2cap_recv_acldata_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_L2CAP_RECV_APPS, &byte, sizeof(byte));
-
- return div_u64(byte * l2cap_recv_acldata_coef.num,
- l2cap_recv_acldata_coef.denom);
-}
-
-/* sco_recv_scodata */
-static DEFINE_RATIONAL(sco_recv_scodata_coef);
-
-static u64 sco_recv_scodata_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_SCO_RECV_SYSTEM, &byte, sizeof(byte));
-
- return div_u64(byte * sco_recv_scodata_coef.num,
- sco_recv_scodata_coef.denom);
-}
-
-static u64 sco_recv_scodata_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PE_SCO_RECV_APPS, &byte, sizeof(byte));
-
- return div_u64(byte * sco_recv_scodata_coef.num,
- sco_recv_scodata_coef.denom);
-}
-
-/* hci_send_acl */
-static DEFINE_RATIONAL(hci_send_acl_coef);
-
-static u64 hci_send_acl_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PT_SEND_ACL_SYSTEM, &byte, sizeof(byte));
-
- return div_u64(byte * hci_send_acl_coef.num, hci_send_acl_coef.denom);
-}
-
-static u64 hci_send_acl_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PT_SEND_ACL_APPS, &byte, sizeof(byte));
-
- return div_u64(byte * hci_send_acl_coef.num, hci_send_acl_coef.denom);
-}
-
-/* hci_send_sco */
-static DEFINE_RATIONAL(hci_send_sco_coef);
-
-static u64 hci_send_sco_system(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PT_SEND_SCO_SYSTEM, &byte, sizeof(byte));
-
- return div_u64(byte * hci_send_sco_coef.num, hci_send_sco_coef.denom);
-}
-
-static u64 hci_send_sco_apps(void)
-{
- u64 byte = 0;
-
- get_parameter_energy(PT_SEND_SCO_APPS, &byte, sizeof(byte));
-
- return div_u64(byte * hci_send_sco_coef.num, hci_send_sco_coef.denom);
-}
-
-
-
-
-
-/* ============================================================================
- * === PARAMETERS ===
- * ============================================================================
- */
-static int get_func_u64(void *data, u64 *val)
-{
- u64 (*func)(void) = data;
- *val = func();
- return 0;
-}
-
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_get_u64, get_func_u64, NULL, "%llu\n");
-
-
-struct param_data {
- char *name;
- struct rational *coef;
- u64 (*system)(void);
- u64 (*apps)(void);
-};
-
-static struct dentry *create_parameter(struct dentry *parent,
- struct param_data *param)
-{
- struct dentry *name, *system, *apps = NULL;
-
- name = swap_debugfs_create_dir(param->name, parent);
- if (name == NULL)
- return NULL;
-
- system = swap_debugfs_create_file("system", 0600, name, param->system,
- &fops_get_u64);
- if (system == NULL)
- goto rm_name;
-
- if (param->apps) {
- apps = swap_debugfs_create_file("apps", 0600, name, param->apps,
- &fops_get_u64);
- if (apps == NULL)
- goto rm_system;
- }
-
- if (create_rational_files(name, param->coef,
- "numerator", "denominator"))
- goto rm_apps;
-
- return name;
-
-rm_apps:
- if (param->apps)
- debugfs_remove(apps);
-rm_system:
- debugfs_remove(system);
-rm_name:
- debugfs_remove(name);
-
- return NULL;
-}
-
-struct param_data parameters[] = {
- {
- .name = "cpu_running",
- .coef = &cpu0_running_coef,
- .system = cpu0_system,
- .apps = cpu0_apps
- },
- {
- .name = "cpuN_running",
- .coef = &cpuN_running_coef,
- .system = cpuN_system,
- .apps = cpuN_apps
- },
- {
- .name = "cpu_idle",
- .coef = &cpu_idle_coef,
- .system = cpu_idle_system,
- .apps = NULL
- },
- {
- .name = "flash_read",
- .coef = &fr_coef,
- .system = fr_system,
- .apps = fr_apps
- },
- {
- .name = "flash_write",
- .coef = &fw_coef,
- .system = fw_system,
- .apps = fw_apps
- },
- {
- .name = "wf_recv",
- .coef = &wf_recv_coef,
- .system = wf_recv_system,
- .apps = wf_recv_apps
- },
- {
- .name = "wf_send",
- .coef = &wf_send_coef,
- .system = wf_send_system,
- .apps = wf_send_apps
- },
- {
- .name = "sco_recv_scodata",
- .coef = &sco_recv_scodata_coef,
- .system = sco_recv_scodata_system,
- .apps = sco_recv_scodata_apps
- },
- {
- .name = "l2cap_recv_acldata",
- .coef = &l2cap_recv_acldata_coef,
- .system = l2cap_recv_acldata_system,
- .apps = l2cap_recv_acldata_apps
- },
- {
- .name = "hci_send_acl",
- .coef = &hci_send_acl_coef,
- .system = hci_send_acl_system,
- .apps = hci_send_acl_apps
- },
- {
- .name = "hci_send_sco",
- .coef = &hci_send_sco_coef,
- .system = hci_send_sco_system,
- .apps = hci_send_sco_apps
- }
-};
-
-enum {
- parameters_cnt = sizeof(parameters) / sizeof(struct param_data)
-};
-
-
-
-
-
-/* ============================================================================
- * === INIT/EXIT ===
- * ============================================================================
- */
-static struct dentry *energy_dir;
-
-/**
- * @brief Destroy debugfs for LCD
- *
- * @return Dentry of energy debugfs
- */
-struct dentry *get_energy_dir(void)
-{
- return energy_dir;
-}
-
-/**
- * @brief Destroy debugfs for energy
- *
- * @return Void
- */
-void exit_debugfs_energy(void)
-{
- lcd_exit();
- exit_lcd_debugfs();
-
- if (energy_dir)
- debugfs_remove_recursive(energy_dir);
-
- energy_dir = NULL;
-}
-
-/**
- * @brief Create debugfs for energy
- *
- * @return Error code
- */
-int init_debugfs_energy(void)
-{
- int i;
- struct dentry *swap_dir, *dentry;
-
- swap_dir = swap_debugfs_getdir();
- if (swap_dir == NULL)
- return -ENOENT;
-
- energy_dir = swap_debugfs_create_dir("energy", swap_dir);
- if (energy_dir == NULL)
- return -ENOMEM;
-
- for (i = 0; i < parameters_cnt; ++i) {
- dentry = create_parameter(energy_dir, ¶meters[i]);
- if (dentry == NULL)
- goto fail;
- }
-
- if (init_lcd_debugfs(energy_dir))
- goto fail;
-
- /* Actually, the only goal of lcd_init() is to register lcd screen's
- debugfs, so it is called here. */
- if (lcd_init()) {
- exit_lcd_debugfs();
- }
-
- return 0;
-
-fail:
- exit_debugfs_energy();
- return -ENOMEM;
-}
+++ /dev/null
-#ifndef _DEBUGFS_ENERGY_H
-#define _DEBUGFS_ENERGY_H
-
-/**
- * @file energy/debugfs_energy.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- * Debugfs for energy
- */
-
-
-#include <linux/fs.h>
-#include <master/swap_initializer.h>
-
-
-struct dentry;
-
-
-/* based on define DEFINE_SIMPLE_ATTRIBUTE */
-#define SWAP_DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \
-static int __fops ## _open(struct inode *inode, struct file *file) \
-{ \
- int ret; \
- \
- ret = swap_init_simple_open(inode, file); \
- if (ret) \
- return ret; \
- \
- __simple_attr_check_format(__fmt, 0ull); \
- ret = simple_attr_open(inode, file, __get, __set, __fmt); \
- if (ret) \
- swap_init_simple_release(inode, file); \
- \
- return ret; \
-} \
-static int __fops ## _release(struct inode *inode, struct file *file) \
-{ \
- simple_attr_release(inode, file); \
- swap_init_simple_release(inode, file); \
- \
- return 0; \
-} \
-static const struct file_operations __fops = { \
- .owner = THIS_MODULE, \
- .open = __fops ## _open, \
- .release = __fops ## _release, \
- .read = simple_attr_read, \
- .write = simple_attr_write, \
- .llseek = generic_file_llseek, \
-}
-
-
-int init_debugfs_energy(void);
-void exit_debugfs_energy(void);
-
-struct dentry *get_energy_dir(void);
-
-
-#endif /* _DEBUGFS_ENERGY_H */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/energy/swap_energy.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vasiliy Ulyanov <v.ulyanov@samsung.com>
- * Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <linux/file.h>
-#include <linux/spinlock.h>
-#include <linux/magic.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/net.h>
-#include <linux/socket.h>
-#include <linux/skbuff.h>
-#include <linux/string.h>
-#include <linux/fdtable.h>
-#include <net/sock.h>
-#include <ksyms/ksyms.h>
-#include <master/swap_deps.h>
-#include <swap-asm/swap_kprobes.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include <us_manager/sspt/sspt_feature.h>
-#include <linux/atomic.h>
-
-#ifdef CONFIG_SWAP_HOOK_SWITCH_TO
-# include <swap/hook_switch_to.h>
-#else /* CONFIG_SWAP_HOOK_SWITCH_TO */
-# include <kprobe/swap_kprobes.h>
-#endif /* CONFIG_SWAP_HOOK_SWITCH_TO */
-
-#ifdef CONFIG_SWAP_HOOK_ENERGY
-# include <swap/hook_syscall.h>
-# include <swap/hook_energy.h>
-# include <kprobe/swap_td_raw.h>
-#else /* CONFIG_SWAP_HOOK_ENERGY */
-# include <kprobe/swap_kprobes.h>
-#endif /* CONFIG_SWAP_HOOK_ENERGY */
-
-#include "energy.h"
-#include "lcd/lcd_base.h"
-#include "tm_stat.h"
-
-
-#ifndef CONFIG_SWAP_HOOK_ENERGY
-/* ============================================================================
- * = ENERGY_XXX =
- * ============================================================================
- */
-struct kern_probe {
- const char *name;
- struct kretprobe *rp;
-};
-
-static int energy_xxx_once(struct kern_probe p[], int size)
-{
- int i;
- const char *sym;
-
- for (i = 0; i < size; ++i) {
- struct kretprobe *rp = p[i].rp;
-
- sym = p[i].name;
- rp->kp.addr = swap_ksyms(sym);
- if (rp->kp.addr == 0)
- goto not_found;
- }
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
-static int energy_xxx_set(struct kern_probe p[], int size, int *flag)
-{
- int i, ret;
-
- for (i = 0; i < size; ++i) {
- ret = swap_register_kretprobe(p[i].rp);
- if (ret)
- goto fail;
- }
-
- *flag = 1;
- return 0;
-
-fail:
- pr_err("swap_register_kretprobe(%s) ret=%d\n", p[i].name, ret);
-
- for (--i; i != -1; --i)
- swap_unregister_kretprobe(p[i].rp);
-
- return ret;
-}
-
-static void energy_xxx_unset(struct kern_probe p[], int size, int *flag)
-{
- int i;
-
- if (*flag == 0)
- return;
-
- for (i = size - 1; i != -1; --i)
- swap_unregister_kretprobe(p[i].rp);
-
- *flag = 0;
-}
-#endif /* CONFIG_SWAP_HOOK_ENERGY */
-
-
-
-
-
-/* ============================================================================
- * = CPUS_TIME =
- * ============================================================================
- */
-struct cpus_time {
- spinlock_t lock; /* for concurrent access */
- struct tm_stat tm[NR_CPUS];
-};
-
-#define cpus_time_lock(ct, flags) spin_lock_irqsave(&(ct)->lock, flags)
-#define cpus_time_unlock(ct, flags) spin_unlock_irqrestore(&(ct)->lock, flags)
-
-static void cpus_time_init(struct cpus_time *ct, u64 time)
-{
- int cpu;
-
- spin_lock_init(&ct->lock);
-
- for (cpu = 0; cpu < NR_CPUS; ++cpu) {
- tm_stat_init(&ct->tm[cpu]);
- tm_stat_set_timestamp(&ct->tm[cpu], time);
- }
-}
-
-static inline u64 cpu_time_get_running(struct cpus_time *ct, int cpu, u64 now)
-{
- return tm_stat_current_running(&ct->tm[cpu], now);
-}
-
-static void *cpus_time_get_running_all(struct cpus_time *ct, u64 *buf, u64 now)
-{
- int cpu;
-
- for (cpu = 0; cpu < NR_CPUS; ++cpu)
- buf[cpu] = tm_stat_current_running(&ct->tm[cpu], now);
-
- return buf;
-}
-
-static void *cpus_time_sum_running_all(struct cpus_time *ct, u64 *buf, u64 now)
-{
- int cpu;
-
- for (cpu = 0; cpu < NR_CPUS; ++cpu)
- buf[cpu] += tm_stat_current_running(&ct->tm[cpu], now);
-
- return buf;
-}
-
-static void cpus_time_save_entry(struct cpus_time *ct, int cpu, u64 time)
-{
- struct tm_stat *tm = &ct->tm[cpu];
-
- if (unlikely(tm_stat_timestamp(tm))) /* should never happen */
- printk(KERN_INFO "XXX %s[%d/%d]: WARNING tmstamp(%p) set on cpu(%d)\n",
- current->comm, current->tgid, current->pid, tm, cpu);
- tm_stat_set_timestamp(&ct->tm[cpu], time);
-}
-
-static void cpus_time_update_running(struct cpus_time *ct, int cpu, u64 now,
- u64 start_time)
-{
- struct tm_stat *tm = &ct->tm[cpu];
-
- if (unlikely(tm_stat_timestamp(tm) == 0)) {
- /* not initialized. should happen only once per cpu/task */
- printk(KERN_INFO "XXX %s[%d/%d]: nnitializing tmstamp(%p) "
- "on cpu(%d)\n",
- current->comm, current->tgid, current->pid, tm, cpu);
- tm_stat_set_timestamp(tm, start_time);
- }
-
- tm_stat_update(tm, now);
- tm_stat_set_timestamp(tm, 0); /* set timestamp to 0 */
-}
-
-
-
-
-
-struct energy_data {
- /* for __switch_to */
- struct cpus_time ct;
-
- /* for sys_read */
- atomic64_t bytes_read;
-
- /*for sys_write */
- atomic64_t bytes_written;
-
- /*for recvmsg*/
- atomic64_t bytes_recv;
-
- /* for sock_send */
- atomic64_t bytes_send;
-
- /* for l2cap_recv */
- atomic64_t bytes_l2cap_recv_acldata;
-
- /* for sco_recv_scodata */
- atomic64_t bytes_sco_recv_scodata;
-
- /* for hci_send_acl */
- atomic64_t bytes_hci_send_acl;
-
- /* for hci_send_sco */
- atomic64_t bytes_hci_send_sco;
-};
-
-static sspt_feature_id_t feature_id = SSPT_FEATURE_ID_BAD;
-
-static void init_ed(struct energy_data *ed)
-{
- /* instead of get_ntime(), CPU time is initialized to 0 here. Timestamp
- * value will be properly set when the corresponding __switch_to event
- * occurs */
- cpus_time_init(&ed->ct, 0);
- atomic64_set(&ed->bytes_read, 0);
- atomic64_set(&ed->bytes_written, 0);
- atomic64_set(&ed->bytes_recv, 0);
- atomic64_set(&ed->bytes_send, 0);
- atomic64_set(&ed->bytes_l2cap_recv_acldata, 0);
- atomic64_set(&ed->bytes_sco_recv_scodata, 0);
- atomic64_set(&ed->bytes_hci_send_acl, 0);
- atomic64_set(&ed->bytes_hci_send_sco, 0);
-}
-
-static void uninit_ed(struct energy_data *ed)
-{
- cpus_time_init(&ed->ct, 0);
- atomic64_set(&ed->bytes_read, 0);
- atomic64_set(&ed->bytes_written, 0);
- atomic64_set(&ed->bytes_recv, 0);
- atomic64_set(&ed->bytes_send, 0);
- atomic64_set(&ed->bytes_l2cap_recv_acldata, 0);
- atomic64_set(&ed->bytes_sco_recv_scodata, 0);
- atomic64_set(&ed->bytes_hci_send_acl, 0);
- atomic64_set(&ed->bytes_hci_send_sco, 0);
-}
-
-static void *create_ed(void)
-{
- struct energy_data *ed;
-
- ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
- if (ed)
- init_ed(ed);
-
- return (void *)ed;
-}
-
-static void destroy_ed(void *data)
-{
- struct energy_data *ed = (struct energy_data *)data;
- kfree(ed);
-}
-
-
-static int init_feature(void)
-{
- feature_id = sspt_register_feature(create_ed, destroy_ed);
-
- if (feature_id == SSPT_FEATURE_ID_BAD)
- return -EPERM;
-
- return 0;
-}
-
-static void uninit_feature(void)
-{
- sspt_unregister_feature(feature_id);
- feature_id = SSPT_FEATURE_ID_BAD;
-}
-
-static struct energy_data *get_energy_data(struct task_struct *task)
-{
- void *data = NULL;
- struct sspt_proc *proc;
-
- proc = sspt_proc_by_task(task);
- if (proc)
- data = sspt_get_feature_data(proc->feature, feature_id);
-
- return (struct energy_data *)data;
-}
-
-static int check_fs(unsigned long magic)
-{
- switch (magic) {
- case EXT2_SUPER_MAGIC: /* == EXT3_SUPER_MAGIC == EXT4_SUPER_MAGIC */
- case MSDOS_SUPER_MAGIC:
- return 1;
- }
-
- return 0;
-}
-
-static int check_ftype(int fd)
-{
- int err, ret = 0;
- struct kstat kstat;
-
- err = vfs_fstat(fd, &kstat);
- if (err == 0 && S_ISREG(kstat.mode))
- ret = 1;
-
- return ret;
-}
-
-static int check_file(int fd)
-{
- struct file *file;
-
- file = fget(fd);
- if (file) {
- int magic = 0;
- if (file->f_path.dentry && file->f_path.dentry->d_sb)
- magic = file->f_path.dentry->d_sb->s_magic;
-
- fput(file);
-
- if (check_fs(magic) && check_ftype(fd))
- return 1;
- }
-
- return 0;
-}
-
-
-
-
-
-static struct cpus_time ct_idle;
-static struct energy_data ed_system;
-static u64 start_time;
-
-static void init_data_energy(void)
-{
- start_time = get_ntime();
- init_ed(&ed_system);
- cpus_time_init(&ct_idle, 0);
-}
-
-static void uninit_data_energy(void)
-{
- start_time = 0;
- uninit_ed(&ed_system);
- cpus_time_init(&ct_idle, 0);
-}
-
-
-
-
-
-/* ============================================================================
- * = __switch_to =
- * ============================================================================
- */
-static void do_entry_handler_switch(struct task_struct *task)
-{
- int cpu;
- struct cpus_time *ct;
- struct energy_data *ed;
- unsigned long flags;
-
- cpu = smp_processor_id();
-
- ct = task->tgid ? &ed_system.ct : &ct_idle;
- cpus_time_lock(ct, flags);
- cpus_time_update_running(ct, cpu, get_ntime(), start_time);
- cpus_time_unlock(ct, flags);
-
- ed = get_energy_data(task);
- if (ed) {
- ct = &ed->ct;
- cpus_time_lock(ct, flags);
- cpus_time_update_running(ct, cpu, get_ntime(), start_time);
- cpus_time_unlock(ct, flags);
- }
-}
-
-static void do_ret_handler_switch(struct task_struct *task)
-{
- int cpu;
- struct cpus_time *ct;
- struct energy_data *ed;
- unsigned long flags;
-
- cpu = smp_processor_id();
-
- ct = task->tgid ? &ed_system.ct : &ct_idle;
- cpus_time_lock(ct, flags);
- cpus_time_save_entry(ct, cpu, get_ntime());
- cpus_time_unlock(ct, flags);
-
- ed = get_energy_data(task);
- if (ed) {
- ct = &ed->ct;
- cpus_time_lock(ct, flags);
- cpus_time_save_entry(ct, cpu, get_ntime());
- cpus_time_unlock(ct, flags);
- }
-}
-
-#ifndef CONFIG_SWAP_HOOK_SWITCH_TO
-static int ret_handler_switch(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- do_ret_handler_switch(current);
- return 0;
-}
-
-static int entry_handler_switch(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- do_entry_handler_switch(current);
- return 0;
-}
-
-static struct kretprobe switch_to_krp = {
- .entry_handler = entry_handler_switch,
- .handler = ret_handler_switch,
-};
-#endif /* !CONFIG_SWAP_HOOK_SWITCH_TO */
-
-
-
-
-
-/* ============================================================================
- * = sys_read =
- * ============================================================================
- */
-struct sys_read_data {
- int fd;
-};
-
-#ifndef CONFIG_SWAP_HOOK_ENERGY
-static int entry_handler_sys_read(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct sys_read_data *srd = (struct sys_read_data *)ri->data;
-
- srd->fd = (int)swap_get_sarg(regs, 0);
-
- return 0;
-}
-
-static int ret_handler_sys_read(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
-
- if (ret > 0) {
- struct sys_read_data *srd;
-
- srd = (struct sys_read_data *)ri->data;
- if (check_file(srd->fd)) {
- struct energy_data *ed;
-
- ed = get_energy_data(current);
- if (ed)
- atomic64_add(ret, &ed->bytes_read);
-
- atomic64_add(ret, &ed_system.bytes_read);
- }
- }
-
- return 0;
-}
-
-static struct kretprobe sys_read_krp = {
- .entry_handler = entry_handler_sys_read,
- .handler = ret_handler_sys_read,
- .data_size = sizeof(struct sys_read_data)
-};
-
-
-
-
-
-/* ============================================================================
- * = sys_write =
- * ============================================================================
- */
-static int entry_handler_sys_write(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct sys_read_data *srd = (struct sys_read_data *)ri->data;
-
- srd->fd = (int)swap_get_sarg(regs, 0);
-
- return 0;
-}
-
-static int ret_handler_sys_write(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
-
- if (ret > 0) {
- struct sys_read_data *srd;
-
- srd = (struct sys_read_data *)ri->data;
- if (check_file(srd->fd)) {
- struct energy_data *ed;
-
- ed = get_energy_data(current);
- if (ed)
- atomic64_add(ret, &ed->bytes_written);
-
- atomic64_add(ret, &ed_system.bytes_written);
- }
- }
-
- return 0;
-}
-
-static struct kretprobe sys_write_krp = {
- .entry_handler = entry_handler_sys_write,
- .handler = ret_handler_sys_write,
- .data_size = sizeof(struct sys_read_data)
-};
-#endif /* !CONFIG_SWAP_HOOK_ENERGY */
-
-
-
-
-
-/* ============================================================================
- * = wifi =
- * ============================================================================
- */
-static bool check_wlan0(struct socket *sock)
-{
- /* FIXME: hardcode interface */
- const char *name_intrf = "wlan0";
-
- if (sock->sk->sk_dst_cache &&
- sock->sk->sk_dst_cache->dev &&
- !strcmp(sock->sk->sk_dst_cache->dev->name, name_intrf))
- return true;
-
- return false;
-}
-
-static bool check_socket(struct task_struct *task, struct socket *socket)
-{
- bool ret = false;
- unsigned int fd;
- struct files_struct *files;
-
- files = swap_get_files_struct(task);
- if (files == NULL)
- return false;
-
- rcu_read_lock();
- for (fd = 0; fd < files_fdtable(files)->max_fds; ++fd) {
- if (fcheck_files(files, fd) == socket->file) {
- ret = true;
- goto unlock;
- }
- }
-
-unlock:
- rcu_read_unlock();
- swap_put_files_struct(files);
- return ret;
-}
-
-static struct energy_data *get_energy_data_by_socket(struct task_struct *task,
- struct socket *socket)
-{
- struct energy_data *ed;
-
- ed = get_energy_data(task);
- if (ed)
- ed = check_socket(task, socket) ? ed : NULL;
-
- return ed;
-}
-
-#ifndef CONFIG_SWAP_HOOK_ENERGY
-static int wf_sock_eh(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct socket *socket = (struct socket *)swap_get_karg(regs, 0);
-
- *(struct socket **)ri->data = socket;
-
- return 0;
-}
-
-static int wf_sock_aio_eh(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct kiocb *iocb = (struct kiocb *)swap_get_karg(regs, 0);
- struct socket *socket = iocb->ki_filp->private_data;
-
- *(struct socket **)ri->data = socket;
-
- return 0;
-}
-#endif /* CONFIG_SWAP_HOOK_ENERGY */
-
-static void calc_wifi_recv_energy(struct socket *sock, int len)
-{
- struct energy_data *ed;
-
- if (len <= 0 || !check_wlan0(sock))
- return;
-
- ed = get_energy_data_by_socket(current, sock);
- if (ed)
- atomic64_add(len, &ed->bytes_recv);
- atomic64_add(len, &ed_system.bytes_recv);
-}
-
-static void calc_wifi_send_energy(struct socket *sock, int len)
-{
- struct energy_data *ed;
-
- if (len <= 0 || !check_wlan0(sock))
- return;
-
- ed = get_energy_data_by_socket(current, sock);
- if (ed)
- atomic64_add(len, &ed->bytes_send);
- atomic64_add(len, &ed_system.bytes_send);
-}
-
-#ifndef CONFIG_SWAP_HOOK_ENERGY
-static int wf_sock_recv_rh(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
-
- calc_wifi_recv_energy(*(struct socket **)ri->data, ret);
-
- return 0;
-}
-
-static int wf_sock_send_rh(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
-
- calc_wifi_send_energy(*(struct socket **)ri->data, ret);
-
- return 0;
-}
-
-static struct kretprobe sock_recv_krp = {
- .entry_handler = wf_sock_eh,
- .handler = wf_sock_recv_rh,
- .data_size = sizeof(struct socket *)
-};
-
-static struct kretprobe sock_send_krp = {
- .entry_handler = wf_sock_eh,
- .handler = wf_sock_send_rh,
- .data_size = sizeof(struct socket *)
-};
-
-static struct kretprobe sock_aio_read_krp = {
- .entry_handler = wf_sock_aio_eh,
- .handler = wf_sock_recv_rh,
- .data_size = sizeof(struct socket *)
-};
-
-static struct kretprobe sock_aio_write_krp = {
- .entry_handler = wf_sock_aio_eh,
- .handler = wf_sock_send_rh,
- .data_size = sizeof(struct socket *)
-};
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
-static const char sock_aio_read[] = "sock_read_iter";
-static const char sock_aio_write[] = "sock_write_iter";
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
-static const char sock_aio_read[] = "sock_aio_read";
-static const char sock_aio_write[] = "sock_aio_write";
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
-
-static struct kern_probe wifi_probes[] = {
- {
- .name = "sock_recvmsg",
- .rp = &sock_recv_krp,
- },
- {
- .name = "sock_sendmsg",
- .rp = &sock_send_krp,
- },
- {
- .name = sock_aio_read,
- .rp = &sock_aio_read_krp,
- },
- {
- .name = sock_aio_write,
- .rp = &sock_aio_write_krp,
- }
-};
-
-enum { wifi_probes_cnt = ARRAY_SIZE(wifi_probes) };
-static int wifi_flag = 0;
-#endif /* !CONFIG_SWAP_HOOK_ENERGY */
-
-
-
-
-
-/* ============================================================================
- * = bluetooth =
- * ============================================================================
- */
-
-struct swap_bt_data {
- struct socket *socket;
-};
-
-static void calc_bt_recv_energy(struct socket *sock, int len)
-{
- struct energy_data *ed;
-
- if (len <= 0 || !sock)
- return;
-
- ed = get_energy_data_by_socket(current, sock);
- if (ed)
- atomic64_add(len, &ed->bytes_l2cap_recv_acldata);
- atomic64_add(len, &ed_system.bytes_l2cap_recv_acldata);
-}
-
-static void calc_bt_send_energy(struct socket *sock, int len)
-{
- struct energy_data *ed;
-
- if (len <= 0 || !sock)
- return;
-
- ed = get_energy_data_by_socket(current, sock);
- if (ed)
- atomic64_add(len, &ed->bytes_hci_send_sco);
- atomic64_add(len, &ed_system.bytes_hci_send_sco);
-}
-
-#ifndef CONFIG_SWAP_HOOK_ENERGY
-static int bt_entry_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct swap_bt_data *data = (struct swap_bt_data *)ri->data;
- struct socket *sock = (struct socket *)swap_get_sarg(regs, 1);
-
- data->socket = sock ? sock : NULL;
-
- return 0;
-}
-
-static int bt_recvmsg_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- struct swap_bt_data *data = (struct swap_bt_data *)ri->data;
-
- calc_bt_recv_energy(data->socket, ret);
-
- return 0;
-}
-
-static int bt_sendmsg_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- struct swap_bt_data *data = (struct swap_bt_data *)ri->data;
-
- calc_bt_send_energy(data->socket, ret);
-
- return 0;
-}
-
-static struct kretprobe rfcomm_sock_recvmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_recvmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kretprobe l2cap_sock_recvmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_recvmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kretprobe hci_sock_recvmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_recvmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kretprobe sco_sock_recvmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_recvmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-static struct kretprobe rfcomm_sock_sendmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_sendmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kretprobe l2cap_sock_sendmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_sendmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kretprobe hci_sock_sendmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_sendmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kretprobe sco_sock_sendmsg_krp = {
- .entry_handler = bt_entry_handler,
- .handler = bt_sendmsg_handler,
- .data_size = sizeof(struct swap_bt_data)
-};
-
-static struct kern_probe bt_probes[] = {
- {
- .name = "rfcomm_sock_recvmsg",
- .rp = &rfcomm_sock_recvmsg_krp,
- },
- {
- .name = "l2cap_sock_recvmsg",
- .rp = &l2cap_sock_recvmsg_krp,
- },
- {
- .name = "hci_sock_recvmsg",
- .rp = &hci_sock_recvmsg_krp,
- },
- {
- .name = "sco_sock_recvmsg",
- .rp = &sco_sock_recvmsg_krp,
- },
- {
- .name = "rfcomm_sock_sendmsg",
- .rp = &rfcomm_sock_sendmsg_krp,
- },
- {
- .name = "l2cap_sock_sendmsg",
- .rp = &l2cap_sock_sendmsg_krp,
- },
- {
- .name = "hci_sock_sendmsg",
- .rp = &hci_sock_sendmsg_krp,
- },
- {
- .name = "sco_sock_sendmsg",
- .rp = &sco_sock_sendmsg_krp,
- }
-};
-
-enum { bt_probes_cnt = ARRAY_SIZE(bt_probes) };
-static int energy_bt_flag = 0;
-#endif /* CONFIG_SWAP_HOOK_ENERGY */
-
-enum parameter_type {
- PT_CPU,
- PT_READ,
- PT_WRITE,
- PT_WF_RECV,
- PT_WF_SEND,
- PT_L2CAP_RECV,
- PT_SCO_RECV,
- PT_SEND_ACL,
- PT_SEND_SCO
-};
-
-struct cmd_pt {
- enum parameter_type pt;
- void *buf;
- int sz;
-};
-
-static void callback_for_proc(struct sspt_proc *proc, void *data)
-{
- void *f_data = sspt_get_feature_data(proc->feature, feature_id);
- struct energy_data *ed = (struct energy_data *)f_data;
-
- if (ed) {
- unsigned long flags;
- struct cmd_pt *cmdp = (struct cmd_pt *)data;
- u64 *val = cmdp->buf;
-
- switch (cmdp->pt) {
- case PT_CPU:
- cpus_time_lock(&ed->ct, flags);
- cpus_time_sum_running_all(&ed->ct, val, get_ntime());
- cpus_time_unlock(&ed->ct, flags);
- break;
- case PT_READ:
- *val += atomic64_read(&ed->bytes_read);
- break;
- case PT_WRITE:
- *val += atomic64_read(&ed->bytes_written);
- break;
- case PT_WF_RECV:
- *val += atomic64_read(&ed->bytes_recv);
- break;
- case PT_WF_SEND:
- *val += atomic64_read(&ed->bytes_send);
- break;
- case PT_L2CAP_RECV:
- *val += atomic64_read(&ed->bytes_l2cap_recv_acldata);
- break;
- case PT_SCO_RECV:
- *val += atomic64_read(&ed->bytes_sco_recv_scodata);
- break;
- case PT_SEND_ACL:
- *val += atomic64_read(&ed->bytes_hci_send_acl);
- break;
- case PT_SEND_SCO:
- *val += atomic64_read(&ed->bytes_hci_send_sco);
- break;
- default:
- break;
- }
- }
-}
-
-static int current_parameter_apps(enum parameter_type pt, void *buf, int sz)
-{
- struct cmd_pt cmdp;
-
- cmdp.pt = pt;
- cmdp.buf = buf;
- cmdp.sz = sz;
-
- on_each_proc(callback_for_proc, (void *)&cmdp);
-
- return 0;
-}
-
-/**
- * @brief Get energy parameter
- *
- * @param pe Type of energy parameter
- * @param buf Buffer
- * @param sz Buffer size
- * @return Error code
- */
-int get_parameter_energy(enum parameter_energy pe, void *buf, size_t sz)
-{
- unsigned long flags;
- u64 *val = buf; /* currently all parameters are u64 vals */
- int ret = 0;
-
- switch (pe) {
- case PE_TIME_IDLE:
- cpus_time_lock(&ct_idle, flags);
- /* for the moment we consider only CPU[0] idle time */
- *val = cpu_time_get_running(&ct_idle, 0, get_ntime());
- cpus_time_unlock(&ct_idle, flags);
- break;
- case PE_TIME_SYSTEM:
- cpus_time_lock(&ed_system.ct, flags);
- cpus_time_get_running_all(&ed_system.ct, val, get_ntime());
- cpus_time_unlock(&ed_system.ct, flags);
- break;
- case PE_TIME_APPS:
- current_parameter_apps(PT_CPU, buf, sz);
- break;
- case PE_READ_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_read);
- break;
- case PE_WRITE_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_written);
- break;
- case PE_WF_RECV_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_recv);
- break;
- case PE_WF_SEND_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_send);
- break;
- case PE_L2CAP_RECV_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_l2cap_recv_acldata);
- break;
- case PE_SCO_RECV_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_sco_recv_scodata);
- break;
- case PT_SEND_ACL_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_hci_send_acl);
- break;
- case PT_SEND_SCO_SYSTEM:
- *val = atomic64_read(&ed_system.bytes_hci_send_sco);
- break;
- case PE_READ_APPS:
- current_parameter_apps(PT_READ, buf, sz);
- break;
- case PE_WRITE_APPS:
- current_parameter_apps(PT_WRITE, buf, sz);
- break;
- case PE_WF_RECV_APPS:
- current_parameter_apps(PT_WF_RECV, buf, sz);
- break;
- case PE_WF_SEND_APPS:
- current_parameter_apps(PT_WF_SEND, buf, sz);
- break;
- case PE_L2CAP_RECV_APPS:
- current_parameter_apps(PT_L2CAP_RECV, buf, sz);
- break;
- case PE_SCO_RECV_APPS:
- current_parameter_apps(PT_SCO_RECV, buf, sz);
- break;
- case PT_SEND_ACL_APPS:
- current_parameter_apps(PT_SEND_ACL, buf, sz);
- break;
- case PT_SEND_SCO_APPS:
- current_parameter_apps(PT_SEND_SCO, buf, sz);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-#ifdef CONFIG_SWAP_HOOK_ENERGY
-static struct swap_hook_energy hook_energy = {
- .bt_recvmsg = calc_bt_recv_energy,
- .bt_sendmsg = calc_bt_send_energy,
- .wifi_recvmsg = calc_wifi_recv_energy ,
- .wifi_sendmsg = calc_wifi_send_energy
-};
-
-
-static struct td_raw sys_call_tdraw;
-
-static void entry_hook_sys_rw(struct hook_syscall *self, struct pt_regs *regs)
-{
- struct sys_read_data *srd =
- (struct sys_read_data *)swap_td_raw(&sys_call_tdraw, current);
- srd->fd = (int)swap_get_sarg(regs, 0);
-}
-
-static void return_hook_sys_read(struct hook_syscall *self,
- struct pt_regs *regs)
-{
- struct sys_read_data *srd;
- struct energy_data *ed;
- int ret = regs_return_value(regs);
-
- if (ret <= 0)
- return;
-
- srd = (struct sys_read_data *)swap_td_raw(&sys_call_tdraw, current);
- if (!check_file(srd->fd))
- return;
-
- ed = get_energy_data(current);
- if (ed)
- atomic64_add(ret, &ed->bytes_read);
- atomic64_add(ret, &ed_system.bytes_read);
-}
-
-static void return_hook_sys_write(struct hook_syscall *self,
- struct pt_regs *regs)
-{
- struct sys_read_data *srd;
- struct energy_data *ed;
- int ret = regs_return_value(regs);
-
- if (ret > 0)
- return;
-
- srd = (struct sys_read_data *)swap_td_raw(&sys_call_tdraw, current);
- if (!check_file(srd->fd))
- return;
-
- ed = get_energy_data(current);
- if (ed)
- atomic64_add(ret, &ed->bytes_written);
- atomic64_add(ret, &ed_system.bytes_written);
-}
-
-static struct hook_syscall sys_read_hook = {
- .entry = entry_hook_sys_rw,
- .exit = return_hook_sys_read
-};
-
-static struct hook_syscall sys_write_hook = {
- .entry = entry_hook_sys_rw,
- .exit = return_hook_sys_write
-};
-
-
-# ifdef CONFIG_SWAP_HOOK_SWITCH_TO
-static void handler_switch(struct task_struct *prev,
- struct task_struct *next)
-{
- do_entry_handler_switch(prev);
- do_ret_handler_switch(next);
-}
-
-static struct swap_hook_ctx switch_to_hook = {
- .hook = handler_switch
-};
-# endif /* CONFIG_SWAP_HOOK_SWITCH_TO */
-
-int do_set_energy(void)
-{
- int ret = 0;
-
- init_data_energy();
-
- swap_hook_ctx_reg(&switch_to_hook);
- ret = swap_td_raw_reg(&sys_call_tdraw, sizeof(struct sys_read_data));
- if (ret)
- return ret;
-
- hook_syscall_reg(&sys_read_hook, __NR_read);
- hook_syscall_reg(&sys_write_hook, __NR_write);
-
- /* TODO: add compat mode support */
-
- swap_hook_energy_set(&hook_energy);
- /* TODO: init lcd */
-
- return ret;
-}
-
-void do_unset_energy(void)
-{
- /* TODO: uinit lcd */
- swap_hook_energy_unset();
- swap_hook_ctx_unreg(&switch_to_hook);
- hook_syscall_unreg(&sys_write_hook);
- hook_syscall_unreg(&sys_read_hook);
-
- swap_td_raw_unreg(&sys_call_tdraw);
- uninit_data_energy();
-}
-
-int energy_once(void)
-{
- return 0;
-}
-#else /* CONFIG_SWAP_HOOK_ENERGY */
-
-int do_set_energy(void)
-{
- int ret = 0;
-
- init_data_energy();
-
- ret = swap_register_kretprobe(&sys_read_krp);
- if (ret) {
- printk(KERN_INFO "swap_register_kretprobe(sys_read) "
- "result=%d!\n", ret);
- return ret;
- }
-
- ret = swap_register_kretprobe(&sys_write_krp);
- if (ret != 0) {
- printk(KERN_INFO "swap_register_kretprobe(sys_write) "
- "result=%d!\n", ret);
- goto unregister_sys_read;
- }
-
- ret = swap_register_kretprobe(&switch_to_krp);
- if (ret) {
- printk(KERN_INFO "swap_register_kretprobe(__switch_to) "
- "result=%d!\n",
- ret);
- goto unregister_sys_write;
- }
-
- energy_xxx_set(bt_probes, bt_probes_cnt, &energy_bt_flag);
- energy_xxx_set(wifi_probes, wifi_probes_cnt, &wifi_flag);
-
- /* TODO: check return value */
- lcd_set_energy();
-
- return ret;
-
-unregister_sys_read:
- swap_unregister_kretprobe(&sys_read_krp);
-
-unregister_sys_write:
- swap_unregister_kretprobe(&sys_write_krp);
-
- return ret;
-}
-
-void do_unset_energy(void)
-{
- lcd_unset_energy();
- energy_xxx_unset(wifi_probes, wifi_probes_cnt, &wifi_flag);
- energy_xxx_unset(bt_probes, bt_probes_cnt, &energy_bt_flag);
-
- swap_unregister_kretprobe(&switch_to_krp);
- swap_unregister_kretprobe(&sys_write_krp);
- swap_unregister_kretprobe(&sys_read_krp);
-
- uninit_data_energy();
-}
-
-int energy_once(void)
-{
- const char *sym;
-
- sym = "__switch_to";
- switch_to_krp.kp.addr = swap_ksyms(sym);
- if (switch_to_krp.kp.addr == 0)
- goto not_found;
-
- sym = "sys_read";
- sys_read_krp.kp.addr = swap_ksyms(sym);
- if (sys_read_krp.kp.addr == 0)
- goto not_found;
-
- sym = "sys_write";
- sys_write_krp.kp.addr = swap_ksyms(sym);
- if (sys_write_krp.kp.addr == 0)
- goto not_found;
-
- energy_xxx_once(bt_probes, bt_probes_cnt);
- energy_xxx_once(wifi_probes, wifi_probes_cnt);
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
-#endif /* CONFIG_SWAP_HOOK_ENERGY */
-
-static DEFINE_MUTEX(mutex_enable);
-static int energy_enable;
-
-/**
- * @brief Start measuring the energy consumption
- *
- * @return Error code
- */
-int set_energy(void)
-{
- int ret = -EINVAL;
-
- mutex_lock(&mutex_enable);
- if (energy_enable) {
- printk(KERN_INFO "energy profiling is already run!\n");
- goto unlock;
- }
-
- ret = do_set_energy();
- if (ret == 0)
- energy_enable = 1;
-
-unlock:
- mutex_unlock(&mutex_enable);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(set_energy);
-
-/**
- * @brief Stop measuring the energy consumption
- *
- * @return Error code
- */
-int unset_energy(void)
-{
- int ret = 0;
-
- mutex_lock(&mutex_enable);
- if (energy_enable == 0) {
- printk(KERN_INFO "energy profiling is not running!\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- do_unset_energy();
-
- energy_enable = 0;
-unlock:
- mutex_unlock(&mutex_enable);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(unset_energy);
-
-/**
- * @brief Initialization energy
- *
- * @return Error code
- */
-int energy_init(void)
-{
- int ret;
-
- ret = init_feature();
- if (ret)
- printk(KERN_INFO "Cannot init feature\n");
-
- return ret;
-}
-
-/**
- * @brief Deinitialization energy
- *
- * @return Void
- */
-void energy_uninit(void)
-{
- uninit_feature();
-
- if (energy_enable)
- do_unset_energy();
-}
+++ /dev/null
-#ifndef _ENERGY_H
-#define _ENERGY_H
-
-/**
- * @file energy/energy.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENCE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- */
-
-
-#include <linux/types.h>
-
-
-/** Description of parameters */
-enum parameter_energy {
- PE_TIME_IDLE, /**< IDLE working time */
- PE_TIME_SYSTEM, /**< system working time */
- PE_TIME_APPS, /**< apps working time */
- PE_READ_SYSTEM, /**< number of bytes are read by system */
- PE_WRITE_SYSTEM, /**< number of bytes are write by system */
- PE_READ_APPS, /**< number of bytes are read by apps */
- PE_WRITE_APPS, /**< number of bytes are write by apps */
- PE_WF_RECV_SYSTEM, /**< number of bytes are receive by system through wifi */
- PE_WF_SEND_SYSTEM, /**< number of bytes are send by system through wifi */
- PE_WF_RECV_APPS, /**< number of bytes are receive by apps through wifi */
- PE_WF_SEND_APPS, /**< number of bytes are send by apps through wifi */
- PE_L2CAP_RECV_SYSTEM, /**< number of bytes(ACL packets) are recv by system through bluetooth */
- PE_L2CAP_RECV_APPS, /**< number of bytes(ACL packets) are recv by apps through bluetooth */
- PE_SCO_RECV_SYSTEM, /**< number of bytes(SCO packets) are recv by system through bluetooth */
- PE_SCO_RECV_APPS, /**< number of bytes(SCO packets) are recv by apps through bluetooth */
- PT_SEND_ACL_SYSTEM, /**< number of bytes(ACL packets) are send by system through bluetooth */
- PT_SEND_ACL_APPS, /**< number of bytes(ACL packets) are send by apps through bluetooth */
- PT_SEND_SCO_SYSTEM, /**< number of bytes(SCO packets) are send by system through bluetooth */
- PT_SEND_SCO_APPS, /**< number of bytes(SCO packets) are send by apps through bluetooth */
-};
-
-
-int energy_once(void);
-int energy_init(void);
-void energy_uninit(void);
-
-int set_energy(void);
-int unset_energy(void);
-
-int get_parameter_energy(enum parameter_energy pe, void *buf, size_t sz);
-
-#endif /* _ENERGY_H */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * energy/energy_mod.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <master/swap_initializer.h>
-#include "energy.h"
-#include "debugfs_energy.h"
-
-
-SWAP_LIGHT_INIT_MODULE(energy_once, energy_init, energy_uninit,
- init_debugfs_energy, exit_debugfs_energy);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * energy/lcd/lcd_base.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/fb.h>
-#include <energy/tm_stat.h>
-#include <energy/debugfs_energy.h>
-#include "lcd_base.h"
-#include "lcd_debugfs.h"
-
-
-/**
- * @brief Read the number of file
- *
- * @param path of the file
- * @return Value or error(when negative)
- */
-int read_val(const char *path)
-{
- int ret;
- struct file *f;
- unsigned long val;
- enum { buf_len = 32 };
- char buf[buf_len];
-
- f = filp_open(path, O_RDONLY, 0);
- if (IS_ERR(f)) {
- printk(KERN_INFO "cannot open file \'%s\'", path);
- return PTR_ERR(f);
- }
-
- ret = kernel_read(f, 0, buf, sizeof(buf));
- filp_close(f, NULL);
- if (ret < 0)
- return ret;
-
- buf[ret >= buf_len ? buf_len - 1 : ret] = '\0';
-
- ret = kstrtoul(buf, 0, &val);
- if (ret)
- return ret;
-
- return (int)val;
-}
-
-enum {
- brt_no_init = -1,
- brt_cnt = 10
-};
-
-enum power_t {
- PW_ON,
- PW_OFF
-};
-
-struct lcd_priv_data {
- int min_brt;
- int max_brt;
-
- size_t tms_brt_cnt;
- struct tm_stat *tms_brt;
- spinlock_t lock_tms;
- int brt_old;
- enum power_t power;
-
- u64 min_denom;
- u64 min_num;
- u64 max_denom;
- u64 max_num;
-};
-
-static void *create_lcd_priv(struct lcd_ops *ops, size_t tms_brt_cnt)
-{
- int i;
- struct lcd_priv_data *lcd;
-
- if (tms_brt_cnt <= 0) {
- printk(KERN_INFO "error variable tms_brt_cnt=%zu\n",
- tms_brt_cnt);
- return NULL;
- }
-
- lcd = kmalloc(sizeof(*lcd) + sizeof(*lcd->tms_brt) * tms_brt_cnt,
- GFP_KERNEL);
- if (lcd == NULL) {
- printk(KERN_INFO "error: %s - out of memory\n", __func__);
- return NULL;
- }
-
- lcd->tms_brt = (void *)lcd + sizeof(*lcd);
- lcd->tms_brt_cnt = tms_brt_cnt;
-
- lcd->min_brt = ops->get(ops, LPD_MIN_BRIGHTNESS);
- lcd->max_brt = ops->get(ops, LPD_MAX_BRIGHTNESS);
-
- for (i = 0; i < tms_brt_cnt; ++i)
- tm_stat_init(&lcd->tms_brt[i]);
-
- spin_lock_init(&lcd->lock_tms);
-
- lcd->brt_old = brt_no_init;
- lcd->power = PW_OFF;
-
- lcd->min_denom = 1;
- lcd->min_num = 1;
- lcd->max_denom = 1;
- lcd->max_num = 1;
-
- return (void *)lcd;
-}
-
-static void destroy_lcd_priv(void *data)
-{
- kfree(data);
-}
-
-static struct lcd_priv_data *get_lcd_priv(struct lcd_ops *ops)
-{
- return (struct lcd_priv_data *)ops->priv;
-}
-
-static void clean_brightness(struct lcd_ops *ops)
-{
- struct lcd_priv_data *lcd = get_lcd_priv(ops);
- int i;
-
- spin_lock(&lcd->lock_tms);
- for (i = 0; i < lcd->tms_brt_cnt; ++i)
- tm_stat_init(&lcd->tms_brt[i]);
-
- lcd->brt_old = brt_no_init;
- spin_unlock(&lcd->lock_tms);
-}
-
-static int get_brt_num_of_array(struct lcd_priv_data *lcd, int brt)
-{
- if (brt > lcd->max_brt || brt < lcd->min_brt) {
- printk(KERN_INFO "LCD energy error: set brightness=%d, "
- "when brightness[%d..%d]\n",
- brt, lcd->min_brt, lcd->max_brt);
- brt = brt > lcd->max_brt ? lcd->max_brt : lcd->min_brt;
- }
-
- return lcd->tms_brt_cnt * (brt - lcd->min_brt) /
- (lcd->max_brt - lcd->min_brt + 1);
-}
-
-static void set_brightness(struct lcd_ops *ops, int brt)
-{
- struct lcd_priv_data *lcd = get_lcd_priv(ops);
- int n = get_brt_num_of_array(lcd, brt);
-
- spin_lock(&lcd->lock_tms);
-
- if (lcd->power == PW_ON && lcd->brt_old != n) {
- u64 time = get_ntime();
- if (lcd->brt_old != brt_no_init)
- tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
-
- tm_stat_set_timestamp(&lcd->tms_brt[n], time);
- }
- lcd->brt_old = n;
-
- spin_unlock(&lcd->lock_tms);
-}
-
-static void set_power_on_set_brt(struct lcd_priv_data *lcd)
-{
- if (lcd->brt_old != brt_no_init) {
- u64 time = get_ntime();
- tm_stat_set_timestamp(&lcd->tms_brt[lcd->brt_old], time);
- }
-}
-
-static void set_power_on(struct lcd_priv_data *lcd)
-{
- if (lcd->power == PW_OFF)
- set_power_on_set_brt(lcd);
-
- lcd->power = PW_ON;
-}
-
-static void set_power_off_update_brt(struct lcd_priv_data *lcd)
-{
- if (lcd->brt_old != brt_no_init) {
- u64 time = get_ntime();
- tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
- lcd->brt_old = brt_no_init;
- }
-}
-
-static void set_power_off(struct lcd_priv_data *lcd)
-{
- if (lcd->power == PW_ON)
- set_power_off_update_brt(lcd);
-
- lcd->power = PW_OFF;
-}
-
-static void set_power(struct lcd_ops *ops, int val)
-{
- struct lcd_priv_data *lcd = get_lcd_priv(ops);
-
- spin_lock(&lcd->lock_tms);
-
- switch (val) {
- case FB_BLANK_UNBLANK:
- set_power_on(lcd);
- break;
- case FB_BLANK_POWERDOWN:
- set_power_off(lcd);
- break;
- default:
- printk(KERN_INFO "LCD energy error: set power=%d\n", val);
- break;
- }
-
- spin_unlock(&lcd->lock_tms);
-}
-
-static int func_notifier_lcd(struct lcd_ops *ops, enum lcd_action_type action,
- void *data)
-{
- switch (action) {
- case LAT_BRIGHTNESS:
- set_brightness(ops, VOIDP2INT(data));
- break;
- case LAT_POWER:
- set_power(ops, VOIDP2INT(data));
- break;
- default:
- printk(KERN_INFO "LCD energy error: action=%d\n", action);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * @brief Get the array size of LCD
- *
- * @param ops LCD operations
- * @return Array size
- */
-size_t get_lcd_size_array(struct lcd_ops *ops)
-{
- struct lcd_priv_data *lcd = get_lcd_priv(ops);
-
- return lcd->tms_brt_cnt;
-}
-
-/**
- * @brief Get an array of times
- *
- * @param ops LCD operations
- * @param array_time[out] Array of times
- * @return Void
- */
-void get_lcd_array_time(struct lcd_ops *ops, u64 *array_time)
-{
- struct lcd_priv_data *lcd = get_lcd_priv(ops);
- int i;
-
- spin_lock(&lcd->lock_tms);
- for (i = 0; i < lcd->tms_brt_cnt; ++i)
- array_time[i] = tm_stat_running(&lcd->tms_brt[i]);
-
- if (lcd->power == PW_ON && lcd->brt_old != brt_no_init) {
- int old = lcd->brt_old;
- struct tm_stat *tm = &lcd->tms_brt[old];
-
- array_time[old] += get_ntime() - tm_stat_timestamp(tm);
- }
- spin_unlock(&lcd->lock_tms);
-}
-
-static int register_lcd(struct lcd_ops *ops)
-{
- int ret = 0;
-
- ops->priv = create_lcd_priv(ops, brt_cnt);
-
- /* TODO: create init_func() for 'struct rational' */
- ops->min_coef.num = 1;
- ops->min_coef.denom = 1;
- ops->max_coef.num = 1;
- ops->max_coef.denom = 1;
-
- ops->notifier = func_notifier_lcd;
-
- ret = register_lcd_debugfs(ops);
- if (ret)
- destroy_lcd_priv(ops->priv);
-
- return ret;
-}
-
-static void unregister_lcd(struct lcd_ops *ops)
-{
- unregister_lcd_debugfs(ops);
- destroy_lcd_priv(ops->priv);
-}
-
-
-
-
-/* ============================================================================
- * === LCD_INIT/LCD_EXIT ===
- * ============================================================================
- */
-typedef struct lcd_ops *(*get_ops_t)(void);
-
-DEFINITION_LCD_FUNC;
-
-get_ops_t lcd_ops[] = DEFINITION_LCD_ARRAY;
-enum { lcd_ops_cnt = sizeof(lcd_ops) / sizeof(get_ops_t) };
-
-enum ST_LCD_OPS {
- SLO_REGISTER = 1 << 0,
- SLO_SET = 1 << 1
-};
-
-static DEFINE_MUTEX(lcd_lock);
-static enum ST_LCD_OPS stat_lcd_ops[lcd_ops_cnt];
-
-static void do_lcd_exit(void)
-{
- int i;
- struct lcd_ops *ops;
-
- mutex_lock(&lcd_lock);
- for (i = 0; i < lcd_ops_cnt; ++i) {
- ops = lcd_ops[i]();
-
- if (stat_lcd_ops[i] & SLO_SET) {
- ops->unset(ops);
- stat_lcd_ops[i] &= ~SLO_SET;
- }
-
- if (stat_lcd_ops[i] & SLO_REGISTER) {
- unregister_lcd(ops);
- stat_lcd_ops[i] &= ~SLO_REGISTER;
- }
- }
- mutex_unlock(&lcd_lock);
-}
-
-/**
- * @brief LCD deinitialization
- *
- * @return Void
- */
-void lcd_exit(void)
-{
- do_lcd_exit();
-}
-
-static int do_lcd_init(void)
-{
- int i, ret, count = 0;
- struct lcd_ops *ops;
-
- mutex_lock(&lcd_lock);
- for (i = 0; i < lcd_ops_cnt; ++i) {
- ops = lcd_ops[i]();
- if (ops == NULL) {
- printk(KERN_INFO "error %s [ops == NULL]\n", __func__);
- continue;
- }
-
- if (0 == ops->check(ops)) {
- printk(KERN_INFO "error checking %s\n", ops->name);
- continue;
- }
-
- ret = register_lcd(ops);
- if (ret) {
- printk(KERN_INFO "error register_lcd %s\n", ops->name);
- continue;
- }
-
- stat_lcd_ops[i] |= SLO_REGISTER;
- ++count;
- }
- mutex_unlock(&lcd_lock);
-
- return count ? 0 : -EPERM;
-}
-
-/**
- * @brief LCD initialization
- *
- * @return Error code
- */
-int lcd_init(void)
-{
- int ret;
-
- ret = do_lcd_init();
- if (ret)
- printk(KERN_INFO "LCD is not supported\n");
-
- return ret;
-}
-
-
-
-/* ============================================================================
- * === LCD_SET_ENERGY/LCD_UNSET_ENERGY ===
- * ============================================================================
- */
-
-/**
- * @brief Start measuring the energy consumption of LСD
- *
- * @return Error code
- */
-int lcd_set_energy(void)
-{
- int i, ret, count = 0;
- struct lcd_ops *ops;
-
- mutex_lock(&lcd_lock);
- for (i = 0; i < lcd_ops_cnt; ++i) {
- ops = lcd_ops[i]();
- if (stat_lcd_ops[i] & SLO_REGISTER) {
- ret = ops->set(ops);
- if (ret) {
- printk(KERN_INFO "error %s set LCD energy",
- ops->name);
- continue;
- }
-
- set_brightness(ops, ops->get(ops, LPD_BRIGHTNESS));
- set_power(ops, ops->get(ops, LPD_POWER));
-
- stat_lcd_ops[i] |= SLO_SET;
- ++count;
- }
- }
- mutex_unlock(&lcd_lock);
-
- return count ? 0 : -EPERM;
-}
-
-/**
- * @brief Stop measuring the energy consumption of LСD
- *
- * @return Void
- */
-void lcd_unset_energy(void)
-{
- int i, ret;
- struct lcd_ops *ops;
-
- mutex_lock(&lcd_lock);
- for (i = 0; i < lcd_ops_cnt; ++i) {
- ops = lcd_ops[i]();
- if (stat_lcd_ops[i] & SLO_SET) {
- ret = ops->unset(ops);
- if (ret)
- printk(KERN_INFO "error %s unset LCD energy",
- ops->name);
-
- clean_brightness(ops);
- stat_lcd_ops[i] &= ~SLO_SET;
- }
- }
- mutex_unlock(&lcd_lock);
-}
+++ /dev/null
-#ifndef _LCD_BASE_H
-#define _LCD_BASE_H
-
-/**
- * @file energy/lcd/lcd_base.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- * Description of the interface for interacting with LСD
- */
-
-
-#include <linux/errno.h>
-#include <energy/rational_debugfs.h>
-
-
-#define VOIDP2INT(x) ((int)(unsigned long)(x))
-#define INT2VOIDP(x) ((void *)(unsigned long)(x))
-
-
-/** Description of actions */
-enum lcd_action_type {
- LAT_BRIGHTNESS, /**< LCD brightness */
- LAT_POWER /**< LCD power */
-};
-
-
-/** Description of parameters */
-enum lcd_parameter_type {
- LPD_MIN_BRIGHTNESS, /**< minimum brightness value */
- LPD_MAX_BRIGHTNESS, /**< maximum brightness value */
- LPD_BRIGHTNESS, /**< current brightness value */
-
- LPD_POWER /**< current power value */
-};
-
-struct lcd_ops;
-
-/**
- * @brief LCD callback type
- *
- * @param ops LCD operations
- * @return Error code
- */
-typedef int (*call_lcd)(struct lcd_ops *ops);
-
-/**
- * @brief LCD notifier type
- *
- * @param ops LCD operations
- * @param action Event type
- * @param data Date
- * @return Error code
- */
-typedef int (*notifier_lcd)(struct lcd_ops *ops, enum lcd_action_type action,
- void *data);
-
-/**
- * @brief LCD parameter type
- *
- * @param ops LCD operations
- * @param type Requested parameter type
- * @return Requested parameter value
- *
- */
-typedef unsigned long (*get_parameter_lcd)(struct lcd_ops *ops,
- enum lcd_parameter_type type);
-
-
-/**
- * @struct lcd_ops
- * @breaf set of operations available for LСD
- */
-struct lcd_ops {
- char *name; /**< LCD driver name */
- notifier_lcd notifier; /**< Notifier */
- get_parameter_lcd get; /**< Method to obtain the parameters */
-
- call_lcd check; /**< LCD check on device */
- call_lcd set; /**< LCD initialization */
- call_lcd unset; /**< LCD deinitialization */
-
- /* for debugfs */
- struct dentry *dentry; /**< Dentry of debugfs for this LCD */
- struct rational min_coef; /**< Minimum coefficient */
- struct rational max_coef; /**< Maximum coefficient */
-
- void *priv; /**< Private data */
-};
-
-size_t get_lcd_size_array(struct lcd_ops *ops);
-void get_lcd_array_time(struct lcd_ops *ops, u64 *array_time);
-
-int read_val(const char *path);
-
-int lcd_set_energy(void);
-void lcd_unset_energy(void);
-
-int lcd_init(void);
-void lcd_exit(void);
-
-#endif /* _LCD_BASE_H */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * energy/lcd/lcd_debugfs.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <master/swap_debugfs.h>
-#include <energy/lcd/lcd_base.h>
-#include <energy/debugfs_energy.h>
-#include <energy/rational_debugfs.h>
-
-
-static int get_system(void *data, u64 *val)
-{
- struct lcd_ops *ops = (struct lcd_ops *)data;
- const size_t size = get_lcd_size_array(ops);
- const size_t size_1 = size - 1;
- u64 i_max, j_min, t, e = 0;
- u64 *array_time;
- int i, j;
-
- array_time = kmalloc(sizeof(*array_time) * size, GFP_KERNEL);
- if (array_time == NULL)
- return -ENOMEM;
-
- get_lcd_array_time(ops, array_time);
-
- for (i = 0; i < size; ++i) {
- t = array_time[i];
-
- /* e = (i * max + (k - i) * min) * t / k */
- j = size_1 - i;
- i_max = div_u64(i * ops->max_coef.num * t,
- ops->max_coef.denom);
- j_min = div_u64(j * ops->min_coef.num * t,
- ops->min_coef.denom);
- e += div_u64(i_max + j_min, size_1);
- }
-
- kfree(array_time);
-
- *val = e;
-
- return 0;
-}
-
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_get_system, get_system, NULL, "%llu\n");
-
-
-static struct dentry *lcd_dir;
-
-/**
- * @brief Register LCD in debugfs
- *
- * @param ops LCD operations
- * @return Error code
- */
-int register_lcd_debugfs(struct lcd_ops *ops)
-{
- int ret;
- struct dentry *dentry, *system;
-
- if (lcd_dir == NULL)
- return -EINVAL;
-
- dentry = swap_debugfs_create_dir(ops->name, lcd_dir);
- if (dentry == NULL)
- return -ENOMEM;
-
- ret = create_rational_files(dentry, &ops->min_coef,
- "min_num", "min_denom");
- if (ret)
- goto fail;
-
- ret = create_rational_files(dentry, &ops->max_coef,
- "max_num", "max_denom");
- if (ret)
- goto fail;
-
- system = swap_debugfs_create_file("system", 0600, dentry, (void *)ops,
- &fops_get_system);
- if (system == NULL)
- goto fail;
-
- ops->dentry = dentry;
-
- return 0;
-fail:
- debugfs_remove_recursive(dentry);
- return -ENOMEM;
-}
-
-/**
- * @brief Unregister LCD in debugfs
- *
- * @param ops LCD operations
- * @return Void
- */
-void unregister_lcd_debugfs(struct lcd_ops *ops)
-{
- debugfs_remove_recursive(ops->dentry);
-}
-
-/**
- * @brief Destroy debugfs for LCD
- *
- * @return Void
- */
-void exit_lcd_debugfs(void)
-{
- if (lcd_dir)
- debugfs_remove_recursive(lcd_dir);
-
- lcd_dir = NULL;
-}
-
-/**
- * @brief Create debugfs for LCD
- *
- * @param dentry Dentry
- * @return Error code
- */
-int init_lcd_debugfs(struct dentry *energy_dir)
-{
- lcd_dir = swap_debugfs_create_dir("lcd", energy_dir);
- if (lcd_dir == NULL)
- return -ENOMEM;
-
- return 0;
-}
+++ /dev/null
-#ifndef _LCD_DEBUGFS_H
-#define _LCD_DEBUGFS_H
-
-/**
- * @file energy/lcd/lcd_debugfs.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- * Debugfs for LСD
- *
- */
-
-
-struct dentry;
-struct lcd_ops;
-
-int register_lcd_debugfs(struct lcd_ops *ops);
-void unregister_lcd_debugfs(struct lcd_ops *ops);
-
-int init_lcd_debugfs(struct dentry *energy_dir);
-void exit_lcd_debugfs(void);
-
-#endif /* _LCD_DEBUGFS_H */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * energy/lcd/maru.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <kprobe/swap_kprobes.h>
-#include <linux/backlight.h>
-#include "lcd_base.h"
-
-
-
-static const char path_backlight[] =
- "/sys/class/backlight/emulator/brightness";
-static const char path_backlight_min[] =
- "/sys/class/backlight/emulator/min_brightness";
-static const char path_backlight_max[] =
- "/sys/class/backlight/emulator/max_brightness";
-static const char path_power[] =
- "/sys/class/lcd/emulator/lcd_power";
-
-static const char * const all_path[] = {
- path_backlight,
- path_backlight_min,
- path_backlight_max,
- path_power
-};
-
-enum {
- all_path_cnt = sizeof(all_path) / sizeof(char *)
-};
-
-
-static int maru_check(struct lcd_ops *ops)
-{
- int i;
-
- for (i = 0; i < all_path_cnt; ++i) {
- int ret = read_val(all_path[i]);
-
- if (IS_ERR_VALUE(ret))
- return 0;
- }
-
- return 1;
-}
-
-static unsigned long maru_get_parameter(struct lcd_ops *ops,
- enum lcd_parameter_type type)
-{
- switch (type) {
- case LPD_MIN_BRIGHTNESS:
- return read_val(path_backlight_min);
- case LPD_MAX_BRIGHTNESS:
- return read_val(path_backlight_max);
- case LPD_BRIGHTNESS:
- return read_val(path_backlight);
- case LPD_POWER:
- return read_val(path_power);
- default:
- return -EINVAL;
- }
-}
-
-
-
-
-
-static int entry_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-static int ret_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-
-static struct kretprobe set_backlight_krp = {
- .kp.symbol_name = "marubl_send_intensity",
- .entry_handler = entry_handler_set_backlight,
- .handler = ret_handler_set_backlight,
- .data_size = sizeof(int)
-};
-
-
-
-
-
-static int maru_set(struct lcd_ops *ops)
-{
- return swap_register_kretprobe(&set_backlight_krp);
-}
-
-static int maru_unset(struct lcd_ops *ops)
-{
- swap_unregister_kretprobe(&set_backlight_krp);
- return 0;
-}
-
-static struct lcd_ops maru_ops = {
- .name = "maru",
- .check = maru_check,
- .set = maru_set,
- .unset = maru_unset,
- .get = maru_get_parameter
-};
-
-struct lcd_ops *LCD_MAKE_FNAME(maru)(void)
-{
- return &maru_ops;
-}
-
-
-
-
-
-static int entry_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int *brightness = (int *)ri->data;
- struct backlight_device *bd;
-
- bd = (struct backlight_device *)swap_get_karg(regs, 0);
- *brightness = bd->props.brightness;
-
- return 0;
-}
-
-static int ret_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- int *brightness = (int *)ri->data;
-
- if (!ret && maru_ops.notifier)
- maru_ops.notifier(&maru_ops, LAT_BRIGHTNESS,
- INT2VOIDP(*brightness));
-
- return 0;
-}
+++ /dev/null
-#include <kprobe/swap_kprobes.h>
-#include "lcd_base.h"
-
-
-static const char path_backlight[] =
- "/sys/class/backlight/s6e8aa0-bl/brightness";
-static const char path_backlight_min[] =
- "/sys/class/backlight/s6e8aa0-bl/min_brightness";
-static const char path_backlight_max[] =
- "/sys/class/backlight/s6e8aa0-bl/max_brightness";
-static const char path_power[] =
- "/sys/class/lcd/s6e8aa0/lcd_power";
-
-static const char * const all_path[] = {
- path_backlight,
- path_backlight_min,
- path_backlight_max,
- path_power
-};
-
-enum {
- all_path_cnt = sizeof(all_path) / sizeof(char *)
-};
-
-
-
-static int s6e8aa0_check(struct lcd_ops *ops)
-{
- int i;
-
- for (i = 0; i < all_path_cnt; ++i) {
- int ret = read_val(all_path[i]);
-
- if (IS_ERR_VALUE(ret))
- return 0;
- }
-
- return 1;
-}
-
-static unsigned long s6e8aa0_get_parameter(struct lcd_ops *ops,
- enum lcd_parameter_type type)
-{
- switch (type) {
- case LPD_MIN_BRIGHTNESS:
- return read_val(path_backlight_min);
- case LPD_MAX_BRIGHTNESS:
- return read_val(path_backlight_max);
- case LPD_BRIGHTNESS:
- return read_val(path_backlight);
- case LPD_POWER:
- return read_val(path_power);
- default:
- return -EINVAL;
- }
-}
-
-
-
-static int entry_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-static int ret_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-
-static struct kretprobe set_power_krp = {
- .kp.symbol_name = "s6e8aa0_set_power",
- .entry_handler = entry_handler_set_power,
- .handler = ret_handler_set_power,
- .data_size = sizeof(int)
-};
-
-
-static int entry_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-static int ret_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-
-static struct kretprobe set_backlight_krp = {
- .kp.symbol_name = "s6e8aa0_gamma_ctrl",
- .entry_handler = entry_handler_set_backlight,
- .handler = ret_handler_set_backlight,
- .data_size = sizeof(int)
-};
-
-int s6e8aa0_set(struct lcd_ops *ops)
-{
- int ret;
-
- ret = swap_register_kretprobe(&set_power_krp);
- if (ret)
- return ret;
-
- ret = swap_register_kretprobe(&set_backlight_krp);
- if (ret)
- swap_unregister_kretprobe(&set_power_krp);
-
- return ret;
-}
-
-int s6e8aa0_unset(struct lcd_ops *ops)
-{
- swap_unregister_kretprobe(&set_backlight_krp);
- swap_unregister_kretprobe(&set_power_krp);
-
- return 0;
-}
-
-static struct lcd_ops s6e8aa0_ops = {
- .name = "s6e8aa0",
- .check = s6e8aa0_check,
- .set = s6e8aa0_set,
- .unset = s6e8aa0_unset,
- .get = s6e8aa0_get_parameter
-};
-
-struct lcd_ops *LCD_MAKE_FNAME(s6e8aa0)(void)
-{
- return &s6e8aa0_ops;
-}
-
-
-
-
-
-/* ============================================================================
- * === POWER ===
- * ============================================================================
- */
-static int entry_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int *power = (int *)ri->data;
-
- *power = (int)swap_get_karg(regs, 1);
-
- return 0;
-}
-
-static int ret_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- int *power = (int *)ri->data;
-
- if (!ret && s6e8aa0_ops.notifier)
- s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER, INT2VOIDP(*power));
-
- return 0;
-}
-
-
-
-
-
-/* ============================================================================
- * === BACKLIGHT ===
- * ============================================================================
- */
-static int entry_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int *brightness = (int *)ri->data;
- *brightness = (int)swap_get_karg(regs, 1);
-
- return 0;
-}
-
-static int ret_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- int *brightness = (int *)ri->data;
-
- if (!ret && s6e8aa0_ops.notifier)
- s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_BRIGHTNESS,
- INT2VOIDP(*brightness));
-
- return 0;
-}
+++ /dev/null
-#include <kprobe/swap_kprobes.h>
-#include <linux/backlight.h>
-#include "lcd_base.h"
-
-
-static const char path_backlight[] =
- "/sys/class/backlight/s6e8aa0-bl/brightness";
-static const char path_backlight_max[] =
- "/sys/class/backlight/s6e8aa0-bl/max_brightness";
-static const char path_power[] =
- "/sys/class/lcd/s6e8aa0/lcd_power";
-
-static const char * const all_path[] = {
- path_backlight,
- path_backlight_max,
- path_power
-};
-
-enum {
- all_path_cnt = sizeof(all_path) / sizeof(char *)
-};
-
-
-
-static int s6e8aa0_check(struct lcd_ops *ops)
-{
- int i;
-
- for (i = 0; i < all_path_cnt; ++i) {
- int ret = read_val(all_path[i]);
-
- if (IS_ERR_VALUE(ret))
- return 0;
- }
-
- return 1;
-}
-
-static unsigned long s6e8aa0_get_parameter(struct lcd_ops *ops,
- enum lcd_parameter_type type)
-{
- switch (type) {
- case LPD_MIN_BRIGHTNESS:
- return 0;
- case LPD_MAX_BRIGHTNESS:
- return read_val(path_backlight_max);
- case LPD_BRIGHTNESS:
- return read_val(path_backlight);
- case LPD_POWER:
- return read_val(path_power);
- }
-
- return -EINVAL;
-}
-
-
-
-static int entry_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-static int ret_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-
-static struct kretprobe set_power_krp = {
- .kp.symbol_name = "s6e8aa0_set_power",
- .entry_handler = entry_handler_set_power,
- .handler = ret_handler_set_power,
- .data_size = sizeof(int)
-};
-
-
-static int entry_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-static int ret_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-
-static struct kretprobe set_backlight_krp = {
- .kp.symbol_name = "s6e8aa0_update_status",
- .entry_handler = entry_handler_set_backlight,
- .handler = ret_handler_set_backlight,
- .data_size = sizeof(int)
-};
-
-int s6e8aa0_set(struct lcd_ops *ops)
-{
- int ret;
-
- ret = swap_register_kretprobe(&set_power_krp);
- if (ret)
- return ret;
-
- ret = swap_register_kretprobe(&set_backlight_krp);
- if (ret)
- swap_unregister_kretprobe(&set_power_krp);
-
- return ret;
-}
-
-int s6e8aa0_unset(struct lcd_ops *ops)
-{
- swap_unregister_kretprobe(&set_backlight_krp);
- swap_unregister_kretprobe(&set_power_krp);
-
- return 0;
-}
-
-static struct lcd_ops s6e8aa0_ops = {
- .name = "s6e8aa0_panel",
- .check = s6e8aa0_check,
- .set = s6e8aa0_set,
- .unset = s6e8aa0_unset,
- .get = s6e8aa0_get_parameter
-};
-
-struct lcd_ops *LCD_MAKE_FNAME(s6e8aa0_panel)(void)
-{
- return &s6e8aa0_ops;
-}
-
-
-
-
-
-/* ============================================================================
- * === POWER ===
- * ============================================================================
- */
-static int entry_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int *power = (int *)ri->data;
-
- *power = (int)swap_get_karg(regs, 1);
-
- return 0;
-}
-
-static int ret_handler_set_power(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- int *power = (int *)ri->data;
-
- if (!ret && s6e8aa0_ops.notifier)
- s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER,
- INT2VOIDP(*power));
-
- return 0;
-}
-
-
-
-
-
-/* ============================================================================
- * === BACKLIGHT ===
- * ============================================================================
- */
-static int entry_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int *brightness = (int *)ri->data;
- struct backlight_device *bd;
-
- bd = (struct backlight_device *)swap_get_karg(regs, 0);
- *brightness = bd->props.brightness;
-
- return 0;
-}
-
-static int ret_handler_set_backlight(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret = regs_return_value(regs);
- int *brightness = (int *)ri->data;
-
- if (!ret && s6e8aa0_ops.notifier)
- s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_BRIGHTNESS,
- INT2VOIDP(*brightness));
-
- return 0;
-}
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * energy/rational_debugfs.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/dcache.h>
-#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <master/swap_debugfs.h>
-#include "debugfs_energy.h"
-#include "rational_debugfs.h"
-
-
-static int denom_set(void *data, u64 val)
-{
- if (val == 0)
- return -EINVAL;
-
- *(u64 *)data = val;
- return 0;
-}
-
-static int denom_get(void *data, u64 *val)
-{
- *val = *(u64 *)data;
- return 0;
-}
-
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_denom, denom_get, denom_set, "%llu\n");
-
-/**
- * @brief Create file in debugfs for rational struct
- *
- * @param parent Dentry parent
- * @param r Pointer to the rational struct
- * @param num_name File name of numerator
- * @param denom_name File name of denominator
- * @return Error code
- */
-int create_rational_files(struct dentry *parent, struct rational *r,
- const char *num_name, const char *denom_name)
-{
- struct dentry *d_num, *d_denom;
-
- d_num = swap_debugfs_create_u64(num_name, 0600, parent, &r->num);
- if (d_num == NULL)
- return -ENOMEM;
-
- d_denom = swap_debugfs_create_file(denom_name, 0600, parent, &r->denom,
- &fops_denom);
- if (d_denom == NULL) {
- debugfs_remove(d_num);
- return -ENOMEM;
- }
-
- return 0;
-}
+++ /dev/null
-#ifndef _RATIONAL_DEBUGFS_H
-#define _RATIONAL_DEBUGFS_H
-
-/**
- * @file energy/rational_debugfs.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-
-#include <linux/types.h>
-
-
-/**
- * @struct rational
- * @brief Description of rational number
- */
-struct rational {
- u64 num; /**< Numerator */
- u64 denom; /**< Denominator */
-};
-
-
-/**
- * @def DEFINE_RATIONAL
- * Initialize of rational struct @hideinitializer
- */
-#define DEFINE_RATIONAL(rational_name) \
- struct rational rational_name = { \
- .num = 1, \
- .denom = 1 \
- }
-
-
-struct dentry;
-
-int create_rational_files(struct dentry *parent, struct rational *r,
- const char *num_name, const char *denom_name);
-
-
-#endif /* _RATIONAL_DEBUGFS_H */
+++ /dev/null
-#ifndef _TM_STAT_H
-#define _TM_STAT_H
-
-/**
- * @file energy/tm_stat.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYFIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- */
-
-
-#include <linux/types.h>
-#include <linux/time.h>
-
-
-/**
- * @struct tm_stat
- * @brief Description of statistic time
- */
-struct tm_stat {
- u64 timestamp; /**< Time stamp */
- u64 running; /**< Running time */
-};
-
-/**
- * @def DEFINE_TM_STAT
- * Initialize of tm_stat struct @hideinitializer
- */
-#define DEFINE_TM_STAT(tm_name) \
- struct tm_stat tm_name = { \
- .timestamp = 0, \
- .running = 0 \
- }
-
-
-static inline u64 get_ntime(void)
-{
- struct timespec ts;
- getnstimeofday(&ts);
- return timespec_to_ns(&ts);
-}
-
-static inline void tm_stat_init(struct tm_stat *tm)
-{
- tm->timestamp = 0;
- tm->running = 0;
-}
-
-static inline void tm_stat_set_timestamp(struct tm_stat *tm, u64 time)
-{
- tm->timestamp = time;
-}
-
-static inline u64 tm_stat_timestamp(struct tm_stat *tm)
-{
- return tm->timestamp;
-}
-
-static inline void tm_stat_update(struct tm_stat *tm, u64 time)
-{
- tm->running += time - tm->timestamp;
-}
-
-static inline u64 tm_stat_running(struct tm_stat *tm)
-{
- return tm->running;
-}
-
-static inline u64 tm_stat_current_running(struct tm_stat *tm, u64 now)
-{
- if (unlikely(now < tm->timestamp))
- printk(KERN_INFO "XXX %p WARNING now(%llu) < tmstmp(%llu)\n",
- tm, now, tm->timestamp);
- return tm->timestamp ? tm->running + now - tm->timestamp : tm->running;
-}
-
-#endif /* _TM_STAT_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_fbiprobe.o
-swap_fbiprobe-y := fbiprobe.o \
- fbi_msg.o
+++ /dev/null
-/**
- * fbiprobe/fbi_msg.c
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Packing and writing data.
- */
-
-
-#include <linux/types.h>
-#include <linux/string.h>
-#include <writer/swap_msg.h>
-
-struct msg_fbi {
- u32 var_id;
- u32 size;
- char var_data[0];
-} __packed;
-
-
-static char *pack_fbi_info(char *payload, unsigned long var_id, size_t size,
- char *msg_buf)
-{
- struct msg_fbi *fbi_m = (struct msg_fbi *)payload;
-
- fbi_m->var_id = var_id;
- fbi_m->size = size;
- if (size != 0) {
- /* FIXME Possible out of buffer! */
- memcpy(&fbi_m->var_data, msg_buf, size);
- }
-
- /*
- * If size is 0 that mean we cannot get data for this probe.
- * But we pack it like error code
- */
-
- return payload + sizeof(struct msg_fbi) + size;
-}
-
-void fbi_msg(unsigned long var_id, size_t size, char *msg_buf)
-{
- struct swap_msg *m;
- void *p;
- void *buf_end;
-
- m = swap_msg_get(MSG_FBI);
- p = swap_msg_payload(m);
-
- buf_end = pack_fbi_info(p, var_id, size, msg_buf);
-
- swap_msg_flush(m, buf_end - p);
-
- swap_msg_put(m);
-}
+++ /dev/null
-/*
- * @file fbiprobe/fbi_msg.h
- *
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * Function body instrumetation
- *
- */
-
-#ifndef __FBI_MSG_H__
-#define __FBI_MSG_H__
-
-#include <linux/types.h>
-
-void fbi_msg(unsigned long var_id, size_t size, char *msg_buf);
-
-#endif /* __FBI_MSG_H__ */
+++ /dev/null
-/*
- * @file fbiprobe/fbi_probe.h
- *
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov : FBI implement
- * 2014 Vitaliy Cherepanov: FBI implement, portage
- *
- * @section DESCRIPTION
- *
- * Function body instrumentation.
- *
- */
-
-#ifndef __FBI_PROBE_MODULE_H__
-#define __FBI_PROBE_MODULE_H__
-
-#include <linux/kernel.h>
-
-/* MESSAGES */
-
-#define MODULE_NAME "SWAP_FBI_PROBE"
-
-/* FBI_DEBUG_ON:
- * val | DEBUG | MSG | WARN | ERR | CRITICAL|
- * ----+-------+-----+------+-----+---------|
- * 0 | OFF | OFF | OFF | OFF | OFF |
- * 1 | OFF | OFF | OFF | OFF | ON |
- * 2 | OFF | OFF | OFF | ON | ON |
- * 3 | OFF | OFF | ON | ON | ON |
- * 4 | OFF | ON | ON | ON | ON |
- * 5 | ON | ON | ON | ON | ON |
- */
-
-#define FBI_DEBUG_LEVEL 3
-
-/** Prints debug message.*/
-#if (FBI_DEBUG_LEVEL >= 5)
-#define print_debug(msg, args...) \
- printk(KERN_DEBUG MODULE_NAME " DEBUG : " msg, ##args)
-#else
-#define print_debug(msg, args...)
-#endif
-
-/** Prints info message.*/
-#if (FBI_DEBUG_LEVEL >= 4)
-#define print_msg(msg, args...) \
- printk(KERN_INFO MODULE_NAME " : " msg, ##args)
-#else
-#define print_msg(msg, args...)
-#endif
-
-/** Prints warning message.*/
-#if (FBI_DEBUG_LEVEL >= 3)
-#define print_warn(msg, args...) \
- printk(KERN_WARNING MODULE_NAME " WARNING : " msg, ##args)
-#else
-#define print_warn(msg, args...)
-#endif
-
-/** Prints error message.*/
-#if (FBI_DEBUG_LEVEL >= 2)
-#define print_err(msg, args...) \
- printk(KERN_ERR MODULE_NAME " ERROR : " msg, ##args)
-#else
-#define print_err(msg, args...)
-#endif
-
-/** Prints critical error message.*/
-#if (FBI_DEBUG_LEVEL >= 1)
-#define print_crit(msg, args...) \
- printk(KERN_CRIT MODULE_NAME " CRITICAL : " msg, ##args)
-#else
-#define print_crit(msg, args...)
-#endif
-
-#endif /* __FBI_PROBE_MODULE_H__ */
+++ /dev/null
-/*
- * @file fbiprobe/fbi_probe.c
- *
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov : FBI implement
- * 2014 Vitaliy Cherepanov: FBI implement, portage
- *
- * @section DESCRIPTION
- *
- * Function body instrumetation
- *
- */
-
-#include "fbiprobe.h"
-#include "fbi_probe_module.h"
-#include "fbi_msg.h"
-#include "regs.h"
-
-#include <us_manager/us_manager.h>
-#include <us_manager/probes/probes.h>
-#include <us_manager/probes/register_probes.h>
-
-#include <uprobe/swap_uprobes.h>
-#include <us_manager/sspt/sspt_ip.h>
-
-#include <kprobe/swap_kprobes_deps.h>
-#include <linux/module.h>
-
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/dcache.h>
-#include <linux/mm_types.h>
-
-#include <us_manager/sspt/sspt_page.h>
-#include <us_manager/sspt/sspt_file.h>
-
-#define DIRECT_ADDR (0xFF)
-#define MAX_STRING_LEN (512)
-
-/* on fails. return NULL, set size to 0 */
-/* you shoud free allocated data buffer */
-static char *fbi_probe_alloc_and_read_from_addr(const struct fbi_var_data *fbid,
- unsigned long addr,
- uint32_t *size)
-{
- uint8_t i, j;
- char *buf = NULL;
- struct fbi_step *step;
-
- *size = 0;
-
- /* get final variable address */
- step = fbid->steps;
- for (i = 0; i != fbid->steps_count; i++) {
- /* dereference */
- for (j = 0; j != step->ptr_order; j++) {
- unsigned long new_addr;
- /* equel to: addr = *addr */
- if (!read_proc_vm_atomic(current, addr, &new_addr,
- sizeof(new_addr))) {
- print_warn("p = 0x%lx step #%d ptr_order #%d\n",
- addr, i + 1, j + 1);
- goto exit_fail;
- }
- addr = new_addr;
- print_debug("dereference addr = 0x%lx;\n", addr);
- }
-
- /* offset */
- addr += step->data_offset;
- print_debug("addr + offset = 0x%lx;\n", addr);
- step++;
- }
-
- /* calculate data size */
- if (fbid->data_size == 0) {
- /*
- * that mean variable is string and
- * we need to calculate string length
- */
-
- *size = strnlen_user((const char __user *)addr, MAX_STRING_LEN);
- if (*size == 0) {
- print_warn("Cannot get string from 0x%lx\n", addr);
- goto exit_fail;
- }
- } else {
- /* else use size from fbi struct */
- *size = fbid->data_size;
- }
-
- buf = kmalloc(*size, GFP_KERNEL);
- if (buf == NULL) {
- print_warn("Not enough memory\n");
- goto exit_fail_size_0;
- }
-
- if (!read_proc_vm_atomic(current, addr, buf, *size)) {
- print_warn("Error reading data at 0x%lx, task %d\n",
- addr, current->pid);
- goto exit_fail_free_buf;
- }
-
- if (fbid->data_size == 0) {
- /*
- * that mean variable is string and
- * we need to add terminate '\0'
- */
- buf[*size - 1] = '\0';
- }
-
- return buf;
-
-exit_fail_free_buf:
- kfree(buf);
- buf = NULL;
-exit_fail_size_0:
- *size = 0;
-exit_fail:
- return NULL;
-
-}
-
-static int fbi_probe_get_data_from_reg(const struct fbi_var_data *fbi_i,
- struct pt_regs *regs)
-{
- unsigned long *reg_ptr;
-
- reg_ptr = get_ptr_by_num(regs, fbi_i->reg_n);
- if (reg_ptr == NULL) {
- print_err("fbi_probe_get_data_from_reg: Wrong register number!\n");
- return 0;
- }
-
- fbi_msg(fbi_i->var_id, fbi_i->data_size, (char *)reg_ptr);
-
- return 0;
-}
-
-static int fbi_probe_get_data_from_ptrs(const struct fbi_var_data *fbi_i,
- struct pt_regs *regs)
-{
- unsigned long *reg_ptr;
- unsigned long addr;
- uint32_t size = 0;
- void *buf = NULL;
-
- reg_ptr = get_ptr_by_num(regs, fbi_i->reg_n);
- if (reg_ptr == NULL) {
- print_err("fbi_probe_get_data_from_ptrs: Wrong register number!\n");
- goto send_msg;
- }
-
- addr = *reg_ptr + fbi_i->reg_offset;
- print_warn("reg = %p; off = 0x%llx; addr = 0x%lx!\n", reg_ptr,
- fbi_i->reg_offset, addr);
-
- buf = fbi_probe_alloc_and_read_from_addr(fbi_i, addr, &size);
-
-send_msg:
- /* If buf is NULL size will be 0.
- * That mean we cannot get data for this probe.
- * But we should send probe message with packed data size 0
- * as error message.
- */
- fbi_msg(fbi_i->var_id, size, buf);
-
- if (buf != NULL)
- kfree(buf);
- else
- print_err("cannot get data from ptrs\n");
-
- return 0;
-}
-
-static struct vm_area_struct *find_vma_exe_by_dentry(struct mm_struct *mm,
- struct dentry *dentry)
-{
- struct vm_area_struct *vma;
-
- /* FIXME: down_write(&mm->mmap_sem); up_write(&mm->mmap_sem); */
- /* TODO FILTER vma */
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
- if (vma->vm_file &&
- (vma->vm_file->f_path.dentry == dentry))
- /* found */
- goto exit;
- }
-
- /* not found */
- vma = NULL;
-exit:
- return vma;
-}
-
-static int fbi_probe_get_data_from_direct_addr(const struct fbi_var_data *fbi_i,
- struct sspt_ip *ip,
- struct pt_regs *regs)
-{
- struct vm_area_struct *vma;
- unsigned long addr;
- uint32_t size = 0;
- char *buf;
-
- /* register offset is global address */
- vma = find_vma_exe_by_dentry(current->mm, ip->page->file->dentry);
- if (vma == NULL) {
- print_warn("cannot locate dentry\n");
- goto exit;
- }
-
- addr = vma->vm_start + fbi_i->reg_offset;
-
- print_debug("DIRECT_ADDR reg_offset = %llx\n", fbi_i->reg_offset);
- print_debug("DIRECT_ADDR vm_start = %lx\n", vma->vm_start);
- print_debug("DIRECT_ADDR res_addr = %lx\n", addr);
-
- buf = fbi_probe_alloc_and_read_from_addr(fbi_i, addr, &size);
- /* If buf is NULL size will be 0.
- * That mean we cannot get data for this probe.
- * But we should send probe message with packed data size 0
- * as error message.
- */
- fbi_msg(fbi_i->var_id, size, buf);
-
- if (buf != NULL) {
- kfree(buf);
- } else {
- print_warn("get data by direct addr failed (0x%lx :0x%llx)\n",
- addr, fbi_i->reg_offset);
- }
-exit:
- return 0;
-}
-
-static int fbi_probe_handler(struct uprobe *p, struct pt_regs *regs)
-{
- struct sspt_ip *ip = container_of(p, struct sspt_ip, uprobe);
- struct fbi_info *fbi_i = &ip->desc->info.fbi_i;
- struct fbi_var_data *fbi_d = NULL;
- uint8_t i;
-
- if (ip->desc->type != SWAP_FBIPROBE) {
- /* How this can occure? Doesn't matter, just print and go */
- print_err("Not FBI probe in FBI handler!\n");
- return 0;
- }
-
- for (i = 0; i != fbi_i->var_count; i++) {
- fbi_d = &fbi_i->vars[i];
- if (fbi_d->reg_n == DIRECT_ADDR) {
- if (0 != fbi_probe_get_data_from_direct_addr(fbi_d, ip,
- regs))
- print_err("fbi_probe_get_data_from_direct_addr error\n");
- } else if (fbi_d->steps_count == 0) {
- if (0 != fbi_probe_get_data_from_reg(fbi_d, regs))
- print_err("fbi_probe_get_data_from_reg error\n");
- } else {
- if (0 != fbi_probe_get_data_from_ptrs(fbi_d, regs))
- print_err("fbi_probe_get_data_from_ptrs error\n");
- }
- }
-
- return 0;
-}
-
-/* FBI probe interfaces */
-void fbi_probe_cleanup(struct probe_info *probe_i)
-{
- uint8_t i;
- struct fbi_info *fbi_i = &(probe_i->fbi_i);
-
- for (i = 0; i != fbi_i->var_count; i++) {
- if (fbi_i->vars[i].steps != NULL) {
- if (fbi_i->vars[i].steps != NULL)
- kfree(fbi_i->vars[i].steps);
- fbi_i->vars[i].steps = NULL;
- fbi_i->vars[i].steps_count = 0;
- }
- }
-
- kfree(fbi_i->vars);
- fbi_i->vars = NULL;
-}
-
-void fbi_probe_init(struct sspt_ip *ip)
-{
- ip->uprobe.pre_handler = (uprobe_pre_handler_t)fbi_probe_handler;
-}
-
-void fbi_probe_uninit(struct sspt_ip *ip)
-{
- if (ip != NULL)
- fbi_probe_cleanup(&ip->desc->info);
-}
-
-static int fbi_probe_register_probe(struct sspt_ip *ip)
-{
- return swap_register_uprobe(&ip->uprobe);
-}
-
-static void fbi_probe_unregister_probe(struct sspt_ip *ip, int disarm)
-{
- __swap_unregister_uprobe(&ip->uprobe, disarm);
-}
-
-static struct uprobe *fbi_probe_get_uprobe(struct sspt_ip *ip)
-{
- return &ip->uprobe;
-}
-
-int fbi_probe_copy(struct probe_info *dest, const struct probe_info *source)
-{
- uint8_t steps_count;
- size_t steps_size;
- size_t vars_size;
- struct fbi_var_data *vars;
- struct fbi_step *steps_source;
- struct fbi_step *steps_dest = NULL;
- uint8_t i, n;
- int ret = 0;
-
- memcpy(dest, source, sizeof(*source));
-
- vars_size = source->fbi_i.var_count * sizeof(*source->fbi_i.vars);
- vars = kmalloc(vars_size, GFP_KERNEL);
- if (vars == NULL)
- return -ENOMEM;
-
- memcpy(vars, source->fbi_i.vars, vars_size);
-
- for (i = 0; i != source->fbi_i.var_count; i++) {
- steps_dest = NULL;
- steps_count = vars[i].steps_count;
- steps_size = sizeof(*steps_source) * steps_count;
- steps_source = vars[i].steps;
-
- if (steps_size != 0 && steps_source != NULL) {
- steps_dest = kmalloc(steps_size, GFP_KERNEL);
- if (steps_dest == NULL) {
- print_err("can not alloc data\n");
- n = i;
- ret = -ENOMEM;
- goto err;
- }
-
- memcpy(steps_dest, steps_source, steps_size);
- }
- vars[i].steps = steps_dest;
- }
-
- dest->fbi_i.vars = vars;
-
- return ret;
-err:
- for (i = 0; i < n; i++)
- kfree(vars[i].steps);
- kfree(vars);
- return ret;
-}
-
-/* Register */
-static struct probe_iface fbi_probe_iface = {
- .init = fbi_probe_init,
- .uninit = fbi_probe_uninit,
- .reg = fbi_probe_register_probe,
- .unreg = fbi_probe_unregister_probe,
- .get_uprobe = fbi_probe_get_uprobe,
- .copy = fbi_probe_copy,
- .cleanup = fbi_probe_cleanup
-};
-
-static int __init fbiprobe_module_init(void)
-{
- int ret = 0;
- ret = swap_register_probe_type(SWAP_FBIPROBE, &fbi_probe_iface);
- print_debug("Init done. Result=%d\n", ret);
- return ret;
-}
-
-static void __exit fbiprobe_module_exit(void)
-{
- swap_unregister_probe_type(SWAP_FBIPROBE);
-}
-
-module_init(fbiprobe_module_init);
-module_exit(fbiprobe_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP fbiprobe");
-MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>; Vitaliy Cherepanov <v.cherepanov@samsung.com>");
-
+++ /dev/null
-/*
- * @file fbi_probe/fbi_probe.h
- *
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov : FBI implement
- * 2014 Vitaliy Cherepanov: FBI implement, portage
- *
- * @section DESCRIPTION
- *
- * Function body instrumentation.
- *
- */
-
-#ifndef __FBI_PROBE_H__
-#define __FBI_PROBE_H__
-
-#include <linux/types.h>
-
-/* FBI step */
-struct fbi_step {
- uint8_t ptr_order; /* Specifies what is located on the address:
- * ptr_order = 0 - variable
- * ptr_order = 1 - pointer to variable
- * ptr_order = 2 - pointer to pointer
- * etc. */
-
- uint64_t data_offset;
-} __packed;
-
-/* FBI var data */
-struct fbi_var_data {
- /* Variable position is evaluated by the following rule:
- * var_position = *(pointer_to_register) - reg_offset
- * It is expected that the offset is not null only when we're taking
- * var value from stack.
- */
- uint64_t var_id; /* Variable identifier
- * Used to specify var */
- uint64_t reg_offset; /* Offset relative to the registers value
- * address, specified with reg_n */
- uint8_t reg_n; /* Register number. Hope times of cpu
- * with more than 2 million ones are very
- * far from us */
- uint32_t data_size; /* Data size to be read */
-
- uint8_t steps_count; /* Count of steps to extract variable
- * value */
- struct fbi_step *steps; /* extract steps */
-};
-
-/* FBI info */
-struct fbi_info {
- uint8_t var_count;
- struct fbi_var_data *vars;
-};
-
-#endif /* __FBI_PROBE_H__ */
+++ /dev/null
-/*
- * @file fbiprobe/fbi_probe.h
- *
- * @author Aleksandr Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov : FBI implement
- * 2014 Vitaliy Cherepanov: FBI portage
- *
- * @section DESCRIPTION
- *
- * Function body instrumetation
- *
- */
-
-#ifndef __REGS_H__
-#define __REGS_H__
-
-#include <linux/ptrace.h>
-
-#include "fbi_probe_module.h"
-/* This function is used to compare register number and its name on x86 arch.
- * For ARM it is dumb.
- * List of registers and their nums on x86:
- * ax 0
- * bx 1
- * cx 2
- * dx 3
- * si 4
- * di 5
- * bp 6
- * sp 7
- */
-
-static inline unsigned long *get_ptr_by_num(struct pt_regs *regs,
- unsigned char reg_num)
-{
- unsigned long *reg = NULL;
- /* FIXME: bad way to use "sizeof(long) " */
- if (reg_num < sizeof(struct pt_regs) / sizeof(long)) {
- reg = (unsigned long *)regs;
- reg = ®[reg_num];
- }
-
- return reg;
-}
-
-#endif /* __REGS_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_gtp.o
-swap_gtp-y := gt_module.o \
- gt_debugfs.o
+++ /dev/null
-#ifndef __GT_H__
-#define __GT_H__
-
-#define GT_PREFIX "SWAP GOT PATCHER: "
-
-#endif /* __GT_H__ */
+++ /dev/null
-#include <linux/debugfs.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <master/swap_debugfs.h>
-#include "gt.h"
-#include "gt_debugfs.h"
-#include "gt_module.h"
-
-#define GT_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
-
-static const char GT_FOLDER[] = "got_patcher";
-static const char GT_LINKER[] = "linker";
-static const char GT_PATH[] = "path";
-static const char GT_FIXUP_ADDR[] = "dl_fixup_addr";
-static const char GT_RELOC_ADDR[] = "dl_reloc_addr";
-static const char GT_ENABLE[] = "enable";
-static const char GT_BY_PATH[] = "by_path";
-static const char GT_BY_PID[] = "by_pid";
-static const char GT_BY_ID[] = "by_id";
-static const char GT_ADD[] = "add";
-static const char GT_DEL[] = "del";
-static const char GT_DEL_ALL[] = "del_all";
-static const char GT_LIST_TARGETS[] = "list_targets";
-static const char GT_HANDLER[] = "handler";
-static const char GT_HANDLER_FIXUP_OFF[] = "fixup_handler_off";
-static const char GT_HANDLER_RELOC_OFF[] = "reloc_handler_off";
-static const char GT_PROC_FEATURES_OFF[] = "proc_features_off";
-static const char GT_PTHREAD[] = "pthread";
-static const char GT_MINIMAL_INIT[] = "minimal_init_off";
-
-static struct dentry *gt_root;
-
-
-/* Type for function that handles string grabbed from userspace */
-typedef int (*sh_t)(char *);
-
-/* Type for function that handles unsigned long grabbed from userspace */
-typedef int (*ulh_t)(unsigned long);
-
-/* Type for function that handles pid grabbed from userspace */
-typedef int (*ph_t)(pid_t);
-
-
-static ssize_t get_string_and_call(const char __user *buf, size_t len,
- sh_t cb)
-{
- char *string;
- ssize_t ret;
-
- string = kmalloc(len, GFP_KERNEL);
- if (string == NULL) {
- ret = -ENOMEM;
- goto get_string_write_out;
- }
-
- if (copy_from_user(string, buf, len)) {
- ret = -EINVAL;
- goto get_string_write_out;
- }
-
- string[len - 1] = '\0';
-
- ret = cb(string);
-
-get_string_write_out:
- kfree(string);
-
- return ret == 0 ? len : ret;
-}
-
-static ssize_t get_ul_and_call(const char __user *buf, size_t len, ulh_t cb)
-{
- ssize_t ret;
- char *ulstring;
- unsigned long ul;
-
- ulstring = kmalloc(len, GFP_KERNEL);
- if (ulstring == NULL) {
- ret = -ENOMEM;
- goto get_ul_write_out;
- }
-
- if (copy_from_user(ulstring, buf, len)) {
- ret = -EINVAL;
- goto get_ul_write_out;
- }
-
- ulstring[len - 1] = '\0';
-
- ret = kstrtoul(ulstring, 16, &ul);
- if (ret != 0)
- goto get_ul_write_out;
-
- ret = cb(ul);
-
-get_ul_write_out:
- kfree(ulstring);
-
- return ret == 0 ? len : ret;
-}
-
-static ssize_t get_pid_and_call(const char __user *buf, size_t len, ph_t cb)
-{
- ssize_t ret;
- char *pidstring;
- pid_t pid;
-
- pidstring = kmalloc(len, GFP_KERNEL);
- if (pidstring == NULL) {
- ret = -ENOMEM;
- goto get_pid_write_out;
- }
-
- if (copy_from_user(pidstring, buf, len)) {
- ret = -EINVAL;
- goto get_pid_write_out;
- }
-
- pidstring[len - 1] = '\0';
-
- ret = kstrtoul(pidstring, 10, (unsigned long *)&pid);
- if (ret != 0)
- goto get_pid_write_out;
-
- ret = cb(pid);
-
-get_pid_write_out:
- kfree(pidstring);
-
- return ret == 0 ? len : ret;
-}
-
-/* ===========================================================================
- * = TARGETS =
- * ===========================================================================
- */
-
-static ssize_t handler_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_set_handler_path);
-}
-
-static ssize_t handler_fixup_off_write(struct file *file,
- const char __user *buf, size_t len,
- loff_t *ppos)
-{
- return get_ul_and_call(buf, len, gtm_set_handler_fixup_off);
-}
-
-static ssize_t handler_reloc_off_write(struct file *file,
- const char __user *buf, size_t len,
- loff_t *ppos)
-{
- return get_ul_and_call(buf, len, gtm_set_handler_reloc_off);
-}
-
-static ssize_t proc_features_off_write(struct file *file,
- const char __user *buf, size_t len,
- loff_t *ppos)
-{
- return get_ul_and_call(buf, len, gtm_set_proc_features_off);
-}
-
-static const struct file_operations handler_path_fops = {
- .owner = THIS_MODULE,
- .write = handler_path_write,
-};
-
-static const struct file_operations handler_fixup_off_fops = {
- .owner = THIS_MODULE,
- .write = handler_fixup_off_write,
-};
-
-static const struct file_operations handler_reloc_off_fops = {
- .owner = THIS_MODULE,
- .write = handler_reloc_off_write,
-};
-
-static const struct file_operations proc_features_off_fops = {
- .owner = THIS_MODULE,
- .write = proc_features_off_write,
-};
-
-/* ===========================================================================
- * = TARGETS =
- * ===========================================================================
- */
-
-static ssize_t by_path_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_add_by_path);
-}
-
-static ssize_t by_path_del_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_del_by_path);
-}
-
-static ssize_t by_pid_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_pid_and_call(buf, len, gtm_add_by_pid);
-}
-
-static ssize_t by_pid_del_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_pid_and_call(buf, len, gtm_del_by_pid);
-}
-
-static ssize_t by_id_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_add_by_id);
-}
-
-static ssize_t by_id_del_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_del_by_id);
-}
-
-static ssize_t del_all_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
-
- ret = gtm_del_all();
-
- return ret == 0 ? len : ret;
-}
-
-static ssize_t target_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *targets;
-
- ret = gtm_get_targets(&targets);
- if (ret < 0)
- return ret;
-
- ret = simple_read_from_buffer(buf, len, ppos, targets, ret);
- kfree(targets);
-
- return ret;
-}
-
-static const struct file_operations by_path_add_fops = {
- .owner = THIS_MODULE,
- .write = by_path_add_write,
-};
-
-static const struct file_operations by_path_del_fops = {
- .owner = THIS_MODULE,
- .write = by_path_del_write,
-};
-
-static const struct file_operations by_pid_add_fops = {
- .owner = THIS_MODULE,
- .write = by_pid_add_write,
-};
-
-static const struct file_operations by_pid_del_fops = {
- .owner = THIS_MODULE,
- .write = by_pid_del_write,
-};
-
-static const struct file_operations by_id_add_fops = {
- .owner = THIS_MODULE,
- .write = by_id_add_write,
-};
-
-static const struct file_operations by_id_del_fops = {
- .owner = THIS_MODULE,
- .write = by_id_del_write,
-};
-
-static const struct file_operations del_all_fops = {
- .owner = THIS_MODULE,
- .write = del_all_write,
-};
-
-static const struct file_operations target_fops = {
- .owner = THIS_MODULE,
- .read = target_read,
-};
-
-/* ===========================================================================
- * = ENABLE =
- * ===========================================================================
- */
-
-static ssize_t enable_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
-{
- char val[2];
-
- val[0] = (gtm_status() == GT_ON ? '1' : '0');
- val[1] = '\0';
-
- return simple_read_from_buffer(buf, len, ppos, val, 2);
-}
-
-static ssize_t enable_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char val[2];
- size_t val_size;
-
- val_size = min(len, (sizeof(val) - 1));
- if (copy_from_user(val, buf, val_size))
- return -EFAULT;
-
- val[1] = '\0';
- switch(val[0]) {
- case '0':
- ret = gtm_switch(GT_OFF);
- break;
- case '1':
- ret = gtm_switch(GT_ON);
- break;
- default:
- printk(GT_PREFIX "Invalid state!\n");
- return -EINVAL;
- }
-
- return ret == 0 ? len : ret;
-}
-
-static const struct file_operations enable_fops = {
- .owner = THIS_MODULE,
- .write = enable_write,
- .read = enable_read,
-};
-
-
-/* ===========================================================================
- * = LINKER =
- * ===========================================================================
- */
-
-static ssize_t linker_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_set_linker_path);
-}
-
-static ssize_t fixup_off_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_ul_and_call(buf, len, gtm_set_fixup_off);
-}
-
-static ssize_t reloc_off_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_ul_and_call(buf, len, gtm_set_reloc_off);
-}
-
-static const struct file_operations linker_path_fops = {
- .owner = THIS_MODULE,
- .write = linker_path_write,
-};
-
-static const struct file_operations fixup_off_fops = {
- .owner = THIS_MODULE,
- .write = fixup_off_write,
-};
-
-static const struct file_operations reloc_off_fops = {
- .owner = THIS_MODULE,
- .write = reloc_off_write,
-};
-
-
-
-/* ===========================================================================
- * = PTHREAD =
- * ===========================================================================
- */
-
-static ssize_t pthread_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, gtm_set_pthread_path);
-}
-
-static ssize_t init_off_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_ul_and_call(buf, len, gtm_set_init_off);
-}
-
-static const struct file_operations pthread_path_fops = {
- .owner = THIS_MODULE,
- .write = pthread_path_write,
-};
-
-static const struct file_operations pthread_init_off_fops = {
- .owner = THIS_MODULE,
- .write = init_off_write,
-};
-
-
-
-
-int gtd_init(void)
-{
- struct dentry *swap_dentry, *root, *linker, *by_path, *by_pid,
- *by_id, *handler, *pthread, *dentry;
- int ret;
-
- ret = -ENODEV;
- if (!debugfs_initialized())
- goto fail;
-
- ret = -ENOENT;
- swap_dentry = swap_debugfs_getdir();
- if (!swap_dentry)
- goto fail;
-
- ret = -ENOMEM;
- root = swap_debugfs_create_dir(GT_FOLDER, swap_dentry);
- if (IS_ERR_OR_NULL(root))
- goto fail;
-
- gt_root = root;
-
- linker = swap_debugfs_create_dir(GT_LINKER, root);
- if (IS_ERR_OR_NULL(linker)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, linker,
- NULL, &linker_path_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_FIXUP_ADDR, GT_DEFAULT_PERMS,
- linker, NULL, &fixup_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_RELOC_ADDR, GT_DEFAULT_PERMS,
- linker, NULL, &reloc_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- by_path = swap_debugfs_create_dir(GT_BY_PATH, root);
- if (IS_ERR_OR_NULL(by_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_path,
- NULL, &by_path_add_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_path,
- NULL, &by_path_del_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- by_pid = swap_debugfs_create_dir(GT_BY_PID, root);
- if (IS_ERR_OR_NULL(by_pid)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_pid,
- NULL, &by_pid_add_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_pid,
- NULL, &by_pid_del_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- by_id = swap_debugfs_create_dir(GT_BY_ID, root);
- if (IS_ERR_OR_NULL(by_id)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_id,
- NULL, &by_id_add_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_id,
- NULL, &by_id_del_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_DEL_ALL, GT_DEFAULT_PERMS, root,
- NULL, &del_all_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_LIST_TARGETS, GT_DEFAULT_PERMS, root,
- NULL, &target_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_ENABLE, GT_DEFAULT_PERMS, root,
- NULL, &enable_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- handler = swap_debugfs_create_dir(GT_HANDLER, root);
- if (IS_ERR_OR_NULL(handler)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, handler,
- NULL, &handler_path_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_HANDLER_FIXUP_OFF,
- GT_DEFAULT_PERMS, handler,
- NULL, &handler_fixup_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_HANDLER_RELOC_OFF,
- GT_DEFAULT_PERMS, handler, NULL,
- &handler_reloc_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_PROC_FEATURES_OFF,
- GT_DEFAULT_PERMS, handler, NULL,
- &proc_features_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- pthread = swap_debugfs_create_dir(GT_PTHREAD, root);
- if (IS_ERR_OR_NULL(pthread)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, pthread,
- NULL, &pthread_path_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(GT_MINIMAL_INIT, GT_DEFAULT_PERMS,
- pthread, NULL,
- &pthread_init_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- return 0;
-
-remove:
- debugfs_remove_recursive(root);
-
-fail:
- printk(GT_PREFIX "Debugfs initialization failure: %d\n", ret);
-
- return ret;
-}
-
-void gtd_exit(void)
-{
- if (gt_root)
- debugfs_remove_recursive(gt_root);
- gt_root = NULL;
-}
+++ /dev/null
-#ifndef __GT_DEBUGFS_H__
-#define __GT_DEBUGFS_H__
-
-int gtd_init(void);
-void gtd_exit(void);
-
-#endif /* __GT_DEBUGFS_H__ */
+++ /dev/null
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/string.h>
-#include <linux/namei.h>
-#include <linux/slab.h>
-#include <linux/limits.h>
-#include <kprobe/swap_ktd.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/sspt/sspt_page.h>
-#include <us_manager/sspt/sspt_file.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/probes/probe_info_new.h>
-#include <us_manager/us_common_file.h>
-#include <loader/loader.h>
-#include <master/swap_initializer.h>
-#include "gt.h"
-#include "gt_module.h"
-#include "gt_debugfs.h"
-
-
-enum task_id_t {
- GT_SET_BY_DENTRY,
- GT_SET_BY_PID,
- GT_SET_BY_ID,
-};
-
-struct l_probe_el {
- struct list_head list;
-
- struct pf_group *pfg;
- enum task_id_t task_id;
- union {
- pid_t tgid;
- struct dentry *dentry;
- char *id;
- };
-
- struct probe_new p_fixup;
- struct probe_new p_reloc;
- struct probe_new p_init;
-};
-
-struct bin_data_t {
- struct dentry *dentry;
- unsigned long off;
-};
-
-typedef unsigned long (*rh_t)(struct uretprobe_instance *, struct pt_regs *,
- struct hd_t *);
-
-/* Typedef for compare function that removes data from list */
-typedef bool (*cf_t)(struct l_probe_el*, void *);
-
-static LIST_HEAD(_reg_probes);
-static LIST_HEAD(_unreg_probes);
-static DEFINE_MUTEX(_linker_probes_lock);
-
-static enum gt_status _status = GT_OFF;
-static struct bin_data_t _linker_fixup;
-static struct bin_data_t _linker_reloc;
-static struct bin_data_t _handler_fixup;
-static struct bin_data_t _handler_reloc;
-static struct bin_data_t _proc_features;
-static struct bin_data_t _pthread_init;
-
-
-static inline void _lock_probes_list(void)
-{
- mutex_lock(&_linker_probes_lock);
-}
-
-static inline void _unlock_probes_list(void)
-{
- mutex_unlock(&_linker_probes_lock);
-}
-
-static inline bool _is_enable(void)
-{
- return _status == GT_ON;
-}
-
-static inline bool _is_bin_data_available(struct bin_data_t *bin_data)
-{
- return (bin_data->dentry && bin_data->off);
-}
-
-static inline bool _is_linker_data_available(void)
-{
- return _is_bin_data_available(&_linker_fixup) &&
- _is_bin_data_available(&_linker_reloc);
-}
-
-static inline bool _is_handler_data_available(void)
-{
- return _is_bin_data_available(&_handler_fixup) &&
- _is_bin_data_available(&_handler_reloc) &&
- _is_bin_data_available(&_proc_features);
-}
-
-static inline bool _is_pthread_data_available(void)
-{
- return _is_bin_data_available(&_pthread_init);
-}
-
-
-
-
-/* ===========================================================================
- * = TASK DATA MANIPULATION =
- * ===========================================================================
- */
-
-static void gt_ktd_init(struct task_struct *task, void *data)
-{
- bool *in_handler = (bool *)data;
-
- *in_handler = false;
-}
-
-static void gt_ktd_exit(struct task_struct *task, void *data)
-{
-}
-
-static struct ktask_data gt_ktd = {
- .init = gt_ktd_init,
- .exit = gt_ktd_exit,
- .size = sizeof(bool),
-};
-
-static inline bool _is_in_handler(void)
-{
- return *(bool *)swap_ktd(>_ktd, current);
-}
-
-static inline void _set_in_handler(bool is_in_handler)
-{
- *(bool *)swap_ktd(>_ktd, current) = is_in_handler;
-}
-
-static inline bool _check_by_dentry(struct l_probe_el *l_probe, void *data)
-{
- struct dentry *dentry = (struct dentry *)data;
-
- if (l_probe->task_id == GT_SET_BY_DENTRY &&
- l_probe->dentry == dentry)
- return true;
-
- return false;
-}
-
-static inline bool _check_by_pid(struct l_probe_el *l_probe, void *data)
-{
- pid_t tgid = *(pid_t *)data;
-
- if (l_probe->task_id == GT_SET_BY_PID &&
- l_probe->tgid == tgid)
- return true;
-
- return false;
-}
-
-static inline bool _check_by_id(struct l_probe_el *l_probe, void *data)
-{
- char *id = (char *)data;
-
- if (l_probe->task_id == GT_SET_BY_ID &&
- strncmp(l_probe->id, id, PATH_MAX))
- return true;
-
- return false;
-}
-
-static inline bool _del_all(struct l_probe_el *l_probe, void *data)
-{
- return true;
-}
-
-/* ===========================================================================
- * = LINKER HANDLERS =
- * ===========================================================================
- */
-
-static unsigned long _redirect_to_handler(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- struct hd_t *hd, unsigned long off)
-{
- unsigned long base;
- unsigned long vaddr;
-
- base = lpd_get_handlers_base(hd);
- if (base == 0)
- return 0;
-
- vaddr = base + off;
- loader_module_prepare_ujump(ri, regs, vaddr);
-
- return vaddr;
-}
-
-static unsigned long _redirect_to_fixup_handler(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- struct hd_t *hd)
-{
- return _redirect_to_handler(ri, regs, hd, _handler_fixup.off);
-}
-
-static unsigned long _redirect_to_reloc_handler(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- struct hd_t *hd)
-{
- return _redirect_to_handler(ri, regs, hd, _handler_reloc.off);
-}
-
-static unsigned long _redirect_to_proc_features(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- struct hd_t *hd)
-{
- return _redirect_to_handler(ri, regs, hd, _proc_features.off);
-}
-
-
-
-static int _process_eh(struct uretprobe_instance *ri, struct pt_regs *regs,
- rh_t rh, struct dentry *dentry)
-{
- struct pd_t *pd = lpd_get_by_task(current);
- struct hd_t *hd;
- unsigned long vaddr = 0;
- unsigned long old_pc = swap_get_upc(regs);
-
- if ((dentry == NULL) || _is_in_handler())
- goto out_set_orig;
-
- hd = lpd_get_hd(pd, dentry);
- if (hd == NULL)
- goto out_set_orig;
-
- if ((lpd_get_state(hd) == NOT_LOADED || lpd_get_state(hd) == FAILED) &&
- lpd_get_init_state(pd)) {
- vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
- } else if (lpd_get_state(hd) == LOADED) {
- _set_in_handler(true);
- vaddr = rh(ri, regs, hd);
- }
-
-out_set_orig:
- loader_set_priv_origin(ri, vaddr);
-
- /* PC change check */
- return old_pc != swap_get_upc(regs);
-}
-
-static int dl_fixup_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- return _process_eh(ri, regs, &_redirect_to_fixup_handler,
- _handler_fixup.dentry);
-}
-
-
-
-static void _restore_exec(struct uretprobe_instance *ri, struct hd_t *hd)
-{
- if (_is_in_handler())
- _set_in_handler(false);
-}
-
-static int _process_rh(struct uretprobe_instance *ri, struct pt_regs *regs,
- rh_t rh, struct dentry *dentry)
-{
- struct pd_t *pd = lpd_get_by_task(current);
- struct hd_t *hd;
-
- if (dentry == NULL)
- return 0;
-
- hd = lpd_get_hd(pd, dentry);
- if (hd == NULL)
- return 0;
-
- switch (lpd_get_state(hd)) {
- case NOT_LOADED:
- break;
- case LOADING:
- loader_loading_ret(ri, regs, pd, hd);
- /* Patch all binaries */
- if (lpd_get_state(hd))
- rh(ri, regs, hd);
- break;
- case LOADED:
- /* TODO Check does we need this if library is loaded
- * with RTLD_NOW ? */
- _restore_exec(ri, hd);
- break;
- case FAILED:
- loader_failed_ret(ri, regs, pd, hd);
- break;
- case ERROR:
- default:
- break;
- }
-
- return 0;
-}
-
-static int common_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- return _process_rh(ri, regs, &_redirect_to_proc_features,
- _proc_features.dentry);
-}
-
-/* TODO Make ordinary interface. Now real data_size is set in init, because
- * it is unknown in this module during compile time. */
-static struct probe_desc pin_fixup = MAKE_URPROBE(dl_fixup_eh, common_rh, 0);
-
-
-static int dl_reloc_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- return _process_eh(ri, regs, &_redirect_to_reloc_handler,
- _handler_reloc.dentry);
-}
-
-/* TODO Make ordinary interface. Now real data_size is set in init, because
- * it is unknown in this module during compile time. */
-static struct probe_desc pin_reloc = MAKE_URPROBE(dl_reloc_eh, common_rh, 0);
-
-
-static int pthread_init_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
-
- /* Wait until pthread is inited, if it is going to be inited before
- * loading our library */
- lpd_set_init_state(pd, false);
-
- return 0;
-}
-
-static int pthread_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
-
- lpd_set_init_state(pd, true);
-
- return 0;
-}
-
-/* TODO Make ordinary interface. Now real data_size is set in init, because
- * it is unknown in this module during compile time. */
-static struct probe_desc pin_pinit = MAKE_URPROBE(pthread_init_eh,
- pthread_init_rh, 0);
-
-
-static int _register_probe_el_no_lock(struct l_probe_el *l_probe)
-{
- int ret;
-
- /* register 'fix_up' */
- l_probe->p_fixup.desc = &pin_fixup;
- l_probe->p_fixup.offset = _linker_fixup.off;
- ret = pin_register(&l_probe->p_fixup, l_probe->pfg,
- _linker_fixup.dentry);
- if (ret != 0) {
- printk(GT_PREFIX "Error register linker fixup probe. "
- "Linker dentry %s, "
- "dl_fixup() offset = 0x%lx\n",
- _linker_fixup.dentry->d_name.name,
- _linker_fixup.off);
- return ret;
- }
-
- /* register 'reloc' */
- l_probe->p_reloc.desc = &pin_reloc;
- l_probe->p_reloc.offset = _linker_reloc.off;
- ret = pin_register(&l_probe->p_reloc, l_probe->pfg,
- _linker_reloc.dentry);
- if (ret != 0) {
- printk(GT_PREFIX "Error register linker reloc probe. "
- "Linker dentry %s, "
- "dl_reloc() offset = 0x%lx\n",
- _linker_reloc.dentry->d_name.name,
- _linker_reloc.off);
-
- goto reg_probe_el_fail;
- }
-
- /* register 'init' */
- l_probe->p_init.desc = &pin_pinit;
- l_probe->p_init.offset = _pthread_init.off;
- ret = pin_register(&l_probe->p_init, l_probe->pfg,
- _pthread_init.dentry);
- if (ret != 0) {
- printk(GT_PREFIX "Error register pthread minimal init. "
- "Pthread dentry %s, "
- "__pthread_minimal_init() offset = 0x%lx\n",
- _pthread_init.dentry->d_name.name,
- _pthread_init.off);
-
- goto reg_probe_pthread_fail;
- }
-
- return 0;
-
-reg_probe_pthread_fail:
- pin_unregister(&l_probe->p_reloc, l_probe->pfg);
-
-reg_probe_el_fail:
- pin_unregister(&l_probe->p_fixup, l_probe->pfg);
-
- return ret;
-}
-
-static int _unregister_probe_el_no_lock(struct l_probe_el *l_probe)
-{
- pin_unregister(&l_probe->p_init, l_probe->pfg);
- pin_unregister(&l_probe->p_reloc, l_probe->pfg);
- pin_unregister(&l_probe->p_fixup, l_probe->pfg);
-
- return 0;
-}
-
-static int _disable_got_patcher(void)
-{
- struct l_probe_el *l_probe;
- int ret = 0;
-
- if (!_is_linker_data_available())
- return -EINVAL;
-
- _lock_probes_list();
-
- list_for_each_entry(l_probe, &_reg_probes, list) {
- ret = _unregister_probe_el_no_lock(l_probe);
- if (ret == 0)
- list_move_tail(&l_probe->list, &_unreg_probes);
- }
-
- _unlock_probes_list();
-
- _status = GT_OFF;
-
- return ret;
-}
-
-static int _enable_got_patcher(void)
-{
- struct l_probe_el *l_probe;
- int ret;
-
- if (!_is_linker_data_available() || !_is_handler_data_available() ||
- !_is_pthread_data_available())
- return -EINVAL;
-
- _lock_probes_list();
-
- list_for_each_entry(l_probe, &_unreg_probes, list) {
- ret = _register_probe_el_no_lock(l_probe);
- if (ret == 0)
- list_move_tail(&l_probe->list, &_reg_probes);
- else
- goto enable_patcher_fail_unlock;
- }
-
- _unlock_probes_list();
-
- _status = GT_ON;
-
- return 0;
-enable_patcher_fail_unlock:
- _unlock_probes_list();
-
- printk(GT_PREFIX "Error registering linker probes, disabling...");
- _disable_got_patcher();
-
- return ret;
-}
-
-
-/* ===========================================================================
- * = LIST MANIPULATIONS =
- * ===========================================================================
- */
-static struct l_probe_el *_create_linker_probe_el(void)
-{
- struct l_probe_el *l_probe;
-
- l_probe = kzalloc(sizeof(*l_probe), GFP_KERNEL);
- if (l_probe == NULL)
- return NULL;
-
- INIT_LIST_HEAD(&l_probe->list);
-
- return l_probe;
-}
-
-static void _destroy_linker_probe_el_no_lock(struct l_probe_el *l_probe)
-{
- if (l_probe->pfg)
- put_pf_group(l_probe->pfg);
-
- if (l_probe->task_id == GT_SET_BY_DENTRY && l_probe->dentry != NULL)
- swap_put_dentry(l_probe->dentry);
-
- if (l_probe->task_id == GT_SET_BY_ID && l_probe->id != NULL)
- kfree(l_probe->id);
-
- kfree(l_probe);
-}
-
-static void _clean_linker_probes(void)
-{
- struct l_probe_el *l_probe, *n;
- int ret;
-
- _lock_probes_list();
-
- list_for_each_entry_safe(l_probe, n, &_reg_probes, list) {
- ret = _unregister_probe_el_no_lock(l_probe);
- if (ret != 0)
- printk(GT_PREFIX "Cannot remove linker probe!\n");
- list_del(&l_probe->list);
- _destroy_linker_probe_el_no_lock(l_probe);
- }
-
- list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) {
- list_del(&l_probe->list);
- _destroy_linker_probe_el_no_lock(l_probe);
- }
-
- _unlock_probes_list();
-}
-
-
-/* ===========================================================================
- * = STRING MANIPULATIONS =
- * ===========================================================================
- */
-
-static size_t _get_dentry_len(struct dentry *dentry)
-{
- return strnlen(dentry->d_name.name, PATH_MAX);
-}
-
-static size_t _get_pid_len(pid_t pid)
-{
- /* FIXME Return constant - maximum digits for 10-based unsigned long on
- * 64 bit architecture to avoid complicated evaluations. */
- return 20;
-}
-
-static size_t _get_id_len(char *id)
-{
- return strnlen(id, PATH_MAX);
-}
-
-static size_t _get_item_len(struct l_probe_el *l_probe)
-{
- if (l_probe->task_id == GT_SET_BY_DENTRY)
- return _get_dentry_len(l_probe->dentry);
- else if (l_probe->task_id == GT_SET_BY_PID)
- return _get_pid_len(l_probe->tgid);
- else if (l_probe->task_id == GT_SET_BY_ID)
- return _get_id_len(l_probe->id);
-
- printk(GT_PREFIX "Error! Unknown process identificator!\n");
- return 0;
-}
-
-static char *_copy_dentry_item(char *dest, struct dentry *dentry)
-{
- size_t len;
-
- len = strnlen(dentry->d_name.name, PATH_MAX);
- memcpy(dest, dentry->d_name.name, len);
-
- return (dest + len);
-}
-
-static char *_copy_pid_item(char *dest, pid_t pid)
-{
- int ret;
- size_t len;
-
- len = _get_pid_len(pid);
-
- ret = snprintf(dest, len, "%lu", (unsigned long)pid);
- if (ret >= len)
- printk(GT_PREFIX "PID string is truncated!\n");
-
- return (dest + ret);
-}
-
-static char *_copy_id_item(char *dest, char *id)
-{
- size_t len;
-
- len = strnlen(id, PATH_MAX);
- memcpy(dest, id, len);
-
- return (dest + len);
-}
-
-static char *_copy_item(char *dest, struct l_probe_el *l_probe)
-{
- if (l_probe->task_id == GT_SET_BY_DENTRY)
- return _copy_dentry_item(dest, l_probe->dentry);
- else if (l_probe->task_id == GT_SET_BY_PID)
- return _copy_pid_item(dest, l_probe->tgid);
- else if (l_probe->task_id == GT_SET_BY_ID)
- return _copy_id_item(dest, l_probe->id);
-
- printk(GT_PREFIX "Error! Unknown process identificator!\n");
- return dest;
-}
-
-static char *_add_separator(char *dest)
-{
- const char sep = ':';
-
- *dest = sep;
-
- return (dest + 1);
-}
-
-static int _add_to_list(struct l_probe_el *l_probe)
-{
- int ret = 0;
-
- _lock_probes_list();
- if (_is_enable()) {
- ret = _register_probe_el_no_lock(l_probe);
- if (ret == 0)
- list_add_tail(&l_probe->list, &_reg_probes);
- } else {
- list_add_tail(&l_probe->list, &_unreg_probes);
- }
-
- _unlock_probes_list();
-
- return ret;
-}
-
-static int _remove_from_list(cf_t cb, void *data)
-{
- struct l_probe_el *l_probe, *n;
- int ret = 0;
-
- _lock_probes_list();
-
- list_for_each_entry_safe(l_probe, n, &_reg_probes, list) {
- if (cb(l_probe, data)) {
- ret = _unregister_probe_el_no_lock(l_probe);
- if (ret == 0) {
- list_del(&l_probe->list);
- _destroy_linker_probe_el_no_lock(l_probe);
- }
- goto remove_from_list_unlock;
- }
- }
-
- list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) {
- if (cb(l_probe, data)) {
- list_del(&l_probe->list);
- _destroy_linker_probe_el_no_lock(l_probe);
- goto remove_from_list_unlock;
- }
- }
-
-remove_from_list_unlock:
- _unlock_probes_list();
-
- return ret;
-}
-
-
-
-
-int gtm_set_linker_path(char *path)
-{
- struct dentry *dentry;
-
- dentry = swap_get_dentry(path);
- if (dentry == NULL)
- return -EINVAL;
-
- /* We use get_dentry only once, so use put dentry also only once */
- if (_linker_fixup.dentry != NULL ||
- _linker_reloc.dentry != NULL) {
- if (_linker_fixup.dentry != NULL)
- swap_put_dentry(_linker_fixup.dentry);
- else
- swap_put_dentry(_linker_reloc.dentry);
- }
-
- _linker_fixup.dentry = dentry;
- _linker_reloc.dentry = dentry;
-
- return 0;
-}
-
-int gtm_set_fixup_off(unsigned long offset)
-{
- _linker_fixup.off = offset;
-
- return 0;
-}
-
-int gtm_set_reloc_off(unsigned long offset)
-{
- _linker_reloc.off = offset;
-
- return 0;
-}
-
-int gtm_switch(enum gt_status stat)
-{
- int ret;
-
- switch (stat) {
- case GT_ON:
- ret = _enable_got_patcher();
- break;
- case GT_OFF:
- ret = _disable_got_patcher();
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-enum gt_status gtm_status(void)
-{
- return _status;
-}
-
-int gtm_add_by_path(char *path)
-{
- struct dentry *dentry;
- struct pf_group *pfg;
- struct l_probe_el *l_probe;
- int ret;
-
- dentry = swap_get_dentry(path);
- if (dentry == NULL)
- return -EINVAL;
-
- pfg = get_pf_group_by_dentry(dentry, dentry);
- if (pfg == NULL) {
- ret = -ENOMEM;
- goto add_by_path_put_dentry;
- }
-
- l_probe = _create_linker_probe_el();
- if (l_probe == NULL) {
- ret = -ENOMEM;
- goto add_by_path_put_pfg;
- }
-
- l_probe->pfg = pfg;
- l_probe->task_id = GT_SET_BY_DENTRY;
- l_probe->dentry = dentry;
-
- ret = _add_to_list(l_probe);
- if (ret != 0)
- goto add_by_path_free;
-
- return 0;
-
-add_by_path_free:
- kfree(l_probe);
-
-add_by_path_put_pfg:
- put_pf_group(pfg);
-
-add_by_path_put_dentry:
- swap_put_dentry(dentry);
-
- return ret;
-}
-
-int gtm_add_by_pid(pid_t pid)
-{
- struct pf_group *pfg;
- struct l_probe_el *l_probe;
- int ret;
-
- /* TODO Add dentry as private data */
- pfg = get_pf_group_by_tgid(pid, NULL);
- if (pfg == NULL)
- return -ENOMEM;
-
- l_probe = _create_linker_probe_el();
- if (l_probe == NULL) {
- ret = -ENOMEM;
- goto add_by_pid_put_pfg;
- }
-
- l_probe->pfg = pfg;
- l_probe->task_id = GT_SET_BY_PID;
- l_probe->tgid = pid;
-
- ret = _add_to_list(l_probe);
- if (ret != 0)
- goto add_by_pid_free;
-
- return 0;
-
-add_by_pid_free:
- kfree(l_probe);
-
-add_by_pid_put_pfg:
- put_pf_group(pfg);
-
- return ret;
-}
-
-int gtm_add_by_id(char *id)
-{
- char *new_id;
- struct pf_group *pfg;
- struct l_probe_el *l_probe;
- size_t len;
- int ret;
-
- len = strnlen(id, PATH_MAX);
- new_id = kmalloc(len, GFP_KERNEL);
- if (new_id == NULL)
- return -ENOMEM;
-
- /* TODO Add dentry as private data */
- pfg = get_pf_group_by_comm(id, NULL);
- if (pfg == NULL) {
- ret = -ENOMEM;
- goto add_by_id_free_id;
- }
-
- l_probe = _create_linker_probe_el();
- if (l_probe == NULL) {
- ret = -ENOMEM;
- goto add_by_id_put_pfg;
- }
-
- l_probe->pfg = pfg;
- l_probe->task_id = GT_SET_BY_ID;
- l_probe->id = new_id;
-
- ret = _add_to_list(l_probe);
- if (ret != 0)
- goto add_by_id_free;
-
- return 0;
-
-add_by_id_free:
- kfree(l_probe);
-
-add_by_id_put_pfg:
- put_pf_group(pfg);
-
-add_by_id_free_id:
- kfree(new_id);
-
- return ret;
-}
-
-int gtm_del_by_path(char *path)
-{
- struct dentry *dentry;
- int ret = 0;
-
- dentry = swap_get_dentry(path);
- if (dentry == NULL)
- return -EINVAL;
-
- ret = _remove_from_list(_check_by_dentry, dentry);
-
- return ret;
-}
-
-int gtm_del_by_pid(pid_t pid)
-{
- int ret = 0;
-
- ret = _remove_from_list(_check_by_pid, &pid);
-
- return ret;
-}
-
-int gtm_del_by_id(char *id)
-{
- int ret = 0;
-
- ret = _remove_from_list(_check_by_id, id);
-
- return ret;
-}
-
-int gtm_del_all(void)
-{
- int ret = 0;
-
- ret = _remove_from_list(_del_all, NULL);
-
- return ret;
-}
-
-ssize_t gtm_get_targets(char **targets)
-{
- struct l_probe_el *l_probe;
- char *ptr;
- size_t len = 0;
-
- _lock_probes_list();
- list_for_each_entry(l_probe, &_reg_probes, list) {
- /* Add separator */
- len += _get_item_len(l_probe) + 1;
- }
-
- list_for_each_entry(l_probe, &_unreg_probes, list) {
- /* Add separator */
- len += _get_item_len(l_probe) + 1;
- }
- _unlock_probes_list();
-
- *targets = kmalloc(len, GFP_KERNEL);
- if (*targets == NULL)
- return -ENOMEM;
-
- ptr = *targets;
-
- _lock_probes_list();
-
- list_for_each_entry(l_probe, &_reg_probes, list) {
- ptr = _copy_item(ptr, l_probe);
- ptr = _add_separator(ptr);
- }
-
- list_for_each_entry(l_probe, &_unreg_probes, list) {
- ptr = _copy_item(ptr, l_probe);
- ptr = _add_separator(ptr);
- }
-
- _unlock_probes_list();
-
- *targets[len - 1] = '\0';
-
- return len;
-}
-
-int gtm_set_handler_path(char *path)
-{
- struct dentry *dentry;
- int ret;
-
- /* We use get_dentry only once, so use put dentry also only once */
- dentry = swap_get_dentry(path);
- if (dentry == NULL)
- return -EINVAL;
-
- if (_handler_fixup.dentry)
- swap_put_dentry(_handler_fixup.dentry);
-
- if (_handler_reloc.dentry)
- swap_put_dentry(_handler_reloc.dentry);
-
- if (_proc_features.dentry)
- swap_put_dentry(_proc_features.dentry);
-
- _handler_fixup.dentry = dentry;
- _handler_reloc.dentry = dentry;
- _proc_features.dentry = dentry;
-
- /* TODO Do smth with this:
- * make interface for loader to remove handlers
- * and add/delete when gtm_set_handler() is called
- * Check container_of() may be useful */
- ret = loader_add_handler(path);
-
- return ret;
-}
-
-int gtm_set_handler_fixup_off(unsigned long offset)
-{
- _handler_fixup.off = offset;
-
- return 0;
-}
-
-int gtm_set_handler_reloc_off(unsigned long offset)
-{
- _handler_reloc.off = offset;
-
- return 0;
-}
-
-int gtm_set_proc_features_off(unsigned long offset)
-{
- _proc_features.off = offset;
-
- return 0;
-}
-
-int gtm_set_pthread_path(char *path)
-{
- struct dentry *dentry;
-
- dentry = swap_get_dentry(path);
- if (dentry == NULL)
- return -EINVAL;
-
- if (_pthread_init.dentry != NULL)
- swap_put_dentry(_pthread_init.dentry);
-
- _pthread_init.dentry = dentry;
-
- return 0;
-}
-
-int gtm_set_init_off(unsigned long offset)
-{
- _pthread_init.off = offset;
-
- return 0;
-}
-
-static int gtm_init(void)
-{
- int ret;
-
- ret = gtd_init();
- if (ret)
- goto gtd_init_err;
-
- ret = swap_ktd_reg(>_ktd);
- if (ret)
- goto gtd_ktd_reg_err;
-
- return 0;
-
-gtd_ktd_reg_err:
- gtd_exit();
-
-gtd_init_err:
- return ret;
-}
-
-static void gtm_exit(void)
-{
- swap_ktd_unreg(>_ktd);
- gtd_exit();
-
- _clean_linker_probes();
-
- /* We use get_dentry only once, so use put dentry also only once */
- if (_handler_fixup.dentry != NULL ||
- _handler_reloc.dentry != NULL) {
- if (_handler_fixup.dentry != NULL)
- swap_put_dentry(_handler_fixup.dentry);
- else
- swap_put_dentry(_handler_reloc.dentry);
- _handler_reloc.dentry = NULL;
- _handler_fixup.dentry = NULL;
- }
-}
-
-SWAP_LIGHT_INIT_MODULE(NULL, gtm_init, gtm_exit, NULL, NULL);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP GOT Patcher Module");
-MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>");
+++ /dev/null
-#ifndef __GT_MODULE_H__
-#define __GT_MODULE_H__
-
-#include <linux/types.h>
-
-enum gt_status {
- GT_ON,
- GT_OFF
-};
-
-/* Linker data */
-int gtm_set_linker_path(char *path);
-int gtm_set_fixup_off(unsigned long offset);
-int gtm_set_reloc_off(unsigned long offset);
-int gtm_switch(enum gt_status stat);
-enum gt_status gtm_status(void);
-
-/* Target processes data */
-int gtm_add_by_path(char *path);
-int gtm_add_by_pid(pid_t pid);
-int gtm_add_by_id(char *id);
-int gtm_del_by_path(char *path);
-int gtm_del_by_pid(pid_t pid);
-int gtm_del_by_id(char *id);
-int gtm_del_all(void);
-/* Returns len on success, error code on fail.
- * Allocates memory with malloc(), caller should free it */
-ssize_t gtm_get_targets(char **targets);
-
-/* Handlers data */
-int gtm_set_handler_path(char *path);
-int gtm_set_handler_fixup_off(unsigned long offset);
-int gtm_set_handler_reloc_off(unsigned long offset);
-int gtm_set_proc_features_off(unsigned long offset);
-
-/* Pthread data */
-int gtm_set_pthread_path(char *path);
-int gtm_set_init_off(unsigned long offset);
-
-#endif /* __GT_MODULE_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_kprobe.o
-swap_kprobe-y := \
- swap_kprobes_deps.o \
- swap_slots.o \
- swap_td_raw.o \
- swap_ktd.o
-
-
-### ARM64
-swap_kprobe-$(CONFIG_ARM64) += \
- arch/arm64/swap-asm/simulate-insn.o \
- arch/arm64/swap-asm/condn-helpers.o
-
-
-ifeq ($(CONFIG_SWAP_KERNEL_IMMUTABLE), y)
-
-swap_kprobe-y += swap_no_kprobes.o
-
-else # CONFIG_SWAP_KERNEL_IMMUTABLE
-
-swap_kprobe-y += swap_kprobes.o
-
-### ARM
-swap_kprobe-$(CONFIG_ARM) += \
- arch/arm/swap-asm/swap_kprobes.o \
- ../arch/arm/probes/probes_arm.o
-
-ifeq ($(CONFIG_STRICT_MEMORY_RWX), y)
-swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/memory_rwx.o
-endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y)
-
-
-### ARM64
-swap_kprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_kprobes.o \
- arch/arm64/swap-asm/dbg_interface.o \
- arch/arm64/swap-asm/kprobes-arm64.o
-
-
-### X86
-swap_kprobe-$(CONFIG_X86) += arch/x86/swap-asm/swap_kprobes.o
-
-endif # CONFIG_SWAP_KERNEL_IMMUTABLE
+++ /dev/null
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- */
-
-
-#include <asm/page.h>
-#include <asm/cacheflush.h>
-#include <asm/mmu_writeable.h>
-
-
-#include <ksyms/ksyms.h>
-
-
-static struct mm_struct *swap_init_mm;
-static int (*swap_set_memory_ro)(unsigned long addr, int numpages);
-static int (*swap_set_memory_rw)(unsigned long addr, int numpages);
-
-
-static int get_pte_cb(pte_t *ptep, pgtable_t token,
- unsigned long addr, void *data)
-{
- *(pte_t *)data = *ptep;
-
- return 1;
-}
-
-static pte_t get_pte(unsigned long page_addr)
-{
- pte_t pte = 0;
-
- apply_to_page_range(swap_init_mm, page_addr,
- PAGE_SIZE, get_pte_cb, &pte);
-
- return pte;
-}
-
-static void write_to_module(unsigned long addr, unsigned long val)
-{
- unsigned long *maddr = (unsigned long *)addr;
- unsigned long page_addr = addr & PAGE_MASK;
- pte_t pte;
-
- pte = get_pte(page_addr);
- if (pte_write(pte) == 0) {
- unsigned long flags;
- DEFINE_SPINLOCK(mem_lock);
-
- spin_lock_irqsave(&mem_lock, flags);
- if (swap_set_memory_rw(page_addr, 1) == 0) {
- *maddr = val;
- swap_set_memory_ro(page_addr, 1);
- } else {
- printk(KERN_INFO "RWX: failed to write memory %08lx (%08lx)\n",
- addr, val);
- }
- spin_unlock_irqrestore(&mem_lock, flags);
- } else {
- *maddr = val;
- }
-
- flush_icache_range(addr, addr + sizeof(long));
-}
-
-void mem_rwx_write_u32(unsigned long addr, unsigned long val)
-{
- if (addr < MODULES_VADDR || addr >= MODULES_END) {
- /*
- * if addr doesn't belongs kernel space,
- * segmentation fault will occur
- */
- mem_text_write_kernel_word((long *)addr, val);
- } else {
- write_to_module(addr, val);
- }
-}
-
-int mem_rwx_once(void)
-{
- const char *sym;
-
- sym = "set_memory_ro";
- swap_set_memory_ro = (void *)swap_ksyms(sym);
- if (swap_set_memory_ro == NULL)
- goto not_found;
-
- sym = "set_memory_rw";
- swap_set_memory_rw = (void *)swap_ksyms(sym);
- if (swap_set_memory_rw == NULL)
- goto not_found;
-
- sym = "init_mm";
- swap_init_mm = (void *)swap_ksyms(sym);
- if (swap_init_mm == NULL)
- goto not_found;
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
+++ /dev/null
-/**
- * @file kprobe/arch/asm-arm/memory_rwx.h
- *
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- */
-
-
-#ifndef _MEMORY_RWX_H
-#define _MEMORY_RWX_H
-
-
-int mem_rwx_once(void);
-void mem_rwx_write_u32(unsigned long addr, unsigned long val);
-
-
-#endif /* _MEMORY_RWX_H */
+++ /dev/null
-/**
- * kprobe/arch/asm-arm/swap_kprobes.c
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM/MIPS
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation; Support x86.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- * @author Alexander Shirshikov <a.shirshikov@samsung.com>: initial implementation for Thumb
- * @author Stanislav Andreev <s.andreev@samsung.com>: added time debug profiling support; BUG() message fix
- * @author Stanislav Andreev <s.andreev@samsung.com>: redesign of kprobe functionality -
- * kprobe_handler() now called via undefined instruction hooks
- * @author Stanislav Andreev <s.andreev@samsung.com>: hash tables search implemented for uprobes
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2014
- *
- * @section DESCRIPTION
- *
- * SWAP kprobe implementation for ARM architecture.
- */
-
-
-#include <linux/kconfig.h>
-
-#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
-# error "Kernel is immutable"
-#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
-
-
-#include <linux/module.h>
-#include <asm/cacheflush.h>
-#include <asm/traps.h>
-#include <linux/ptrace.h>
-#include <linux/list.h>
-#include <linux/hash.h>
-#include <ksyms/ksyms.h>
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_kdebug.h>
-#include <kprobe/swap_kprobes.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <arch/arm/probes/probes_arm.h>
-#include "swap_kprobes.h"
-
-
-static void (*__swap_register_undef_hook)(struct undef_hook *hook);
-static void (*__swap_unregister_undef_hook)(struct undef_hook *hook);
-
-
-/**
- * @brief Creates trampoline for kprobe.
- *
- * @param p Pointer to kp_core.
- * @param sm Pointer to slot manager
- * @return 0 on success, error code on error.
- */
-int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm)
-{
- u32 *tramp;
- int ret;
-
- tramp = swap_slot_alloc(sm);
- if (tramp == NULL)
- return -ENOMEM;
-
- p->opcode = *(unsigned long *)p->addr;
- ret = make_trampoline_arm(p->addr, p->opcode, tramp);
- if (ret) {
- swap_slot_free(sm, tramp);
- return ret;
- }
-
- flush_icache_range((unsigned long)tramp,
- (unsigned long)tramp + KPROBES_TRAMP_LEN);
-
- p->ainsn.insn = (unsigned long *)tramp;
-
- return 0;
-}
-
-/**
- * @brief Prepares singlestep for current CPU.
- *
- * @param p Pointer to kprobe.
- * @param regs Pointer to CPU registers data.
- * @return Void.
- */
-static void prepare_singlestep(struct kp_core *p, struct pt_regs *regs)
-{
- regs->ARM_pc = (unsigned long)p->ainsn.insn;
-}
-
-static void save_previous_kp_core(struct kp_core_ctlblk *kcb)
-{
- kcb->prev_kp_core.p = kp_core_running();
- kcb->prev_kp_core.status = kcb->kp_core_status;
-}
-
-/**
- * @brief Restores previous kp_core.
- *
- * @param kcb Pointer to kp_core_ctlblk which contains previous kp_core.
- * @return Void.
- */
-void restore_previous_kp_core(struct kp_core_ctlblk *kcb)
-{
- kp_core_running_set(kcb->prev_kp_core.p);
- kcb->kp_core_status = kcb->prev_kp_core.status;
-}
-
-static int kprobe_handler(struct pt_regs *regs)
-{
- struct kp_core *p, *cur;
- struct kp_core_ctlblk *kcb;
- struct kctx *ctx = current_kctx;
-
- if (regs->ARM_pc == sched_addr)
- switch_to_bits_set(ctx, SWITCH_TO_KP);
-
- kcb = kp_core_ctlblk();
- cur = kp_core_running();
-
- rcu_read_lock();
- p = kp_core_by_addr(regs->ARM_pc);
- if (p)
- kp_core_get(p);
- rcu_read_unlock();
-
- if (p) {
- if (cur) {
- /* Kprobe is pending, so we're recursing. */
- switch (kcb->kp_core_status) {
- case KPROBE_HIT_ACTIVE:
- case KPROBE_HIT_SSDONE:
- /* A pre- or post-handler probe got us here. */
- save_previous_kp_core(kcb);
- kp_core_running_set(p);
- kcb->kp_core_status = KPROBE_REENTER;
- prepare_singlestep(p, regs);
- restore_previous_kp_core(kcb);
- break;
- default:
- /* impossible cases */
- BUG();
- }
- } else {
- kp_core_running_set(p);
- kcb->kp_core_status = KPROBE_HIT_ACTIVE;
-
- if (!(regs->ARM_cpsr & PSR_I_BIT))
- local_irq_enable();
-
- if (!p->handlers.pre(p, regs)) {
- kcb->kp_core_status = KPROBE_HIT_SS;
- prepare_singlestep(p, regs);
- kp_core_running_set(NULL);
- }
- }
- kp_core_put(p);
- } else {
- goto no_kprobe;
- }
-
- switch_to_bits_reset(ctx, SWITCH_TO_KP);
-
- return 0;
-
-no_kprobe:
- printk(KERN_INFO "no_kprobe: Not one of ours: let kernel handle it %p\n",
- (unsigned long *)regs->ARM_pc);
- return 1;
-}
-
-/**
- * @brief Trap handler.
- *
- * @param regs Pointer to CPU register data.
- * @param instr Instruction.
- * @return kprobe_handler result.
- */
-int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
-{
- int ret;
-
- if (likely(instr == BREAK_ARM)) {
- ret = kprobe_handler(regs);
- } else {
- struct kp_core *p;
-
- rcu_read_lock();
- p = kp_core_by_addr(regs->ARM_pc);
-
- /* skip false exeption */
- ret = p && (p->opcode == instr) ? 0 : 1;
- rcu_read_unlock();
- }
-
- return ret;
-}
-
-/**
- * @brief Probe pre handler.
- *
- * @param p Pointer to fired kprobe.
- * @param regs Pointer to CPU registers data.
- * @return 0.
- */
-int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
-{
- struct jprobe *jp = container_of(p, struct jprobe, kp);
- entry_point_t entry = (entry_point_t)jp->entry;
-
- if (entry) {
- entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
- regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
- } else {
- swap_jprobe_return();
- }
-
- return 0;
-}
-
-/**
- * @brief Jprobe return stub.
- *
- * @return Void.
- */
-void swap_jprobe_return(void)
-{
-}
-EXPORT_SYMBOL_GPL(swap_jprobe_return);
-
-/**
- * @brief Break handler stub.
- *
- * @param p Pointer to fired kprobe.
- * @param regs Pointer to CPU registers data.
- * @return 0.
- */
-int swap_longjmp_break_handler (struct kprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(swap_longjmp_break_handler);
-
-#ifdef CONFIG_STRICT_MEMORY_RWX
-#include "memory_rwx.h"
-
-static void write_u32(unsigned long addr, unsigned long val)
-{
- mem_rwx_write_u32(addr, val);
-}
-#else /* CONFIG_STRICT_MEMORY_RWX */
-static void write_u32(unsigned long addr, unsigned long val)
-{
- *(long *)addr = val;
- flush_icache_range(addr, addr + sizeof(long));
-}
-#endif /* CONFIG_STRICT_MEMORY_RWX */
-
-/**
- * @brief Arms kprobe.
- *
- * @param p Pointer to target kprobe.
- * @return Void.
- */
-void arch_kp_core_arm(struct kp_core *core)
-{
- write_u32(core->addr, BREAK_ARM);
-}
-
-/**
- * @brief Disarms kprobe.
- *
- * @param p Pointer to target kprobe.
- * @return Void.
- */
-void arch_kp_core_disarm(struct kp_core *core)
-{
- write_u32(core->addr, core->opcode);
-}
-
-/**
- * @brief Kretprobe trampoline. Provides jumping to probe handler.
- *
- * @return Void.
- */
-void __naked swap_kretprobe_trampoline(void)
-{
- __asm__ __volatile__ (
- "stmdb sp!, {r0 - r11}\n"
- "mov r0, sp\n" /* struct pt_regs -> r0 */
- "bl swap_trampoline_handler\n"
- "mov lr, r0\n"
- "ldmia sp!, {r0 - r11}\n"
- "bx lr\n"
- : : : "memory");
-}
-
-/**
- * @brief Prepares kretprobes, saves ret address, makes function return to
- * trampoline.
- *
- * @param ri Pointer to kretprobe_instance.
- * @param regs Pointer to CPU registers data.
- * @return Void.
- */
-void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- unsigned long *ptr_ret_addr;
-
- /* for __switch_to probe */
- if ((unsigned long)ri->rp->kp.addr == sched_addr) {
- struct thread_info *tinfo = (struct thread_info *)regs->ARM_r2;
-
- ptr_ret_addr = (unsigned long *)&tinfo->cpu_context.pc;
- ri->sp = NULL;
- ri->task = tinfo->task;
- switch_to_bits_set(kctx_by_task(tinfo->task), SWITCH_TO_RP);
- } else {
- ptr_ret_addr = (unsigned long *)®s->ARM_lr;
- ri->sp = (unsigned long *)regs->ARM_sp;
- }
-
- /* Save the return address */
- ri->ret_addr = (unsigned long *)*ptr_ret_addr;
-
- /* Replace the return addr with trampoline addr */
- *ptr_ret_addr = (unsigned long)&swap_kretprobe_trampoline;
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * jumper *
- ******************************************************************************
- */
-struct cb_data {
- unsigned long ret_addr;
- unsigned long r0;
-
- jumper_cb_t cb;
- char data[0];
-};
-
-static unsigned long __used get_r0(struct cb_data *data)
-{
- return data->r0;
-}
-
-static unsigned long __used jump_handler(struct cb_data *data)
-{
- unsigned long ret_addr = data->ret_addr;
-
- /* call callback */
- data->cb(data->data);
-
- /* FIXME: potential memory leak, when process kill */
- kfree(data);
-
- return ret_addr;
-}
-
-/* FIXME: restore condition flags */
-
-/**
- * @brief Jumper trampoline.
- *
- * @return Void.
- */
-void jump_trampoline(void);
-__asm(
- "jump_trampoline:\n"
-
- "push {r0 - r12}\n"
- "mov r1, r0\n" /* data --> r1 */
- "bl get_r0\n"
- "str r0, [sp]\n" /* restore r0 */
- "mov r0, r1\n" /* data --> r0 */
- "bl jump_handler\n"
- "mov lr, r0\n"
- "pop {r0 - r12}\n"
- "bx lr\n"
-);
-
-/**
- * @brief Get jumper address.
- *
- * @return Jumper address.
- */
-unsigned long get_jump_addr(void)
-{
- return (unsigned long)&jump_trampoline;
-}
-EXPORT_SYMBOL_GPL(get_jump_addr);
-
-/**
- * @brief Set jumper probe callback.
- *
- * @param ret_addr Jumper probe return address.
- * @param regs Pointer to CPU registers data.
- * @param cb Jumper callback of jumper_cb_t type.
- * @param data Data that should be stored in cb_data.
- * @param size Size of the data.
- * @return 0.
- */
-int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
- jumper_cb_t cb, void *data, size_t size)
-{
- struct cb_data *cb_data;
-
- cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
- if (cb_data == NULL)
- return -ENOMEM;
-
- /* save data */
- if (size)
- memcpy(cb_data->data, data, size);
-
- /* save info for restore */
- cb_data->ret_addr = ret_addr;
- cb_data->cb = cb;
- cb_data->r0 = regs->ARM_r0;
-
- /* save cb_data to r0 */
- regs->ARM_r0 = (long)cb_data;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(set_jump_cb);
-
-
-
-
-/**
- * @brief Registers hook on specified instruction.
- *
- * @param hook Pointer to struct undef_hook.
- * @return Void.
- */
-void swap_register_undef_hook(struct undef_hook *hook)
-{
- __swap_register_undef_hook(hook);
-}
-EXPORT_SYMBOL_GPL(swap_register_undef_hook);
-
-/**
- * @brief Unregisters hook.
- *
- * @param hook Pointer to struct undef_hook.
- * @return Void.
- */
-void swap_unregister_undef_hook(struct undef_hook *hook)
-{
- __swap_unregister_undef_hook(hook);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_undef_hook);
-
-/* kernel probes hook */
-static struct undef_hook undef_ho_k = {
- .instr_mask = 0,
- .instr_val = 0,
- .cpsr_mask = MODE_MASK,
- .cpsr_val = SVC_MODE,
- .fn = kprobe_trap_handler
-};
-
-/**
- * @brief Arch-dependend module deps initialization stub.
- *
- * @return 0.
- */
-int arch_init_module_deps(void)
-{
- const char *sym;
-#ifdef CONFIG_STRICT_MEMORY_RWX
- int ret;
-
- ret = mem_rwx_once();
- if (ret)
- return ret;
-#endif /* CONFIG_STRICT_MEMORY_RWX */
-
- sym = "register_undef_hook";
- __swap_register_undef_hook = (void *)swap_ksyms(sym);
- if (__swap_register_undef_hook == NULL)
- goto not_found;
-
- sym = "unregister_undef_hook";
- __swap_unregister_undef_hook = (void *)swap_ksyms(sym);
- if (__swap_unregister_undef_hook == NULL)
- goto not_found;
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
-/**
- * @brief Initializes kprobes module for ARM arch.
- *
- * @return 0 on success, error code on error.
- */
-int swap_arch_init_kprobes(void)
-{
- swap_register_undef_hook(&undef_ho_k);
-
- return 0;
-}
-
-/**
- * @brief Uninitializes kprobe module.
- *
- * @return Void.
- */
-void swap_arch_exit_kprobes(void)
-{
- swap_unregister_undef_hook(&undef_ho_k);
-}
-
-
-/* export symbol for probes_arm.h */
-EXPORT_SYMBOL_GPL(noret_arm);
-EXPORT_SYMBOL_GPL(make_trampoline_arm);
-
-#include <arch/arm/probes/tramps_arm.h>
-/* export symbol for tramps_arm.h */
-EXPORT_SYMBOL_GPL(gen_insn_execbuf);
-EXPORT_SYMBOL_GPL(pc_dep_insn_execbuf);
-EXPORT_SYMBOL_GPL(b_r_insn_execbuf);
-EXPORT_SYMBOL_GPL(b_cond_insn_execbuf);
-EXPORT_SYMBOL_GPL(blx_off_insn_execbuf);
+++ /dev/null
-/**
- * @file kprobe/arch/asm-arm/swap_kprobes.h
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>:
- * initial implementation for ARM/MIPS
- * @author Alexey Gerenkov <a.gerenkov@samsung.com>:
- * User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>:
- * redesign module for separating core and arch parts
- * @author Alexander Shirshikov <a.shirshikov@samsung.com>:
- * initial implementation for Thumb
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * ARM arch-dependent kprobes interface declaration.
- */
-
-
-#ifndef _SWAP_ASM_ARM_KPROBES_H
-#define _SWAP_ASM_ARM_KPROBES_H
-
-#include <linux/sched.h>
-#include <linux/compiler.h>
-#include <arch/arm/probes/probes.h>
-
-typedef unsigned long kprobe_opcode_t;
-
-
-/** Kprobes trampoline length */
-#define KPROBES_TRAMP_LEN PROBES_TRAMP_LEN
-
-/** User register offset */
-#define UREGS_OFFSET 8
-
-/**
- * @struct prev_kp_core
- * @brief Stores previous kp_core.
- * @var prev_kp_core::p
- * Pointer to kp_core struct.
- * @var prev_kp_core::status
- * Kprobe status.
- */
-struct prev_kp_core {
- struct kp_core *p;
- unsigned long status;
-};
-
-/**
- * @brief Gets task pc.
- *
- * @param p Pointer to task_struct
- * @return Value in pc.
- */
-static inline unsigned long arch_get_task_pc(struct task_struct *p)
-{
- return task_thread_info(p)->cpu_context.pc;
-}
-
-/**
- * @brief Sets task pc.
- *
- * @param p Pointer to task_struct.
- * @param val Value that should be set.
- * @return Void.
- */
-static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
-{
- task_thread_info(p)->cpu_context.pc = val;
-}
-
-/**
- * @brief Gets syscall registers.
- *
- * @param sp Pointer to stack.
- * @return Pointer to CPU regs data.
- */
-static inline struct pt_regs *swap_get_syscall_uregs(unsigned long sp)
-{
- return (struct pt_regs *)(sp + UREGS_OFFSET);
-}
-
-/**
- * @brief Gets stack pointer.
- *
- * @param regs Pointer to CPU registers data.
- * @return Stack address.
- */
-static inline unsigned long swap_get_stack_ptr(struct pt_regs *regs)
-{
- return regs->ARM_sp;
-}
-
-/**
- * @brief Sets stack pointer.
- *
- * @param regs Pointer to CPU registers data.
- * @param sp New stack pointer value.
- * @return Void
- */
-static inline void swap_set_stack_ptr(struct pt_regs *regs, unsigned long sp)
-{
- regs->ARM_sp = sp;
-}
-
-/**
- * @brief Gets instruction pointer.
- *
- * @param regs Pointer to CPU registers data.
- * @return Pointer to pc.
- */
-static inline unsigned long swap_get_kpc(struct pt_regs *regs)
-{
- return regs->ARM_pc | !!thumb_mode(regs);
-}
-
-/**
- * @brief Sets instruction pointer.
- *
- * @param regs Pointer to CPU registers data.
- * @param val Address that should be stored in pc.
- * @return Void.
- */
-static inline void swap_set_kpc(struct pt_regs *regs, unsigned long val)
-{
- if (val & 1) {
- regs->ARM_pc = val & ~1;
- regs->ARM_cpsr |= PSR_T_BIT;
- } else {
- regs->ARM_pc = val;
- regs->ARM_cpsr &= ~PSR_T_BIT;
- }
-}
-
-/**
- * @brief Gets specified argument.
- *
- * @param regs Pointer to CPU registers data.
- * @param num Number of the argument.
- * @return Argument value.
- */
-static inline unsigned long swap_get_arg(struct pt_regs *regs, int num)
-{
- return regs->uregs[num];
-}
-
-/**
- * @brief Sets specified argument.
- *
- * @param regs Pointer to CPU registers data.
- * @param num Number of the argument.
- * @param val New argument value.
- * @return Void.
- */
-static inline void swap_set_arg(struct pt_regs *regs, int num,
- unsigned long val)
-{
- regs->uregs[num] = val;
-}
-
-/**
- * @struct kp_core_ctlblk
- * @brief Per-cpu kp_core control block.
- * @var kp_core_ctlblk::kp_core_status
- * Kprobe status.
- * @var kp_core_ctlblk::prev_kp_core
- * Previous kp_core.
- */
-struct kp_core_ctlblk {
- unsigned long kp_core_status;
- struct prev_kp_core prev_kp_core;
-};
-
-/**
- * @struct swap_arch_specific_insn
- * @brief Architecture specific copy of original instruction.
- * @var swap_arch_specific_insn::insn
- * Copy of the original instruction.
- */
-struct swap_arch_specific_insn {
- kprobe_opcode_t *insn;
-};
-
-typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long,
- unsigned long, unsigned long,
- unsigned long, unsigned long);
-
-struct undef_hook;
-
-void swap_register_undef_hook(struct undef_hook *hook);
-void swap_unregister_undef_hook(struct undef_hook *hook);
-
-int arch_init_module_deps(void);
-
-struct slot_manager;
-struct kretprobe;
-struct kretprobe_instance;
-struct kp_core;
-struct kprobe;
-
-int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm);
-void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-
-void arch_kp_core_arm(struct kp_core *p);
-void arch_kp_core_disarm(struct kp_core *p);
-
-int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs);
-int swap_longjmp_break_handler(struct kprobe *p, struct pt_regs *regs);
-
-void restore_previous_kp_core(struct kp_core_ctlblk *kcb);
-
-void __naked swap_kretprobe_trampoline(void);
-
-/**
- * @brief Gets arguments of kernel functions.
- *
- * @param regs Pointer to CPU registers data.
- * @param n Number of the argument.
- * @return Argument value.
- */
-static inline unsigned long swap_get_karg(struct pt_regs *regs, unsigned long n)
-{
- switch (n) {
- case 0:
- return regs->ARM_r0;
- case 1:
- return regs->ARM_r1;
- case 2:
- return regs->ARM_r2;
- case 3:
- return regs->ARM_r3;
- }
-
- return *((unsigned long *)regs->ARM_sp + n - 4);
-}
-
-/**
- * @brief swap_get_karg wrapper.
- *
- * @param regs Pointer to CPU registers data.
- * @param n Number of the argument.
- * @return Argument value.
- */
-static inline unsigned long swap_get_sarg(struct pt_regs *regs, unsigned long n)
-{
- return swap_get_karg(regs, n);
-}
-
-/* jumper */
-typedef unsigned long (*jumper_cb_t)(void *);
-
-unsigned long get_jump_addr(void);
-int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
- jumper_cb_t cb, void *data, size_t size);
-
-int swap_arch_init_kprobes(void);
-void swap_arch_exit_kprobes(void);
-
-/* void gen_insn_execbuf (void); */
-/* void pc_dep_insn_execbuf (void); */
-/* void gen_insn_execbuf_holder (void); */
-/* void pc_dep_insn_execbuf_holder (void); */
-
-#endif /* _SWAP_ASM_ARM_KPROBES_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_ARM_PROBES_H
-#define _SWAP_ASM_ARM_PROBES_H
-
-
-#define BREAK_ARM 0xffffdeff
-#define BREAK_THUMB (BREAK_ARM & 0xffff)
-#define RET_BREAK_ARM BREAK_ARM
-#define RET_BREAK_THUMB BREAK_THUMB
-
-#define PROBES_TRAMP_LEN (9 * 4)
-#define PROBES_TRAMP_INSN_IDX 2
-#define PROBES_TRAMP_RET_BREAK_IDX 5
-
-
-#endif /* _SWAP_ASM_ARM_PROBES_H */
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/condn-helpers.c
- *
- * Copyright (C) 2013 Linaro Limited
- *
- * Copied from: arch/arm/kernel/kprobes-common.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * Description:
- *
- * AArch64 and AArch32 shares same conditional(CNZV) flags encoding.
- * This file implements conditional check helpers compatible with
- * both AArch64 and AArch32 modes. Uprobes on v8 can handle both 32-bit
- * & 64-bit user-space instructions, so we abstract the common functions
- * in this file. While AArch64 and AArch32 specific instruction handling
- * are implemented in separate files, this file contains common bits.
- */
-
-
-#include <linux/module.h>
-#include "condn-helpers.h"
-
-
-static unsigned long __check_eq(unsigned long pstate)
-{
- return pstate & PSR_Z_BIT;
-}
-
-static unsigned long __check_ne(unsigned long pstate)
-{
- return (~pstate) & PSR_Z_BIT;
-}
-
-static unsigned long __check_cs(unsigned long pstate)
-{
- return pstate & PSR_C_BIT;
-}
-
-static unsigned long __check_cc(unsigned long pstate)
-{
- return (~pstate) & PSR_C_BIT;
-}
-
-static unsigned long __check_mi(unsigned long pstate)
-{
- return pstate & PSR_N_BIT;
-}
-
-static unsigned long __check_pl(unsigned long pstate)
-{
- return (~pstate) & PSR_N_BIT;
-}
-
-static unsigned long __check_vs(unsigned long pstate)
-{
- return pstate & PSR_V_BIT;
-}
-
-static unsigned long __check_vc(unsigned long pstate)
-{
- return (~pstate) & PSR_V_BIT;
-}
-
-static unsigned long __check_hi(unsigned long pstate)
-{
- pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
- return pstate & PSR_C_BIT;
-}
-
-static unsigned long __check_ls(unsigned long pstate)
-{
- pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
- return (~pstate) & PSR_C_BIT;
-}
-
-static unsigned long __check_ge(unsigned long pstate)
-{
- pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
- return (~pstate) & PSR_N_BIT;
-}
-
-static unsigned long __check_lt(unsigned long pstate)
-{
- pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
- return pstate & PSR_N_BIT;
-}
-
-static unsigned long __check_gt(unsigned long pstate)
-{
- /*PSR_N_BIT ^= PSR_V_BIT */
- unsigned long temp = pstate ^ (pstate << 3);
-
- temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
- return (~temp) & PSR_N_BIT;
-}
-
-static unsigned long __check_le(unsigned long pstate)
-{
- /*PSR_N_BIT ^= PSR_V_BIT */
- unsigned long temp = pstate ^ (pstate << 3);
-
- temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
- return temp & PSR_N_BIT;
-}
-
-static unsigned long __check_al(unsigned long pstate)
-{
- return true;
-}
-
-probes_pstate_check_t * const probe_condition_checks[16] = {
- &__check_eq, &__check_ne, &__check_cs, &__check_cc,
- &__check_mi, &__check_pl, &__check_vs, &__check_vc,
- &__check_hi, &__check_ls, &__check_ge, &__check_lt,
- &__check_gt, &__check_le, &__check_al, &__check_al
-};
-EXPORT_SYMBOL_GPL(probe_condition_checks);
+++ /dev/null
-#ifndef _ASM_CONDN_HELPER_H
-#define _ASM_CONDN_HELPER_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-typedef unsigned long (probes_pstate_check_t)(unsigned long);
-
-
-extern probes_pstate_check_t * const probe_condition_checks[16];
-
-
-#endif /* _ASM_CONDN_HELPER_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/kconfig.h>
-
-#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
-# error "Kernel is immutable"
-#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
-
-
-#include <linux/module.h>
-#include <linux/rwlock.h>
-#include <asm/debug-monitors.h>
-#include <ksyms/ksyms.h>
-#include "dbg_interface.h"
-
-
-
-
-/* ============================================================================
- * = BRK IMPLEMENT =
- * ============================================================================
- */
-static LIST_HEAD(brk_list);
-static DEFINE_RWLOCK(brk_list_lock);
-
-void dbg_brk_hook_reg(struct brk_hook *hook)
-{
- write_lock(&brk_list_lock);
- list_add(&hook->list, &brk_list);
- write_unlock(&brk_list_lock);
-}
-EXPORT_SYMBOL_GPL(dbg_brk_hook_reg);
-
-void dbg_brk_hook_unreg(struct brk_hook *hook)
-{
- write_lock(&brk_list_lock);
- list_del(&hook->list);
- write_unlock(&brk_list_lock);
-}
-EXPORT_SYMBOL_GPL(dbg_brk_hook_unreg);
-
-static enum dbg_code call_brk_hook(struct pt_regs *regs, unsigned int esr)
-{
- struct brk_hook *hook;
- enum dbg_code (*fn)(struct pt_regs *regs, unsigned int esr) = NULL;
-
- read_lock(&brk_list_lock);
- list_for_each_entry(hook, &brk_list, list)
- if (((esr & hook->esr_mask) == hook->esr_val) &&
- ((regs->pstate & hook->spsr_mask) == hook->spsr_val))
- fn = hook->fn;
- read_unlock(&brk_list_lock);
-
- return fn ? fn(regs, esr) : DBG_ERROR;
-}
-
-
-typedef int (*dbg_fn_t)(unsigned long addr, unsigned int esr,
- struct pt_regs *regs);
-
-static dbg_fn_t *brk_handler_ptr;
-static dbg_fn_t orig_brk_handler;
-
-static int brk_handler(unsigned long addr, unsigned int esr,
- struct pt_regs *regs)
-{
- /* call the registered breakpoint handler */
- if (call_brk_hook(regs, esr) == DBG_HANDLED)
- return 0;
-
- return orig_brk_handler(addr, esr, regs);
-}
-
-static void init_brk(dbg_fn_t *fn)
-{
- brk_handler_ptr = fn;
- orig_brk_handler = *brk_handler_ptr;
- *brk_handler_ptr = brk_handler;
-}
-
-static void uninit_brk(void)
-{
- *brk_handler_ptr = orig_brk_handler;
-}
-
-
-
-
-
-
-/* ============================================================================
- * = INIT / EXIT =
- * ============================================================================
- */
-int dbg_iface_init(void)
-{
- struct fault_info {
- int (*fn)(unsigned long addr, unsigned int esr,
- struct pt_regs *regs);
- int sig;
- int code;
- const char *name;
- };
-
- struct fault_info *debug_finfo;
- struct fault_info *finfo_brk;
-
- debug_finfo = (struct fault_info *)swap_ksyms("debug_fault_info");
- if (debug_finfo == NULL) {
- pr_err("cannot found 'debug_fault_info'\n");
- return -EINVAL;
- }
-
- finfo_brk = &debug_finfo[DBG_ESR_EVT_BRK];
-
- init_brk(&finfo_brk->fn);
-
- return 0;
-}
-
-void dbg_iface_uninit(void)
-{
- uninit_brk();
-}
+++ /dev/null
-#ifndef _ASM_DBG_INTERFACE_H
-#define _ASM_DBG_INTERFACE_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/list.h>
-#include <linux/types.h>
-
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
-# include <asm/esr.h>
-#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) */
-# define ESR_ELx_IL (1 << 25)
-# define ESR_ELx_EC_MASK 0xfc000000
-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) */
-
-#define ESR_ELx_EC_BRK 0xf0000000
-#define BRK_COMM_MASK 0x0000ffff
-
-#define DBG_BRK_ESR_MASK (ESR_ELx_EC_MASK | ESR_ELx_IL | BRK_COMM_MASK)
-#define DBG_BRK_ESR(x) (ESR_ELx_EC_BRK | ESR_ELx_IL | (BRK_COMM_MASK & (x)))
-
-#define MAKE_BRK(v) ((((v) & 0xffff) << 5) | 0xd4200000)
-
-
-enum dbg_code {
- DBG_HANDLED,
- DBG_ERROR,
-};
-
-
-struct brk_hook {
- struct list_head list;
- u32 spsr_mask;
- u32 spsr_val;
- u32 esr_mask;
- u32 esr_val;
- enum dbg_code (*fn)(struct pt_regs *regs, unsigned int esr);
-};
-
-
-void dbg_brk_hook_reg(struct brk_hook *hook);
-void dbg_brk_hook_unreg(struct brk_hook *hook);
-
-int dbg_iface_init(void);
-void dbg_iface_uninit(void);
-
-
-#endif /* _ASM_DBG_INTERFACE_H */
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/kprobes-arm64.c
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/ptrace.h>
-#include <kprobe/swap_kprobes.h>
-
-#include "probes-decode.h"
-#include "kprobes-arm64.h"
-#include "simulate-insn.h"
-#include "condn-helpers.h"
-
-
-/*
- * condition check functions for kp_cores simulation
- */
-static unsigned long __check_pstate(struct kp_core *p, struct pt_regs *regs)
-{
- struct swap_arch_specific_insn *asi = &p->ainsn;
- unsigned long pstate = regs->pstate & 0xffffffff;
-
- return asi->pstate_cc(pstate);
-}
-
-static unsigned long __check_cbz(struct kp_core *p, struct pt_regs *regs)
-{
- return check_cbz((u32)p->opcode, regs);
-}
-
-static unsigned long __check_cbnz(struct kp_core *p, struct pt_regs *regs)
-{
- return check_cbnz((u32)p->opcode, regs);
-}
-
-static unsigned long __check_tbz(struct kp_core *p, struct pt_regs *regs)
-{
- return check_tbz((u32)p->opcode, regs);
-}
-
-static unsigned long __check_tbnz(struct kp_core *p, struct pt_regs *regs)
-{
- return check_tbnz((u32)p->opcode, regs);
-}
-
-/*
- * prepare functions for instruction simulation
- */
-static void prepare_none(struct kp_core *p, struct swap_arch_specific_insn *asi)
-{
-}
-
-static void prepare_bcond(struct kp_core *p,
- struct swap_arch_specific_insn *asi)
-{
- kprobe_opcode_t insn = p->opcode;
-
- asi->check_condn = __check_pstate;
- asi->pstate_cc = probe_condition_checks[insn & 0xf];
-}
-
-static void prepare_cbz_cbnz(struct kp_core *p,
- struct swap_arch_specific_insn *asi)
-{
- kprobe_opcode_t insn = p->opcode;
-
- asi->check_condn = (insn & (1 << 24)) ? __check_cbnz : __check_cbz;
-}
-
-static void prepare_tbz_tbnz(struct kp_core *p,
- struct swap_arch_specific_insn *asi)
-{
- kprobe_opcode_t insn = p->opcode;
-
- asi->check_condn = (insn & (1 << 24)) ? __check_tbnz : __check_tbz;
-}
-
-
-/* Load literal (PC-relative) instructions
- * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
- *
- * opcode[26]: V=0, Load GP registers, simulate them.
- * Encoding: xx01 1000 xxxx xxxx xxxx xxxx xxxx xxxx
- * opcode[31:30]: op = 00, 01 - LDR literal
- * opcode[31:30]: op = 10, - LDRSW literal
- *
- * 1. V=1 -Load FP/AdvSIMD registers
- * Encoding: xx01 1100 xxxx xxxx xxxx xxxx xxxx xxxx
- * 2. V=0,opc=11 -PRFM(Prefetch literal)
- * Encoding: 1101 1000 xxxx xxxx xxxx xxxx xxxx xxxx
- * Reject FP/AdvSIMD literal load & PRFM literal.
- */
-static const struct aarch64_decode_item load_literal_subtable[] = {
- DECODE_REJECT(0x1C000000, 0x3F000000),
- DECODE_REJECT(0xD8000000, 0xFF000000),
- DECODE_LITERAL(0x18000000, 0xBF000000, prepare_none,
- simulate_ldr_literal),
- DECODE_LITERAL(0x98000000, 0xFF000000, prepare_none,
- simulate_ldrsw_literal),
- DECODE_END,
-};
-
-/* AArch64 instruction decode table for kp_cores:
- * The instruction will fall into one of the 3 groups:
- * 1. Single stepped out-of-the-line slot.
- * -Most instructions fall in this group, those does not
- * depend on PC address.
- *
- * 2. Should be simulated because of PC-relative/literal access.
- * -All branching and PC-relative insrtcutions are simulated
- * in C code, making use of saved pt_regs
- * Catch: SIMD/NEON register context are not saved while
- * entering debug exception, so are rejected for now.
- *
- * 3. Cannot be probed(not safe) so are rejected.
- * - Exception generation and exception return instructions
- * - Exclusive monitor(LDREX/STREX family)
- *
- */
-static const struct aarch64_decode_item aarch64_decode_table[] = {
- /*
- * Data processing - PC relative(literal) addressing:
- * Encoding: xxx1 0000 xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_LITERAL(0x10000000, 0x1F000000, prepare_none,
- simulate_adr_adrp),
-
- /*
- * Data processing - Add/Substract Immediate:
- * Encoding: xxx1 0001 xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x11000000, 0x1F000000),
-
- /*
- * Data processing
- * Encoding:
- * xxx1 0010 0xxx xxxx xxxx xxxx xxxx xxxx (Logical)
- * xxx1 0010 1xxx xxxx xxxx xxxx xxxx xxxx (Move wide)
- * xxx1 0011 0xxx xxxx xxxx xxxx xxxx xxxx (Bitfield)
- * xxx1 0011 1xxx xxxx xxxx xxxx xxxx xxxx (Extract)
- */
- DECODE_SINGLESTEP(0x12000000, 0x1E000000),
-
- /*
- * Data processing - SIMD/FP/AdvSIMD/Crypto-AES/SHA
- * Encoding: xxx0 111x xxxx xxxx xxxx xxxx xxxx xxxx
- * Encoding: xxx1 111x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x0E000000, 0x0E000000),
-
- /*
- * Data processing - Register
- * Encoding: xxxx 101x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x0A000000, 0x0E000000),
-
- /* Branching Instructions
- *
- * Encoding:
- * x001 01xx xxxx xxxx xxxx xxxx xxxx xxxx (uncondtional Branch)
- * x011 010x xxxx xxxx xxxx xxxx xxxx xxxx (compare & branch)
- * x011 011x xxxx xxxx xxxx xxxx xxxx xxxx (Test & Branch)
- * 0101 010x xxxx xxxx xxxx xxxx xxxx xxxx (Conditional, immediate)
- * 1101 011x xxxx xxxx xxxx xxxx xxxx xxxx (Unconditional,register)
- */
- DECODE_BRANCH(0x14000000, 0x7C000000, prepare_none,
- simulate_b_bl),
- DECODE_BRANCH(0x34000000, 0x7E000000, prepare_cbz_cbnz,
- simulate_cbz_cbnz),
- DECODE_BRANCH(0x36000000, 0x7E000000, prepare_tbz_tbnz,
- simulate_tbz_tbnz),
- DECODE_BRANCH(0x54000000, 0xFE000000, prepare_bcond,
- simulate_b_cond),
- DECODE_BRANCH(0xD6000000, 0xFE000000, prepare_none,
- simulate_br_blr_ret),
-
- /* System insn:
- * Encoding: 1101 0101 00xx xxxx xxxx xxxx xxxx xxxx
- *
- * Note: MSR immediate (update PSTATE daif) is not safe handling
- * within kp_cores, rejected.
- *
- * Don't re-arrange these decode table entries.
- */
- DECODE_REJECT(0xD500401F, 0xFFF8F01F),
- DECODE_SINGLESTEP(0xD5000000, 0xFFC00000),
-
- /* Exception Generation:
- * Encoding: 1101 0100 xxxx xxxx xxxx xxxx xxxx xxxx
- * Instructions: SVC, HVC, SMC, BRK, HLT, DCPS1, DCPS2, DCPS3
- */
- DECODE_REJECT(0xD4000000, 0xFF000000),
-
- /*
- * Load/Store - Exclusive monitor
- * Encoding: xx00 1000 xxxx xxxx xxxx xxxx xxxx xxxx
- *
- * Reject exlusive monitor'ed instructions
- */
- DECODE_REJECT(0x08000000, 0x3F000000),
-
- /*
- * Load/Store - PC relative(literal):
- * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE(0x18000000, 0x3B000000, load_literal_subtable),
-
- /*
- * Load/Store - Register Pair
- * Encoding:
- * xx10 1x00 0xxx xxxx xxxx xxxx xxxx xxxx
- * xx10 1x00 1xxx xxxx xxxx xxxx xxxx xxxx
- * xx10 1x01 0xxx xxxx xxxx xxxx xxxx xxxx
- * xx10 1x01 1xxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x28000000, 0x3A000000),
-
- /*
- * Load/Store - Register
- * Encoding:
- * xx11 1x00 xx0x xxxx xxxx 00xx xxxx xxxx (unscaled imm)
- * xx11 1x00 xx0x xxxx xxxx 01xx xxxx xxxx (imm post-indexed)
- * xx11 1x00 xx0x xxxx xxxx 10xx xxxx xxxx (unpriviledged)
- * xx11 1x00 xx0x xxxx xxxx 11xx xxxx xxxx (imm pre-indexed)
- *
- * xx11 1x00 xx10 xxxx xxxx xx10 xxxx xxxx (register offset)
- *
- * xx11 1x01 xxxx xxxx xxxx xxxx xxxx xxxx (unsigned imm)
- */
- DECODE_SINGLESTEP(0x38000000, 0x3B200000),
- DECODE_SINGLESTEP(0x38200200, 0x38300300),
- DECODE_SINGLESTEP(0x39000000, 0x3B000000),
-
- /*
- * Load/Store - AdvSIMD
- * Encoding:
- * 0x00 1100 0x00 0000 xxxx xxxx xxxx xxxx (Multiple-structure)
- * 0x00 1100 1x0x xxxx xxxx xxxx xxxx xxxx (Multi-struct post-indexed)
- * 0x00 1101 0xx0 0000 xxxx xxxx xxxx xxxx (Single-structure))
- * 0x00 1101 1xxx xxxx xxxx xxxx xxxx xxxx (Single-struct post-index)
- */
- DECODE_SINGLESTEP(0x0C000000, 0xBFBF0000),
- DECODE_SINGLESTEP(0x0C800000, 0xBFA00000),
- DECODE_SINGLESTEP(0x0D000000, 0xBF9F0000),
- DECODE_SINGLESTEP(0x0D800000, 0xBF800000),
-
- /* Unallocated: xxx0 0xxx xxxx xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT(0x00000000, 0x18000000),
- DECODE_END,
-};
-
-static int kp_core_decode_insn(kprobe_opcode_t insn,
- struct swap_arch_specific_insn *asi,
- const struct aarch64_decode_item *tbl)
-{
- unsigned int entry, ret = INSN_REJECTED;
-
- for (entry = 0; !decode_table_end(tbl[entry]); entry++) {
- if (decode_table_hit(tbl[entry], insn))
- break;
- }
-
- switch (decode_get_type(tbl[entry])) {
- case DECODE_TYPE_END:
- case DECODE_TYPE_REJECT:
- default:
- ret = INSN_REJECTED;
- break;
-
- case DECODE_TYPE_SINGLESTEP:
- ret = INSN_GOOD;
- break;
-
- case DECODE_TYPE_SIMULATE:
- asi->prepare = decode_prepare_fn(tbl[entry]);
- asi->handler = decode_handler_fn(tbl[entry]);
- ret = INSN_GOOD_NO_SLOT;
- break;
-
- case DECODE_TYPE_TABLE:
- /* recurse with next level decode table */
- ret = kp_core_decode_insn(insn, asi,
- decode_sub_table(tbl[entry]));
- };
-
- return ret;
-}
-
-/* Return:
- * INSN_REJECTED If instruction is one not allowed to kprobe,
- * INSN_GOOD If instruction is supported and uses instruction slot,
- * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
- */
-enum kp_core_insn arm_kp_core_decode_insn(kprobe_opcode_t insn,
- struct swap_arch_specific_insn *asi)
-{
- return kp_core_decode_insn(insn, asi, aarch64_decode_table);
-}
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/kprobes-arm64.h
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#ifndef _ARM_KERNEL_KPROBES_ARM64_H
-#define _ARM_KERNEL_KPROBES_ARM64_H
-
-enum kp_core_insn {
- INSN_REJECTED,
- INSN_GOOD_NO_SLOT,
- INSN_GOOD,
-};
-
-extern kp_core_pstate_check_t * const kp_core_condition_checks[16];
-
-enum kp_core_insn arm_kp_core_decode_insn(kprobe_opcode_t insn,
- struct swap_arch_specific_insn *asi);
-
-#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/probes-decode.h
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#ifndef _ARM_KERNEL_PROBES_DECODE_H
-#define _ARM_KERNEL_PROBES_DECODE_H
-
-/*
- * The following definitions and macros are used to build instruction
- * decoding tables.
- */
-enum decode_type {
- DECODE_TYPE_END,
- DECODE_TYPE_SINGLESTEP,
- DECODE_TYPE_SIMULATE,
- DECODE_TYPE_TABLE,
- DECODE_TYPE_REJECT,
-};
-
-struct aarch64_decode_item;
-
-struct aarch64_decode_header {
- enum decode_type type;
- u32 mask;
- u32 val;
-};
-
-struct aarch64_decode_actions {
- kp_core_prepare_t *prepare;
- kp_core_handler_t *handler;
-};
-
-struct aarch64_decode_table {
- const struct aarch64_decode_item *tbl;
-};
-
-union aarch64_decode_handler {
- struct aarch64_decode_actions actions;
- struct aarch64_decode_table table;
-};
-
-struct aarch64_decode_item {
- struct aarch64_decode_header header;
- union aarch64_decode_handler decode;
-};
-
-#define decode_get_type(_entry) ((_entry).header.type)
-
-#define decode_table_end(_entry) \
- ((_entry).header.type == DECODE_TYPE_END)
-
-#define decode_table_hit(_entry, insn) \
- ((insn & (_entry).header.mask) == (_entry).header.val)
-
-#define decode_prepare_fn(_entry) ((_entry).decode.actions.prepare)
-#define decode_handler_fn(_entry) ((_entry).decode.actions.handler)
-#define decode_sub_table(_entry) ((_entry).decode.table.tbl)
-
-#define DECODE_ADD_HEADER(_type, _val, _mask) \
- .header = { \
- .type = _type, \
- .mask = _mask, \
- .val = _val, \
- }
-
-#define DECODE_ADD_ACTION(_prepare, _handler) \
- .decode = { \
- .actions = { \
- .prepare = _prepare, \
- .handler = _handler, \
- } \
- }
-
-#define DECODE_ADD_TABLE(_table) \
- .decode = { \
- .table = {.tbl = _table} \
- }
-
-#define DECODE_REJECT(_v, _m) \
- { DECODE_ADD_HEADER(DECODE_TYPE_REJECT, _v, _m) }
-
-#define DECODE_SINGLESTEP(_v, _m) \
- { DECODE_ADD_HEADER(DECODE_TYPE_SINGLESTEP, _v, _m) }
-
-#define DECODE_SIMULATE(_v, _m, _p, _h) \
- { DECODE_ADD_HEADER(DECODE_TYPE_SIMULATE, _v, _m), \
- DECODE_ADD_ACTION(_p, _h) }
-
-#define DECODE_TABLE(_v, _m, _table) \
- { DECODE_ADD_HEADER(DECODE_TYPE_TABLE, _v, _m), \
- DECODE_ADD_TABLE(_table) }
-
-#define DECODE_LITERAL(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
-#define DECODE_BRANCH(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
-
-/* should be the last element in decode structure */
-#define DECODE_END { .header = {.type = DECODE_TYPE_END, } }
-
-#endif /* _ARM_KERNEL_PROBES_DECODE_H */
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/simulate-insn.c
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/ptrace.h>
-
-#include "simulate-insn.h"
-
-#define sign_extend(x, signbit) \
- ((x) | (0 - ((x) & ((long)1 << (signbit)))))
-
-#define bbl_displacement(insn) \
- sign_extend(((insn) & 0x3ffffff) << 2, 27)
-
-#define bcond_displacement(insn) \
- sign_extend(((insn >> 5) & 0xfffff) << 2, 21)
-
-#define cbz_displacement(insn) \
- sign_extend(((insn >> 5) & 0xfffff) << 2, 21)
-
-#define tbz_displacement(insn) \
- sign_extend(((insn >> 5) & 0x3fff) << 2, 15)
-
-#define ldr_displacement(insn) \
- sign_extend(((insn >> 5) & 0xfffff) << 2, 21)
-
-
-unsigned long check_cbz(u32 opcode, struct pt_regs *regs)
-{
- int xn = opcode & 0x1f;
-
- return (opcode & (1 << 31)) ?
- !(regs->regs[xn]) : !(regs->regs[xn] & 0xffffffff);
-}
-EXPORT_SYMBOL_GPL(check_cbz);
-
-unsigned long check_cbnz(u32 opcode, struct pt_regs *regs)
-{
- int xn = opcode & 0x1f;
-
- return (opcode & (1 << 31)) ?
- (regs->regs[xn]) : (regs->regs[xn] & 0xffffffff);
-}
-EXPORT_SYMBOL_GPL(check_cbnz);
-
-unsigned long check_tbz(u32 opcode, struct pt_regs *regs)
-{
- int xn = opcode & 0x1f;
- int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
-
- return ~((regs->regs[xn] >> bit_pos) & 0x1);
-}
-EXPORT_SYMBOL_GPL(check_tbz);
-
-unsigned long check_tbnz(u32 opcode, struct pt_regs *regs)
-{
- int xn = opcode & 0x1f;
- int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
-
- return (regs->regs[xn] >> bit_pos) & 0x1;
-}
-EXPORT_SYMBOL_GPL(check_tbnz);
-
-/*
- * instruction simulate functions
- */
-void simulate_none(u32 opcode, long addr, struct pt_regs *regs)
-{
-}
-
-void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
-{
- long res, imm, xn, shift;
-
- xn = opcode & 0x1f;
- shift = (opcode >> 31) ? 12 : 0; /* check insn ADRP/ADR */
- imm = ((opcode >> 3) & 0xffffc) | ((opcode >> 29) & 0x3);
- res = addr + 8 + (sign_extend(imm, 20) << shift);
-
- regs->regs[xn] = opcode & 0x80000000 ? res & 0xfffffffffffff000 : res;
- regs->pc += 4;
-}
-EXPORT_SYMBOL_GPL(simulate_adr_adrp);
-
-void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
-{
- int disp = bbl_displacement(opcode);
-
- /* Link register is x30 */
- if (opcode & (1 << 31))
- regs->regs[30] = addr + 4;
-
- regs->pc = addr + disp;
-}
-EXPORT_SYMBOL_GPL(simulate_b_bl);
-
-void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
-{
- int disp = bcond_displacement(opcode);
-
- regs->pc = addr + disp;
-}
-EXPORT_SYMBOL_GPL(simulate_b_cond);
-
-void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs)
-{
- int xn = (opcode >> 5) & 0x1f;
-
- /* Link register is x30 */
- if (((opcode >> 21) & 0x3) == 1)
- regs->regs[30] = addr + 4;
-
- regs->pc = regs->regs[xn];
-}
-EXPORT_SYMBOL_GPL(simulate_br_blr_ret);
-
-void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
-{
- int disp = cbz_displacement(opcode);
-
- regs->pc = addr + disp;
-}
-EXPORT_SYMBOL_GPL(simulate_cbz_cbnz);
-
-void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
-{
- int disp = tbz_displacement(opcode);
-
- regs->pc = addr + disp;
-}
-EXPORT_SYMBOL_GPL(simulate_tbz_tbnz);
-
-void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
-{
- u64 *load_addr;
- int xn = opcode & 0x1f;
- int disp = ldr_displacement(opcode);
-
- load_addr = (u64 *) (addr + disp);
-
- if (opcode & (1 << 30)) /* x0-x31 */
- regs->regs[xn] = *load_addr;
- else /* w0-w31 */
- *(u32 *) (®s->regs[xn]) = (*(u32 *) (load_addr));
-}
-EXPORT_SYMBOL_GPL(simulate_ldr_literal);
-
-void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
-{
- u64 *load_addr;
- long data;
- int xn = opcode & 0x1f;
- int disp = ldr_displacement(opcode);
-
- load_addr = (u64 *) (addr + disp);
- data = *load_addr;
-
- regs->regs[xn] = sign_extend(data, 63);
-}
-EXPORT_SYMBOL_GPL(simulate_ldrsw_literal);
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/simulate-insn.h
- *
- * Copyright (C) 2013 Linaro Limited
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#ifndef _ARM_KERNEL_SIMULATE_INSN_H
-#define _ARM_KERNEL_SIMULATE_INSN_H
-
-unsigned long check_cbz(u32 opcode, struct pt_regs *regs);
-unsigned long check_cbnz(u32 opcode, struct pt_regs *regs);
-unsigned long check_tbz(u32 opcode, struct pt_regs *regs);
-unsigned long check_tbnz(u32 opcode, struct pt_regs *regs);
-void simulate_none(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs);
-void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs);
-
-#endif /* _ARM_KERNEL_SIMULATE_INSN_H */
+++ /dev/null
-/*
- * Copied from: arch/arm64/kernel/kprobes.c
- *
- * Kprobes support for ARM64
- *
- * Copyright (C) 2013 Linaro Limited.
- * Author: Sandeepa Prabhu <sandeepa.prabhu@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/kconfig.h>
-
-#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
-# error "Kernel is immutable"
-#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
-
-
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <asm/cacheflush.h>
-#include <asm/debug-monitors.h>
-#include <ksyms/ksyms.h>
-#include <kprobe/swap_ktd.h>
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_kprobes.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include "swap_kprobes.h"
-#include "kprobes-arm64.h"
-#include "dbg_interface.h"
-
-
-#define BRK_BP 0x63
-#define BRK_PSEUDO_SS 0x64
-#define BRK64_OPCODE_BP MAKE_BRK(BRK_BP)
-#define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS)
-
-
-#ifdef CONFIG_CPU_BIG_ENDIAN
-static u32 conv_inst(u32 inst)
-{
- return swab32(inst);
-}
-#else /* CONFIG_CPU_BIG_ENDIAN */
-static u32 conv_inst(u32 inst)
-{
- return inst;
-}
-#endif /* CONFIG_CPU_BIG_ENDIAN */
-
-
-static void flush_icache(unsigned long addr, size_t size)
-{
- flush_icache_range(addr, addr + size);
-}
-
-static void write_u32(u32 *addr, u32 val)
-{
- *addr = val;
- flush_icache((unsigned long)addr, sizeof(val));
-}
-
-void arch_kp_core_arm(struct kp_core *p)
-{
- write_u32((u32 *)p->addr, conv_inst(BRK64_OPCODE_BP));
-}
-
-void arch_kp_core_disarm(struct kp_core *p)
-{
- write_u32((u32 *)p->addr, p->opcode);
-}
-
-
-struct restore_data {
- unsigned long restore_addr;
-};
-
-static void ktd_restore_init(struct task_struct *task, void *data)
-{
- struct restore_data *rdata = (struct restore_data *)data;
-
- rdata->restore_addr = 0;
-}
-
-static void ktd_restore_exit(struct task_struct *task, void *data)
-{
- struct restore_data *rdata = (struct restore_data *)data;
-
- WARN(rdata->restore_addr, "restore_addr=%lx", rdata->restore_addr);
-}
-
-struct ktask_data ktd_restore = {
- .init = ktd_restore_init,
- .exit = ktd_restore_exit,
- .size = sizeof(struct restore_data),
-};
-
-static DEFINE_PER_CPU(struct restore_data, per_cpu_restore_i);
-static DEFINE_PER_CPU(struct restore_data, per_cpu_restore_st);
-
-static struct restore_data *current_restore_td(void)
-{
- if (swap_in_interrupt())
- return &__get_cpu_var(per_cpu_restore_i);
- else if (switch_to_bits_get(current_kctx, SWITCH_TO_ALL))
- return &__get_cpu_var(per_cpu_restore_st);
-
- return (struct restore_data *)swap_ktd(&ktd_restore, current);
-}
-
-
-static void arch_prepare_ss_slot(struct kp_core *p)
-{
- /* prepare insn slot */
- p->ainsn.insn[0] = p->opcode;
- p->ainsn.insn[1] = conv_inst(BRK64_OPCODE_PSEUDO_SS);
-
- flush_icache((unsigned long)p->ainsn.insn, KPROBES_TRAMP_LEN);
-}
-
-static void arch_prepare_simulate(struct kp_core *p)
-{
- if (p->ainsn.prepare)
- p->ainsn.prepare(p, &p->ainsn);
-}
-
-int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm)
-{
- kprobe_opcode_t insn;
-
- /* copy instruction */
- insn = *(kprobe_opcode_t *)p->addr;
- p->opcode = insn;
-
- /* decode instruction */
- switch (arm_kp_core_decode_insn(insn, &p->ainsn)) {
- case INSN_REJECTED: /* insn not supported */
- return -EINVAL;
-
- case INSN_GOOD_NO_SLOT: /* insn need simulation */
- p->ainsn.insn = NULL;
- arch_prepare_simulate(p);
- break;
-
- case INSN_GOOD: /* instruction uses slot */
- p->ainsn.insn = swap_slot_alloc(sm);
- if (!p->ainsn.insn)
- return -ENOMEM;
-
- arch_prepare_ss_slot(p);
- break;
- };
-
- return 0;
-}
-
-static void save_previous_kp_core(struct kp_core_ctlblk *kcb,
- unsigned long restore_addr)
-{
- kcb->prev_kp_core.p = kp_core_running();
- kcb->prev_kp_core.status = kcb->kp_core_status;
- kcb->prev_kp_core.restore_addr = restore_addr;
-}
-
-void restore_previous_kp_core(struct kp_core_ctlblk *kcb)
-{
- kp_core_running_set(kcb->prev_kp_core.p);
- kcb->kp_core_status = kcb->prev_kp_core.status;
- current_restore_td()->restore_addr = kcb->prev_kp_core.restore_addr;
-}
-
-static void set_ss_context(struct kp_core_ctlblk *kcb, unsigned long addr)
-{
- kcb->ss_ctx.ss_status = KP_CORE_STEP_PENDING;
- kcb->ss_ctx.match_addr = addr + sizeof(kprobe_opcode_t);
-}
-
-static void clear_ss_context(struct kp_core_ctlblk *kcb)
-{
- kcb->ss_ctx.ss_status = KP_CORE_STEP_NONE;
- kcb->ss_ctx.match_addr = 0;
-}
-
-static void nop_singlestep_skip(struct pt_regs *regs)
-{
- /* set return addr to next pc to continue */
- regs->pc += sizeof(kprobe_opcode_t);
-}
-
-static int post_kp_core_handler(struct kp_core_ctlblk *kcb,
- struct pt_regs *regs);
-
-static void arch_simulate_insn(struct kp_core *p, struct pt_regs *regs)
-{
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- if (p->ainsn.handler)
- p->ainsn.handler(p->opcode, (long)p->addr, regs);
-
- /* single step simulated, now go for post processing */
- post_kp_core_handler(kcb, regs);
-}
-
-static bool is_ss_setup(struct restore_data *restore)
-{
- return !!restore->restore_addr;
-}
-
-static void setup_singlestep(struct kp_core *p, struct pt_regs *regs,
- struct kp_core_ctlblk *kcb, int reenter)
-{
- struct restore_data *restore = current_restore_td();
-
- if (reenter) {
- save_previous_kp_core(kcb, restore->restore_addr);
- kp_core_running_set(p);
- kcb->kp_core_status = KPROBE_REENTER;
- } else {
- kcb->kp_core_status = KPROBE_HIT_SS;
- }
-
- if (p->ainsn.insn) {
- unsigned long slot = (unsigned long)p->ainsn.insn;
-
- /* prepare for single stepping */
- restore->restore_addr = regs->pc + 4;
- regs->pc = slot;
-
- set_ss_context(kcb, slot); /* mark pending ss */
- } else {
- restore->restore_addr = 0; /* reset */
-
- /* insn simulation */
- arch_simulate_insn(p, regs);
- }
-}
-
-static int reenter_kp_core(struct kp_core *p, struct pt_regs *regs,
- struct kp_core_ctlblk *kcb)
-{
- switch (kcb->kp_core_status) {
- case KPROBE_HIT_SSDONE:
- case KPROBE_HIT_ACTIVE:
- if (!p->ainsn.check_condn || p->ainsn.check_condn(p, regs)) {
- setup_singlestep(p, regs, kcb, 1);
- } else {
- /* condition failed, it's NOP so skip stepping */
- nop_singlestep_skip(regs);
- }
- break;
- case KPROBE_HIT_SS:
- pr_warn("Unrecoverable kp_core detected at %lx\n", p->addr);
- BUG();
- default:
- WARN_ON(1);
- return 0;
- }
-
- return 1;
-}
-
-static int post_kp_core_handler(struct kp_core_ctlblk *kcb,
- struct pt_regs *regs)
-{
- struct kp_core *cur = kp_core_running();
- struct restore_data *restore = current_restore_td();
-
- if (cur == NULL) {
- WARN_ON(1);
- return 0;
- }
-
- /* return addr restore if non-branching insn */
- if (is_ss_setup(restore)) {
- regs->pc = restore->restore_addr;
- restore->restore_addr = 0;
- kp_core_put(cur);
- } else {
- WARN_ON(1);
- }
-
- /* restore back original saved kp_core variables and continue */
- if (kcb->kp_core_status == KPROBE_REENTER) {
- restore_previous_kp_core(kcb);
- } else { /* call post handler */
- kcb->kp_core_status = KPROBE_HIT_SSDONE;
- kp_core_running_set(NULL);
- }
-
- return 1;
-}
-
-static enum dbg_code kprobe_handler(struct pt_regs *regs, unsigned int esr)
-{
- struct kp_core *p, *cur;
- struct kp_core_ctlblk *kcb;
- unsigned long addr = regs->pc;
- struct restore_data *restore = current_restore_td();
-
- kcb = kp_core_ctlblk();
- cur = kp_core_running();
-
- rcu_read_lock();
- p = kp_core_by_addr(addr);
- if (p)
- kp_core_get(p);
- rcu_read_unlock();
-
- if (p) {
- if (cur && reenter_kp_core(p, regs, kcb)) {
- if (!is_ss_setup(restore))
- kp_core_put(p);
-
- return DBG_HANDLED;
- } else if (!p->ainsn.check_condn ||
- p->ainsn.check_condn(p, regs)) {
- /* Probe hit and conditional execution check ok. */
- kp_core_running_set(p);
- kcb->kp_core_status = KPROBE_HIT_ACTIVE;
-
- if (!(regs->pstate & PSR_I_BIT))
- local_irq_enable();
-
- if (!p->handlers.pre(p, regs)) {
- kcb->kp_core_status = KPROBE_HIT_SS;
- setup_singlestep(p, regs, kcb, 0);
- }
-
- local_irq_disable();
- } else {
- /*
- * Breakpoint hit but conditional check failed,
- * so just skip handling since it is NOP.
- */
- nop_singlestep_skip(regs);
- }
-
- if (!is_ss_setup(restore))
- kp_core_put(p);
- } else if (*(kprobe_opcode_t *)addr != BRK64_OPCODE_BP) {
- /*
- * The breakpoint instruction was removed right
- * after we hit it. Another cpu has removed
- * either a probepoint or a debugger breakpoint
- * at this address. In either case, no further
- * handling of this interrupt is appropriate.
- * Return back to original instruction, and continue.
- */
- } else {
- pr_info("no_kprobe: pc=%llx\n", regs->pc);
- }
-
- return DBG_HANDLED;
-}
-
-static enum dbg_code kp_core_ss_hit(struct kp_core_ctlblk *kcb,
- unsigned long addr)
-{
- if ((kcb->ss_ctx.ss_status == KP_CORE_STEP_PENDING)
- && (kcb->ss_ctx.match_addr == addr)) {
- /* clear pending ss */
- clear_ss_context(kcb);
- return DBG_HANDLED;
- }
-
- /* not ours, kp_cores should ignore it */
- return DBG_ERROR;
-}
-
-static enum dbg_code kprobe_ss_handler(struct pt_regs *regs, unsigned int esr)
-{
- enum dbg_code ret;
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- /* check, and return error if this is not our step */
- ret = kp_core_ss_hit(kcb, regs->pc);
- if (ret == DBG_HANDLED) {
- /* single step complete, call post handlers */
- post_kp_core_handler(kcb, regs);
- }
-
- return ret;
-}
-
-static struct brk_hook dbg_bp = {
- .spsr_mask = PSR_MODE_MASK,
- .spsr_val = PSR_MODE_EL1h,
- .esr_mask = DBG_BRK_ESR_MASK,
- .esr_val = DBG_BRK_ESR(BRK_BP),
- .fn = kprobe_handler,
-};
-
-static struct brk_hook dbg_ss = {
- .spsr_mask = PSR_MODE_MASK,
- .spsr_val = PSR_MODE_EL1h,
- .esr_mask = DBG_BRK_ESR_MASK,
- .esr_val = DBG_BRK_ESR(BRK_PSEUDO_SS),
- .fn = kprobe_ss_handler,
-};
-
-
-
-
-
-/* ============================================================================
- * = KRETPROBE =
- * ============================================================================
- */
-void swap_kretprobe_trampoline(void);
-__asm(
- ".text\n"
- ".global swap_kretprobe_trampoline\n"
- "swap_kretprobe_trampoline:\n"
- "stp x6, x7, [sp,#-16]!\n"
- "stp x4, x5, [sp,#-16]!\n"
- "stp x2, x3, [sp,#-16]!\n"
- "stp x0, x1, [sp,#-16]!\n"
- "mov x0, sp\n" /* struct pt_regs (x0..x7) */
- "bl swap_trampoline_handler\n"
- "mov x30, x0\n" /* set real lr */
- "ldp x0, x1, [sp],#16\n"
- "ldp x2, x3, [sp],#16\n"
- "ldp x4, x5, [sp],#16\n"
- "ldp x6, x7, [sp],#16\n"
- "ret\n"
-);
-
-void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- ri->ret_addr = (unsigned long *)regs->regs[30]; /* lr */
- regs->regs[30] = (unsigned long)&swap_kretprobe_trampoline;
- ri->sp = (unsigned long *)regs->sp;
-}
-
-
-
-
-
-/* ============================================================================
- * = JUMPER =
- * ============================================================================
- */
-struct cb_data {
- unsigned long ret_addr;
- unsigned long x0;
-
- jumper_cb_t cb;
- char data[0];
-};
-
-static unsigned long __used get_x0(struct cb_data *data)
-{
- return data->x0;
-}
-
-static unsigned long __used jump_handler(struct cb_data *data)
-{
- unsigned long ret_addr = data->ret_addr;
-
- /* call callback */
- data->cb(data->data);
-
- /* FIXME: potential memory leak, when process kill */
- kfree(data);
-
- return ret_addr;
-}
-
-void jump_trampoline(void);
-__asm(
- ".text\n"
- "jump_trampoline:\n"
-
- "stp x6, x7, [sp,#-16]!\n"
- "stp x4, x5, [sp,#-16]!\n"
- "stp x2, x3, [sp,#-16]!\n"
- "stp x0, x1, [sp,#-16]!\n"
- "mov x1, x0\n" /* data --> x1 */
- "bl get_x0\n"
- "str x0, [sp]\n" /* restore x0 */
- "mov x0, x1\n" /* data --> x0 */
- "bl jump_handler\n"
- "mov x30, x0\n" /* set lr */
- "ldp x0, x1, [sp],#16\n"
- "ldp x2, x3, [sp],#16\n"
- "ldp x4, x5, [sp],#16\n"
- "ldp x6, x7, [sp],#16\n"
- "ret\n"
-);
-
-unsigned long get_jump_addr(void)
-{
- return (unsigned long)&jump_trampoline;
-}
-EXPORT_SYMBOL_GPL(get_jump_addr);
-
-int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
- jumper_cb_t cb, void *data, size_t size)
-{
- struct cb_data *cb_data;
-
- cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
- if (cb_data == NULL)
- return -ENOMEM;
-
- /* save data */
- if (size)
- memcpy(cb_data->data, data, size);
-
- /* save info for restore */
- cb_data->ret_addr = ret_addr;
- cb_data->cb = cb;
- cb_data->x0 = regs->regs[0];
-
- /* save cb_data to x0 */
- regs->regs[0] = (long)cb_data;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(set_jump_cb);
-
-
-
-
-
-/* ============================================================================
- * = ARCH INIT/EXIT KPROBES =
- * ============================================================================
- */
-int arch_init_module_deps(void)
-{
- return 0;
-}
-
-int swap_arch_init_kprobes(void)
-{
- int ret;
-
- ret = swap_ktd_reg(&ktd_restore);
- if (ret)
- return ret;
-
- ret = dbg_iface_init();
- if (ret)
- swap_ktd_unreg(&ktd_restore);
-
- dbg_brk_hook_reg(&dbg_ss);
- dbg_brk_hook_reg(&dbg_bp);
-
- return 0;
-}
-
-void swap_arch_exit_kprobes(void)
-{
- dbg_brk_hook_unreg(&dbg_ss);
- dbg_brk_hook_unreg(&dbg_bp);
- dbg_iface_uninit();
- swap_ktd_unreg(&ktd_restore);
-}
+++ /dev/null
-#ifndef _ASM_ARM64_KPROBES_H
-#define _ASM_ARM64_KPROBES_H
-
-/*
- * Copied from: arch/arm64/kernel/kprobes.h
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/types.h>
-
-
-#define MAX_INSN_SIZE 2
-#define KPROBES_TRAMP_LEN (MAX_INSN_SIZE * 4) /* 4 - instruction size */
-
-
-struct kprobe;
-struct kp_core;
-struct slot_manager;
-struct swap_arch_specific_insn;
-struct kretprobe_instance;
-
-
-typedef u32 kprobe_opcode_t;
-typedef unsigned long (kp_core_pstate_check_t)(unsigned long);
-typedef unsigned long (kp_core_condition_check_t)(struct kp_core *,
- struct pt_regs *);
-typedef void (kp_core_prepare_t)(struct kp_core *,
- struct swap_arch_specific_insn *);
-typedef void (kp_core_handler_t)(u32 opcode, long addr, struct pt_regs *);
-
-
-/* architecture specific copy of original instruction */
-struct swap_arch_specific_insn {
- kprobe_opcode_t *insn;
- kp_core_pstate_check_t *pstate_cc;
- kp_core_condition_check_t *check_condn;
- kp_core_prepare_t *prepare;
- kp_core_handler_t *handler;
-};
-
-struct prev_kp_core {
- struct kp_core *p;
- unsigned int status;
- unsigned long restore_addr; /* restore address after single step */
-};
-
-enum ss_status {
- KP_CORE_STEP_NONE,
- KP_CORE_STEP_PENDING,
-};
-
-/* Single step context for kp_core */
-struct kp_core_step_ctx {
- enum ss_status ss_status;
- unsigned long match_addr;
-};
-
-/* kp_core control block */
-struct kp_core_ctlblk {
- unsigned int kp_core_status;
- struct prev_kp_core prev_kp_core;
- struct kp_core_step_ctx ss_ctx;
-};
-
-
-static inline unsigned long swap_get_karg(struct pt_regs *regs,
- unsigned long n)
-{
- return n < 8 ? regs->regs[n] : *(((long *)regs->sp) + (n - 8));
-}
-
-static inline unsigned long swap_get_sarg(struct pt_regs *regs,
- unsigned long n)
-{
- return swap_get_karg(regs, n);
-}
-
-static inline unsigned long swap_get_kpc(struct pt_regs *regs)
-{
- return regs->pc;
-}
-
-static inline void swap_set_kpc(struct pt_regs *regs, unsigned long val)
-{
- regs->pc = val;
-}
-
-
-void arch_kp_core_arm(struct kp_core *p);
-void arch_kp_core_disarm(struct kp_core *p);
-
-int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm);
-void restore_previous_kp_core(struct kp_core_ctlblk *kcb);
-
-void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-void swap_kretprobe_trampoline(void);
-
-
-static inline unsigned long arch_get_task_pc(struct task_struct *p)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0xdeadc0de;
-}
-
-static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
-}
-
-static inline int swap_setjmp_pre_handler(struct kprobe *p,
- struct pt_regs *regs)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0;
-}
-
-/* jumper */
-typedef unsigned long (*jumper_cb_t)(void *);
-
-unsigned long get_jump_addr(void);
-int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
- jumper_cb_t cb, void *data, size_t size);
-
-
-int arch_init_module_deps(void);
-
-int swap_arch_init_kprobes(void);
-void swap_arch_exit_kprobes(void);
-
-
-#endif /* _ASM_ARM64_KPROBES_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_ASM_ARM_PROBES_H
-#define _SWAP_ASM_ARM_PROBES_H
-
-
-#define BREAK_ARM 0xffffdeff
-#define BREAK_THUMB (BREAK_ARM & 0xffff)
-#define RET_BREAK_ARM BREAK_ARM
-#define RET_BREAK_THUMB BREAK_THUMB
-
-#define PROBES_TRAMP_LEN (9 * 4)
-#define PROBES_TRAMP_INSN_IDX 2
-#define PROBES_TRAMP_RET_BREAK_IDX 5
-
-
-#endif /* _SWAP_ASM_ARM_PROBES_H */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/kprobe/arch/asm-mips/dbi_kprobes.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * 2006-2007 Ekaterina Gorelkina <e.gorelkina@samsung.com>:
- * initial implementation for ARM/MIPS
- * 2008-2009 Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
- * Probes initial implementation; Support x86/ARM/MIPS for both
- * user-space and kernel space.
- * 2010 Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module
- * for separating core and arch parts
- * 2012 Stanislav Andreev <s.andreev@samsung.com>: added time debug
- * profiling support; BUG() message fix
- */
-
-#include "dbi_kprobes.h"
-#include <kprobe/dbi_kprobes.h>
-
-#include <kprobe/dbi_kdebug.h>
-#include <kprobe/dbi_insn_slots.h>
-#include <kprobe/dbi_kprobes_deps.h>
-#include <kprobe/dbi_uprobes.h>
-#include <ksyms/ksyms.h>
-
-unsigned int *arr_traps_original;
-
-
-unsigned int arr_traps_template[] = { 0x3c010000, /* lui a1 [0] */
- 0x24210000, /* addiu a1, a1 [1] */
- 0x00200008, /* jr a1 [2] */
- 0x00000000, /* nop */
- 0xffffffff /* end */
-};
-
-struct kprobe trampoline_p = {
- .addr = (kprobe_opcode_t *)&kretprobe_trampoline,
- .pre_handler = trampoline_probe_handler
-};
-
-void gen_insn_execbuf(void);
-
-void gen_insn_execbuf_holder(void)
-{
- asm volatile(".global gen_insn_execbuf\n"
- "gen_insn_execbuf:\n"
- "nop\n" /* original instruction */
- "nop\n" /* ssbreak */
- "nop\n"); /* retbreak */
-}
-
-
-int arch_check_insn(struct arch_specific_insn *ainsn)
-{
- int ret = 0;
-
- switch (MIPS_INSN_OPCODE(ainsn->insn[0])) {
- case MIPS_BEQ_OPCODE: /* B, BEQ */
- case MIPS_BEQL_OPCODE: /* BEQL */
- case MIPS_BNE_OPCODE: /* BNE */
- case MIPS_BNEL_OPCODE: /* BNEL */
- case MIPS_BGTZ_OPCODE: /* BGTZ */
- case MIPS_BGTZL_OPCODE: /* BGTZL */
- case MIPS_BLEZ_OPCODE: /* BLEZ */
- case MIPS_BLEZL_OPCODE: /* BLEZL */
- case MIPS_J_OPCODE: /* J */
- case MIPS_JAL_OPCODE: /* JAL */
- DBPRINTF("arch_check_insn: opcode");
- ret = -EFAULT;
- break;
- case MIPS_REGIMM_OPCODE:
- /* BAL, BGEZ, BGEZAL, BGEZALL, BGEZL,
- * BLTZ, BLTZAL, BLTZALL, BLTZL */
- switch (MIPS_INSN_RT(ainsn->insn[0])) {
- case MIPS_BLTZ_RT:
- case MIPS_BGEZ_RT:
- case MIPS_BLTZL_RT:
- case MIPS_BGEZL_RT:
- case MIPS_BLTZAL_RT:
- case MIPS_BGEZAL_RT:
- case MIPS_BLTZALL_RT:
- case MIPS_BGEZALL_RT:
- DBPRINTF("arch_check_insn: REGIMM opcode\n");
- ret = -EFAULT;
- break;
- }
- break;
- /* BC1F, BC1FL, BC1T, BC1TL */
- case MIPS_COP1_OPCODE:
- /* BC2F, BC2FL, BC2T, BC2TL */
- case MIPS_COP2_OPCODE:
- if (MIPS_INSN_RS(ainsn->insn[0]) == MIPS_BC_RS) {
- DBPRINTF("arch_check_insn: COP1 opcode\n");
- ret = -EFAULT;
- }
- break;
- case MIPS_SPECIAL_OPCODE:
- /* BREAK, JALR, JALR.HB, JR, JR.HB */
- switch (MIPS_INSN_FUNC(ainsn->insn[0])) {
- case MIPS_JR_FUNC:
- case MIPS_JALR_FUNC:
- case MIPS_BREAK_FUNC:
- case MIPS_SYSCALL_FUNC:
- DBPRINTF("arch_check_insn: SPECIAL opcode\n");
- ret = -EFAULT;
- break;
- }
- break;
- }
- return ret;
-}
-
-int arch_prepare_kprobe(struct kprobe *p)
-{
- kprobe_opcode_t insns[KPROBES_TRAMP_LEN];
-
- int ret = 0;
- if (!ret) {
- kprobe_opcode_t insn[MAX_INSN_SIZE];
- struct arch_specific_insn ainsn;
- /* insn: must be on special executable page on i386. */
- p->ainsn.insn = get_insn_slot(NULL, 0);
- if (!p->ainsn.insn)
- return -ENOMEM;
- memcpy(insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
- ainsn.insn = insn;
- ret = arch_check_insn(&ainsn);
- if (!ret) {
- p->opcode = *p->addr;
- p->ainsn.boostable = 0;
- memcpy(insns, gen_insn_execbuf, sizeof(insns));
- insns[KPROBES_TRAMP_INSN_IDX] = insn[0];
- insns[KPROBES_TRAMP_SS_BREAK_IDX] =
- BREAKPOINT_INSTRUCTION;
- insns[KPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
- DBPRINTF("arch_prepare_kprobe: insn %lx", insn[0]);
- DBPRINTF("arch_prepare_kprobe: to %p - %lx %lx %lx",
- p->ainsn.insn, insns[0],
- insns[1], insns[2]);
- memcpy(p->ainsn.insn, insns, sizeof(insns));
- } else {
- free_insn_slot(&kprobe_insn_pages, NULL, p->ainsn.insn);
- }
- }
-
- return ret;
-}
-
-int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
-{
- int ret = 0;
- kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
-
- if ((unsigned long) p->addr & 0x01) {
- DBPRINTF("Attempt to register kprobe at an unaligned address");
- ret = -EINVAL;
- }
-
- if (!ret) {
- kprobe_opcode_t insn[MAX_INSN_SIZE];
- struct arch_specific_insn ainsn;
-
- if (!read_proc_vm_atomic(task, (unsigned long) p->addr,
- &insn,
- MAX_INSN_SIZE *
- sizeof(kprobe_opcode_t)))
- panic("failed to read memory %p!\n", p->addr);
- ainsn.insn = insn;
- ret = arch_check_insn(&ainsn);
- if (!ret) {
- p->opcode = insn[0];
- p->ainsn.insn = get_insn_slot(task, atomic);
- if (!p->ainsn.insn)
- return -ENOMEM;
- p->ainsn.boostable = 0;
- memcpy(insns, gen_insn_execbuf, sizeof(insns));
- insns[UPROBES_TRAMP_INSN_IDX] = insn[0];
- insns[UPROBES_TRAMP_SS_BREAK_IDX] =
- BREAKPOINT_INSTRUCTION;
- insns[UPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
- DBPRINTF("arch_prepare_uprobe: insn %lx", insn[0]);
- DBPRINTF("arch_prepare_uprobe: to %p - %lx %lx %lx",
- p->ainsn.insn, insns[0], insns[1], insns[2]);
-
- if (!write_proc_vm_atomic(task,
- (unsigned long) p->ainsn.insn,
- insns, sizeof(insns))) {
- panic("failed to write memory %p!\n",
- p->ainsn.insn);
- DBPRINTF("failed to write insn slot to "
- "process memory: insn %p, addr %p, "
- "probe %p!",
- insn, p->ainsn.insn, p->addr);
- /* printk("failed to write insn slot to process
- * memory: %p/%d insn %lx, addr %p,
- * probe %p!\n",task, task->pid, insn,
- * p->ainsn.insn, p->addr);*/
- free_insn_slot(&uprobe_insn_pages, task,
- p->ainsn.insn);
- return -EINVAL;
- }
- }
- }
-
- return ret;
-}
-
-void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
-{
- if (p->ss_addr) {
- regs->cp0_epc = (unsigned long)p->ss_addr;
- p->ss_addr = NULL;
- } else
- regs->cp0_epc = (unsigned long)p->ainsn.insn;
-}
-
-
-void save_previous_kprobe(struct kprobe_ctlblk *kcb, struct kprobe *cur_p)
-{
- if (kcb->prev_kprobe.kp != NULL) {
- panic("no space to save new probe[]: task = %d/%s, prev %d/%p,"
- " current %d/%p, new %d/%p,",
- current->pid, current->comm, kcb->prev_kprobe.kp->tgid,
- kcb->prev_kprobe.kp->addr, kprobe_running()->tgid,
- kprobe_running()->addr, cur_p->tgid, cur_p->addr);
- }
-
- kcb->prev_kprobe.kp = kprobe_running();
- kcb->prev_kprobe.status = kcb->kprobe_status;
-}
-
-void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
-{
- __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
- kcb->kprobe_status = kcb->prev_kprobe.status;
- kcb->prev_kprobe.kp = NULL;
- kcb->prev_kprobe.status = 0;
-}
-
-void set_current_kprobe(struct kprobe *p,
- struct pt_regs *regs,
- struct kprobe_ctlblk *kcb)
-{
- __get_cpu_var(current_kprobe) = p;
- DBPRINTF("set_current_kprobe[]: p=%p addr=%p\n", p, p->addr);
-}
-
-int kprobe_handler(struct pt_regs *regs)
-{
- struct kprobe *p = 0;
- int ret = 0, pid = 0, retprobe = 0, reenter = 0;
- kprobe_opcode_t *addr = NULL, *ssaddr = 0;
- struct kprobe_ctlblk *kcb;
-
- /* We're in an interrupt, but this is clear and BUG()-safe. */
-
- addr = (kprobe_opcode_t *) regs->cp0_epc;
- DBPRINTF("regs->regs[ 31 ] = 0x%lx\n", regs->regs[31]);
-
- preempt_disable();
-
- kcb = get_kprobe_ctlblk();
-
- if (user_mode(regs)) {
- /* DBPRINTF("exception[%lu] from user mode %s/%u addr %p(%lx).",
- * nCount, current->comm,
- * current->pid, addr, regs->uregs[14]); */
- pid = current->tgid;
- }
-
- /* Check we're not actually recursing */
- if (kprobe_running()) {
- DBPRINTF("lock???");
- p = get_kprobe(addr, pid);
- if (p) {
- if (!pid && (addr ==
- (kprobe_opcode_t *)kretprobe_trampoline)) {
- save_previous_kprobe(kcb, p);
- kcb->kprobe_status = KPROBE_REENTER;
- reenter = 1;
- } else {
- /* We have reentered the kprobe_handler(), since
- * another probe was hit while within the
- * handler. We here save the original kprobes
- * variables and just single step on the
- * instruction of the new probe without calling
- * any user handlers.
- */
- if (!p->ainsn.boostable) {
- save_previous_kprobe(kcb, p);
- set_current_kprobe(p, regs, kcb);
- }
- kprobes_inc_nmissed_count(p);
- prepare_singlestep(p, regs);
- if (!p->ainsn.boostable)
- kcb->kprobe_status = KPROBE_REENTER;
- preempt_enable_no_resched();
- return 1;
- }
- } else {
- if (pid) {
- /* we can reenter probe upon
- * uretprobe exception */
- DBPRINTF("check for UNDEF_INSTRUCTION %p\n",
- addr);
- /* UNDEF_INSTRUCTION from user space */
- p = get_kprobe_by_insn_slot(
- addr-UPROBES_TRAMP_RET_BREAK_IDX,
- pid, current);
- if (p) {
- save_previous_kprobe(kcb, p);
- kcb->kprobe_status = KPROBE_REENTER;
- reenter = 1;
- retprobe = 1;
- DBPRINTF("uretprobe %p\n", addr);
- }
- }
- if (!p) {
- p = __get_cpu_var(current_kprobe);
- DBPRINTF("kprobe_running !!! p = 0x%p "
- "p->break_handler = 0x%p", p,
- p->break_handler);
- /* if (p->break_handler &&
- * p->break_handler(p, regs)) {
- * DBPRINTF("kprobe_running !!! goto ss");
- * goto ss_probe;
- * } */
- DBPRINTF("unknown uprobe at %p cur at %p/%p\n",
- addr, p->addr, p->ainsn.insn);
- if (pid)
- ssaddr = p->ainsn.insn +
- UPROBES_TRAMP_SS_BREAK_IDX;
- else
- ssaddr = p->ainsn.insn +
- KPROBES_TRAMP_SS_BREAK_IDX;
- if (addr == ssaddr) {
- regs->cp0_epc =
- (unsigned long)(p->addr + 1);
- DBPRINTF("finish step at %p cur at "
- "%p/%p, redirect to %lx\n",
- addr, p->addr,
- p->ainsn.insn, regs->cp0_epc);
-
- if (kcb->kprobe_status ==
- KPROBE_REENTER) {
- restore_previous_kprobe(kcb);
- } else {
- reset_current_kprobe();
- }
- }
- DBPRINTF("kprobe_running !!! goto no");
- ret = 1;
- /* If it's not ours, can't be delete race,
- * (we hold lock). */
- DBPRINTF("no_kprobe");
- goto no_kprobe;
- }
- }
- }
-
- /* if(einsn != UNDEF_INSTRUCTION) { */
- DBPRINTF("get_kprobe %p-%d", addr, pid);
- if (!p)
- p = get_kprobe(addr, pid);
- if (!p) {
- if (pid) {
- DBPRINTF("search UNDEF_INSTRUCTION %p\n", addr);
- /* UNDEF_INSTRUCTION from user space */
- p = get_kprobe_by_insn_slot(
- addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
- if (!p) {
- /* Not one of ours: let kernel handle it */
- DBPRINTF("no_kprobe");
- /* printk("no_kprobe2 ret = %d\n", ret); */
- goto no_kprobe;
- }
- retprobe = 1;
- DBPRINTF("uretprobe %p\n", addr);
- } else {
- /* Not one of ours: let kernel handle it */
- DBPRINTF("no_kprobe");
- /* printk(KERN_INFO "no_kprobe2 ret = %d\n", ret); */
- goto no_kprobe;
- }
- }
-
- set_current_kprobe(p, regs, kcb);
- if (!reenter)
- kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-
- if (retprobe) /* (einsn == UNDEF_INSTRUCTION) */
- ret = trampoline_probe_handler(p, regs);
- else if (p->pre_handler) {
- ret = p->pre_handler(p, regs);
- if (!p->ainsn.boostable)
- kcb->kprobe_status = KPROBE_HIT_SS;
- else if (p->pre_handler != trampoline_probe_handler) {
- reset_current_kprobe();
- }
- }
-
- if (ret) {
- DBPRINTF("p->pre_handler[] 1");
- /* handler has already set things up, so skip ss setup */
- return 1;
- }
- DBPRINTF("p->pre_handler 0");
-
-no_kprobe:
- preempt_enable_no_resched();
- return ret;
-}
-
-void patch_suspended_task_ret_addr(struct task_struct *p, struct kretprobe *rp)
-{
- DBPRINTF("patch_suspended_task_ret_addr is not implemented");
-}
-
-int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
-{
- struct jprobe *jp = container_of(p, struct jprobe, kp);
- kprobe_pre_entry_handler_t pre_entry;
- entry_point_t entry;
-
- DBPRINTF("pjp = 0x%p jp->entry = 0x%p", jp, jp->entry);
- entry = (entry_point_t) jp->entry;
- pre_entry = (kprobe_pre_entry_handler_t) jp->pre_entry;
- /* if(!entry) */
- /* DIE("entry NULL", regs) */
- DBPRINTF("entry = 0x%p jp->entry = 0x%p", entry, jp->entry);
-
- /* call handler for all kernel probes and user space
- * ones which belong to current tgid */
- if (!p->tgid || (p->tgid == current->tgid)) {
- if (!p->tgid && (p->addr == sched_addr) && sched_rp) {
- struct task_struct *p, *g;
- rcu_read_lock();
- /* swapper task */
- if (current != &init_task)
- patch_suspended_task_ret_addr(&init_task,
- sched_rp);
- /* other tasks */
- do_each_thread(g, p) {
- if (p == current)
- continue;
- patch_suspended_task_ret_addr(p, sched_rp);
- } while_each_thread(g, p);
- rcu_read_unlock();
- }
- if (pre_entry)
- p->ss_addr = (void *)pre_entry(jp->priv_arg, regs);
- if (entry) {
- entry(regs->regs[4], regs->regs[5], regs->regs[6],
- regs->regs[7], regs->regs[8], regs->regs[9]);
- } else {
- if (p->tgid)
- arch_ujprobe_return();
- else
- dbi_jprobe_return();
- }
- } else if (p->tgid)
- arch_ujprobe_return();
-
- prepare_singlestep(p, regs);
-
- return 1;
-}
-
-
-void dbi_jprobe_return(void)
-{
- preempt_enable_no_resched();
-}
-
-void arch_ujprobe_return(void)
-{
- preempt_enable_no_resched();
-}
-
-int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-
-void arch_arm_kprobe(struct kprobe *p)
-{
- *p->addr = BREAKPOINT_INSTRUCTION;
- flush_icache_range((unsigned long) p->addr,
- (unsigned long) p->addr + sizeof(kprobe_opcode_t));
-}
-
-void arch_disarm_kprobe(struct kprobe *p)
-{
- *p->addr = p->opcode;
- flush_icache_range((unsigned long) p->addr,
- (unsigned long) p->addr + sizeof(kprobe_opcode_t));
-}
-
-int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
-{
- struct kretprobe_instance *ri = NULL;
- struct hlist_head *head, empty_rp;
- struct hlist_node *node, *tmp;
- unsigned long flags, orig_ret_address = 0;
- unsigned long trampoline_address =
- (unsigned long) &kretprobe_trampoline;
- struct kretprobe *crp = NULL;
- struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
-
- DBPRINTF("start");
-
- if (p && p->tgid) {
- /* in case of user space retprobe trampoline
- * is at the Nth instruction of US tramp */
- trampoline_address =
- (unsigned long)(p->ainsn.insn +
- UPROBES_TRAMP_RET_BREAK_IDX);
- }
-
- INIT_HLIST_HEAD(&empty_rp);
- spin_lock_irqsave(&kretprobe_lock, flags);
- head = kretprobe_inst_table_head(current);
-
- /*
- * It is possible to have multiple instances associated with a given
- * task either because an multiple functions in the call path
- * have a return probe installed on them, and/or more then one
- * return probe was registered for a target function.
- *
- * We can handle this because:
- * - instances are always inserted at the head of the list
- * - when multiple return probes are registered for the same
- * function, the first instance's ret_addr will point to the
- * real return address, and all the rest will point to
- * kretprobe_trampoline
- */
- hlist_for_each_entry_safe(ri, node, tmp, head, hlist)
- {
- if (ri->task != current)
- /* another task is sharing our hash bucket */
- continue;
- if (ri->rp && ri->rp->handler)
- ri->rp->handler(ri, regs, ri->rp->priv_arg);
-
- orig_ret_address = (unsigned long) ri->ret_addr;
- recycle_rp_inst(ri);
- if (orig_ret_address != trampoline_address)
- /*
- * This is the real return address. Any other
- * instances associated with this task are for
- * other calls deeper on the call stack
- */
- break;
- }
- kretprobe_assert(ri, orig_ret_address, trampoline_address);
- /* BUG_ON(!orig_ret_address ||
- * (orig_ret_address == trampoline_address)); */
- if (trampoline_address != (unsigned long) &kretprobe_trampoline)
- if (ri->rp)
- BUG_ON(ri->rp->kp.tgid == 0);
-
- if (ri->rp && ri->rp->kp.tgid)
- BUG_ON(trampoline_address ==
- (unsigned long) &kretprobe_trampoline);
-
- regs->regs[31] = orig_ret_address;
- DBPRINTF("regs->cp0_epc = 0x%lx", regs->cp0_epc);
- if (trampoline_address != (unsigned long) &kretprobe_trampoline)
- regs->cp0_epc = orig_ret_address;
- else
- regs->cp0_epc = regs->cp0_epc + 4;
- DBPRINTF("regs->cp0_epc = 0x%lx", regs->cp0_epc);
- DBPRINTF("regs->cp0_status = 0x%lx", regs->cp0_status);
-
- if (p) { /* ARM, MIPS, X86 user space */
- if (kcb->kprobe_status == KPROBE_REENTER)
- restore_previous_kprobe(kcb);
- else
- reset_current_kprobe();
- }
-
- spin_unlock_irqrestore(&kretprobe_lock, flags);
- hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist)
- {
- hlist_del(&ri->hlist);
- kfree(ri);
- }
- preempt_enable_no_resched();
- /*
- * By returning a non-zero value, we are telling
- * kprobe_handler() that we don't want the post_handler
- * to run (and have re-enabled preemption)
- */
- return 1;
-}
-
-void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
-{
-
- struct kretprobe_instance *ri;
-
- DBPRINTF("start\n");
- /* TODO: test - remove retprobe after func entry but before its exit */
- ri = get_free_rp_inst(rp);
- if (ri != NULL) {
- ri->rp = rp;
- ri->task = current;
- ri->ret_addr = (kprobe_opcode_t *) regs->regs[31];
- if (rp->kp.tgid)
- regs->regs[31] =
- (unsigned long)(rp->kp.ainsn.insn +
- UPROBES_TRAMP_RET_BREAK_IDX);
- else /* Replace the return addr with trampoline addr */
- regs->regs[31] = (unsigned long) &kretprobe_trampoline;
- add_rp_inst(ri);
- } else {
- DBPRINTF("WARNING: missed retprobe %p\n", rp->kp.addr);
- rp->nmissed++;
- }
-}
-
-DECLARE_MOD_CB_DEP(flush_icache_range, \
- void, unsigned long __user start, unsigned long __user end);
-DECLARE_MOD_CB_DEP(flush_icache_page, \
- void, struct vm_area_struct *vma, struct page *page);
-DECLARE_MOD_CB_DEP(flush_cache_page, \
- void, struct vm_area_struct *vma, unsigned long page);
-
-int arch_init_module_deps()
-{
- INIT_MOD_DEP_CB(flush_icache_range, r4k_flush_icache_range);
- INIT_MOD_DEP_CB(flush_icache_page, r4k_flush_icache_page);
- INIT_MOD_DEP_CB(flush_cache_page, r4k_flush_cache_page);
-
- return 0;
-}
-
-
-int __init arch_init_kprobes(void)
-{
- unsigned int do_bp_handler;
- unsigned int kprobe_handler_addr;
-
- unsigned int insns_num = 0;
- unsigned int code_size = 0;
-
- unsigned int reg_hi;
- unsigned int reg_lo;
-
- int ret;
-
- if (arch_init_module_dependencies()) {
- DBPRINTF("Unable to init module dependencies\n");
- return -1;
- }
-
- do_bp_handler = (unsigned int)swap_ksyms("do_bp");
-
- kprobe_handler_addr = (unsigned int) &kprobe_handler;
- insns_num = sizeof(arr_traps_template) / sizeof(arr_traps_template[0]);
- code_size = insns_num * sizeof(unsigned int);
- DBPRINTF("insns_num = %d\n", insns_num);
- /* Save original code */
- arr_traps_original = kmalloc(code_size, GFP_KERNEL);
- if (!arr_traps_original) {
- DBPRINTF("Unable to allocate space for "
- "original code of <do_bp>!\n");
- return -1;
- }
- memcpy(arr_traps_original, (void *) do_bp_handler, code_size);
-
- reg_hi = HIWORD(kprobe_handler_addr);
- reg_lo = LOWORD(kprobe_handler_addr);
- if (reg_lo >= 0x8000)
- reg_hi += 0x0001;
- arr_traps_template[REG_HI_INDEX] |= reg_hi;
- arr_traps_template[REG_LO_INDEX] |= reg_lo;
-
- /* Insert new code */
- memcpy((void *) do_bp_handler, arr_traps_template, code_size);
- flush_icache_range(do_bp_handler, do_bp_handler + code_size);
- ret = dbi_register_kprobe(&trampoline_p);
- if (ret != 0) {
- /* dbi_unregister_jprobe(&do_exit_p, 0); */
- return ret;
- }
-}
-
-void __exit dbi_arch_exit_kprobes(void)
-{
- unsigned int do_bp_handler;
-
- unsigned int insns_num = 0;
- unsigned int code_size = 0;
-
- /* Get instruction address */
- do_bp_handler = (unsigned int)swap_ksyms("do_undefinstr");
-
- /* dbi_unregister_jprobe(&do_exit_p, 0); */
-
- /* Replace back the original code */
-
- insns_num = sizeof(arr_traps_template) / sizeof(arr_traps_template[0]);
- code_size = insns_num * sizeof(unsigned int);
- memcpy((void *) do_bp_handler, arr_traps_original, code_size);
- flush_icache_range(do_bp_handler, do_bp_handler + code_size);
- kfree(arr_traps_original);
- arr_traps_original = NULL;
-}
-
-
+++ /dev/null
-#ifndef _SRC_ASM_MIPS_KPROBES_H
-#define _SRC_ASM_MIPS_KPROBES_H
-
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/kprobe/arch/asm-mips/dbi_kprobes.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * 2006-2007 Ekaterina Gorelkina <e.gorelkina@samsung.com>:
- * initial implementation for ARM/MIPS
- * 2008-2009 Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
- * Probes initial implementation; Support x86/ARM/MIPS for both
- * user-space and kernel space.
- * 2010 Ekaterina Gorelkina <e.gorelkina@samsung.com>:
- * redesign module for separating core and arch parts
- *
- */
-
-#include <kprobe/dbi_kprobes_deps.h>
-#include <kprobe/dbi_kprobes.h>
-
-typedef unsigned long kprobe_opcode_t;
-
-#define BREAKPOINT_INSTRUCTION 0x0000000d
-
-#ifndef KPROBES_RET_PROBE_TRAMP
-#define UNDEF_INSTRUCTION 0x0000004d
-#endif
-
-#define MAX_INSN_SIZE 1
-
-# define UPROBES_TRAMP_LEN 3
-# define UPROBES_TRAMP_INSN_IDX 0
-# define UPROBES_TRAMP_SS_BREAK_IDX 1
-# define UPROBES_TRAMP_RET_BREAK_IDX 2
-# define KPROBES_TRAMP_LEN UPROBES_TRAMP_LEN
-# define KPROBES_TRAMP_INSN_IDX UPROBES_TRAMP_INSN_IDX
-# define KPROBES_TRAMP_SS_BREAK_IDX UPROBES_TRAMP_SS_BREAK_IDX
-# define KPROBES_TRAMP_RET_BREAK_IDX UPROBES_TRAMP_RET_BREAK_IDX
-
-#define REG_HI_INDEX 0
-#define REG_LO_INDEX 1
-#define NOTIFIER_CALL_CHAIN_INDEX 0
-
-
-#define MIPS_INSN_OPCODE_MASK 0xFC000000
-#define MIPS_INSN_RS_MASK 0x03E00000
-#define MIPS_INSN_RT_MASK 0x001F0000
-/* #define MIPS_INSN_UN_MASK 0x0000FFC0 */
-#define MIPS_INSN_FUNC_MASK 0x0000003F
-#define MIPS_INSN_OPCODE(insn) (insn & MIPS_INSN_OPCODE_MASK)
-#define MIPS_INSN_RS(insn) (insn & MIPS_INSN_RS_MASK)
-#define MIPS_INSN_RT(insn) (insn & MIPS_INSN_RT_MASK)
-#define MIPS_INSN_FUNC(insn) (insn & MIPS_INSN_FUNC_MASK)
-/* opcodes 31..26 */
-#define MIPS_BEQ_OPCODE 0x10000000
-#define MIPS_BNE_OPCODE 0x14000000
-#define MIPS_BLEZ_OPCODE 0x18000000
-#define MIPS_BGTZ_OPCODE 0x1C000000
-#define MIPS_BEQL_OPCODE 0x50000000
-#define MIPS_BNEL_OPCODE 0x54000000
-#define MIPS_BLEZL_OPCODE 0x58000000
-#define MIPS_BGTZL_OPCODE 0x5C000000
-#define MIPS_REGIMM_OPCODE 0x04000000
-#define MIPS_SPECIAL_OPCODE 0x00000000
-#define MIPS_COP1_OPCODE 0x44000000
-#define MIPS_COP2_OPCODE 0x48000000
-#define MIPS_J_OPCODE 0x08000000
-#define MIPS_JAL_OPCODE 0x0C000000
-#define MIPS_JALX_OPCODE 0x74000000
-/* rs 25..21 */
-#define MIPS_BC_RS 0x01000000
-/* rt 20..16 */
-#define MIPS_BLTZ_RT 0x00000000
-#define MIPS_BGEZ_RT 0x00010000
-#define MIPS_BLTZL_RT 0x00020000
-#define MIPS_BGEZL_RT 0x00030000
-#define MIPS_BLTZAL_RT 0x00100000
-#define MIPS_BGEZAL_RT 0x00110000
-#define MIPS_BLTZALL_RT 0x00120000
-#define MIPS_BGEZALL_RT 0x00130000
-/* unnamed 15..6 */
-/* function 5..0 */
-#define MIPS_JR_FUNC 0x00000008
-#define MIPS_JALR_FUNC 0x00000009
-#define MIPS_BREAK_FUNC 0x0000000D
-#define MIPS_SYSCALL_FUNC 0x0000000C
-
-
-/* per-cpu kprobe control block */
-struct kprobe_ctlblk {
- unsigned long kprobe_status;
- struct prev_kprobe prev_kprobe;
-};
-
-/* Architecture specific copy of original instruction */
-struct arch_specific_insn {
- /* copy of the original instruction */
- kprobe_opcode_t *insn;
- /*
- * If this flag is not 0, this kprobe can be boost when its
- * post_handler and break_handler is not set.
- */
- int boostable;
-};
-
-typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long,
- unsigned long, unsigned long,
- unsigned long, unsigned long);
-
-
-void gen_insn_execbuf_holder(void);
-
-void patch_suspended_task_ret_addr(struct task_struct *p, struct kretprobe *rp);
-int arch_init_module_deps(void);
-
-#endif /* _SRC_ASM_MIPS_KPROBES_H */
+++ /dev/null
-/**
- * arch/asm-x86/swap_kprobes.c
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- * @author Stanislav Andreev <s.andreev@samsung.com>: added time debug profiling support; BUG() message fix
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) IBM Corporation, 2002, 2004
- *
- * @section DESCRIPTION
- *
- * SWAP krpobes arch-dependend part for x86.
- */
-
-
-#include <linux/kconfig.h>
-
-#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
-# error "Kernel is immutable"
-#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
-
-
-#include<linux/module.h>
-#include <linux/kdebug.h>
-
-#include "swap_kprobes.h"
-#include <kprobe/swap_kprobes.h>
-#include <kprobe/swap_td_raw.h>
-#include <kprobe/swap_kdebug.h>
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_kprobes_deps.h>
-
-
-static int (*swap_fixup_exception)(struct pt_regs *regs);
-static void *(*swap_text_poke)(void *addr, const void *opcode, size_t len);
-static void (*swap_show_registers)(struct pt_regs *regs);
-
-
-#define SWAP_SAVE_REGS_STRING \
- /* Skip cs, ip, orig_ax and gs. */ \
- "subl $16, %esp\n" \
- "pushl %fs\n" \
- "pushl %es\n" \
- "pushl %ds\n" \
- "pushl %eax\n" \
- "pushl %ebp\n" \
- "pushl %edi\n" \
- "pushl %esi\n" \
- "pushl %edx\n" \
- "pushl %ecx\n" \
- "pushl %ebx\n"
-#define SWAP_RESTORE_REGS_STRING \
- "popl %ebx\n" \
- "popl %ecx\n" \
- "popl %edx\n" \
- "popl %esi\n" \
- "popl %edi\n" \
- "popl %ebp\n" \
- "popl %eax\n" \
- /* Skip ds, es, fs, gs, orig_ax, and ip. Note: don't pop cs here*/\
- "addl $24, %esp\n"
-
-
-/*
- * Function return probe trampoline:
- * - init_kprobes() establishes a probepoint here
- * - When the probed function returns, this probe
- * causes the handlers to fire
- */
-__asm(
- ".global swap_kretprobe_trampoline\n"
- "swap_kretprobe_trampoline:\n"
- "pushf\n"
- SWAP_SAVE_REGS_STRING
- "movl %esp, %eax\n"
- "call swap_trampoline_handler\n"
- /* move eflags to cs */
- "movl 56(%esp), %edx\n"
- "movl %edx, 52(%esp)\n"
- /* replace saved flags with true return address. */
- "movl %eax, 56(%esp)\n"
- SWAP_RESTORE_REGS_STRING
- "popf\n"
- "ret\n"
-);
-
-/* insert a jmp code */
-static __always_inline void set_jmp_op(void *from, void *to)
-{
- struct __arch_jmp_op {
- char op;
- long raddr;
- } __packed * jop;
- jop = (struct __arch_jmp_op *) from;
- jop->raddr = (long) (to) - ((long) (from) + 5);
- jop->op = RELATIVEJUMP_INSTRUCTION;
-}
-
-/**
- * @brief Check if opcode can be boosted.
- *
- * @param opcodes Opcode to check.
- * @return Non-zero if opcode can be boosted.
- */
-int swap_can_boost(kprobe_opcode_t *opcodes)
-{
-#define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf) \
- (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \
- (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \
- (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \
- (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \
- << (row % 32))
- /*
- * Undefined/reserved opcodes, conditional jump, Opcode Extension
- * Groups, and some special opcodes can not be boost.
- */
- static const unsigned long twobyte_is_boostable[256 / 32] = {
- /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
- W(0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) |
- W(0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |
- W(0x30, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) |
- W(0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1) |
- W(0x70, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1),
- W(0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |
- W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
- W(0xa0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1) |
- W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1),
- W(0xc0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) |
- W(0xd0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1),
- W(0xe0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1) |
- W(0xf0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0)
- /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
-
- };
-#undef W
- kprobe_opcode_t opcode;
- kprobe_opcode_t *orig_opcodes = opcodes;
-retry:
- if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1)
- return 0;
- opcode = *(opcodes++);
-
- /* 2nd-byte opcode */
- if (opcode == 0x0f) {
- if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1)
- return 0;
- return test_bit(*opcodes, twobyte_is_boostable);
- }
-
- switch (opcode & 0xf0) {
- case 0x60:
- if (0x63 < opcode && opcode < 0x67)
- goto retry; /* prefixes */
- /* can't boost Address-size override and bound */
- return (opcode != 0x62 && opcode != 0x67);
- case 0x70:
- return 0; /* can't boost conditional jump */
- case 0xc0:
- /* can't boost software-interruptions */
- return (0xc1 < opcode && opcode < 0xcc) || opcode == 0xcf;
- case 0xd0:
- /* can boost AA* and XLAT */
- return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7);
- case 0xe0:
- /* can boost in/out and absolute jmps */
- return ((opcode & 0x04) || opcode == 0xea);
- case 0xf0:
- if ((opcode & 0x0c) == 0 && opcode != 0xf1)
- goto retry; /* lock/rep(ne) prefix */
- /* clear and set flags can be boost */
- return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe));
- default:
- if (opcode == 0x26 || opcode == 0x36 || opcode == 0x3e)
- goto retry; /* prefixes */
- /* can't boost CS override and call */
- return (opcode != 0x2e && opcode != 0x9a);
- }
-}
-EXPORT_SYMBOL_GPL(swap_can_boost);
-
-/*
- * returns non-zero if opcode modifies the interrupt flag.
- */
-static int is_IF_modifier(kprobe_opcode_t opcode)
-{
- switch (opcode) {
- case 0xfa: /* cli */
- case 0xfb: /* sti */
- case 0xcf: /* iret/iretd */
- case 0x9d: /* popf/popfd */
- return 1;
- }
- return 0;
-}
-
-/**
- * @brief Creates trampoline for kprobe.
- *
- * @param p Pointer to kprobe.
- * @param sm Pointer to slot manager
- * @return 0 on success, error code on error.
- */
-int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm)
-{
- /* insn: must be on special executable page on i386. */
- p->ainsn.insn = swap_slot_alloc(sm);
- if (p->ainsn.insn == NULL)
- return -ENOMEM;
-
- memcpy(p->ainsn.insn, (void *)p->addr, MAX_INSN_SIZE);
-
- p->opcode = *(char *)p->addr;
- p->ainsn.boostable = swap_can_boost((void *)p->addr) ? 0 : -1;
-
- return 0;
-}
-
-/**
- * @brief Prepares singlestep for current CPU.
- *
- * @param p Pointer to kprobe.
- * @param regs Pointer to CPU registers data.
- * @return Void.
- */
-static void prepare_singlestep(struct kp_core *p, struct pt_regs *regs)
-{
- regs->flags |= TF_MASK;
- regs->flags &= ~IF_MASK;
-
- /* single step inline if the instruction is an int3 */
- if (p->opcode == BREAKPOINT_INSTRUCTION)
- regs->ip = (unsigned long)p->addr;
- else
- regs->ip = (unsigned long)p->ainsn.insn;
-}
-
-/**
- * @brief Saves previous kprobe.
- *
- * @param kcb Pointer to kp_core_ctlblk struct whereto save current kprobe.
- * @param p_run Pointer to kprobe.
- * @return Void.
- */
-static void save_previous_kp_core(struct kp_core_ctlblk *kcb, struct kp_core *cur)
-{
- if (kcb->prev_kp_core.p != NULL) {
- panic("no space to save new probe[]: "
- "task = %d/%s, prev %08lx, current %08lx, new %08lx,",
- current->pid, current->comm, kcb->prev_kp_core.p->addr,
- kp_core_running()->addr, cur->addr);
- }
-
-
- kcb->prev_kp_core.p = kp_core_running();
- kcb->prev_kp_core.status = kcb->kp_core_status;
-}
-
-/**
- * @brief Restores previous kp_core.
- *
- * @param kcb Pointer to kp_core_ctlblk which contains previous kp_core.
- * @return Void.
- */
-void restore_previous_kp_core(struct kp_core_ctlblk *kcb)
-{
- kp_core_running_set(kcb->prev_kp_core.p);
- kcb->kp_core_status = kcb->prev_kp_core.status;
- kcb->prev_kp_core.p = NULL;
- kcb->prev_kp_core.status = 0;
-}
-
-static void set_current_kp_core(struct kp_core *p, struct pt_regs *regs,
- struct kp_core_ctlblk *kcb)
-{
- kp_core_running_set(p);
- kcb->kp_core_saved_eflags = kcb->kp_core_old_eflags =
- (regs->EREG(flags) & (TF_MASK | IF_MASK));
- if (is_IF_modifier(p->opcode))
- kcb->kp_core_saved_eflags &= ~IF_MASK;
-}
-
-static int setup_singlestep(struct kp_core *p, struct pt_regs *regs,
- struct kp_core_ctlblk *kcb)
-{
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
- if (p->ainsn.boostable == 1) {
- /* Boost up -- we can execute copied instructions directly */
- kp_core_running_set(NULL);
- regs->ip = (unsigned long)p->ainsn.insn;
-
- return 1;
- }
-#endif /* !CONFIG_PREEMPT */
-
- prepare_singlestep(p, regs);
- kcb->kp_core_status = KPROBE_HIT_SS;
-
- return 1;
-}
-
-
-struct regs_td {
- struct pt_regs *sp_regs;
- struct pt_regs regs;
-};
-
-static struct td_raw kp_tdraw;
-static DEFINE_PER_CPU(struct regs_td, per_cpu_regs_td_i);
-static DEFINE_PER_CPU(struct regs_td, per_cpu_regs_td_st);
-
-static struct regs_td *current_regs_td(void)
-{
- if (swap_in_interrupt())
- return &__get_cpu_var(per_cpu_regs_td_i);
- else if (switch_to_bits_get(current_kctx, SWITCH_TO_ALL))
- return &__get_cpu_var(per_cpu_regs_td_st);
-
- return (struct regs_td *)swap_td_raw(&kp_tdraw, current);
-}
-
-/** Stack address. */
-unsigned long swap_kernel_sp(struct pt_regs *regs)
-{
- struct pt_regs *sp_regs = current_regs_td()->sp_regs;
-
- if (sp_regs == NULL)
- sp_regs = regs;
-
- return kernel_stack_pointer(sp_regs);
-}
-EXPORT_SYMBOL_GPL(swap_kernel_sp);
-
-void exec_trampoline(void);
-void exec_trampoline_int3(void);
-__asm(
- ".text\n"
- "exec_trampoline:\n"
- "call exec_handler\n"
- "exec_trampoline_int3:\n"
- "int3\n"
-);
-
-static int __used exec_handler(void)
-{
- struct kp_core *p = kp_core_running();
- struct pt_regs *regs = ¤t_regs_td()->regs;
-
- return p->handlers.pre(p, regs);
-}
-
-static int after_exec_trampoline(struct pt_regs *regs)
-{
- int ret = (int)regs->ax;
- struct kp_core *p = kp_core_running();
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- /*
- * Restore regs from stack.
- * Don't restore SP and SS registers because they are invalid (- 8)
- */
- memcpy(regs, ¤t_regs_td()->regs, sizeof(*regs) - 8);
-
- if (ret) {
- kp_core_put(p);
- return 1;
- }
-
- setup_singlestep(p, regs, kcb);
- if (!(regs->flags & TF_MASK))
- kp_core_put(p);
-
- return 1;
-}
-
-#define KSTAT_NOT_FOUND 0x00
-#define KSTAT_FOUND 0x01
-#define KSTAT_PREPARE_KCB 0x02
-
-static unsigned long kprobe_pre_handler(struct kp_core *p,
- struct pt_regs *regs,
- struct kp_core_ctlblk *kcb)
-{
- int ret = KSTAT_NOT_FOUND;
- unsigned long addr = regs->ip - 1;
-
- /* Check we're not actually recursing */
- if (kp_core_running()) {
- if (p) {
- if (kcb->kp_core_status == KPROBE_HIT_SS &&
- *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
- regs->flags &= ~TF_MASK;
- regs->flags |= kcb->kp_core_saved_eflags;
- goto out;
- }
-
- /* We have reentered the kprobe_handler(), since
- * another probe was hit while within the handler.
- * We here save the original kprobes variables and
- * just single step on the instruction of the new probe
- * without calling any user handlers.
- */
- save_previous_kp_core(kcb, p);
- set_current_kp_core(p, regs, kcb);
- prepare_singlestep(p, regs);
- kcb->kp_core_status = KPROBE_REENTER;
-
- ret = KSTAT_FOUND;
- goto out;
- } else {
- if (*(char *)addr != BREAKPOINT_INSTRUCTION) {
- /* The breakpoint instruction was removed by
- * another cpu right after we hit, no further
- * handling of this interrupt is appropriate
- */
- regs->EREG(ip) -= sizeof(kprobe_opcode_t);
-
- ret = KSTAT_FOUND;
- goto out;
- }
-
- goto out;
- }
- }
-
- if (!p) {
- if (*(char *)addr != BREAKPOINT_INSTRUCTION) {
- /*
- * The breakpoint instruction was removed right
- * after we hit it. Another cpu has removed
- * either a probepoint or a debugger breakpoint
- * at this address. In either case, no further
- * handling of this interrupt is appropriate.
- * Back up over the (now missing) int3 and run
- * the original instruction.
- */
- regs->EREG(ip) -= sizeof(kprobe_opcode_t);
-
- ret = KSTAT_FOUND;
- }
-
- goto out;
- }
-
- set_current_kp_core(p, regs, kcb);
- kcb->kp_core_status = KPROBE_HIT_ACTIVE;
-
- ret = KSTAT_PREPARE_KCB;
-out:
- return ret;
-}
-
-static int __kprobe_handler(struct pt_regs *regs)
-{
- int ret;
- struct kp_core *p;
- struct kp_core_ctlblk *kcb;
- unsigned long addr = regs->ip - 1;
- struct kctx *ctx = current_kctx;
-
- if (addr == sched_addr)
- switch_to_bits_set(ctx, SWITCH_TO_KP);
-
- kcb = kp_core_ctlblk();
-
- rcu_read_lock();
- p = kp_core_by_addr(addr);
- kp_core_get(p);
- rcu_read_unlock();
-
- if (able2resched(ctx)) {
- ret = kprobe_pre_handler(p, regs, kcb);
- if (ret == KSTAT_PREPARE_KCB) {
- struct regs_td *rtd = current_regs_td();
-
- /* save regs to stack */
- rtd->regs = *regs;
- rtd->sp_regs = regs;
-
- regs->ip = (unsigned long)exec_trampoline;
- return 1;
- }
-
- if (!(regs->flags & TF_MASK))
- kp_core_put(p);
- } else {
- ret = kprobe_pre_handler(p, regs, kcb);
- if (ret == KSTAT_PREPARE_KCB) {
- int rr;
-
- current_regs_td()->sp_regs = NULL;
- rr = p->handlers.pre(p, regs);
- if (rr) {
- switch_to_bits_reset(ctx, SWITCH_TO_KP);
- kp_core_put(p);
- return 1;
- }
-
- setup_singlestep(p, regs, kcb);
- }
-
- /*
- * If TF is enabled then processing instruction
- * takes place in two stages.
- */
- if (regs->flags & TF_MASK) {
- preempt_disable();
- } else {
- switch_to_bits_reset(ctx, SWITCH_TO_KP);
- kp_core_put(p);
- }
- }
-
- return !!ret;
-}
-
-static int kprobe_handler(struct pt_regs *regs)
-{
- int ret;
-
- if (regs->ip == (unsigned long)exec_trampoline_int3 + 1)
- ret = after_exec_trampoline(regs);
- else
- ret = __kprobe_handler(regs);
-
- return ret;
-}
-
-/**
- * @brief Probe pre handler.
- *
- * @param p Pointer to fired kprobe.
- * @param regs Pointer to CPU registers data.
- * @return 0.
- */
-int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
-{
- struct jprobe *jp = container_of(p, struct jprobe, kp);
- kprobe_pre_entry_handler_t pre_entry;
-
- unsigned long addr;
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- pre_entry = (kprobe_pre_entry_handler_t) jp->pre_entry;
-
- kcb->jprobe_saved_regs = *regs;
- kcb->jprobe_saved_esp = (unsigned long *)swap_kernel_sp(regs);
- addr = (unsigned long)(kcb->jprobe_saved_esp);
-
- /* TBD: As Linus pointed out, gcc assumes that the callee
- * owns the argument space and could overwrite it, e.g.
- * tailcall optimization. So, to be absolutely safe
- * we also save and restore enough stack bytes to cover
- * the argument area. */
- memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,
- MIN_STACK_SIZE(addr));
- regs->EREG(flags) &= ~IF_MASK;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
- trace_hardirqs_off();
-#endif
-
- regs->EREG(ip) = (unsigned long)(jp->entry);
-
- return 1;
-}
-
-/**
- * @brief Jprobe return end.
- *
- * @return Void.
- */
-void swap_jprobe_return_end(void);
-
-/**
- * @brief Jprobe return code.
- *
- * @return Void.
- */
-void swap_jprobe_return(void)
-{
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- asm volatile(" xchgl %%ebx,%%esp\n"
- " int3\n"
- " .globl swap_jprobe_return_end\n"
- " swap_jprobe_return_end:\n"
- " nop\n"
- : : "b" (kcb->jprobe_saved_esp) : "memory");
-}
-EXPORT_SYMBOL_GPL(swap_jprobe_return);
-
-void arch_ujprobe_return(void)
-{
-}
-
-/*
- * Called after single-stepping. p->addr is the address of the
- * instruction whose first byte has been replaced by the "int 3"
- * instruction. To avoid the SMP problems that can occur when we
- * temporarily put back the original opcode to single-step, we
- * single-stepped a copy of the instruction. The address of this
- * copy is p->ainsn.insn.
- *
- * This function prepares to return from the post-single-step
- * interrupt. We have to fix up the stack as follows:
- *
- * 0) Except in the case of absolute or indirect jump or call instructions,
- * the new eip is relative to the copied instruction. We need to make
- * it relative to the original instruction.
- *
- * 1) If the single-stepped instruction was pushfl, then the TF and IF
- * flags are set in the just-pushed eflags, and may need to be cleared.
- *
- * 2) If the single-stepped instruction was a call, the return address
- * that is atop the stack is the address following the copied instruction.
- * We need to make it the address following the original instruction.
- *
- * This function also checks instruction size for preparing direct execution.
- */
-static void resume_execution(struct kp_core *p,
- struct pt_regs *regs,
- struct kp_core_ctlblk *kcb)
-{
- unsigned long *tos;
- unsigned long copy_eip = (unsigned long) p->ainsn.insn;
- unsigned long orig_eip = (unsigned long) p->addr;
- kprobe_opcode_t insns[2];
-
- regs->EREG(flags) &= ~TF_MASK;
-
- tos = (unsigned long *)swap_kernel_sp(regs);
- insns[0] = p->ainsn.insn[0];
- insns[1] = p->ainsn.insn[1];
-
- switch (insns[0]) {
- case 0x9c: /* pushfl */
- *tos &= ~(TF_MASK | IF_MASK);
- *tos |= kcb->kp_core_old_eflags;
- break;
- case 0xc2: /* iret/ret/lret */
- case 0xc3:
- case 0xca:
- case 0xcb:
- case 0xcf:
- case 0xea: /* jmp absolute -- eip is correct */
- /* eip is already adjusted, no more changes required */
- p->ainsn.boostable = 1;
- goto no_change;
- case 0xe8: /* call relative - Fix return addr */
- *tos = orig_eip + (*tos - copy_eip);
- break;
- case 0x9a: /* call absolute -- same as call absolute, indirect */
- *tos = orig_eip + (*tos - copy_eip);
- goto no_change;
- case 0xff:
- if ((insns[1] & 0x30) == 0x10) {
- /*
- * call absolute, indirect
- * Fix return addr; eip is correct.
- * But this is not boostable
- */
- *tos = orig_eip + (*tos - copy_eip);
- goto no_change;
- } else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute
- * indirect */
- ((insns[1] & 0x31) == 0x21)) {
- /* jmp far, absolute indirect */
- /* eip is correct. And this is boostable */
- p->ainsn.boostable = 1;
- goto no_change;
- }
- default:
- break;
- }
-
- if (p->ainsn.boostable == 0) {
- if ((regs->EREG(ip) > copy_eip) &&
- (regs->EREG(ip) - copy_eip) + 5 < MAX_INSN_SIZE) {
- /*
- * These instructions can be executed directly if it
- * jumps back to correct address.
- */
- set_jmp_op((void *)regs->EREG(ip),
- (void *)orig_eip +
- (regs->EREG(ip) - copy_eip));
- p->ainsn.boostable = 1;
- } else {
- p->ainsn.boostable = -1;
- }
- }
-
- regs->EREG(ip) = orig_eip + (regs->EREG(ip) - copy_eip);
-
-no_change:
- return;
-}
-
-/*
- * Interrupts are disabled on entry as trap1 is an interrupt gate and they
- * remain disabled thoroughout this function.
- */
-static int post_kprobe_handler(struct pt_regs *regs)
-{
- struct kctx *ctx = current_kctx;
- struct kp_core *cur = kp_core_running();
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- if (!cur)
- return 0;
-
- resume_execution(cur, regs, kcb);
- regs->flags |= kcb->kp_core_saved_eflags;
-#ifndef CONFIG_X86
- trace_hardirqs_fixup_flags(regs->EREG(flags));
-#endif /* CONFIG_X86 */
- /* Restore back the original saved kprobes variables and continue. */
- if (kcb->kp_core_status == KPROBE_REENTER) {
- restore_previous_kp_core(kcb);
- goto out;
- }
- kp_core_running_set(NULL);
-
-out:
- if (!able2resched(ctx))
- swap_preempt_enable_no_resched();
-
- switch_to_bits_reset(ctx, SWITCH_TO_KP);
- kp_core_put(cur);
-
- /*
- * if somebody else is singlestepping across a probe point, eflags
- * will have TF set, in which case, continue the remaining processing
- * of do_debug, as if this is not a probe hit.
- */
- if (regs->EREG(flags) & TF_MASK)
- return 0;
-
- return 1;
-}
-
-static int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
-{
- struct kp_core *cur = kp_core_running();
- struct kp_core_ctlblk *kcb = kp_core_ctlblk();
-
- switch (kcb->kp_core_status) {
- case KPROBE_HIT_SS:
- case KPROBE_REENTER:
- /*
- * We are here because the instruction being single
- * stepped caused a page fault. We reset the current
- * kprobe and the eip points back to the probe address
- * and allow the page fault handler to continue as a
- * normal page fault.
- */
- regs->ip = cur->addr;
- regs->flags |= kcb->kp_core_old_eflags;
- if (kcb->kp_core_status == KPROBE_REENTER)
- restore_previous_kp_core(kcb);
- else
- kp_core_running_set(NULL);
- break;
- case KPROBE_HIT_ACTIVE:
- case KPROBE_HIT_SSDONE:
- /*
- * In case the user-specified fault handler returned
- * zero, try to fix up.
- */
- if (swap_fixup_exception(regs))
- return 1;
-
- /*
- * fixup_exception() could not handle it,
- * Let do_page_fault() fix it.
- */
- break;
- default:
- break;
- }
- return 0;
-}
-
-static int kprobe_exceptions_notify(struct notifier_block *self,
- unsigned long val, void *data)
-{
- struct die_args *args = (struct die_args *) data;
- int ret = NOTIFY_DONE;
-
- DBPRINTF("val = %ld, data = 0x%X", val, (unsigned int) data);
-
- if (args->regs == NULL || swap_user_mode(args->regs))
- return ret;
-
- DBPRINTF("switch (val) %lu %d %d", val, DIE_INT3, DIE_TRAP);
- switch (val) {
-#ifdef CONFIG_KPROBES
- case DIE_INT3:
-#else
- case DIE_TRAP:
-#endif
- DBPRINTF("before kprobe_handler ret=%d %p",
- ret, args->regs);
- if (kprobe_handler (args->regs))
- ret = NOTIFY_STOP;
- DBPRINTF("after kprobe_handler ret=%d %p",
- ret, args->regs);
- break;
- case DIE_DEBUG:
- if (post_kprobe_handler(args->regs))
- ret = NOTIFY_STOP;
- break;
- case DIE_GPF:
- if (kp_core_running() &&
- kprobe_fault_handler(args->regs, args->trapnr))
- ret = NOTIFY_STOP;
- break;
- default:
- break;
- }
- DBPRINTF("ret=%d", ret);
- /* if(ret == NOTIFY_STOP) */
- /* handled_exceptions++; */
-
- return ret;
-}
-
-static struct notifier_block kprobe_exceptions_nb = {
- .notifier_call = kprobe_exceptions_notify,
- .priority = INT_MAX
-};
-
-/**
- * @brief Arms kp_core.
- *
- * @param core Pointer to target kp_core.
- * @return Void.
- */
-void arch_kp_core_arm(struct kp_core *p)
-{
- swap_text_poke((void *)p->addr,
- ((unsigned char[]){BREAKPOINT_INSTRUCTION}), 1);
-}
-
-/**
- * @brief Disarms kp_core.
- *
- * @param core Pointer to target kp_core.
- * @return Void.
- */
-void arch_kp_core_disarm(struct kp_core *p)
-{
- swap_text_poke((void *)p->addr, &p->opcode, 1);
-}
-
-/**
- * @brief Prepares kretprobes, saves ret address, makes function return to
- * trampoline.
- *
- * @param ri Pointer to kretprobe_instance.
- * @param regs Pointer to CPU registers data.
- * @return Void.
- */
-void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- unsigned long *ptr_ret_addr = (unsigned long *)swap_kernel_sp(regs);
-
- /* for __switch_to probe */
- if ((unsigned long)ri->rp->kp.addr == sched_addr) {
- struct task_struct *next = (struct task_struct *)swap_get_karg(regs, 1);
- ri->sp = NULL;
- ri->task = next;
- switch_to_bits_set(kctx_by_task(next), SWITCH_TO_RP);
- } else {
- ri->sp = ptr_ret_addr;
- }
-
- /* Save the return address */
- ri->ret_addr = (unsigned long *)*ptr_ret_addr;
-
- /* Replace the return addr with trampoline addr */
- *ptr_ret_addr = (unsigned long)&swap_kretprobe_trampoline;
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * jumper *
- ******************************************************************************
- */
-struct cb_data {
- unsigned long ret_addr;
- unsigned long bx;
-
- jumper_cb_t cb;
- char data[0];
-};
-
-static unsigned long __used get_bx(struct cb_data *data)
-{
- return data->bx;
-}
-
-static unsigned long __used jump_handler(struct cb_data *data)
-{
- unsigned long ret_addr = data->ret_addr;
-
- /* call callback */
- data->cb(data->data);
-
- /* FIXME: potential memory leak, when process kill */
- kfree(data);
-
- return ret_addr;
-}
-
-void jump_trampoline(void);
-__asm(
- "jump_trampoline:\n"
- "pushf\n"
- SWAP_SAVE_REGS_STRING
- "movl %ebx, %eax\n" /* data --> ax */
- "call get_bx\n"
- "movl %eax, (%esp)\n" /* restore bx */
- "movl %ebx, %eax\n" /* data --> ax */
- "call jump_handler\n"
- /* move flags to cs */
- "movl 56(%esp), %edx\n"
- "movl %edx, 52(%esp)\n"
- /* replace saved flags with true return address. */
- "movl %eax, 56(%esp)\n"
- SWAP_RESTORE_REGS_STRING
- "popf\n"
- "ret\n"
-);
-
-unsigned long get_jump_addr(void)
-{
- return (unsigned long)&jump_trampoline;
-}
-EXPORT_SYMBOL_GPL(get_jump_addr);
-
-int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
- jumper_cb_t cb, void *data, size_t size)
-{
- struct cb_data *cb_data;
-
- cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
- if (cb_data == NULL)
- return -ENOMEM;
-
- /* save data */
- if (size)
- memcpy(cb_data->data, data, size);
-
- /* save info for restore */
- cb_data->ret_addr = ret_addr;
- cb_data->cb = cb;
- cb_data->bx = regs->bx;
-
- /* save cb_data to bx */
- regs->bx = (long)cb_data;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(set_jump_cb);
-
-
-
-
-
-/**
- * @brief Initializes x86 module deps.
- *
- * @return 0 on success, negative error code on error.
- */
-int arch_init_module_deps()
-{
- const char *sym;
-
- sym = "fixup_exception";
- swap_fixup_exception = (void *)swap_ksyms(sym);
- if (swap_fixup_exception == NULL)
- goto not_found;
-
- sym = "text_poke";
- swap_text_poke = (void *)swap_ksyms(sym);
- if (swap_text_poke == NULL)
- goto not_found;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
- sym = "show_registers";
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */
- sym = "show_regs";
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */
- swap_show_registers = (void *)swap_ksyms(sym);
- if (swap_show_registers == NULL)
- goto not_found;
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol %s(...) not found\n", sym);
- return -ESRCH;
-}
-
-/**
- * @brief Initializes kprobes module for ARM arch.
- *
- * @return 0 on success, error code on error.
- */
-int swap_arch_init_kprobes(void)
-{
- int ret;
-
- ret = swap_td_raw_reg(&kp_tdraw, sizeof(struct regs_td));
- if (ret)
- return ret;
-
- ret = register_die_notifier(&kprobe_exceptions_nb);
- if (ret)
- swap_td_raw_unreg(&kp_tdraw);
-
- return ret;
-}
-
-/**
- * @brief Uninitializes kprobe module.
- *
- * @return Void.
- */
-void swap_arch_exit_kprobes(void)
-{
- unregister_die_notifier(&kprobe_exceptions_nb);
- swap_td_raw_unreg(&kp_tdraw);
-}
+++ /dev/null
-/**
- * @file kprobe/arch/asm-x86/swap_kprobes.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) IBM Corporation, 2002, 2004
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Arch-dependent kprobes interface for x86 arch.
- */
-
-#ifndef _SWAP_ASM_X86_KPROBES_H
-#define _SWAP_ASM_X86_KPROBES_H
-
-
-#include <linux/version.h>
-#include <kprobe/swap_kprobes_deps.h>
-
-/**
- * @brief Opcode type.
- */
-typedef u8 kprobe_opcode_t;
-
-#define BREAKPOINT_INSTRUCTION 0xcc
-#define RELATIVEJUMP_INSTRUCTION 0xe9
-
-#define BP_INSN_SIZE 1
-#define MAX_INSN_SIZE 16
-#define MAX_STACK_SIZE 64
-
-#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
- (((unsigned long)current_thread_info()) \
- + THREAD_SIZE - (ADDR))) \
- ? (MAX_STACK_SIZE) \
- : (((unsigned long)current_thread_info()) \
- + THREAD_SIZE - (ADDR)))
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-
-#define EREG(rg) e##rg
-#define XREG(rg) x##rg
-#define ORIG_EAX_REG orig_eax
-
-#else
-
-#define EREG(rg) rg
-#define XREG(rg) rg
-#define ORIG_EAX_REG orig_ax
-
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
-#define TF_MASK X86_EFLAGS_TF
-#define IF_MASK X86_EFLAGS_IF
-#endif
-#define UPROBES_TRAMP_LEN (MAX_INSN_SIZE+sizeof(kprobe_opcode_t))
-#define UPROBES_TRAMP_INSN_IDX 0
-#define UPROBES_TRAMP_RET_BREAK_IDX MAX_INSN_SIZE
-#define KPROBES_TRAMP_LEN MAX_INSN_SIZE
-#define KPROBES_TRAMP_INSN_IDX 0
-
-static inline int swap_user_mode(struct pt_regs *regs)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
- return user_mode(regs);
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
- return user_mode_vm(regs);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
-}
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
-#define swap_in_interrupt() (in_interrupt() & ~HARDIRQ_OFFSET)
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
-
-static inline unsigned long arch_get_task_pc(struct task_struct *p)
-{
- /* FIXME: Not implemented yet */
- return 0;
-}
-
-static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
-{
- /* FIXME: Not implemented yet */
-}
-
-static inline struct pt_regs *swap_get_syscall_uregs(unsigned long sp)
-{
- return NULL; /* FIXME currently not implemented for x86 */
-}
-
-static inline unsigned long swap_get_stack_ptr(struct pt_regs *regs)
-{
- return regs->EREG(sp);
-}
-
-static inline void swap_set_stack_ptr(struct pt_regs *regs, unsigned long sp)
-{
- regs->EREG(sp) = sp;
-}
-
-static inline unsigned long swap_get_kpc(struct pt_regs *regs)
-{
- return regs->ip;
-}
-
-static inline void swap_set_kpc(struct pt_regs *regs, unsigned long val)
-{
- regs->ip = val;
-}
-
-static inline unsigned long swap_get_arg(struct pt_regs *regs, int num)
-{
- unsigned long arg = 0;
- read_proc_vm_atomic(current, regs->EREG(sp) + (1 + num) * 4,
- &arg, sizeof(arg));
- return arg;
-}
-
-static inline void swap_set_arg(struct pt_regs *regs, int num,
- unsigned long val)
-{
- write_proc_vm_atomic(current, regs->EREG(sp) + (1 + num) * 4,
- &val, sizeof(val));
-}
-
-/**
- * @struct prev_kp_core
- * @brief Stores previous kp_core.
- * @var prev_kp_core::kp
- * Pointer to kp_core struct.
- * @var prev_kp_core::status
- * kp_core status.
- */
-struct prev_kp_core {
- struct kp_core *p;
- unsigned long status;
-};
-
-/**
- * @struct kp_core_ctlblk
- * @brief Per-cpu kp_core control block.
- * @var kp_core_ctlblk::kp_core_status
- * kp_core status.
- * @var kp_core_ctlblk::prev_kp_core
- * Previous kp_core.
- */
-struct kp_core_ctlblk {
- unsigned long kp_core_status;
- struct prev_kp_core prev_kp_core;
- struct pt_regs jprobe_saved_regs;
- unsigned long kp_core_old_eflags;
- unsigned long kp_core_saved_eflags;
- unsigned long *jprobe_saved_esp;
- kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
-};
-
-
-/**
- * @struct swap_arch_specific_insn
- * @brief Architecture specific copy of original instruction.
- * @var swap_arch_specific_insn::insn
- * Copy of the original instruction.
- * @var swap_arch_specific_insn::boostable
- * If this flag is not 0, this kp_core can be boost when its
- * post_handler and break_handler is not set.
- */
-struct swap_arch_specific_insn {
- kprobe_opcode_t *insn;
- int boostable;
-};
-
-/**
- * @brief Entry point.
- */
-typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long,
- unsigned long, unsigned long,
- unsigned long, unsigned long);
-
-int arch_init_module_deps(void);
-
-struct kprobe;
-struct kp_core;
-struct slot_manager;
-struct kretprobe_instance;
-
-int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm);
-void arch_kp_core_arm(struct kp_core *core);
-void arch_kp_core_disarm(struct kp_core *core);
-int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs);
-void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
- struct pt_regs *regs);
-void swap_kretprobe_trampoline(void);
-
-void restore_previous_kp_core(struct kp_core_ctlblk *kcb);
-int swap_can_boost(kprobe_opcode_t *opcodes);
-static inline int arch_check_insn(struct swap_arch_specific_insn *ainsn)
-{
- return 0;
-}
-
-unsigned long swap_kernel_sp(struct pt_regs *regs);
-
-static inline unsigned long swap_get_karg(struct pt_regs *regs, unsigned long n)
-{
- switch (n) {
- case 0:
- return regs->ax;
- case 1:
- return regs->dx;
- case 2:
- return regs->cx;
- }
-
- /*
- * 2 = 3 - 1
- * 3 - arguments from registers
- * 1 - return address saved on top of the stack
- */
- return *((unsigned long *)swap_kernel_sp(regs) + n - 2);
-}
-
-static inline unsigned long swap_get_sarg(struct pt_regs *regs, unsigned long n)
-{
- /* 1 - return address saved on top of the stack */
- return *((unsigned long *)kernel_stack_pointer(regs) + n + 1);
-}
-
-/* jumper */
-typedef unsigned long (*jumper_cb_t)(void *);
-
-unsigned long get_jump_addr(void);
-int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
- jumper_cb_t cb, void *data, size_t size);
-
-int swap_arch_init_kprobes(void);
-void swap_arch_exit_kprobes(void);
-
-#endif /* _SWAP_ASM_X86_KPROBES_H */
+++ /dev/null
-/**
- * @file kprobe/swap_kdebug.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Header for debug purposes.
- */
-
-
-#ifndef _SWAP_KPROBE_DEBUG_H
-#define _SWAP_KPROBE_DEBUG_H
-
-/* #define _DEBUG */
-
-#ifdef _DEBUG
-#define DBPRINTF(format, args...) do { \
- if (1) { \
- char *f = __FILE__; \
- char *n = strrchr(f, '/'); \
- printk(KERN_INFO "%s : %u : %s : " format "\n" , \
- (n) ? n+1 : f, __LINE__, __func__, ##args); \
- } \
- } while (0)
-#else
-#define DBPRINTF(format, args...)
-#endif
-
-
-#endif /* _SWAP_KPROBE_DEBUG_H */
+++ /dev/null
-/**
- * kprobe/swap_kprobes.c
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM and MIPS
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) IBM Corporation, 2002, 2004
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * SWAP kprobe implementation. Dynamic kernel functions instrumentation.
- */
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-#include <linux/config.h>
-#endif
-
-#include <linux/hash.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/stop_machine.h>
-#include <linux/delay.h>
-#include <ksyms/ksyms.h>
-#include <master/swap_initializer.h>
-#include <swap-asm/swap_kprobes.h>
-#include "swap_ktd.h"
-#include "swap_slots.h"
-#include "swap_ktd.h"
-#include "swap_td_raw.h"
-#include "swap_kdebug.h"
-#include "swap_kprobes.h"
-#include "swap_kprobes_deps.h"
-
-
-#define KRETPROBE_STACK_DEPTH 64
-
-
-/**
- * @var sched_addr
- * @brief Scheduler address.
- */
-unsigned long sched_addr;
-static unsigned long exit_addr;
-static unsigned long do_group_exit_addr;
-static unsigned long sys_exit_group_addr;
-static unsigned long sys_exit_addr;
-
-/**
- * @var sm
- * @brief Current slot manager. Slots are the places where trampolines are
- * located.
- */
-struct slot_manager sm;
-
-static DEFINE_SPINLOCK(kretprobe_lock); /* Protects kretprobe_inst_table */
-
-static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
-static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
-
-/**
- * @var kprobe_count
- * @brief Count of kprobes.
- */
-atomic_t kprobe_count;
-EXPORT_SYMBOL_GPL(kprobe_count);
-
-
-static void *(*__module_alloc)(unsigned long size);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
-static void (*__module_free)(void *module_region);
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
-static void (*__module_free)(struct module *mod, void *module_region);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
-
-static void *wrapper_module_alloc(unsigned long size)
-{
- return __module_alloc(size);
-}
-
-static void wrapper_module_free(void *module_region)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
- __module_free(module_region);
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
- __module_free(NULL, module_region);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
-}
-
-static void *sm_alloc(struct slot_manager *sm)
-{
- return wrapper_module_alloc(PAGE_SIZE);
-}
-
-static void sm_free(struct slot_manager *sm, void *ptr)
-{
- wrapper_module_free(ptr);
-}
-
-static void init_sm(void)
-{
- sm.slot_size = KPROBES_TRAMP_LEN;
- sm.alloc = sm_alloc;
- sm.free = sm_free;
- INIT_HLIST_HEAD(&sm.page_list);
-}
-
-static void exit_sm(void)
-{
- /* FIXME: free */
-}
-
-static struct hlist_head *kpt_head_by_addr(unsigned long addr)
-{
- return &kprobe_table[hash_ptr((void *)addr, KPROBE_HASH_BITS)];
-}
-
-static void kretprobe_assert(struct kretprobe_instance *ri,
- unsigned long orig_ret_address,
- unsigned long trampoline_address)
-{
- if (!orig_ret_address || (orig_ret_address == trampoline_address)) {
- struct task_struct *task;
- if (ri == NULL)
- panic("kretprobe BUG!: ri = NULL\n");
-
- task = ri->task;
-
- if (task == NULL)
- panic("kretprobe BUG!: task = NULL\n");
-
- if (ri->rp == NULL)
- panic("kretprobe BUG!: ri->rp = NULL\n");
-
- panic("kretprobe BUG!: "
- "Processing kretprobe %p @ %08lx (%d/%d - %s)\n",
- ri->rp, ri->rp->kp.addr, ri->task->tgid,
- ri->task->pid, ri->task->comm);
- }
-}
-
-struct kpc_data {
- struct kp_core *running;
- struct kp_core_ctlblk ctlblk;
-};
-
-struct kctx {
- struct kpc_data kpc;
- unsigned long st_flags;
-};
-
-static void ktd_cur_init(struct task_struct *task, void *data)
-{
- struct kctx *ctx = (struct kctx *)data;
-
- memset(ctx, 0, sizeof(*ctx));
-}
-
-static void ktd_cur_exit(struct task_struct *task, void *data)
-{
- struct kctx *ctx = (struct kctx *)data;
-
- WARN(ctx->kpc.running, "running=%p\n", ctx->kpc.running);
-}
-
-struct ktask_data ktd_cur = {
- .init = ktd_cur_init,
- .exit = ktd_cur_exit,
- .size = sizeof(struct kctx),
-};
-
-struct kctx *kctx_by_task(struct task_struct *task)
-{
- return (struct kctx *)swap_ktd(&ktd_cur, task);
-}
-
-void switch_to_bits_set(struct kctx *ctx, unsigned long mask)
-{
- ctx->st_flags |= mask;
-}
-
-void switch_to_bits_reset(struct kctx *ctx, unsigned long mask)
-{
- ctx->st_flags &= ~mask;
-}
-
-unsigned long switch_to_bits_get(struct kctx *ctx, unsigned long mask)
-{
- return ctx->st_flags & mask;
-}
-
-static DEFINE_PER_CPU(struct kpc_data, per_cpu_kpc_data_i);
-static DEFINE_PER_CPU(struct kpc_data, per_cpu_kpc_data_st);
-
-static struct kpc_data *kp_core_data(void)
-{
- struct kctx *ctx = current_kctx;
-
- if (swap_in_interrupt())
- return &__get_cpu_var(per_cpu_kpc_data_i);
- else if (switch_to_bits_get(ctx, SWITCH_TO_ALL))
- return &__get_cpu_var(per_cpu_kpc_data_st);
-
- return &ctx->kpc;
-}
-
-static int kprobe_cur_reg(void)
-{
- return swap_ktd_reg(&ktd_cur);
-}
-
-static void kprobe_cur_unreg(void)
-{
- swap_ktd_unreg(&ktd_cur);
-}
-
-struct kp_core *kp_core_running(void)
-{
- return kp_core_data()->running;
-}
-
-void kp_core_running_set(struct kp_core *p)
-{
- kp_core_data()->running = p;
-}
-
-/**
- * @brief Gets kp_core_ctlblk for the current CPU.
- *
- * @return Current CPU struct kp_core_ctlblk.
- */
-struct kp_core_ctlblk *kp_core_ctlblk(void)
-{
- return &kp_core_data()->ctlblk;
-}
-
-/*
- * This routine is called either:
- * - under the kprobe_mutex - during kprobe_[un]register()
- * OR
- * - with preemption disabled - from arch/xxx/kernel/kprobes.c
- */
-
-/**
- * @brief Gets kp_core.
- *
- * @param addr Probe address.
- * @return kprobe_core for addr.
- */
-struct kp_core *kp_core_by_addr(unsigned long addr)
-{
- struct hlist_head *head;
- struct kp_core *core;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- head = kpt_head_by_addr(addr);
- swap_hlist_for_each_entry_rcu(core, node, head, hlist) {
- if (core->addr == addr)
- return core;
- }
-
- return NULL;
-}
-
-
-static int alloc_nodes_kretprobe(struct kretprobe *rp);
-
-/* Called with kretprobe_lock held */
-static struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp)
-{
- struct kretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
- return ri;
- }
-
- if (!alloc_nodes_kretprobe(rp)) {
- swap_hlist_for_each_entry(ri, node, &rp->free_instances,
- uflist) {
- return ri;
- }
- }
-
- return NULL;
-}
-
-/* Called with kretprobe_lock held */
-static struct kretprobe_instance *
-get_free_rp_inst_no_alloc(struct kretprobe *rp)
-{
- struct kretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
- return ri;
- }
-
- return NULL;
-}
-
-/* Called with kretprobe_lock held */
-static struct kretprobe_instance *get_used_rp_inst(struct kretprobe *rp)
-{
- struct kretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->used_instances, uflist) {
- return ri;
- }
-
- return NULL;
-}
-
-/* Called with kretprobe_lock held */
-static void add_rp_inst(struct kretprobe_instance *ri)
-{
- /*
- * Remove rp inst off the free list -
- * Add it back when probed function returns
- */
- hlist_del(&ri->uflist);
-
- /* Add rp inst onto table */
- INIT_HLIST_NODE(&ri->hlist);
-
- hlist_add_head(&ri->hlist,
- &kretprobe_inst_table[hash_ptr(ri->task,
- KPROBE_HASH_BITS)]);
-
- /* Also add this rp inst to the used list. */
- INIT_HLIST_NODE(&ri->uflist);
- hlist_add_head(&ri->uflist, &ri->rp->used_instances);
-}
-
-/* Called with kretprobe_lock held */
-static void recycle_rp_inst(struct kretprobe_instance *ri)
-{
- if (ri->rp) {
- hlist_del(&ri->hlist);
- /* remove rp inst off the used list */
- hlist_del(&ri->uflist);
- /* put rp inst back onto the free list */
- INIT_HLIST_NODE(&ri->uflist);
- hlist_add_head(&ri->uflist, &ri->rp->free_instances);
- }
-}
-
-static struct hlist_head *kretprobe_inst_table_head(void *hash_key)
-{
- return &kretprobe_inst_table[hash_ptr(hash_key, KPROBE_HASH_BITS)];
-}
-
-static void free_rp_inst(struct kretprobe *rp)
-{
- struct kretprobe_instance *ri;
- while ((ri = get_free_rp_inst_no_alloc(rp)) != NULL) {
- hlist_del(&ri->uflist);
- kfree(ri);
- }
-}
-
-static void kp_core_remove(struct kp_core *core)
-{
- /* TODO: check boostable for x86 and MIPS */
- swap_slot_free(&sm, core->ainsn.insn);
-}
-
-static void kp_core_wait(struct kp_core *p)
-{
- int ms = 1;
-
- while (atomic_read(&p->usage)) {
- msleep(ms);
- ms += ms < 7 ? 1 : 0;
- }
-}
-
-static struct kp_core *kp_core_create(unsigned long addr)
-{
- struct kp_core *core;
-
- core = kzalloc(sizeof(*core), GFP_KERNEL);
- if (core) {
- INIT_HLIST_NODE(&core->hlist);
- core->addr = addr;
- atomic_set(&core->usage, 0);
- rwlock_init(&core->handlers.lock);
- }
-
- return core;
-}
-
-static void kp_core_free(struct kp_core *core)
-{
- WARN_ON(atomic_read(&core->usage));
- kfree(core);
-}
-
-static int pre_handler_one(struct kp_core *core, struct pt_regs *regs)
-{
- int ret = 0;
- struct kprobe *p = core->handlers.kps[0];
-
- if (p && p->pre_handler)
- ret = p->pre_handler(p, regs);
-
- return ret;
-}
-
-static int pre_handler_multi(struct kp_core *core, struct pt_regs *regs)
-{
- int i, ret = 0;
-
- /* TODO: add sync use kprobe */
- for (i = 0; i < ARRAY_SIZE(core->handlers.kps); ++i) {
- struct kprobe *p = core->handlers.kps[i];
-
- if (p && p->pre_handler) {
- ret = p->pre_handler(p, regs);
- if (ret)
- break;
- }
- }
-
- return ret;
-}
-
-static int kp_core_add_kprobe(struct kp_core *core, struct kprobe *p)
-{
- int i, ret = 0;
- unsigned long flags;
- struct kp_handlers *h = &core->handlers;
-
- write_lock_irqsave(&h->lock, flags);
- if (h->pre == NULL) {
- h->pre = pre_handler_one;
- } else if (h->pre == pre_handler_one) {
- h->pre = pre_handler_multi;
- }
-
- for (i = 0; i < ARRAY_SIZE(core->handlers.kps); ++i) {
- if (core->handlers.kps[i])
- continue;
-
- core->handlers.kps[i] = p;
- goto unlock;
- }
-
- pr_err("all kps slots is busy\n");
- ret = -EBUSY;
-unlock:
- write_unlock_irqrestore(&h->lock, flags);
- return ret;
-}
-
-static void kp_core_del_kprobe(struct kp_core *core, struct kprobe *p)
-{
- int i, cnt = 0;
- unsigned long flags;
- struct kp_handlers *h = &core->handlers;
-
- write_lock_irqsave(&h->lock, flags);
- for (i = 0; i < ARRAY_SIZE(h->kps); ++i) {
- if (h->kps[i] == p)
- h->kps[i] = NULL;
-
- if (h->kps[i] == NULL)
- ++cnt;
- }
- write_unlock_irqrestore(&h->lock, flags);
-
- if (cnt == ARRAY_SIZE(h->kps)) {
- arch_kp_core_disarm(core);
- synchronize_sched();
-
- hlist_del_rcu(&core->hlist);
- synchronize_rcu();
-
- kp_core_wait(core);
- kp_core_remove(core);
- kp_core_free(core);
- }
-}
-
-static DEFINE_MUTEX(kp_mtx);
-/**
- * @brief Registers kprobe.
- *
- * @param p Pointer to the target kprobe.
- * @return 0 on success, error code on error.
- */
-int swap_register_kprobe(struct kprobe *p)
-{
- struct kp_core *core;
- unsigned long addr;
- int ret = 0;
- /*
- * If we have a symbol_name argument look it up,
- * and add it to the address. That way the addr
- * field can either be global or relative to a symbol.
- */
- if (p->symbol_name) {
- if (p->addr)
- return -EINVAL;
- p->addr = swap_ksyms(p->symbol_name);
- }
-
- if (!p->addr)
- return -EINVAL;
-
- addr = p->addr + p->offset;
-
- mutex_lock(&kp_mtx);
- core = kp_core_by_addr(addr);
- if (core == NULL) {
- core = kp_core_create(addr);
- if (core == NULL) {
- pr_err("Out of memory\n");
- ret = -ENOMEM;
- goto unlock;
- }
-
- ret = arch_kp_core_prepare(core, &sm);
- if (ret) {
- kp_core_free(core);
- goto unlock;
- }
-
- ret = kp_core_add_kprobe(core, p);
- if (ret) {
- kp_core_free(core);
- goto unlock;
- }
-
- hlist_add_head_rcu(&core->hlist, kpt_head_by_addr(core->addr));
- arch_kp_core_arm(core);
- } else {
- ret = kp_core_add_kprobe(core, p);
- }
-
-unlock:
- mutex_unlock(&kp_mtx);
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_register_kprobe);
-
-/**
- * @brief Unregistes kprobe.
- *
- * @param kp Pointer to the target kprobe.
- * @return Void.
- */
-void swap_unregister_kprobe(struct kprobe *p)
-{
- unsigned long addr = p->addr + p->offset;
- struct kp_core *core;
-
- mutex_lock(&kp_mtx);
- core = kp_core_by_addr(addr);
- BUG_ON(core == NULL);
-
- kp_core_del_kprobe(core, p);
- mutex_unlock(&kp_mtx);
-
- /* Set 0 addr for reusability if symbol_name is used */
- if (p->symbol_name)
- p->addr = 0;
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kprobe);
-
-/**
- * @brief Registers jprobe.
- *
- * @param jp Pointer to the target jprobe.
- * @return swap_register_kprobe result.
- */
-int swap_register_jprobe(struct jprobe *jp)
-{
- /* Todo: Verify probepoint is a function entry point */
- jp->kp.pre_handler = swap_setjmp_pre_handler;
-
- return swap_register_kprobe(&jp->kp);
-}
-EXPORT_SYMBOL_GPL(swap_register_jprobe);
-
-/**
- * @brief Unregisters jprobe.
- *
- * @param jp Pointer to the target jprobe.
- * @return Void.
- */
-void swap_unregister_jprobe(struct jprobe *jp)
-{
- swap_unregister_kprobe(&jp->kp);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_jprobe);
-
-/*
- * This kprobe pre_handler is registered with every kretprobe. When probe
- * hits it will set up the return probe.
- */
-static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
-{
- struct kretprobe *rp = container_of(p, struct kretprobe, kp);
- struct kretprobe_instance *ri;
- unsigned long flags = 0;
-
- /* TODO: consider to only swap the RA
- * after the last pre_handler fired */
- spin_lock_irqsave(&kretprobe_lock, flags);
-
- /* TODO: test - remove retprobe after func entry but before its exit */
- ri = get_free_rp_inst(rp);
- if (ri != NULL) {
- int skip = 0;
-
- ri->rp = rp;
- ri->task = current;
-
- if (rp->entry_handler)
- skip = rp->entry_handler(ri, regs);
-
- if (skip) {
- add_rp_inst(ri);
- recycle_rp_inst(ri);
- } else {
- swap_arch_prepare_kretprobe(ri, regs);
- add_rp_inst(ri);
- }
- } else {
- ++rp->nmissed;
- }
-
- spin_unlock_irqrestore(&kretprobe_lock, flags);
-
- return 0;
-}
-
-/**
- * @brief Trampoline probe handler.
- *
- * @param p Pointer to the fired kprobe.
- * @param regs Pointer to CPU registers data.
- * @return orig_ret_address
- */
-unsigned long swap_trampoline_handler(struct pt_regs *regs)
-{
- struct kretprobe_instance *ri = NULL;
- struct hlist_head *head;
- unsigned long flags, orig_ret_address = 0;
- unsigned long trampoline_address;
-
- struct kp_core_ctlblk *kcb;
-
- struct hlist_node *tmp;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- trampoline_address = (unsigned long)&swap_kretprobe_trampoline;
-
- kcb = kp_core_ctlblk();
-
- spin_lock_irqsave(&kretprobe_lock, flags);
-
- /*
- * We are using different hash keys (current and mm) for finding kernel
- * space and user space probes. Kernel space probes can change mm field
- * in task_struct. User space probes can be shared between threads of
- * one process so they have different current but same mm.
- */
- head = kretprobe_inst_table_head(current);
-
-#ifdef CONFIG_X86
- regs->XREG(cs) = __KERNEL_CS | get_kernel_rpl();
- regs->EREG(ip) = trampoline_address;
- regs->ORIG_EAX_REG = 0xffffffff;
-#endif
-
- /*
- * It is possible to have multiple instances associated with a given
- * task either because an multiple functions in the call path
- * have a return probe installed on them, and/or more then one
- * return probe was registered for a target function.
- *
- * We can handle this because:
- * - instances are always inserted at the head of the list
- * - when multiple return probes are registered for the same
- * function, the first instance's ret_addr will point to the
- * real return address, and all the rest will point to
- * kretprobe_trampoline
- */
- swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
- if (ri->task != current)
- /* another task is sharing our hash bucket */
- continue;
- if (ri->rp && ri->rp->handler) {
- /*
- * Set fake current probe, we don't
- * want to go into recursion
- */
- kp_core_running_set((struct kp_core *)0xfffff);
- kcb->kp_core_status = KPROBE_HIT_ACTIVE;
- ri->rp->handler(ri, regs);
- kp_core_running_set(NULL);
- }
-
- orig_ret_address = (unsigned long)ri->ret_addr;
- recycle_rp_inst(ri);
- if (orig_ret_address != trampoline_address)
- /*
- * This is the real return address. Any other
- * instances associated with this task are for
- * other calls deeper on the call stack
- */
- break;
- }
- kretprobe_assert(ri, orig_ret_address, trampoline_address);
-
- if (kcb->kp_core_status == KPROBE_REENTER)
- restore_previous_kp_core(kcb);
- else
- kp_core_running_set(NULL);
-
- switch_to_bits_reset(current_kctx, SWITCH_TO_RP);
- spin_unlock_irqrestore(&kretprobe_lock, flags);
-
- /*
- * By returning a non-zero value, we are telling
- * kprobe_handler() that we don't want the post_handler
- * to run (and have re-enabled preemption)
- */
-
- return orig_ret_address;
-}
-
-#define SCHED_RP_NR 200
-#define COMMON_RP_NR 10
-
-static int alloc_nodes_kretprobe(struct kretprobe *rp)
-{
- int alloc_nodes;
- struct kretprobe_instance *inst;
- int i;
-
- DBPRINTF("Alloc aditional mem for retprobes");
-
- if (rp->kp.addr == sched_addr) {
- rp->maxactive += SCHED_RP_NR; /* max (100, 2 * NR_CPUS); */
- alloc_nodes = SCHED_RP_NR;
- } else {
-#if 1/* def CONFIG_PREEMPT */
- rp->maxactive += max(COMMON_RP_NR, 2 * NR_CPUS);
-#else
- rp->maxacpptive += NR_CPUS;
-#endif
- alloc_nodes = COMMON_RP_NR;
- }
-
- for (i = 0; i < alloc_nodes; i++) {
- inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_ATOMIC);
- if (inst == NULL) {
- free_rp_inst(rp);
- return -ENOMEM;
- }
- INIT_HLIST_NODE(&inst->uflist);
- hlist_add_head(&inst->uflist, &rp->free_instances);
- }
-
- DBPRINTF("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr,
- (unsigned long) (*(rp->kp.addr)),
- (unsigned long) (*(rp->kp.addr + 1)),
- (unsigned long) (*(rp->kp.addr + 2)));
- return 0;
-}
-
-/**
- * @brief Registers kretprobes.
- *
- * @param rp Pointer to the target kretprobe.
- * @return 0 on success, error code on error.
- */
-int swap_register_kretprobe(struct kretprobe *rp)
-{
- int ret = 0;
- struct kretprobe_instance *inst;
- int i;
- DBPRINTF("START");
-
- rp->kp.pre_handler = pre_handler_kretprobe;
-
- /* Pre-allocate memory for max kretprobe instances */
- if (rp->kp.addr == exit_addr) {
- rp->kp.pre_handler = NULL; /* not needed for do_exit */
- rp->maxactive = 0;
- } else if (rp->kp.addr == do_group_exit_addr) {
- rp->kp.pre_handler = NULL;
- rp->maxactive = 0;
- } else if (rp->kp.addr == sys_exit_group_addr) {
- rp->kp.pre_handler = NULL;
- rp->maxactive = 0;
- } else if (rp->kp.addr == sys_exit_addr) {
- rp->kp.pre_handler = NULL;
- rp->maxactive = 0;
- } else if (rp->maxactive <= 0) {
-#if 1/* def CONFIG_PREEMPT */
- rp->maxactive = max(COMMON_RP_NR, 2 * NR_CPUS);
-#else
- rp->maxactive = NR_CPUS;
-#endif
- }
- INIT_HLIST_HEAD(&rp->used_instances);
- INIT_HLIST_HEAD(&rp->free_instances);
- for (i = 0; i < rp->maxactive; i++) {
- inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_KERNEL);
- if (inst == NULL) {
- free_rp_inst(rp);
- return -ENOMEM;
- }
- INIT_HLIST_NODE(&inst->uflist);
- hlist_add_head(&inst->uflist, &rp->free_instances);
- }
-
- DBPRINTF("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr,
- (unsigned long) (*(rp->kp.addr)),
- (unsigned long) (*(rp->kp.addr + 1)),
- (unsigned long) (*(rp->kp.addr + 2)));
- rp->nmissed = 0;
- /* Establish function entry probe point */
- ret = swap_register_kprobe(&rp->kp);
- if (ret != 0)
- free_rp_inst(rp);
-
- DBPRINTF("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr,
- (unsigned long) (*(rp->kp.addr)),
- (unsigned long) (*(rp->kp.addr + 1)),
- (unsigned long) (*(rp->kp.addr + 2)));
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_register_kretprobe);
-
-static int swap_disarm_krp_inst(struct kretprobe_instance *ri);
-
-static void swap_disarm_krp(struct kretprobe *rp)
-{
- struct kretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->used_instances, uflist) {
- if (swap_disarm_krp_inst(ri) != 0) {
- printk(KERN_INFO "%s (%d/%d): cannot disarm "
- "krp instance (%08lx)\n",
- ri->task->comm, ri->task->tgid, ri->task->pid,
- rp->kp.addr);
- }
- }
-}
-
-
-struct unreg_krp_args {
- struct kretprobe **rps;
- size_t size;
- int rp_disarm;
-};
-
-static int __swap_unregister_kretprobes_top(void *data)
-{
- struct unreg_krp_args *args = data;
- struct kretprobe **rps = args->rps;
- size_t size = args->size;
- int rp_disarm = args->rp_disarm;
- unsigned long flags;
- const size_t end = ((size_t) 0) - 1;
-
- for (--size; size != end; --size) {
- if (rp_disarm) {
- spin_lock_irqsave(&kretprobe_lock, flags);
- swap_disarm_krp(rps[size]);
- spin_unlock_irqrestore(&kretprobe_lock, flags);
- }
- }
-
- return 0;
-}
-
-/**
- * @brief Kretprobes unregister top. Unregisters kprobes.
- *
- * @param rps Pointer to the array of pointers to the target kretprobes.
- * @param size Size of rps array.
- * @param rp_disarm Disarm flag. If set kretprobe is disarmed.
- * @return Void.
- */
-void swap_unregister_kretprobes_top(struct kretprobe **rps, size_t size,
- int rp_disarm)
-{
- struct unreg_krp_args args = {
- .rps = rps,
- .size = size,
- .rp_disarm = rp_disarm,
- };
- const size_t end = ((size_t)0) - 1;
-
- for (--size; size != end; --size)
- swap_unregister_kprobe(&rps[size]->kp);
-
- if (rp_disarm) {
- int ret;
-
- ret = stop_machine(__swap_unregister_kretprobes_top,
- &args, NULL);
- if (ret)
- pr_err("%s failed (%d)\n", __func__, ret);
- } else {
- __swap_unregister_kretprobes_top(&args);
- }
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kretprobes_top);
-
-/**
- * @brief swap_unregister_kretprobes_top wrapper for a single kretprobe.
- *
- * @param rp Pointer to the target kretprobe.
- * @param rp_disarm Disarm flag.
- * @return Void.
- */
-void swap_unregister_kretprobe_top(struct kretprobe *rp, int rp_disarm)
-{
- swap_unregister_kretprobes_top(&rp, 1, rp_disarm);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kretprobe_top);
-
-/**
- * @brief Kretprobe unregister bottom. Here is kretprobe memory is released.
- *
- * @param rp Pointer to the target kretprobe.
- * @return Void.
- */
-void swap_unregister_kretprobe_bottom(struct kretprobe *rp)
-{
- unsigned long flags;
- struct kretprobe_instance *ri;
-
- spin_lock_irqsave(&kretprobe_lock, flags);
-
- while ((ri = get_used_rp_inst(rp)) != NULL)
- recycle_rp_inst(ri);
- free_rp_inst(rp);
-
- spin_unlock_irqrestore(&kretprobe_lock, flags);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kretprobe_bottom);
-
-/**
- * @brief swap_unregister_kretprobe_bottom wrapper for several kretprobes.
- *
- * @param rps Pointer to the array of the target kretprobes pointers.
- * @param size Size of rps array.
- * @return Void.
- */
-void swap_unregister_kretprobes_bottom(struct kretprobe **rps, size_t size)
-{
- const size_t end = ((size_t) 0) - 1;
-
- for (--size; size != end; --size)
- swap_unregister_kretprobe_bottom(rps[size]);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kretprobes_bottom);
-
-/**
- * @brief Unregisters kretprobes.
- *
- * @param rpp Pointer to the array of the target kretprobes pointers.
- * @param size Size of rpp array.
- * @return Void.
- */
-void swap_unregister_kretprobes(struct kretprobe **rpp, size_t size)
-{
- swap_unregister_kretprobes_top(rpp, size, 1);
-
- if (!in_atomic())
- synchronize_sched();
-
- swap_unregister_kretprobes_bottom(rpp, size);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kretprobes);
-
-/**
- * @brief swap_unregister_kretprobes wrapper for a single kretprobe.
- *
- * @param rp Pointer to the target kretprobe.
- * @return Void.
- */
-void swap_unregister_kretprobe(struct kretprobe *rp)
-{
- swap_unregister_kretprobes(&rp, 1);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_kretprobe);
-
-static inline void rm_task_trampoline(struct task_struct *p,
- struct kretprobe_instance *ri)
-{
- arch_set_task_pc(p, (unsigned long)ri->ret_addr);
-}
-
-static int swap_disarm_krp_inst(struct kretprobe_instance *ri)
-{
- unsigned long *tramp = (unsigned long *)&swap_kretprobe_trampoline;
- unsigned long *sp = ri->sp;
- unsigned long *found = NULL;
- int retval = -ENOENT;
-
- if (!sp) {
- unsigned long pc = arch_get_task_pc(ri->task);
-
- printk(KERN_INFO "---> [%d] %s (%d/%d): pc = %08lx, ra = %08lx, tramp= %08lx (%08lx)\n",
- task_cpu(ri->task),
- ri->task->comm, ri->task->tgid, ri->task->pid,
- pc, (long unsigned int)ri->ret_addr,
- (long unsigned int)tramp,
- (ri->rp ? ri->rp->kp.addr : 0));
-
- /* __switch_to retprobe handling */
- if (pc == (unsigned long)tramp) {
- rm_task_trampoline(ri->task, ri);
- return 0;
- }
-
- return -EINVAL;
- }
-
- while (sp > ri->sp - KRETPROBE_STACK_DEPTH) {
- if (*sp == (unsigned long)tramp) {
- found = sp;
- break;
- }
- sp--;
- }
-
- if (found) {
- printk(KERN_INFO "---> [%d] %s (%d/%d): tramp (%08lx) "
- "found at %08lx (%08lx /%+ld) - %08lx\n",
- task_cpu(ri->task),
- ri->task->comm, ri->task->tgid, ri->task->pid,
- (long unsigned int)tramp,
- (long unsigned int)found, (long unsigned int)ri->sp,
- (unsigned long)(found - ri->sp), ri->rp ? ri->rp->kp.addr : 0);
- *found = (unsigned long)ri->ret_addr;
- retval = 0;
- } else {
- printk(KERN_INFO "---> [%d] %s (%d/%d): tramp (%08lx) "
- "NOT found at sp = %08lx - %08lx\n",
- task_cpu(ri->task),
- ri->task->comm, ri->task->tgid, ri->task->pid,
- (long unsigned int)tramp,
- (long unsigned int)ri->sp,
- ri->rp ? ri->rp->kp.addr : 0);
- }
-
- return retval;
-}
-
-static void krp_inst_flush(struct task_struct *task)
-{
- unsigned long flags;
- struct kretprobe_instance *ri;
- struct hlist_node *tmp;
- struct hlist_head *head;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- spin_lock_irqsave(&kretprobe_lock, flags);
- head = kretprobe_inst_table_head(task);
- swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
- if (ri->task == task) {
- printk("task[%u %u %s]: flush krp_inst, ret_addr=%p\n",
- task->tgid, task->pid, task->comm,
- ri->ret_addr);
- recycle_rp_inst(ri);
- }
- }
- spin_unlock_irqrestore(&kretprobe_lock, flags);
-}
-
-static void do_put_task_handler(struct task_struct *task)
-{
- /* task has died */
- krp_inst_flush(task);
- swap_ktd_put_task(task);
-}
-
-#ifdef CONFIG_SWAP_HOOK_TASKDATA
-
-#include <swap/hook_taskdata.h>
-
-static struct hook_taskdata put_hook = {
- .owner = THIS_MODULE,
- .put_task = do_put_task_handler,
-};
-
-static int put_task_once(void)
-{
- return 0;
-}
-
-static int put_task_init(void)
-{
- return hook_taskdata_reg(&put_hook);
-}
-
-static void put_task_uninit(void)
-{
- hook_taskdata_unreg(&put_hook);
-}
-
-#else /* CONFIG_SWAP_HOOK_TASKDATA */
-
-/* Handler is called the last because it is registered the first */
-static int put_task_handler(struct kprobe *p, struct pt_regs *regs)
-{
- struct task_struct *t = (struct task_struct *)swap_get_karg(regs, 0);
-
- do_put_task_handler(t);
-
- return 0;
-}
-
-static struct kprobe put_task_kp = {
- .pre_handler = put_task_handler,
-};
-
-static int put_task_once(void)
-{
- const char *sym = "__put_task_struct";
-
- put_task_kp.addr = swap_ksyms(sym);
- if (put_task_kp.addr == 0) {
- pr_err("ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
- }
-
- return 0;
-}
-
-static int put_task_init(void)
-{
- return swap_register_kprobe(&put_task_kp);
-}
-
-static void put_task_uninit(void)
-{
- swap_unregister_kprobe(&put_task_kp);
-}
-
-#endif /* CONFIG_SWAP_HOOK_TASKDATA */
-
-
-static int init_module_deps(void)
-{
- int ret;
-
- sched_addr = swap_ksyms("__switch_to");
- exit_addr = swap_ksyms("do_exit");
- sys_exit_group_addr = swap_ksyms("sys_exit_group");
- do_group_exit_addr = swap_ksyms("do_group_exit");
- sys_exit_addr = swap_ksyms("sys_exit");
-
- if (sched_addr == 0 ||
- exit_addr == 0 ||
- sys_exit_group_addr == 0 ||
- do_group_exit_addr == 0 ||
- sys_exit_addr == 0) {
- return -ESRCH;
- }
-
- ret = init_module_dependencies();
- if (ret)
- return ret;
-
- return arch_init_module_deps();
-}
-
-static int once(void)
-{
- int i, ret;
- const char *sym;
-
- ret = put_task_once();
- if (ret)
- return ret;
-
- sym = "module_alloc";
- __module_alloc = (void *)swap_ksyms(sym);
- if (__module_alloc == NULL)
- goto not_found;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
- sym = "module_memfree";
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
- sym = "module_free";
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
- __module_free = (void *)swap_ksyms(sym);
- if (__module_free == NULL)
- goto not_found;
-
- ret = init_module_deps();
- if (ret)
- return ret;
-
- /*
- * FIXME allocate the probe table, currently defined statically
- * initialize all list heads
- */
- for (i = 0; i < KPROBE_TABLE_SIZE; ++i) {
- INIT_HLIST_HEAD(&kprobe_table[i]);
- INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
- }
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
-static int init_kprobes(void)
-{
- int ret;
-
- init_sm();
- atomic_set(&kprobe_count, 0);
-
- ret = swap_td_raw_init();
- if (ret)
- return ret;
-
- ret = swap_arch_init_kprobes();
- if (ret)
- goto td_raw_uninit;
-
- ret = swap_ktd_init();
- if (ret)
- goto arch_kp_exit;
-
- ret = kprobe_cur_reg();
- if (ret)
- goto ktd_uninit;
-
- ret = put_task_init();
- if (ret)
- goto cur_uninit;
-
- return 0;
-
-cur_uninit:
- kprobe_cur_unreg();
-ktd_uninit:
- swap_ktd_uninit_top();
- swap_ktd_uninit_bottom();
-arch_kp_exit:
- swap_arch_exit_kprobes();
-td_raw_uninit:
- swap_td_raw_uninit();
- return ret;
-}
-
-static void exit_kprobes(void)
-{
- swap_ktd_uninit_top();
- put_task_uninit();
- kprobe_cur_unreg();
- swap_ktd_uninit_bottom();
- swap_arch_exit_kprobes();
- swap_td_raw_uninit();
- exit_sm();
-}
-
-SWAP_LIGHT_INIT_MODULE(once, init_kprobes, exit_kprobes, NULL, NULL);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/**
- * @file kprobe/swap_kprobes.h
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM and MIPS
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) IBM Corporation, 2002, 2004
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * SWAP kprobe interface definition.
- */
-
-
-#ifndef _SWAP_KPROBES_H
-#define _SWAP_KPROBES_H
-
-
-#include <linux/kconfig.h>
-
-#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
-# error "Kernel is immutable"
-#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
-
-
-#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */
-#include <linux/notifier.h>
-#include <linux/percpu.h>
-#include <linux/spinlock.h>
-#include <linux/rcupdate.h>
-#include <linux/sched.h>
-#include <linux/pagemap.h>
-
-#include <swap-asm/swap_kprobes.h>
-
-
-/* kp_core_status settings */
-/** Kprobe hit active */
-#define KPROBE_HIT_ACTIVE 0x00000001
-/** Kprobe hit ss */
-#define KPROBE_HIT_SS 0x00000002
-/** Kprobe reenter */
-#define KPROBE_REENTER 0x00000004
-/** Kprobe hit ss done */
-#define KPROBE_HIT_SSDONE 0x00000008
-
-/** High word */
-#define HIWORD(x) (((x) & 0xFFFF0000) >> 16)
-/** Low word */
-#define LOWORD(x) ((x) & 0x0000FFFF)
-
-/** Invalid value */
-#define INVALID_VALUE 0xFFFFFFFF
-/** Invalid pointer */
-#define INVALID_POINTER (void *)INVALID_VALUE
-
-/** Jprobe entry */
-#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry
-
-
-struct kprobe;
-struct pt_regs;
-struct kretprobe;
-struct kretprobe_instance;
-
-/**
- * @brief Kprobe pre-handler pointer.
- */
-typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *);
-
-/**
- * @brief Kprobe break handler pointer.
- */
-typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *);
-
-/**
- * @brief Kprobe post handler pointer.
- */
-typedef void (*kprobe_post_handler_t) (struct kprobe *,
- struct pt_regs *,
- unsigned long flags);
-
-/**
- * @brief Kprobe fault handler pointer.
- */
-typedef int (*kprobe_fault_handler_t) (struct kprobe *,
- struct pt_regs *,
- int trapnr);
-
-/**
- * @brief Kretprobe handler pointer.
- */
-typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
- struct pt_regs *);
-
-struct kprobe;
-struct kp_core;
-
-struct kp_handlers {
- int (*pre)(struct kp_core *, struct pt_regs *);
-
- rwlock_t lock;
- struct kprobe *kps[4];
-};
-
-struct kp_core {
- struct hlist_node hlist;
- atomic_t usage;
-
- struct kp_handlers handlers;
-
- unsigned long addr;
- kprobe_opcode_t opcode;
-
- struct swap_arch_specific_insn ainsn;
-};
-
-/**
- * @struct kprobe
- * @brief Main kprobe struct.
- */
-struct kprobe {
- unsigned long addr; /**< Location of the probe point. */
- char *symbol_name; /**< Symbol name of the probe point. */
- unsigned long offset; /**< Offset into the symbol.*/
- /**< Called before addr is executed. */
- kprobe_pre_handler_t pre_handler;
-};
-
-/**
- * @brief Kprobe pre-entry handler pointer.
- */
-typedef unsigned long (*kprobe_pre_entry_handler_t) (void *priv_arg,
- struct pt_regs *regs);
-
-
-/**
- * @struct jprobe
- * @brief Special probe type that uses setjmp-longjmp type tricks to resume
- * execution at a specified entry with a matching prototype corresponding
- * to the probed function - a trick to enable arguments to become
- * accessible seamlessly by probe handling logic.
- * Note:
- * Because of the way compilers allocate stack space for local variables
- * etc upfront, regardless of sub-scopes within a function, this mirroring
- * principle currently works only for probes placed on function entry points.
- */
-struct jprobe {
- struct kprobe kp; /**< This probes kprobe.*/
- kprobe_opcode_t *entry; /**< Probe handling code to jump to.*/
- /** Handler which will be called before 'entry'. */
- kprobe_pre_entry_handler_t pre_entry;
- void *priv_arg; /**< Private args.*/
-};
-
-
-/**
- * @struct jprobe_instance
- * @brief Jprobe instance struct.
- */
-struct jprobe_instance {
- /* either on free list or used list */
- struct hlist_node uflist; /**< Jprobes hash list. */
- struct hlist_node hlist; /**< Jprobes hash list. */
- struct jprobe *jp; /**< Pointer to the target jprobe. */
- /** Pointer to the target task_struct. */
- struct task_struct *task;
-};
-
-
-
-
-
-/**
- * @struct kretprobe
- * @brief Function-return probe
- * Note: User needs to provide a handler function, and initialize maxactive.
- */
-struct kretprobe {
- struct kprobe kp; /**< Kprobe of this kretprobe.*/
- kretprobe_handler_t handler; /**< Handler of this kretprobe.*/
- kretprobe_handler_t entry_handler; /**< Entry handler of this kretprobe.*/
- /** The maximum number of instances of the probed function that can be
- * active concurrently. */
- int maxactive;
- /** Tracks the number of times the probed function's return was ignored,
- * due to maxactive being too low. */
- int nmissed;
- size_t data_size; /**< Size of the data. */
- /** List of this probe's free_instances. */
- struct hlist_head free_instances;
- /** List of this probe's used_instances. */
- struct hlist_head used_instances;
-
-#ifdef CONFIG_ARM
- unsigned arm_noret:1; /**< No-return flag for ARM.*/
- unsigned thumb_noret:1; /**< No-return flag for Thumb.*/
-#endif
-
-};
-
-/**
- * @struct kretprobe_instance
- * @brief Instance of kretprobe.
- */
-struct kretprobe_instance {
- /* either on free list or used list */
- struct hlist_node uflist; /**< Kretprobe hash list.*/
- struct hlist_node hlist; /**< Kretprobe hash list.*/
- struct kretprobe *rp; /**< Pointer to this instance's kretprobe.*/
- unsigned long *ret_addr; /**< Return address.*/
- unsigned long *sp; /**< Stack pointer.*/
- struct task_struct *task; /**< Pointer to the target task_struct.*/
- char data[0]; /**< Pointer to data.*/
-};
-
-
-/*
- * Large value for fast but memory consuming implementation
- * it is good when a lot of probes are instrumented
- */
-/* #define KPROBE_HASH_BITS 6 */
-#define KPROBE_HASH_BITS 16
-#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
-
-
-static void inline kp_core_get(struct kp_core *p)
-{
- atomic_inc(&p->usage);
-}
-
-static void inline kp_core_put(struct kp_core *p)
-{
- atomic_dec(&p->usage);
-}
-
-
-/* Get the kp_core at this addr (if any) - called with rcu_read_lock() */
-struct kp_core *kp_core_by_addr(unsigned long addr);
-
-int swap_register_kprobe(struct kprobe *p);
-void swap_unregister_kprobe(struct kprobe *p);
-
-int swap_register_jprobe(struct jprobe *p);
-void swap_unregister_jprobe(struct jprobe *p);
-void swap_jprobe_return(void);
-
-
-int swap_register_kretprobe(struct kretprobe *rp);
-void swap_unregister_kretprobe(struct kretprobe *rp);
-void swap_unregister_kretprobes(struct kretprobe **rpp, size_t size);
-
-/*
- * use:
- * swap_unregister_kretprobe[s]_top();
- * synchronize_sched();
- * swap_unregister_kretprobe[s]_bottom();
- *
- * rp_disarm - indicates the need for restoration of the return address
- */
-void swap_unregister_kretprobe_top(struct kretprobe *rp, int rp_disarm);
-void swap_unregister_kretprobes_top(struct kretprobe **rps, size_t size,
- int rp_disarm);
-void swap_unregister_kretprobe_bottom(struct kretprobe *rp);
-void swap_unregister_kretprobes_bottom(struct kretprobe **rps, size_t size);
-
-
-unsigned long swap_trampoline_handler(struct pt_regs *regs);
-
-
-extern atomic_t kprobe_count;
-extern unsigned long sched_addr;
-
-struct kp_core *kp_core_running(void);
-void kp_core_running_set(struct kp_core *p);
-struct kp_core_ctlblk *kp_core_ctlblk(void);
-
-
-struct kctx;
-
-/* for __switch_to support */
-#define SWITCH_TO_KP 0b0001
-#define SWITCH_TO_RP 0b0010
-#define SWITCH_TO_ALL (SWITCH_TO_KP | SWITCH_TO_RP)
-
-#define current_kctx kctx_by_task(current)
-struct kctx *kctx_by_task(struct task_struct *task);
-
-void switch_to_bits_set(struct kctx *ctx, unsigned long mask);
-void switch_to_bits_reset(struct kctx *ctx, unsigned long mask);
-unsigned long switch_to_bits_get(struct kctx *ctx, unsigned long mask);
-
-
-#ifndef swap_in_interrupt
-#define swap_in_interrupt() in_interrupt()
-#endif /* swap_in_interrupt */
-
-static inline int able2resched(struct kctx *ctx)
-{
- if (swap_in_interrupt() || switch_to_bits_get(ctx, SWITCH_TO_ALL))
- return 0;
-
- return 1;
-}
-
-
-#endif /* _SWAP_KPROBES_H */
-
+++ /dev/null
-/**
- * kprobe/swap_kprobes_deps.c
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * SWAP kprobe kernel-dependent dependencies.
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-
-#include <asm/pgtable.h>
-
-#include "swap_kprobes_deps.h"
-#include "swap_kdebug.h"
-
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
-/* kernel define 'pgd_offset_k' redefinition */
-#undef pgd_offset_k
-#define pgd_offset_k(addr) pgd_offset(init_task.active_mm, addr)
-#endif
-
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
-#ifndef is_zero_pfn
-
-static unsigned long swap_zero_pfn ;
-
-#endif /* is_zero_pfn */
-#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)
-static inline void *swap_kmap_atomic(struct page *page)
-{
- return kmap_atomic(page);
-}
-static inline void swap_kunmap_atomic(void *kvaddr)
-{
- kunmap_atomic(kvaddr);
-}
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) */
-static inline void *swap_kmap_atomic(struct page *page)
-{
- return kmap_atomic(page, KM_USER0);
-}
-
-static inline void swap_kunmap_atomic(void *kvaddr)
-{
- kunmap_atomic(kvaddr, KM_USER0);
-}
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) */
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
-DECLARE_MOD_FUNC_DEP(do_mmap, unsigned long, struct file *file,
- unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, vm_flags_t vm_flags,
- unsigned long pgoff, unsigned long *populate);
-DECLARE_MOD_DEP_WRAPPER(swap_do_mmap,
- unsigned long,
- struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, vm_flags_t vm_flags,
- unsigned long pgoff, unsigned long *populate)
-IMP_MOD_DEP_WRAPPER(do_mmap, file, addr, len,
- prot, flags, vm_flags, pgoff, populate)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
-DECLARE_MOD_FUNC_DEP(do_mmap_pgoff, unsigned long, struct file *file,
- unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long pgoff,
- unsigned long *populate);
-DECLARE_MOD_DEP_WRAPPER(swap_do_mmap_pgoff,
- unsigned long,
- struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long pgoff,
- unsigned long *populate)
-IMP_MOD_DEP_WRAPPER(do_mmap_pgoff, file, addr, len,
- prot, flags, pgoff, populate)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
-DECLARE_MOD_FUNC_DEP(do_mmap_pgoff, unsigned long, struct file *file,
- unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long pgoff);
-DECLARE_MOD_DEP_WRAPPER(swap_do_mmap_pgoff,
- unsigned long,
- struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long pgoff)
-IMP_MOD_DEP_WRAPPER(do_mmap_pgoff, file, addr, len, prot, flags, pgoff)
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
-EXPORT_SYMBOL_GPL(swap_do_mmap);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
-EXPORT_SYMBOL_GPL(swap_do_mmap_pgoff);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
-
-/* copy_to_user_page */
-#ifndef copy_to_user_page
-static DECLARE_MOD_FUNC_DEP(copy_to_user_page, void, struct vm_area_struct *vma,
- struct page *page, unsigned long uaddr, void *dst,
- const void *src, unsigned long len);
-DECLARE_MOD_DEP_WRAPPER(swap_copy_to_user_page,
- void,
- struct vm_area_struct *vma, struct page *page,
- unsigned long uaddr, void *dst, const void *src,
- unsigned long len)
-IMP_MOD_DEP_WRAPPER(copy_to_user_page, vma, page, uaddr, dst, src, len)
-#else /* copy_to_user_page */
-#define swap_copy_to_user_page copy_to_user_page
-#endif /* copy_to_user_page */
-
-
-static DECLARE_MOD_FUNC_DEP(find_extend_vma, struct vm_area_struct *,
- struct mm_struct *mm, unsigned long addr);
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
-static DECLARE_MOD_FUNC_DEP(handle_mm_fault, int, struct mm_struct *mm,
- struct vm_area_struct *vma, unsigned long address,
- int write_access);
-#endif
-#else
-static DECLARE_MOD_FUNC_DEP(handle_mm_fault, int, struct mm_struct *mm,
- struct vm_area_struct *vma, unsigned long address,
- unsigned int flags);
-#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) */
-
-#ifdef __HAVE_ARCH_GATE_AREA
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-static DECLARE_MOD_FUNC_DEP(get_gate_vma, struct vm_area_struct *,
- struct mm_struct *mm);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-static DECLARE_MOD_FUNC_DEP(get_gate_vma, struct vm_area_struct *,
- struct task_struct *tsk);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-DECLARE_MOD_FUNC_DEP(in_gate_area, int, struct mm_struct *mm,
- unsigned long addr);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-DECLARE_MOD_FUNC_DEP(in_gate_area, int, struct task_struct *task,
- unsigned long addr);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-static DECLARE_MOD_FUNC_DEP(in_gate_area_no_mm, int, unsigned long addr);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-static DECLARE_MOD_FUNC_DEP(in_gate_area_no_task, int, unsigned long addr);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#endif /* __HAVE_ARCH_GATE_AREA */
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
-static DECLARE_MOD_FUNC_DEP(follow_page_mask, \
- struct page *, struct vm_area_struct *vma, \
- unsigned long address, unsigned int foll_flags, \
- unsigned int *page_mask);
-DECLARE_MOD_DEP_WRAPPER(swap_follow_page_mask,
- struct page *,
- struct vm_area_struct *vma, unsigned long address,
- unsigned int foll_flags, unsigned int *page_mask)
-IMP_MOD_DEP_WRAPPER(follow_page_mask, vma, address, foll_flags, page_mask)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-static DECLARE_MOD_FUNC_DEP(follow_page, \
- struct page *, struct vm_area_struct *vma, \
- unsigned long address, unsigned int foll_flags);
-DECLARE_MOD_DEP_WRAPPER(swap_follow_page,
- struct page *,
- struct vm_area_struct *vma, unsigned long address,
- unsigned int foll_flags)
-IMP_MOD_DEP_WRAPPER(follow_page, vma, address, foll_flags)
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-
-static DECLARE_MOD_FUNC_DEP(__flush_anon_page, \
- void, struct vm_area_struct *vma, struct page *page, \
- unsigned long vmaddr);
-static DECLARE_MOD_FUNC_DEP(vm_normal_page, \
- struct page *, struct vm_area_struct *vma, \
- unsigned long addr, pte_t pte);
-
-
-#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
-static DECLARE_MOD_FUNC_DEP(put_task_struct, \
- void, struct task_struct *tsk);
-#else
-static DECLARE_MOD_FUNC_DEP(put_task_struct, \
- void, struct rcu_head *rhp);
-#endif
-
-DECLARE_MOD_DEP_WRAPPER(swap_find_extend_vma,
- struct vm_area_struct *,
- struct mm_struct *mm, unsigned long addr)
-IMP_MOD_DEP_WRAPPER(find_extend_vma, mm, addr)
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
-DECLARE_MOD_DEP_WRAPPER(swap_handle_mm_fault,
- int,
- struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long address, int write_access)
-{
- if (in_atomic())
- return VM_FAULT_ERROR | VM_FAULT_OOM;
-
- IMP_MOD_DEP_WRAPPER(handle_mm_fault, mm, vma, address, write_access)
-}
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18) */
-#else /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) */
-DECLARE_MOD_DEP_WRAPPER(swap_handle_mm_fault,
- int,
- struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long address, unsigned int flags)
-{
- if (in_atomic())
- return VM_FAULT_ERROR | VM_FAULT_OOM;
-
- IMP_MOD_DEP_WRAPPER(handle_mm_fault, mm, vma, address, flags)
-}
-#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) */
-
-struct vm_area_struct *swap_get_gate_vma(struct mm_struct *mm)
-{
-#ifdef __HAVE_ARCH_GATE_AREA
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-IMP_MOD_DEP_WRAPPER(get_gate_vma, mm)
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-IMP_MOD_DEP_WRAPPER(get_gate_vma, tsk)
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#else /* __HAVE_ARCH_GATE_AREA */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- return get_gate_vma(mm);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
- return get_gate_vma(tsk);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#endif /* __HAVE_ARCH_GATE_AREA */
-}
-
-#ifdef CONFIG_HUGETLB_PAGE
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-DECLARE_MOD_FUNC_DEP(follow_hugetlb_page, \
- int, \
- struct mm_struct *mm, struct vm_area_struct *vma, \
- struct page **pages, struct vm_area_struct **vmas, \
- unsigned long *position, int *length, int i, \
- unsigned int flags);
-DECLARE_MOD_DEP_WRAPPER(swap_follow_hugetlb_page,
- int,
- struct mm_struct *mm, struct vm_area_struct *vma,
- struct page **pages, struct vm_area_struct **vmas,
- unsigned long *position, int *length, int i,
- unsigned int flags)
-IMP_MOD_DEP_WRAPPER(follow_hugetlb_page, \
- mm, vma, pages, vmas, position, length, i, flags)
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) */
-DECLARE_MOD_FUNC_DEP(follow_hugetlb_page, \
- long, \
- struct mm_struct *mm, struct vm_area_struct *vma, \
- struct page **pages, struct vm_area_struct **vmas, \
- unsigned long *position, unsigned long *nr_pages, \
- long i, unsigned int flags);
-DECLARE_MOD_DEP_WRAPPER(swap_follow_hugetlb_page,
- long,
- struct mm_struct *mm, struct vm_area_struct *vma,
- struct page **pages, struct vm_area_struct **vmas,
- unsigned long *position, unsigned long *nr_pages,
- long i, unsigned int flags)
-IMP_MOD_DEP_WRAPPER(follow_hugetlb_page, \
- mm, vma, pages, vmas, position, nr_pages, i, flags)
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) */
-
-#else /* CONFIG_HUGETLB_PAGE */
-#define swap_follow_hugetlb_page follow_hugetlb_page
-#endif /* CONFIG_HUGETLB_PAGE */
-
-static inline int swap_in_gate_area(struct task_struct *task,
- unsigned long addr)
-{
-#ifdef __HAVE_ARCH_GATE_AREA
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- struct mm_struct *mm;
-
- if (task == NULL)
- return 0;
-
- mm = task->mm;
- IMP_MOD_DEP_WRAPPER(in_gate_area, mm, addr)
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
- IMP_MOD_DEP_WRAPPER(in_gate_area, task, addr)
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#else /*__HAVE_ARCH_GATE_AREA */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- struct mm_struct *mm;
-
- if (task == NULL)
- return 0;
-
- mm = task->mm;
- return in_gate_area(mm, addr);
-#else
- return in_gate_area(task, addr);
-#endif
-#endif/*__HAVE_ARCH_GATE_AREA */
-}
-
-
-#ifdef __HAVE_ARCH_GATE_AREA
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-DECLARE_MOD_DEP_WRAPPER(swap_in_gate_area_no_mm, int, unsigned long addr)
-IMP_MOD_DEP_WRAPPER(in_gate_area_no_mm, addr)
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-DECLARE_MOD_DEP_WRAPPER(swap_in_gate_area_no_task, int, unsigned long addr)
-IMP_MOD_DEP_WRAPPER(in_gate_area_no_task, addr)
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#endif /* __HAVE_ARCH_GATE_AREA */
-
-static inline int swap_in_gate_area_no_xxx(unsigned long addr)
-{
-#ifdef __HAVE_ARCH_GATE_AREA
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- return swap_in_gate_area_no_mm(addr);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
- return swap_in_gate_area_no_task(addr);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#else /* __HAVE_ARCH_GATE_AREA */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- return in_gate_area_no_mm(addr);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
- return in_gate_area_no_task(addr);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#endif /* __HAVE_ARCH_GATE_AREA */
-}
-
-DECLARE_MOD_DEP_WRAPPER(swap__flush_anon_page,
- void,
- struct vm_area_struct *vma, struct page *page,
- unsigned long vmaddr)
-IMP_MOD_DEP_WRAPPER(__flush_anon_page, vma, page, vmaddr)
-
-static inline void swap_flush_anon_page(struct vm_area_struct *vma,
- struct page *page,
- unsigned long vmaddr)
-{
-#if defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM)
- if (PageAnon(page))
- swap__flush_anon_page(vma, page, vmaddr);
-#else /* defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM) */
- flush_anon_page(vma, page, vmaddr);
-#endif /* defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM) */
-}
-
-DECLARE_MOD_DEP_WRAPPER(swap_vm_normal_page,
- struct page *,
- struct vm_area_struct *vma, unsigned long addr,
- pte_t pte)
-IMP_MOD_DEP_WRAPPER(vm_normal_page, vma, addr, pte)
-
-
-
-/**
- * @brief Initializes module dependencies.
- *
- * @return 0.
- */
-int init_module_dependencies(void)
-{
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
- INIT_MOD_DEP_VAR(handle_mm_fault, handle_mm_fault);
-#endif
-
-#ifndef copy_to_user_page
- INIT_MOD_DEP_VAR(copy_to_user_page, copy_to_user_page);
-#endif /* copy_to_user_page */
-
- INIT_MOD_DEP_VAR(find_extend_vma, find_extend_vma);
-
-#ifdef CONFIG_HUGETLB_PAGE
- INIT_MOD_DEP_VAR(follow_hugetlb_page, follow_hugetlb_page);
-#endif
-
-#ifdef __HAVE_ARCH_GATE_AREA
- INIT_MOD_DEP_VAR(in_gate_area, in_gate_area);
- INIT_MOD_DEP_VAR(get_gate_vma, get_gate_vma);
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- INIT_MOD_DEP_VAR(in_gate_area_no_mm, in_gate_area_no_mm);
-#else /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
- INIT_MOD_DEP_VAR(in_gate_area_no_task, in_gate_area_no_task);
-#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
- INIT_MOD_DEP_VAR(follow_page_mask, follow_page_mask);
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
- INIT_MOD_DEP_VAR(follow_page, follow_page);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-
-#ifndef is_zero_pfn
- swap_zero_pfn = page_to_pfn(ZERO_PAGE(0));
-#endif /* is_zero_pfn */
-#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
-
-#if defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM)
- INIT_MOD_DEP_VAR(__flush_anon_page, __flush_anon_page);
-#endif /* defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM) */
-
- INIT_MOD_DEP_VAR(vm_normal_page, vm_normal_page);
-
-#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
-# if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11))
- INIT_MOD_DEP_VAR(put_task_struct, put_task_struct);
-# else
- INIT_MOD_DEP_VAR(put_task_struct, __put_task_struct);
-# endif
-#else /*2.6.16 */
- INIT_MOD_DEP_VAR(put_task_struct, __put_task_struct_cb);
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
- INIT_MOD_DEP_VAR(do_mmap, do_mmap);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
- INIT_MOD_DEP_VAR(do_mmap_pgoff, do_mmap_pgoff);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
-
- return 0;
-}
-
-
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
-
-static int do_access_process_vm(struct task_struct *tsk, struct mm_struct *mm,
- unsigned long addr, void *buf, int len,
- int write)
-{
- struct vm_area_struct *vma;
- void *old_buf = buf;
-
- while (len) {
- int bytes, ret, offset;
- void *maddr;
- struct page *page = NULL;
-
-# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
- ret = get_user_pages(tsk, mm, addr, 1, write, 1, &page, &vma);
-# else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */
- ret = get_user_pages_remote(tsk, mm, addr, 1,
- FOLL_WRITE | FOLL_FORCE,
- &page, &vma);
-# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */
-
- if (ret <= 0) {
-#ifndef CONFIG_HAVE_IOREMAP_PROT
- break;
-#else
- /*
- * Check if this is a VM_IO | VM_PFNMAP VMA, which
- * we can access using slightly different code.
- */
- vma = find_vma(mm, addr);
- if (!vma || vma->vm_start > addr)
- break;
- if (vma->vm_ops && vma->vm_ops->access)
- ret = vma->vm_ops->access(vma, addr, buf, len,
- write);
- if (ret <= 0)
- break;
- bytes = ret;
-#endif
- } else {
- bytes = len;
- offset = addr & (PAGE_SIZE-1);
- if (bytes > PAGE_SIZE-offset)
- bytes = PAGE_SIZE-offset;
-
- maddr = kmap(page);
- if (write) {
- swap_copy_to_user_page(vma, page, addr,
- maddr + offset,
- buf, bytes);
- set_page_dirty_lock(page);
- } else {
- copy_from_user_page(vma, page, addr,
- buf, maddr + offset, bytes);
- }
- kunmap(page);
- put_page(page);
- }
- len -= bytes;
- buf += bytes;
- addr += bytes;
- }
-
- return buf - old_buf;
-}
-
-int swap_access_process_vm(struct task_struct *tsk, unsigned long addr,
- void *buf, int len, int write)
-{
- int ret;
- struct mm_struct *mm;
-
- mm = get_task_mm(tsk);
- if (!mm)
- return 0;
-
- ret = do_access_process_vm(tsk, mm, addr, buf, len, write);
- mmput(mm);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_access_process_vm);
-
-#else /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) /* FIXME: must be < 32 */
-#define GUP_FLAGS_WRITE 0x1
-#define GUP_FLAGS_FORCE 0x2
-#define GUP_FLAGS_IGNORE_VMA_PERMISSIONS 0x4
-#define GUP_FLAGS_IGNORE_SIGKILL 0x8
-#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
-static inline int use_zero_page(struct vm_area_struct *vma)
-{
- /*
- * We don't want to optimize FOLL_ANON for make_pages_present()
- * when it tries to page in a VM_LOCKED region. As to VM_SHARED,
- * we want to get the page from the page tables to make sure
- * that we serialize and update with any other user of that
- * mapping.
- */
- if (vma->vm_flags & (VM_LOCKED | VM_SHARED))
- return 0;
- /*
- * And if we have a fault routine, it's not an anonymous region.
- */
- return !vma->vm_ops || !vma->vm_ops->fault;
-}
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
-
-#ifdef __HAVE_COLOR_ZERO_PAGE
-
-static inline int swap_is_zero_pfn(unsigned long pfn)
-{
- unsigned long offset_from_zero_pfn = pfn - swap_zero_pfn;
- return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT);
-}
-
-#else /* __HAVE_COLOR_ZERO_PAGE */
-
-static inline int swap_is_zero_pfn(unsigned long pfn)
-{
- return pfn == swap_zero_pfn;
-}
-#endif /* __HAVE_COLOR_ZERO_PAGE */
-
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) */
-
-static inline int swap_is_zero_pfn(unsigned long pfn)
-{
-#ifndef is_zero_pfn
- return pfn == swap_zero_pfn;
-#else /* is_zero_pfn */
- return is_zero_pfn(pfn);
-#endif /* is_zero_pfn */
-}
-
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) */
-
-static inline int stack_guard_page(struct vm_area_struct *vma,
- unsigned long addr)
-{
- return stack_guard_page_start(vma, addr) ||
- stack_guard_page_end(vma, addr+PAGE_SIZE);
-}
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
-
-/**
- * @brief Gets user pages uprobe.
- *
- * @param tsk Pointer to the task_struct.
- * @param mm Pointer to the mm_struct.
- * @param start Starting address.
- * @param nr_pages Pages number.
- * @param gup_flags Flags.
- * @param pages Pointer to the array of pointers to the target page structs.
- * @param vmas Pointer to the array of pointers to the target vm_area_struct.
- * @param nonblocking Pointer to int.
- * @return negative error code on error, positive result otherwise.
- */
-long __get_user_pages_uprobe(struct task_struct *tsk, struct mm_struct *mm,
- unsigned long start, unsigned long nr_pages,
- unsigned int gup_flags, struct page **pages,
- struct vm_area_struct **vmas, int *nonblocking)
-{
- long i;
- unsigned long vm_flags;
- unsigned int page_mask;
-
- if (!nr_pages)
- return 0;
-
- VM_BUG_ON(!!pages != !!(gup_flags & FOLL_GET));
-
- /*
- * Require read or write permissions.
- * If FOLL_FORCE is set, we only require the "MAY" flags.
- */
- vm_flags = (gup_flags & FOLL_WRITE) ?
- (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
- vm_flags &= (gup_flags & FOLL_FORCE) ?
- (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
-
- /*
- * If FOLL_FORCE and FOLL_NUMA are both set, handle_mm_fault
- * would be called on PROT_NONE ranges. We must never invoke
- * handle_mm_fault on PROT_NONE ranges or the NUMA hinting
- * page faults would unprotect the PROT_NONE ranges if
- * _PAGE_NUMA and _PAGE_PROTNONE are sharing the same pte/pmd
- * bitflag. So to avoid that, don't set FOLL_NUMA if
- * FOLL_FORCE is set.
- */
- if (!(gup_flags & FOLL_FORCE))
- gup_flags |= FOLL_NUMA;
-
- i = 0;
-
- do {
- struct vm_area_struct *vma;
-
- vma = swap_find_extend_vma(mm, start);
- if (!vma && swap_in_gate_area(tsk, start)) {
- unsigned long pg = start & PAGE_MASK;
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
-
- /* user gate pages are read-only */
- if (gup_flags & FOLL_WRITE)
- return i ? : -EFAULT;
- if (pg > TASK_SIZE)
- pgd = pgd_offset_k(pg);
- else
- pgd = pgd_offset_gate(mm, pg);
- BUG_ON(pgd_none(*pgd));
- pud = pud_offset(pgd, pg);
- BUG_ON(pud_none(*pud));
- pmd = pmd_offset(pud, pg);
- if (pmd_none(*pmd))
- return i ? : -EFAULT;
- VM_BUG_ON(pmd_trans_huge(*pmd));
- pte = pte_offset_map(pmd, pg);
- if (pte_none(*pte)) {
- pte_unmap(pte);
- return i ? : -EFAULT;
- }
- vma = swap_get_gate_vma(mm);
- if (pages) {
- struct page *page;
-
- page = swap_vm_normal_page(vma, start, *pte);
- if (!page) {
- if (!(gup_flags & FOLL_DUMP) &&
- swap_is_zero_pfn(pte_pfn(*pte)))
- page = pte_page(*pte);
- else {
- pte_unmap(pte);
- return i ? : -EFAULT;
- }
- }
- pages[i] = page;
- get_page(page);
- }
- pte_unmap(pte);
- page_mask = 0;
- goto next_page;
- }
-
- if (!vma ||
- (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
- !(vm_flags & vma->vm_flags))
- return i ? : -EFAULT;
-
- if (is_vm_hugetlb_page(vma)) {
- i = swap_follow_hugetlb_page(mm, vma, pages, vmas,
- &start, &nr_pages, i, gup_flags);
- continue;
- }
-
- do {
- struct page *page;
- unsigned int foll_flags = gup_flags;
- unsigned int page_increm;
-
- /*
- * If we have a pending SIGKILL, don't keep faulting
- * pages and potentially allocating memory.
- */
- if (unlikely(fatal_signal_pending(current)))
- return i ? i : -ERESTARTSYS;
-
- /* cond_resched(); */
- while (!(page = swap_follow_page_mask(vma, start,
- foll_flags, &page_mask))) {
- int ret;
- unsigned int fault_flags = 0;
-
- /* For mlock, just skip the stack guard page. */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
- if (foll_flags & FOLL_POPULATE) {
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) */
- if (foll_flags & FOLL_MLOCK) {
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) */
- if (stack_guard_page(vma, start))
- goto next_page;
- }
- if (foll_flags & FOLL_WRITE)
- fault_flags |= FAULT_FLAG_WRITE;
- if (nonblocking)
- fault_flags |= FAULT_FLAG_ALLOW_RETRY;
- if (foll_flags & FOLL_NOWAIT)
- fault_flags |=
- (FAULT_FLAG_ALLOW_RETRY |
- FAULT_FLAG_RETRY_NOWAIT);
-
- ret = swap_handle_mm_fault(mm, vma, start,
- fault_flags);
-
- if (ret & VM_FAULT_ERROR) {
- if (ret & VM_FAULT_OOM)
- return i ? i : -ENOMEM;
- if (ret & (VM_FAULT_HWPOISON |
- VM_FAULT_HWPOISON_LARGE)) {
- if (i)
- return i;
- else if (gup_flags &
- FOLL_HWPOISON)
- return -EHWPOISON;
- else
- return -EFAULT;
- }
- if (ret & VM_FAULT_SIGBUS)
- return i ? i : -EFAULT;
- BUG();
- }
-
- if (tsk) {
- if (ret & VM_FAULT_MAJOR)
- tsk->maj_flt++;
- else
- tsk->min_flt++;
- }
-
- if (ret & VM_FAULT_RETRY) {
- if (nonblocking)
- *nonblocking = 0;
- return i;
- }
-
- /*
- * The VM_FAULT_WRITE bit tells us that
- * do_wp_page has broken COW when necessary,
- * even if maybe_mkwrite decided not to set
- * pte_write. We can thus safely do subsequent
- * page lookups as if they were reads. But only
- * do so when looping for pte_write is futile:
- * in some cases userspace may also be wanting
- * to write to the gotten user page, which a
- * read fault here might prevent (a readonly
- * page might get reCOWed by userspace write).
- */
- if ((ret & VM_FAULT_WRITE) &&
- !(vma->vm_flags & VM_WRITE))
- foll_flags &= ~FOLL_WRITE;
-
- /* cond_resched(); */
- }
- if (IS_ERR(page))
- return i ? i : PTR_ERR(page);
- if (pages) {
- pages[i] = page;
-
- swap_flush_anon_page(vma, page, start);
- flush_dcache_page(page);
- page_mask = 0;
- }
-next_page:
- if (vmas) {
- vmas[i] = vma;
- page_mask = 0;
- }
- page_increm = 1 + (~(start >> PAGE_SHIFT) & page_mask);
- if (page_increm > nr_pages)
- page_increm = nr_pages;
- i += page_increm;
- start += page_increm * PAGE_SIZE;
- nr_pages -= page_increm;
- } while (nr_pages && start < vma->vm_end);
- } while (nr_pages);
- return i;
-}
-
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-
-static int __get_user_pages_uprobe(struct task_struct *tsk,
- struct mm_struct *mm, unsigned long start,
- int nr_pages, unsigned int gup_flags,
- struct page **pages,
- struct vm_area_struct **vmas,
- int *nonblocking)
-{
- int i;
- unsigned long vm_flags;
-
- if (nr_pages <= 0)
- return 0;
-
- VM_BUG_ON(!!pages != !!(gup_flags & FOLL_GET));
-
- /*
- * Require read or write permissions.
- * If FOLL_FORCE is set, we only require the "MAY" flags.
- */
- vm_flags = (gup_flags & FOLL_WRITE) ?
- (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
- vm_flags &= (gup_flags & FOLL_FORCE) ?
- (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
- i = 0;
-
- do {
- struct vm_area_struct *vma;
-
- vma = swap_find_extend_vma(mm, start);
- if (!vma && swap_in_gate_area_no_xxx(start)) {
- unsigned long pg = start & PAGE_MASK;
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
-
- /* user gate pages are read-only */
- if (gup_flags & FOLL_WRITE)
- return i ? : -EFAULT;
- if (pg > TASK_SIZE)
- pgd = pgd_offset_k(pg);
- else
- pgd = pgd_offset_gate(mm, pg);
- BUG_ON(pgd_none(*pgd));
- pud = pud_offset(pgd, pg);
- BUG_ON(pud_none(*pud));
- pmd = pmd_offset(pud, pg);
- if (pmd_none(*pmd))
- return i ? : -EFAULT;
- VM_BUG_ON(pmd_trans_huge(*pmd));
- pte = pte_offset_map(pmd, pg);
- if (pte_none(*pte)) {
- pte_unmap(pte);
- return i ? : -EFAULT;
- }
- vma = swap_get_gate_vma(mm);
- if (pages) {
- struct page *page;
-
- page = swap_vm_normal_page(vma, start, *pte);
- if (!page) {
- if (!(gup_flags & FOLL_DUMP) &&
- swap_is_zero_pfn(pte_pfn(*pte)))
- page = pte_page(*pte);
- else {
- pte_unmap(pte);
- return i ? : -EFAULT;
- }
- }
- pages[i] = page;
- get_page(page);
- }
- pte_unmap(pte);
- goto next_page;
- }
-
- if (!vma ||
- (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
- !(vm_flags & vma->vm_flags)) {
- return i ? : -EFAULT;
- }
-
- if (is_vm_hugetlb_page(vma)) {
- i = swap_follow_hugetlb_page(mm, vma, pages, vmas,
- &start, &nr_pages, i, gup_flags);
- continue;
- }
-
- do {
- struct page *page;
- unsigned int foll_flags = gup_flags;
-
- /*
- * If we have a pending SIGKILL, don't keep faulting
- * pages and potentially allocating memory.
- */
- if (unlikely(fatal_signal_pending(current)))
- return i ? i : -ERESTARTSYS;
-
- /* cond_resched(); */
- while (!(page = swap_follow_page(vma, start,
- foll_flags))) {
- int ret;
- unsigned int fault_flags = 0;
-
- /* For mlock, just skip the stack guard page. */
- if (foll_flags & FOLL_MLOCK) {
- if (stack_guard_page(vma, start))
- goto next_page;
- }
- if (foll_flags & FOLL_WRITE)
- fault_flags |= FAULT_FLAG_WRITE;
- if (nonblocking)
- fault_flags |= FAULT_FLAG_ALLOW_RETRY;
- if (foll_flags & FOLL_NOWAIT)
- fault_flags |=
- (FAULT_FLAG_ALLOW_RETRY |
- FAULT_FLAG_RETRY_NOWAIT);
-
- ret = swap_handle_mm_fault(mm, vma, start,
- fault_flags);
-
- if (ret & VM_FAULT_ERROR) {
- if (ret & VM_FAULT_OOM)
- return i ? i : -ENOMEM;
- if (ret & (VM_FAULT_HWPOISON |
- VM_FAULT_HWPOISON_LARGE)) {
- if (i)
- return i;
- else if (gup_flags &
- FOLL_HWPOISON)
- return -EHWPOISON;
- else
- return -EFAULT;
- }
- if (ret & VM_FAULT_SIGBUS)
- return i ? i : -EFAULT;
- BUG();
- }
-
- if (tsk) {
- if (ret & VM_FAULT_MAJOR)
- tsk->maj_flt++;
- else
- tsk->min_flt++;
- }
-
- if (ret & VM_FAULT_RETRY) {
- if (nonblocking)
- *nonblocking = 0;
- return i;
- }
-
- /*
- * The VM_FAULT_WRITE bit tells us that
- * do_wp_page has broken COW when necessary,
- * even if maybe_mkwrite decided not to set
- * pte_write. We can thus safely do subsequent
- * page lookups as if they were reads. But only
- * do so when looping for pte_write is futile:
- * in some cases userspace may also be wanting
- * to write to the gotten user page, which a
- * read fault here might prevent (a readonly
- * page might get reCOWed by userspace write).
- */
- if ((ret & VM_FAULT_WRITE) &&
- !(vma->vm_flags & VM_WRITE))
- foll_flags &= ~FOLL_WRITE;
-
- /* cond_resched(); */
- }
- if (IS_ERR(page))
- return i ? i : PTR_ERR(page);
- if (pages) {
- pages[i] = page;
-
- swap_flush_anon_page(vma, page, start);
- flush_dcache_page(page);
- }
-next_page:
- if (vmas)
- vmas[i] = vma;
- i++;
- start += PAGE_SIZE;
- nr_pages--;
- } while (nr_pages && start < vma->vm_end);
- } while (nr_pages);
-
- return i;
-}
-
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-
-static int __get_user_pages_uprobe(struct task_struct *tsk,
- struct mm_struct *mm,
- unsigned long start, int len, int flags,
- struct page **pages,
- struct vm_area_struct **vmas)
-{
- int i;
- unsigned int vm_flags = 0;
- int write = !!(flags & GUP_FLAGS_WRITE);
- int force = !!(flags & GUP_FLAGS_FORCE);
- int ignore = !!(flags & GUP_FLAGS_IGNORE_VMA_PERMISSIONS);
-
- if (len <= 0)
- return 0;
- /*
- * Require read or write permissions.
- * If 'force' is set, we only require the "MAY" flags.
- */
- vm_flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
- vm_flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
- i = 0;
-
- do {
- struct vm_area_struct *vma;
- unsigned int foll_flags;
-
- vma = find_vma(mm, start);
- if (!vma && swap_in_gate_area(tsk, start)) {
- unsigned long pg = start & PAGE_MASK;
- struct vm_area_struct *gate_vma =
- swap_get_gate_vma(tsk);
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
-
- /* user gate pages are read-only */
- if (!ignore && write)
- return i ? : -EFAULT;
- if (pg > TASK_SIZE)
- pgd = pgd_offset_k(pg);
- else
- pgd = pgd_offset_gate(mm, pg);
- BUG_ON(pgd_none(*pgd));
- pud = pud_offset(pgd, pg);
- BUG_ON(pud_none(*pud));
- pmd = pmd_offset(pud, pg);
- if (pmd_none(*pmd))
- return i ? : -EFAULT;
- pte = pte_offset_map(pmd, pg);
- if (pte_none(*pte)) {
- pte_unmap(pte);
- return i ? : -EFAULT;
- }
- if (pages) {
- struct page *page =
- swap_vm_normal_page(gate_vma, start,
- *pte);
- pages[i] = page;
- if (page)
- get_page(page);
- }
- pte_unmap(pte);
- if (vmas)
- vmas[i] = gate_vma;
- i++;
- start += PAGE_SIZE;
- len--;
- continue;
- }
-
- if (!vma ||
- (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
- (!ignore && !(vm_flags & vma->vm_flags)))
- return i ? : -EFAULT;
-
- if (is_vm_hugetlb_page(vma)) {
- i = swap_follow_hugetlb_page(mm, vma, pages, vmas,
- &start, &len, i, write);
- continue;
- }
-
- foll_flags = FOLL_TOUCH;
- if (pages)
- foll_flags |= FOLL_GET;
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
- if (!write && use_zero_page(vma))
- foll_flags |= FOLL_ANON;
-#endif
-#endif
-
- do {
- struct page *page;
-
- if (write)
- foll_flags |= FOLL_WRITE;
-
-
- /* cond_resched(); */
-
- DBPRINTF("pages = %p vma = %p\n", pages, vma);
- while (!(page = swap_follow_page(vma, start,
- foll_flags))) {
- int ret;
- ret = swap_handle_mm_fault(mm, vma, start,
- foll_flags & FOLL_WRITE);
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
- if (ret & VM_FAULT_WRITE)
- foll_flags &= ~FOLL_WRITE;
-
- switch (ret & ~VM_FAULT_WRITE) {
- case VM_FAULT_MINOR:
- tsk->min_flt++;
- break;
- case VM_FAULT_MAJOR:
- tsk->maj_flt++;
- break;
- case VM_FAULT_SIGBUS:
- return i ? i : -EFAULT;
- case VM_FAULT_OOM:
- return i ? i : -ENOMEM;
- default:
- BUG();
- }
-
-#else
- if (ret & VM_FAULT_ERROR) {
- if (ret & VM_FAULT_OOM)
- return i ? i : -ENOMEM;
- else if (ret & VM_FAULT_SIGBUS)
- return i ? i : -EFAULT;
- BUG();
- }
- if (ret & VM_FAULT_MAJOR)
- tsk->maj_flt++;
- else
- tsk->min_flt++;
-
- /*
- * The VM_FAULT_WRITE bit tells us that
- * do_wp_page has broken COW when necessary,
- * even if maybe_mkwrite decided not to set
- * pte_write. We can thus safely do subsequent
- * page lookups as if they were reads. But only
- * do so when looping for pte_write is futile:
- * in some cases userspace may also be wanting
- * to write to the gotten user page, which a
- * read fault here might prevent (a readonly
- * page might get reCOWed by userspace write).
- */
- if ((ret & VM_FAULT_WRITE) &&
- !(vma->vm_flags & VM_WRITE))
- foll_flags &= ~FOLL_WRITE;
-
- /* cond_resched(); */
-#endif
-
- }
-
- if (IS_ERR(page))
- return i ? i : PTR_ERR(page);
- if (pages) {
- pages[i] = page;
-
- swap_flush_anon_page(vma, page, start);
- flush_dcache_page(page);
- }
- if (vmas)
- vmas[i] = vma;
- i++;
- start += PAGE_SIZE;
- len--;
- } while (len && start < vma->vm_end);
- } while (len);
- return i;
-}
-#endif
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-
-/**
- * @brief Gets user pages uprobe.
- *
- * @param tsk Pointer to the task_struct.
- * @param mm Pointer to the mm_struct.
- * @param start Starting address.
- * @param len Length.
- * @param write Write flag.
- * @param force Force flag.
- * @param pages Pointer to the array of pointers to the target page structs.
- * @param vmas Pointer to the array of pointers to the target vm_area_struct.
- * @return negative error code on error, positive result otherwise.
- */
-int get_user_pages_uprobe(struct task_struct *tsk, struct mm_struct *mm,
- unsigned long start, int len, int write, int force,
- struct page **pages, struct vm_area_struct **vmas)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) /* FIXME: must be >= 32! */
- int flags = FOLL_TOUCH;
-
- if (pages)
- flags |= FOLL_GET;
- if (write)
- flags |= FOLL_WRITE;
- if (force)
- flags |= FOLL_FORCE;
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
- int flags = 0;
-
- if (write)
- flags |= GUP_FLAGS_WRITE;
- if (force)
- flags |= GUP_FLAGS_FORCE;
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-
- return __get_user_pages_uprobe(tsk, mm,
- start, len, flags,
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- pages, vmas, NULL);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
- pages, vmas);
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
-#else
- return get_user_pages(tsk, mm, start, len, write, force, pages, vmas);
-#endif
-}
-
-#define ACCESS_PROCESS_OPTIMIZATION 0
-
-#if ACCESS_PROCESS_OPTIMIZATION
-
-#define GET_STEP_X(LEN, STEP) (((LEN) >= (STEP)) ? (STEP) : (LEN) % (STEP))
-#define GET_STEP_4(LEN) GET_STEP_X((LEN), 4)
-
-static void read_data_current(unsigned long addr, void *buf, int len)
-{
- int step;
- int pos = 0;
-
- for (step = GET_STEP_4(len); len; len -= step) {
- switch (GET_STEP_4(len)) {
- case 1:
- get_user(*(u8 *)(buf + pos),
- (unsigned long *)(addr + pos));
- step = 1;
- break;
-
- case 2:
- case 3:
- get_user(*(u16 *)(buf + pos),
- (unsigned long *)(addr + pos));
- step = 2;
- break;
-
- case 4:
- get_user(*(u32 *)(buf + pos),
- (unsigned long *)(addr + pos));
- step = 4;
- break;
- }
-
- pos += step;
- }
-}
-
-/* not working */
-static void write_data_current(unsigned long addr, void *buf, int len)
-{
- int step;
- int pos = 0;
-
- for (step = GET_STEP_4(len); len; len -= step) {
- switch (GET_STEP_4(len)) {
- case 1:
- put_user(*(u8 *)(buf + pos),
- (unsigned long *)(addr + pos));
- step = 1;
- break;
-
- case 2:
- case 3:
- put_user(*(u16 *)(buf + pos),
- (unsigned long *)(addr + pos));
- step = 2;
- break;
-
- case 4:
- put_user(*(u32 *)(buf + pos),
- (unsigned long *)(addr + pos));
- step = 4;
- break;
- }
-
- pos += step;
- }
-}
-#endif
-
-/**
- * @brief Read-write task memory.
- *
- * @param tsk Pointer to the target task task_struct.
- * @param addr Address to read-write.
- * @param buf Pointer to buffer where to put-get data.
- * @param len Buffer length.
- * @param write Write flag. If 0 - reading, if 1 - writing.
- * @return Read-write size, error code on error.
- */
-int access_process_vm_atomic(struct task_struct *tsk, unsigned long addr,
- void *buf, int len, int write)
-{
- struct mm_struct *mm;
- struct vm_area_struct *vma;
- void *old_buf = buf;
- int atomic;
-
- if (len <= 0)
- return -1;
-
-#if ACCESS_PROCESS_OPTIMIZATION
- if (write == 0 && tsk == current) {
- read_data_current(addr, buf, len);
- return len;
- }
-#endif
-
- mm = tsk->mm; /* function 'get_task_mm' is to be called */
- if (!mm)
- return 0;
-
- /* FIXME: danger: write memory in atomic context */
- atomic = in_atomic();
- WARN_ON(atomic);
-
- /* ignore errors, just check how much was successfully transferred */
- while (len) {
- int bytes, ret, offset;
- void *maddr;
- struct page *page = NULL;
-
- ret = get_user_pages_uprobe(tsk, mm, addr, 1,
- write, 1, &page, &vma);
-
- if (ret <= 0) {
- /*
- * Check if this is a VM_IO | VM_PFNMAP VMA, which
- * we can access using slightly different code.
- */
-#ifdef CONFIG_HAVE_IOREMAP_PROT
- vma = find_vma(mm, addr);
- if (!vma)
- break;
- if (vma->vm_ops && vma->vm_ops->access)
- ret = vma->vm_ops->access(vma, addr, buf,
- len, write);
- if (ret <= 0)
-#endif
- break;
- bytes = ret;
- } else {
- bytes = len;
- offset = addr & (PAGE_SIZE-1);
- if (bytes > PAGE_SIZE-offset)
- bytes = PAGE_SIZE-offset;
-
- maddr = atomic ? swap_kmap_atomic(page) : kmap(page);
-
- if (write) {
- swap_copy_to_user_page(vma, page, addr,
- maddr + offset,
- buf, bytes);
- set_page_dirty_lock(page);
- } else {
- copy_from_user_page(vma, page, addr,
- buf, maddr + offset,
- bytes);
- }
-
- atomic ? swap_kunmap_atomic(maddr) : kunmap(page);
- page_cache_release(page);
- }
- len -= bytes;
- buf += bytes;
- addr += bytes;
- }
-
- return buf - old_buf;
-}
-EXPORT_SYMBOL_GPL(access_process_vm_atomic);
-
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-
-/**
- * @brief Page present.
- *
- * @param mm Pointer to the target mm_struct.
- * @param address Address.
- */
-int page_present(struct mm_struct *mm, unsigned long address)
-{
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *ptep, pte;
- unsigned long pfn;
-
- pgd = pgd_offset(mm, address);
- if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
- goto out;
-
- pud = pud_offset(pgd, address);
- if (pud_none(*pud) || unlikely(pud_bad(*pud)))
- goto out;
-
- pmd = pmd_offset(pud, address);
- if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
- goto out;
-
- ptep = pte_offset_map(pmd, address);
- if (pte_none(*ptep)) {
- pte_unmap(ptep);
- goto out;
- }
-
- pte = *ptep;
- pte_unmap(ptep);
- if (pte_present(pte)) {
- pfn = pte_pfn(pte);
- if (pfn_valid(pfn))
- return 1;
- }
-
-out:
- return 0;
-}
-EXPORT_SYMBOL_GPL(page_present);
-
+++ /dev/null
-/**
- * @file kprobe/swap_kprobes_deps.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * SWAP kprobe kernel-dependent dependencies.
- */
-
-#ifndef _SWAP_KPROBES_DEPS_H
-#define _SWAP_KPROBES_DEPS_H
-
-#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/mempolicy.h>
-#include <linux/highmem.h>
-#include <linux/pagemap.h>
-#include <ksyms/ksyms.h>
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
-#define DECLARE_NODE_PTR_FOR_HLIST(var_name)
-#define swap_hlist_for_each_entry_rcu(tpos, pos, head, member) \
- hlist_for_each_entry_rcu(tpos, head, member)
-#define swap_hlist_for_each_entry_safe(tpos, pos, n, head, member) \
- hlist_for_each_entry_safe(tpos, n, head, member)
-#define swap_hlist_for_each_entry(tpos, pos, head, member) \
- hlist_for_each_entry(tpos, head, member)
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-#define DECLARE_NODE_PTR_FOR_HLIST(var_name) struct hlist_node *var_name
-#define swap_hlist_for_each_entry_rcu(tpos, pos, head, member) \
- hlist_for_each_entry_rcu(tpos, pos, head, member)
-#define swap_hlist_for_each_entry_safe(tpos, pos, n, head, member) \
- hlist_for_each_entry_safe(tpos, pos, n, head, member)
-#define swap_hlist_for_each_entry(tpos, pos, head, member) \
- hlist_for_each_entry(tpos, pos, head, member)
-
-#define list_first_entry_or_null(ptr, type, member) \
- (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
-
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12))
-#define synchronize_sched synchronize_kernel
-#endif
-
-
-/*
- * swap_preempt_enable_no_resched()
- */
-#if (defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)))
-
-#ifdef CONFIG_PREEMPT_COUNT
-#define swap_preempt_enable_no_resched() \
-do { \
- barrier(); \
- preempt_count_dec(); \
-} while (0)
-#else /* !CONFIG_PREEMPT_COUNT */
-#define swap_preempt_enable_no_resched() barrier()
-#endif /* CONFIG_PREEMPT_COUNT */
-
-#else /* !(defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) */
-#define swap_preempt_enable_no_resched() preempt_enable_no_resched()
-#endif /* !(defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) */
-
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 1, 0)
- #define task_job(task) (task->jobctl)
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 1, 0) */
- #define task_job(task) (task->group_stop)
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 1, 0) */
-
-
-
-/* --------------------- Declaration of module dependencies ----------------- */
-
-#define DECLARE_MOD_FUNC_DEP(name, ret, ...) ret(*__ref_##name)(__VA_ARGS__)
-#define DECLARE_MOD_CB_DEP(name, ret, ...) ret(*name)(__VA_ARGS__)
-
-
-/* ---------------- Implementation of module dependencies wrappers ---------- */
-
-#define DECLARE_MOD_DEP_WRAPPER(name, ret, ...) ret name(__VA_ARGS__)
-#define IMP_MOD_DEP_WRAPPER(name, ...) \
-{ \
- return __ref_##name(__VA_ARGS__); \
-}
-
-
-/* --------------------- Module dependencies initialization ----------------- */
-
-#define INIT_MOD_DEP_VAR(dep, name) \
-{ \
- __ref_##dep = (void *) swap_ksyms(#name); \
- if (!__ref_##dep) { \
- DBPRINTF(#name " is not found! Oops. Where is it?"); \
- return -ESRCH; \
- } \
-}
-
-#define INIT_MOD_DEP_CB(dep, name) \
-{ \
- dep = (void *) swap_ksyms(#name); \
- if (!dep) { \
- DBPRINTF(#name " is not found! Oops. Where is it?"); \
- return -ESRCH; \
- } \
-}
-
-
-int init_module_dependencies(void);
-
-
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
-
-int swap_access_process_vm(struct task_struct *tsk, unsigned long addr,
- void *buf, int len, int write);
-
-# define read_proc_vm_atomic(tsk, addr, buf, len) \
- swap_access_process_vm(tsk, addr, buf, len, 0)
-# define write_proc_vm_atomic(tsk, addr, buf, len) \
- swap_access_process_vm(tsk, addr, buf, len, 1)
-
-#else /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-
-int access_process_vm_atomic(struct task_struct *tsk, unsigned long addr,
- void *buf, int len, int write);
-
-# define read_proc_vm_atomic(tsk, addr, buf, len) \
- access_process_vm_atomic(tsk, addr, buf, len, 0)
-# define write_proc_vm_atomic(tsk, addr, buf, len) \
- access_process_vm_atomic(tsk, addr, buf, len, 1)
-
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-
-int page_present(struct mm_struct *mm, unsigned long addr);
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
-unsigned long swap_do_mmap(struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, vm_flags_t vm_flags,
- unsigned long pgoff, unsigned long *populate);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
-unsigned long swap_do_mmap_pgoff(struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long pgoff,
- unsigned long *populate);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
-unsigned long swap_do_mmap_pgoff(struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long pgoff);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0)
-#define swap_hlist_add_after(node, prev) hlist_add_behind(node, prev)
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
-#define swap_hlist_add_after(node, prev) hlist_add_after(node, prev)
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 18, 0)
-#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 18, 0) */
-
-#endif /* _SWAP_KPROBES_DEPS_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <ksyms/ksyms.h>
-#include "swap_ktd.h"
-#include "swap_td_raw.h"
-
-
-#define KTD_PREFIX "[SWAP_KTD] "
-#define kTD_JOBCTL_PREPARE (1 << 31)
-#define KTD_BIT_MAX (sizeof(unsigned long) * 8)
-
-
-struct td {
- struct list_head list;
- struct task_struct *task;
-
- spinlock_t flags_lock;
- unsigned long init_flags;
-};
-
-
-static struct td_raw td_raw;
-static LIST_HEAD(prepare_list);
-static DEFINE_RWLOCK(prepare_lock);
-static int preparing_cnt = 0;
-
-
-static DEFINE_MUTEX(mutex_ktd_nr);
-static struct ktask_data *ktd_array[KTD_BIT_MAX];
-
-
-static bool ktd_init_is(struct ktask_data *ktd, struct td *td)
-{
- return !!(td->init_flags & (1 << ktd->nr_bit));
-}
-
-static void ktd_init(struct ktask_data *ktd, struct td *td,
- struct task_struct *task)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&td->flags_lock, flags);
- if (ktd_init_is(ktd, td))
- goto unlock;
-
- if (ktd->init)
- ktd->init(task, swap_td_raw(&ktd->td_raw, task));
-
- td->init_flags |= 1 << ktd->nr_bit;
-
-unlock:
- spin_unlock_irqrestore(&td->flags_lock, flags);
-}
-
-static void ktd_exit_no_lock(struct ktask_data *ktd, struct td *td,
- struct task_struct *task)
-{
- if (ktd_init_is(ktd, td)) {
- if (ktd->exit)
- ktd->exit(task, swap_td_raw(&ktd->td_raw, task));
-
- td->init_flags &= ~(1 << ktd->nr_bit);
- }
-}
-
-static void ktd_exit_all(struct td *td, struct task_struct *task)
-{
- unsigned long flags;
- unsigned long init_flags;
- int nr_bit = 0;
-
- spin_lock_irqsave(&td->flags_lock, flags);
- init_flags = td->init_flags;
- do {
- if (init_flags & 1)
- ktd_exit_no_lock(ktd_array[nr_bit], td, task);
-
- ++nr_bit;
- init_flags >>= 1;
- } while (init_flags);
- td->init_flags = 0;
- spin_unlock_irqrestore(&td->flags_lock, flags);
-}
-
-
-static bool task_prepare_is(struct task_struct *task)
-{
- return !!(task_job(task) & kTD_JOBCTL_PREPARE);
-}
-
-static void task_prepare_set(struct task_struct *task)
-{
- if (!(task_job(task) & kTD_JOBCTL_PREPARE))
- task_job(task) |= kTD_JOBCTL_PREPARE;
- else
- WARN(1, KTD_PREFIX "already prepare");
-
- ++preparing_cnt;
-}
-
-static void task_prepare_clear(struct task_struct *task)
-{
- if (task_job(task) & kTD_JOBCTL_PREPARE)
- task_job(task) &= ~kTD_JOBCTL_PREPARE;
- else
- WARN(1, KTD_PREFIX "is not prepare");
-
- --preparing_cnt;
-}
-
-static struct task_struct *task_by_td(struct td *td)
-{
- return td->task;
-}
-
-static struct td *td_by_task(struct task_struct *task)
-{
- return (struct td *)swap_td_raw(&td_raw, task);
-}
-
-
-static void task_prepare(struct task_struct *task, struct td *td,
- struct ktask_data *ktd)
-{
- unsigned long flags;
-
- write_lock_irqsave(&prepare_lock, flags);
-
- /* skip multi-preparing task */
- if (task_prepare_is(task))
- goto unlock;
-
- task_prepare_set(task);
-
- INIT_LIST_HEAD(&td->list);
- td->task = task;
- spin_lock_init(&td->flags_lock);
- td->init_flags = 0;
-
- /* add to prepare_list */
- list_add(&td->list, &prepare_list);
-
-unlock:
- write_unlock_irqrestore(&prepare_lock, flags);
-}
-
-static void ktd_exit_all(struct td *td, struct task_struct *task);
-
-static void td_prepare_clear_no_lock(struct td *td, struct task_struct *task)
-{
- if (task_prepare_is(task)) {
- task_prepare_clear(task);
-
- ktd_exit_all(td, task);
-
- /* delete from prepare_list */
- list_del(&td->list);
- }
-}
-
-static void td_prepare_clear(struct td *td, struct task_struct *task)
-{
- unsigned long flags;
-
- write_lock_irqsave(&prepare_lock, flags);
- td_prepare_clear_no_lock(td, task);
- write_unlock_irqrestore(&prepare_lock, flags);
-}
-
-void *swap_ktd(struct ktask_data *ktd, struct task_struct *task)
-{
- struct td *td = td_by_task(task);
-
- if (!likely(task_prepare_is(task)))
- task_prepare(task, td, ktd);
-
- if (!likely(ktd_init_is(ktd, td)))
- ktd_init(ktd, td, task);
-
- return swap_td_raw(&ktd->td_raw, task);
-}
-EXPORT_SYMBOL_GPL(swap_ktd);
-
-
-static int ktd_nr_get_free_bit(void)
-{
- int bit;
-
- for (bit = 0; bit < KTD_BIT_MAX; ++bit) {
- if (ktd_array[bit] == NULL)
- return bit;
- }
-
- return -ENOMEM;
-}
-
-int swap_ktd_reg(struct ktask_data *ktd)
-{
- int ret;
- int free_bit;
-
- mutex_lock(&mutex_ktd_nr);
- free_bit = ktd_nr_get_free_bit();
-
- if (free_bit < 0) {
- ret = free_bit;
- goto unlock;
- }
-
- ret = swap_td_raw_reg(&ktd->td_raw, ktd->size);
- if (ret)
- goto unlock;
-
- ktd->nr_bit = free_bit;
- ktd_array[free_bit] = ktd;
-unlock:
- mutex_unlock(&mutex_ktd_nr);
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_ktd_reg);
-
-void swap_ktd_unreg(struct ktask_data *ktd)
-{
- struct td *td;
- unsigned long flags;
-
- /* exit all task */
- read_lock_irqsave(&prepare_lock, flags);
- list_for_each_entry(td, &prepare_list, list) {
- spin_lock(&td->flags_lock);
- ktd_exit_no_lock(ktd, td, task_by_td(td));
- spin_unlock(&td->flags_lock);
- }
- read_unlock_irqrestore(&prepare_lock, flags);
-
- mutex_lock(&mutex_ktd_nr);
-
- ktd_array[ktd->nr_bit] = NULL;
- swap_td_raw_unreg(&ktd->td_raw);
-
- mutex_unlock(&mutex_ktd_nr);
-}
-EXPORT_SYMBOL_GPL(swap_ktd_unreg);
-
-
-static void do_put_task(struct task_struct *task)
-{
- if (task_prepare_is(task))
- td_prepare_clear(td_by_task(task), task);
-}
-
-#ifdef CONFIG_SWAP_HOOK_TASKDATA
-
-#include <swap/hook_taskdata.h>
-
-static struct hook_taskdata hook_taskdata = {
- .owner = THIS_MODULE,
- .put_task = do_put_task,
-};
-
-static int taskdata_init(void)
-{
- return hook_taskdata_reg(&hook_taskdata);
-}
-
-static void taskdata_uninit(void)
-{
- hook_taskdata_unreg(&hook_taskdata);
-}
-
-void swap_ktd_put_task(struct task_struct *task)
-{
-}
-
-#else /* CONFIG_SWAP_HOOK_TASKDATA */
-
-static int taskdata_init(void)
-{
- return 0;
-}
-
-static void taskdata_uninit(void)
-{
-}
-
-void swap_ktd_put_task(struct task_struct *task)
-{
- do_put_task(task);
-}
-
-#endif /* CONFIG_SWAP_HOOK_TASKDATA */
-
-int swap_ktd_init(void)
-{
- int ret;
-
- WARN(preparing_cnt, KTD_PREFIX "preparing_cnt=%d", preparing_cnt);
-
- preparing_cnt = 0;
-
- ret = swap_td_raw_reg(&td_raw, sizeof(struct td));
- if (ret) {
- pr_err(KTD_PREFIX "registration failed, ret=%d", ret);
- return ret;
- }
-
- ret = taskdata_init();
- if (ret) {
- swap_td_raw_unreg(&td_raw);
- pr_err(KTD_PREFIX "failed to initialize, ret=%d\n", ret);
- }
-
- return ret;
-}
-
-void swap_ktd_uninit_top(void)
-{
- struct td *td;
- unsigned long flags;
-
- /* get injected tasks */
- write_lock_irqsave(&prepare_lock, flags);
- list_for_each_entry(td, &prepare_list, list) {
- get_task_struct(task_by_td(td));
- }
- write_unlock_irqrestore(&prepare_lock, flags);
-}
-
-void swap_ktd_uninit_bottom(void)
-{
- struct td *td, *n;
- unsigned long flags;
-
- /* remove td injection from tasks and put tasks */
- write_lock_irqsave(&prepare_lock, flags);
- list_for_each_entry_safe(td, n, &prepare_list, list) {
- struct task_struct *task = task_by_td(td);
-
- td_prepare_clear_no_lock(td, task);
- put_task_struct(task);
- }
- write_unlock_irqrestore(&prepare_lock, flags);
-
- taskdata_uninit();
- swap_td_raw_unreg(&td_raw);
-
- WARN(preparing_cnt, KTD_PREFIX "preparing_cnt=%d", preparing_cnt);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_TD_H
-#define _SWAP_TD_H
-
-
-#include <linux/list.h>
-#include "swap_td_raw.h"
-
-struct task_struct;
-
-struct ktask_data {
- int nr_bit;
- struct td_raw td_raw;
-
- /* init() and exit() may be called in atomic context */
- void (*init)(struct task_struct *, void *);
- void (*exit)(struct task_struct *, void *);
- unsigned long size;
-};
-
-
-int swap_ktd_reg(struct ktask_data *ktd);
-void swap_ktd_unreg(struct ktask_data *ktd);
-
-void *swap_ktd(struct ktask_data *ktd, struct task_struct *task);
-
-int swap_ktd_init(void);
-void swap_ktd_uninit_top(void);
-void swap_ktd_uninit_bottom(void);
-void swap_ktd_put_task(struct task_struct *task);
-
-
-#endif /* _SWAP_TD_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <kprobe/swap_ktd.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <master/swap_initializer.h>
-
-
-static void ktd_uninit(void)
-{
- swap_ktd_uninit_top();
- swap_ktd_uninit_bottom();
-}
-
-SWAP_LIGHT_INIT_MODULE(init_module_dependencies, swap_ktd_init, ktd_uninit, NULL, NULL);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/**
- * kprobe/swap_slots.c
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) IBM Corporation, 2002, 2004
- * Copyright (C) Samsung Electronics, 2006-2012
- *
- * @section DESCRIPTION
- *
- * SWAP slots implementation.
- */
-
-
-#include <linux/module.h>
-#include <linux/rculist.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#include "swap_slots.h"
-#include "swap_kprobes_deps.h"
-
-
-/**
- * @struct chunk
- * @brief Chunk of memory for trampolines.
- * @var chunk::data
- * Chunk data.
- * @var chunk::first_available
- * Index of the first available block.
- * @var chunk::count_available
- * Count of the blocks.
- * @var chunk::lock
- * Chunk's lock.
- * @var chunk::size
- * Count of the blocks in chunk.
- * @var chunk::index
- * Pointer to allocated memory.
- */
-struct chunk {
- unsigned long *data;
- unsigned long first_available;
- unsigned long count_available;
-
- spinlock_t lock;
- unsigned long size;
- unsigned long *index;
-};
-
-/**
- * @struct fixed_alloc
- * @brief Item of fixed allocs list.
- * @var fixed_alloc::hlist
- * Fixed alloc hash list node.
- * @var fixed_alloc::chunk
- * Chunk.
- */
-struct fixed_alloc {
- struct hlist_node hlist;
- struct chunk chunk;
-};
-
-static int chunk_init(struct chunk *chunk, void *data,
- size_t size, size_t size_block)
-{
- unsigned long i;
- unsigned long *p;
-
- spin_lock_init(&chunk->lock);
- chunk->data = (unsigned long *)data;
- chunk->first_available = 0;
- chunk->count_available = size / size_block;
- chunk->size = chunk->count_available;
-
- chunk->index = kmalloc(sizeof(*chunk->index)*chunk->count_available,
- GFP_ATOMIC);
-
- if (chunk->index == NULL) {
- pr_err("%s: failed to allocate memory\n", __func__);
- return -ENOMEM;
- }
-
- p = chunk->index;
- for (i = 0; i != chunk->count_available; ++p)
- *p = ++i;
-
- return 0;
-}
-
-static void chunk_uninit(struct chunk *chunk)
-{
- kfree(chunk->index);
-}
-
-static void *chunk_allocate(struct chunk *chunk, size_t size_block)
-{
- unsigned long *ret;
-
- if (!chunk->count_available)
- return NULL;
-
- spin_lock(&chunk->lock);
- ret = chunk->data + chunk->first_available*size_block;
- chunk->first_available = chunk->index[chunk->first_available];
- --chunk->count_available;
- spin_unlock(&chunk->lock);
-
- return ret;
-}
-
-static void chunk_deallocate(struct chunk *chunk, void *p, size_t size_block)
-{
- unsigned long idx = ((unsigned long *)p - chunk->data)/size_block;
-
- spin_lock(&chunk->lock);
- chunk->index[idx] = chunk->first_available;
- chunk->first_available = idx;
- ++chunk->count_available;
- spin_unlock(&chunk->lock);
-}
-
-static inline int chunk_check_ptr(struct chunk *chunk, void *p, size_t size)
-{
- if ((chunk->data <= (unsigned long *)p) &&
- ((chunk->data + size/sizeof(chunk->data)) > (unsigned long *)p))
- return 1;
-
- return 0;
-}
-
-static inline int chunk_free(struct chunk *chunk)
-{
- return (chunk->count_available == chunk->size);
-}
-
-static struct fixed_alloc *create_fixed_alloc(struct slot_manager *sm)
-{
- int ret;
- void *data;
- struct fixed_alloc *fa;
-
- fa = kmalloc(sizeof(*fa), GFP_ATOMIC);
- if (fa == NULL)
- return NULL;
-
- data = sm->alloc(sm);
- if (data == NULL)
- goto free_fa;
-
- ret = chunk_init(&fa->chunk, data,
- PAGE_SIZE / sizeof(unsigned long), sm->slot_size);
- if (ret)
- goto free_sm;
-
- return fa;
-
-free_sm:
- sm->free(sm, data);
-free_fa:
- kfree(fa);
- return NULL;
-}
-
-static void free_fixed_alloc(struct slot_manager *sm, struct fixed_alloc *fa)
-{
- chunk_uninit(&fa->chunk);
- sm->free(sm, fa->chunk.data);
- kfree(fa);
-}
-
-
-/**
- * @brief Allocates slot for slot manager.
- *
- * @param[in,out] sm Slot manager that should be filled.
- * @return Pointer to allocated slot.
- */
-void *swap_slot_alloc(struct slot_manager *sm)
-{
- void *free_slot;
- struct fixed_alloc *fa;
- DECLARE_NODE_PTR_FOR_HLIST(pos);
-
- swap_hlist_for_each_entry_rcu(fa, pos, &sm->page_list, hlist) {
- free_slot = chunk_allocate(&fa->chunk, sm->slot_size);
- if (free_slot)
- return free_slot;
- }
-
- fa = create_fixed_alloc(sm);
- if (fa == NULL)
- return NULL;
-
- INIT_HLIST_NODE(&fa->hlist);
- hlist_add_head_rcu(&fa->hlist, &sm->page_list);
-
- return chunk_allocate(&fa->chunk, sm->slot_size);
-}
-EXPORT_SYMBOL_GPL(swap_slot_alloc);
-
-/**
- * @brief Releases allocated slot.
- *
- * @param sm Pointer to the target slot manager.
- * @param slot Pointer to the target slot.
- * @return Void.
- */
-void swap_slot_free(struct slot_manager *sm, void *slot)
-{
- struct fixed_alloc *fa;
- DECLARE_NODE_PTR_FOR_HLIST(pos);
-
- if (slot == NULL)
- return;
-
- swap_hlist_for_each_entry_rcu(fa, pos, &sm->page_list, hlist) {
- if (!chunk_check_ptr(&fa->chunk, slot, PAGE_SIZE))
- continue;
-
- chunk_deallocate(&fa->chunk, slot, sm->slot_size);
-
- if (chunk_free(&fa->chunk)) {
- hlist_del_rcu(&fa->hlist);
- free_fixed_alloc(sm, fa);
- }
-
- return;
- }
-
- panic("%s: slot=%p is not data base\n", __func__, slot);
-}
-EXPORT_SYMBOL_GPL(swap_slot_free);
+++ /dev/null
-/**
- * @file kprobe/swap_slots.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
- * Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) IBM Corporation, 2002, 2004
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * SWAP slots interface declaration.
- */
-
-#ifndef _SWAP_SLOTS_H
-#define _SWAP_SLOTS_H
-
-#include <linux/types.h>
-
-/**
- * @struct slot_manager
- * @brief Manage slots.
- * @var slot_manager::slot_size
- * Size of the slot.
- * @var slot_manager::alloc
- * Memory allocation callback.
- * @var slot_manager::free
- * Memory release callback.
- * @var slot_manager::page_list
- * List of pages.
- * @var slot_manager::data
- * Slot manager data. task_struct pointer usually stored here.
- */
-struct slot_manager {
- unsigned long slot_size; /* FIXME: allocated in long (4 byte) */
- void *(*alloc)(struct slot_manager *sm);
- void (*free)(struct slot_manager *sm, void *ptr);
- struct hlist_head page_list;
- void *data;
-};
-
-void *swap_slot_alloc(struct slot_manager *sm);
-void swap_slot_free(struct slot_manager *sm, void *slot);
-
-#endif /* _SWAP_SLOTS_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2014 Vasiliy Ulyanov <v.ulyanov@samsung.com>
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/sched.h>
-#include <linux/module.h>
-#include "swap_td_raw.h"
-
-
-#define TD_OFFSET 1 /* skip STACK_END_MAGIC */
-#define TD_PREFIX "[SWAP_TD_RAW] "
-#define TD_STACK_USAGE_MAX 0x200
-#define TD_CHUNK_MIN sizeof(long)
-
-
-static DEFINE_MUTEX(mutex_stack_usage);
-static unsigned long stack_usage = 0;
-static LIST_HEAD(td_raw_list);
-
-
-/*
- * take small area from stack
- *
- * 0x00 +--------------------------+
- * | STACK_END_MAGIC |
- * +--------------------------+ <-- bottom of stack;
- * | |
- * | stack |
- * | |
- * 0xff
- *
- */
-
-static void *bottom_of_stack(struct task_struct *task)
-{
- return (void *)(end_of_stack(task) + TD_OFFSET);
-}
-
-int swap_td_raw_reg(struct td_raw *raw, unsigned long size)
-{
- int ret = 0;
-
- size = (size / TD_CHUNK_MIN + !!(size % TD_CHUNK_MIN)) * TD_CHUNK_MIN;
-
- mutex_lock(&mutex_stack_usage);
- if (stack_usage + size > TD_STACK_USAGE_MAX) {
- pr_warn(TD_PREFIX "free stack ended: usage=%ld size=%ld\n",
- stack_usage, size);
- ret = -ENOMEM;
- goto unlock;
- }
-
- raw->offset = stack_usage;
-
- INIT_LIST_HEAD(&raw->list);
- list_add(&raw->list, &td_raw_list);
-
- stack_usage += size;
-
-unlock:
- mutex_unlock(&mutex_stack_usage);
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_td_raw_reg);
-
-void swap_td_raw_unreg(struct td_raw *raw)
-{
- mutex_lock(&mutex_stack_usage);
-
- list_del(&raw->list);
- if (list_empty(&td_raw_list))
- stack_usage = 0;
-
- mutex_unlock(&mutex_stack_usage);
-}
-EXPORT_SYMBOL_GPL(swap_td_raw_unreg);
-
-void *swap_td_raw(struct td_raw *raw, struct task_struct *task)
-{
- return bottom_of_stack(task) + raw->offset;
-}
-EXPORT_SYMBOL_GPL(swap_td_raw);
-
-int swap_td_raw_init(void)
-{
- WARN_ON(stack_usage);
-
- stack_usage = 0;
-
- return 0;
-}
-
-void swap_td_raw_uninit(void)
-{
- WARN_ON(!list_empty(&td_raw_list));
- WARN_ON(stack_usage);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2014 Vasiliy Ulyanov <v.ulyanov@samsung.com>
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_TD_RAW_H
-#define _SWAP_TD_RAW_H
-
-
-#include <linux/list.h>
-
-
-struct td_raw {
- struct list_head list;
- unsigned long offset;
-};
-
-
-int swap_td_raw_reg(struct td_raw *raw, unsigned long size);
-void swap_td_raw_unreg(struct td_raw *raw);
-
-void *swap_td_raw(struct td_raw *raw, struct task_struct *task);
-
-int swap_td_raw_init(void);
-void swap_td_raw_uninit(void);
-
-
-#endif /* _SWAP_TD_RAW_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers \
- $(src)/../writer/Module.symvers
-
-obj-m := swap_ks_features.o
-swap_ks_features-y := ks_features.o \
- ks_features_data.o \
- ks_map.o \
- file_ops.o \
- ksf_msg.o
+++ /dev/null
-/**
- * ks_features/features_data.c
- * @author Vyacheslav Cherkashin: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features
- */
-
-
-#include "ksf_msg.h"
-#include "syscall_list.h"
-
-
-/**
- * @struct feature
- * Feature description.
- * @var feature::cnt
- * Syscalls count.
- * @var feature::feature_list
- * Pointer to feature's syscall list.
- * @var feature::type
- * Featue subtype.
- * @var feature::enable
- * Is feature enable.
- */
-struct feature {
- size_t cnt;
- enum syscall_id *feature_list;
- enum probe_t type;
-
- unsigned enable:1;
-};
-
-/**
- * @def X
- * X-macros for syscall list.
- */
-#define X(name, args) id_sys_##name,
-/**
- * @enum syscall_id
- * Syscall list
- */
-enum syscall_id {
- SYSCALL_LIST
-};
-#undef X
-
-static enum syscall_id id_none[] = {};
-
-static enum syscall_id id_file[] = {
- id_sys_acct,
- id_sys_mount,
-/* TODO:
- * id_sys_umount,
- */
- id_sys_truncate,
-/* TODO:
- * id_sys_stat,
- */
- id_sys_statfs,
- id_sys_statfs64,
-/* TODO:
- * id_sys_lstat,
- */
- id_sys_stat64,
- id_sys_fstat64,
- id_sys_lstat64,
- id_sys_truncate64,
- id_sys_ftruncate64,
- id_sys_setxattr,
- id_sys_getxattr,
- id_sys_listxattr,
- id_sys_removexattr,
- id_sys_chroot,
- id_sys_mknod,
- id_sys_link,
- id_sys_symlink,
- id_sys_unlink,
- id_sys_rename,
- id_sys_chmod,
- id_sys_readlink,
- id_sys_creat,
- id_sys_open,
- id_sys_access,
- id_sys_chown,
-/* TODO:
- * id_sys_chown16,
- * id_sys_utime,
- */
- id_sys_utimes,
- id_sys_pread64,
- id_sys_pwrite64,
- id_sys_preadv,
- id_sys_pwritev,
- id_sys_getcwd,
- id_sys_mkdir,
- id_sys_chdir,
- id_sys_rmdir,
- id_sys_swapon,
- id_sys_swapoff,
- id_sys_uselib,
- id_sys_mknodat,
- id_sys_mkdirat,
- id_sys_unlinkat,
- id_sys_symlinkat,
- id_sys_linkat,
- id_sys_renameat,
- id_sys_futimesat,
- id_sys_faccessat,
- id_sys_fchmodat,
- id_sys_fchownat,
- id_sys_openat,
-/* TODO:
- * id_sys_newfstatat,
- */
- id_sys_readlinkat,
- id_sys_utimensat,
- id_sys_fanotify_mark,
- id_sys_execve,
- id_sys_name_to_handle_at,
- id_sys_open_by_handle_at
-};
-
-static enum syscall_id id_ipc[] = {
- id_sys_msgget,
- id_sys_msgsnd,
- id_sys_msgrcv,
- id_sys_msgctl,
- id_sys_semget,
- id_sys_semop,
- id_sys_semctl,
- id_sys_semtimedop,
- id_sys_shmat,
- id_sys_shmget,
- id_sys_shmdt,
- id_sys_shmctl,
-/* TODO:
- * id_sys_ipc
- */
-};
-
-static enum syscall_id id_net[] = {
- id_sys_shutdown,
- id_sys_sendfile,
- id_sys_sendfile64,
- id_sys_setsockopt,
- id_sys_getsockopt,
- id_sys_bind,
- id_sys_connect,
- id_sys_accept,
- id_sys_accept4,
- id_sys_getsockname,
- id_sys_getpeername,
- id_sys_send,
- id_sys_sendto,
- id_sys_sendmsg,
- id_sys_sendmmsg,
- id_sys_recv,
- id_sys_recvfrom,
- id_sys_recvmsg,
- id_sys_recvmmsg,
- id_sys_socket,
- id_sys_socketpair,
-/* TODO:
- * id_sys_socketcall,
- */
- id_sys_listen
-};
-
-static enum syscall_id id_process[] = {
- id_sys_exit,
- id_sys_exit_group,
- id_sys_wait4,
- id_sys_waitid,
-/* TODO:
- * id_sys_waitpid,
- */
- id_sys_rt_tgsigqueueinfo,
- id_sys_unshare,
- id_sys_fork,
- id_sys_vfork,
-/* TODO: add support CONFIG_CLONE_BACKWARDS
- * id_sys_clone,
- * id_sys_clone,
- */
- id_sys_execve
-};
-
-static enum syscall_id id_signal[] = {
- id_sys_sigpending,
- id_sys_sigprocmask,
-/* TODO:
- * id_sys_sigaltstack,
- */
-/* TODO: add support CONFIG_OLD_SIGSUSPEND and CONFIG_OLD_SIGSUSPEND3
- * id_sys_sigsuspend,
- * id_sys_sigsuspend,
- */
- id_sys_rt_sigsuspend,
- id_sys_sigaction,
- id_sys_rt_sigaction,
- id_sys_rt_sigprocmask,
- id_sys_rt_sigtimedwait,
- id_sys_rt_tgsigqueueinfo,
- id_sys_kill,
- id_sys_tgkill,
-/* TODO:
- * id_sys_signal,
- */
- id_sys_pause,
- id_sys_signalfd,
- id_sys_signalfd4
-};
-
-static enum syscall_id id_desc[] = {
- id_sys_fgetxattr,
- id_sys_flistxattr,
- id_sys_fremovexattr,
-/* TODO:
- * id_sys_fadvise64_64,
- */
- id_sys_pipe2,
- id_sys_dup3,
- id_sys_sendfile,
- id_sys_sendfile64,
- id_sys_preadv,
- id_sys_pwritev,
- id_sys_epoll_create1,
- id_sys_epoll_ctl,
- id_sys_epoll_wait,
- id_sys_epoll_pwait,
- id_sys_inotify_init,
- id_sys_inotify_init1,
- id_sys_inotify_add_watch,
- id_sys_inotify_rm_watch,
- id_sys_mknodat,
- id_sys_mkdirat,
- id_sys_unlinkat,
- id_sys_symlinkat,
- id_sys_linkat,
- id_sys_renameat,
- id_sys_futimesat,
- id_sys_faccessat,
- id_sys_fchmodat,
- id_sys_fchownat,
- id_sys_openat,
-/* TODO:
- * id_sys_newfstatat,
- */
- id_sys_readlinkat,
- id_sys_utimensat,
- id_sys_splice,
- id_sys_vmsplice,
- id_sys_tee,
- id_sys_signalfd,
- id_sys_signalfd4,
- id_sys_timerfd_create,
- id_sys_timerfd_settime,
- id_sys_timerfd_gettime,
- id_sys_eventfd,
- id_sys_eventfd2,
- id_sys_fallocate,
- id_sys_pselect6,
- id_sys_ppoll,
- id_sys_fanotify_init,
- id_sys_fanotify_mark,
- id_sys_syncfs,
-/* TODO:
- * id_sys_mmap_pgoff,
- * id_sys_old_mmap,
- */
- id_sys_name_to_handle_at,
- id_sys_setns
-};
-
-/**
- * @def CREATE_FEATURE
- * Feature initialization.
- */
-#define CREATE_FEATURE(x, _type) \
-{ \
- .cnt = sizeof(x) / sizeof(enum syscall_id), \
- .feature_list = x, \
- .type = _type, \
- .enable = 0 \
-}
-
-static struct feature features[] = {
- CREATE_FEATURE(id_none, PT_KS_NONE),
- CREATE_FEATURE(id_file, PT_KS_FILE),
- CREATE_FEATURE(id_ipc, PT_KS_IPC),
- CREATE_FEATURE(id_process, PT_KS_PROCESS),
- CREATE_FEATURE(id_signal, PT_KS_SIGNAL),
- CREATE_FEATURE(id_net, PT_KS_NETWORK),
- CREATE_FEATURE(id_desc, PT_KS_DESC)
-};
-
-/**
- * @enum
- * Defines feature_cnt.
- */
-enum {
- feature_cnt = sizeof(features) / sizeof(struct feature)
-};
-
-static int feature_index(struct feature *f)
-{
- return f - features;
-}
+++ /dev/null
-#include <linux/kconfig.h>
-
-#ifndef CONFIG_SWAP_HOOK_SYSCALL
-
-#include <linux/module.h>
-#include <linux/dcache.h>
-#include <linux/percpu.h>
-#include <linux/namei.h>
-#include <linux/fcntl.h>
-#include <linux/stat.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <kprobe/swap_kprobes.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <writer/event_filter.h>
-#include "ks_map.h"
-#include "ksf_msg.h"
-#include "file_ops.h"
-
-#define FOPS_PREFIX "[FILE_OPS] "
-
-#define PT_FILE 0x4 /* probe type FILE(04) */
-
-/* path buffer size */
-enum { PATH_LEN = 512 };
-
-struct file_probe {
- int id;
- const char *args;
- int subtype;
- struct kretprobe rp;
-};
-
-#define to_file_probe(_rp) container_of(_rp, struct file_probe, rp)
-
-/* common private data */
-struct file_private {
- struct dentry *dentry;
-};
-
-/* open/creat private data */
-struct open_private {
- int dfd;
- const char __user *name;
- int ret;
-};
-
-/* locks private data */
-struct flock_private {
- struct dentry *dentry;
- int subtype;
-};
-
-#define DECLARE_HANDLER(_name) \
- int _name(struct kretprobe_instance *, struct pt_regs *)
-
-/* generic handlers forward declaration */
-static DECLARE_HANDLER(generic_entry_handler);
-static DECLARE_HANDLER(generic_ret_handler);
-/* open/creat handlers */
-static DECLARE_HANDLER(open_entry_handler);
-static DECLARE_HANDLER(open_ret_handler);
-/* lock handlers */
-static DECLARE_HANDLER(lock_entry_handler);
-static DECLARE_HANDLER(lock_ret_handler);
-/* filp_close helper handlers */
-static DECLARE_HANDLER(filp_close_entry_handler);
-static DECLARE_HANDLER(filp_close_ret_handler);
-
-#define FILE_OPS_OPEN_LIST \
- X(sys_open, sdd), \
- X(sys_openat, dsdd), \
- X(sys_creat, sd)
-
-#define FILE_OPS_CLOSE_LIST \
- X(sys_close, d)
-
-#define FILE_OPS_READ_LIST \
- X(sys_read, dpd), \
- X(sys_readv, dpd), \
- X(sys_pread64, dpxx), \
- X(sys_preadv, dpxxx)
-
-#define FILE_OPS_WRITE_LIST \
- X(sys_write, dpd), \
- X(sys_writev, dpd), \
- X(sys_pwrite64, dpxx), \
- X(sys_pwritev, dpxxx)
-
-#define FILE_OPS_LOCK_LIST \
- X(sys_fcntl, ddd), \
- X(sys_fcntl64, ddd), \
- X(sys_flock, dd)
-
-#define FILE_OPS_LIST \
- FILE_OPS_OPEN_LIST, \
- FILE_OPS_CLOSE_LIST, \
- FILE_OPS_READ_LIST, \
- FILE_OPS_WRITE_LIST, \
- FILE_OPS_LOCK_LIST
-
-#define X(_name, _args) \
- id_##_name
-enum {
- FILE_OPS_LIST
-};
-#undef X
-
-#define __FILE_PROBE_INITIALIZER(_name, _args, _subtype, _dtype, _entry, _ret) \
- { \
- .id = id_##_name, \
- .args = #_args, \
- .subtype = _subtype, \
- .rp = { \
- .kp.symbol_name = #_name, \
- .data_size = sizeof(_dtype), \
- .entry_handler = _entry, \
- .handler = _ret, \
- } \
- }
-
-static struct file_probe fprobes[] = {
-#define X(_name, _args) \
- [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_OPEN, \
- struct open_private, \
- open_entry_handler, \
- open_ret_handler)
- FILE_OPS_OPEN_LIST,
-#undef X
-
-#define X(_name, _args) \
- [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_CLOSE, \
- struct file_private, \
- generic_entry_handler, \
- generic_ret_handler)
- FILE_OPS_CLOSE_LIST,
-#undef X
-
-#define X(_name, _args) \
- [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_READ, \
- struct file_private, \
- generic_entry_handler, \
- generic_ret_handler)
- FILE_OPS_READ_LIST,
-#undef X
-
-#define X(_name, _args) \
- [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_WRITE, \
- struct file_private, \
- generic_entry_handler, \
- generic_ret_handler)
- FILE_OPS_WRITE_LIST,
-#undef X
-
-#define X(_name, _args) \
- [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_OTHER, \
- struct flock_private, \
- lock_entry_handler, \
- lock_ret_handler)
- FILE_OPS_LOCK_LIST
-#undef X
-};
-
-static void *fops_key_func(void *);
-static int fops_cmp_func(void *, void *);
-
-/* percpu buffer to hold the filepath inside handlers */
-static DEFINE_PER_CPU(char[PATH_LEN], __path_buf);
-
-/* map to hold 'interesting' files */
-static DEFINE_MAP(__map, fops_key_func, fops_cmp_func);
-static DEFINE_RWLOCK(__map_lock);
-
-/* enabled/disabled flag */
-static int fops_enabled;
-static DEFINE_MUTEX(fops_lock);
-
-/* GET/PUT debug stuff */
-static int file_get_put_balance;
-static int dentry_get_put_balance;
-
-/* helper probe */
-static struct kretprobe filp_close_krp = {
- .kp.symbol_name = "filp_close",
- .data_size = 0,
- .entry_handler = filp_close_entry_handler,
- .handler = filp_close_ret_handler
-};
-
-/* should be called only from handlers (with preemption disabled) */
-static inline char *fops_path_buf(void)
-{
- return __get_cpu_var(__path_buf);
-}
-
-static inline unsigned fops_dcount(const struct dentry *dentry)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
- return dentry->d_count;
-#else
- return d_count(dentry);
-#endif
-}
-
-/* kernel function args */
-#define fops_karg(_type, _regs, _idx) ((_type)swap_get_karg(_regs, _idx))
-/* syscall args */
-#define fops_sarg(_type, _regs, _idx) ((_type)swap_get_sarg(_regs, _idx))
-/* retval */
-#define fops_ret(_type, _regs) ((_type)regs_return_value(_regs))
-
-#define F_ADDR(_rp) ((unsigned long)(_rp)->kp.addr) /* function address */
-#define R_ADDR(_ri) ((unsigned long)(_ri)->ret_addr) /* return adress */
-
-static void *fops_key_func(void *data)
-{
- /* use ((struct dentry *)data)->d_inode pointer as map key to handle
- * symlinks/hardlinks the same way as the original file */
- return data;
-}
-
-static int fops_cmp_func(void *key_a, void *key_b)
-{
- return key_a - key_b;
-}
-
-static inline struct map *__get_map(void)
-{
- return &__map;
-}
-
-static inline struct map *get_map_read(void)
-{
- read_lock(&__map_lock);
-
- return __get_map();
-}
-
-static inline void put_map_read(struct map *map)
-{
- read_unlock(&__map_lock);
-}
-
-static inline struct map *get_map_write(void)
-{
- write_lock(&__map_lock);
-
- return __get_map();
-}
-
-static inline void put_map_write(struct map *map)
-{
- write_unlock(&__map_lock);
-}
-
-static struct file *__fops_fget(int fd)
-{
- struct file *file;
-
- file = fget(fd);
- if (IS_ERR_OR_NULL(file))
- file = NULL;
- else
- file_get_put_balance++;
-
- return file;
-}
-
-static void __fops_fput(struct file *file)
-{
- file_get_put_balance--;
- fput(file);
-}
-
-static struct dentry *__fops_dget(struct dentry *dentry)
-{
- dentry_get_put_balance++;
-
- return dget(dentry);
-}
-
-static void __fops_dput(struct dentry *dentry)
-{
- dentry_get_put_balance--;
- dput(dentry);
-}
-
-static int fops_dinsert(struct dentry *dentry)
-{
- struct map *map;
- int ret;
-
- map = get_map_write();
- ret = insert(map, __fops_dget(dentry));
- put_map_write(map);
-
- if (ret)
- __fops_dput(dentry);
-
- /* it's ok if dentry is already inserted */
- return ret == -EEXIST ? 0 : ret;
-}
-
-static struct dentry *fops_dsearch(struct dentry *dentry)
-{
- struct dentry *found;
- struct map *map;
-
- map = get_map_read();
- found = search(map, map->key_f(dentry));
- put_map_read(map);
-
- return found;
-}
-
-static struct dentry *fops_dremove(struct dentry *dentry)
-{
- struct dentry *removed;
- struct map *map;
-
- map = get_map_write();
- removed = remove(map, map->key_f(dentry));
- put_map_write(map);
-
- if (removed)
- __fops_dput(removed);
-
- return removed;
-}
-
-static int fops_fcheck(struct task_struct *task, struct file *file)
-{
- struct dentry *dentry;
-
- if (!task || !file)
- return -EINVAL;
-
- dentry = file->f_path.dentry;
-
- /* check if it is a regular file */
- if (!S_ISREG(dentry->d_inode->i_mode))
- return -EBADF;
-
- if (check_event(task))
- /* it is 'our' task: just add the dentry to the map */
- return fops_dinsert(dentry) ? : -EAGAIN;
- else
- /* not 'our' task: check if the file is 'interesting' */
- return fops_dsearch(dentry) ? 0 : -ESRCH;
-}
-
-static char *fops_fpath(struct file *file, char *buf, int buflen)
-{
- char *filename;
-
- path_get(&file->f_path);
- filename = d_path(&file->f_path, buf, buflen);
- path_put(&file->f_path);
-
- if (IS_ERR_OR_NULL(filename)) {
- printk(FOPS_PREFIX "d_path FAILED: %ld\n", PTR_ERR(filename));
- buf[0] = '\0';
- filename = buf;
- }
-
- return filename;
-}
-
-static int generic_entry_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
-
- if (rp) {
- struct file_probe *fprobe = to_file_probe(rp);
- struct file_private *priv = (struct file_private *)ri->data;
- int fd = fops_sarg(int, regs, 0);
- struct file *file = __fops_fget(fd);
-
- if (fops_fcheck(current, file) == 0) {
- char *buf = fops_path_buf();
-
- ksf_msg_file_entry(fd, fprobe->subtype,
- fops_fpath(file, buf, PATH_LEN));
-
- priv->dentry = file->f_path.dentry;
- } else {
- priv->dentry = NULL;
- }
-
- if (file)
- __fops_fput(file);
- }
-
- return 0;
-}
-
-static int generic_ret_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
- struct file_private *priv = (struct file_private *)ri->data;
-
- if (rp && priv->dentry)
- ksf_msg_file_exit(regs, 'x');
-
- return 0;
-}
-
-static int open_private_init(const char *args, struct pt_regs *regs,
- struct open_private *priv)
-{
- int ret = 0;
-
- switch (args[0]) {
- case 'd': /* file name: relative to fd */
- if (args[1] != 's') {
- ret = -EINVAL;
- break;
- }
- priv->dfd = fops_sarg(int, regs, 0);
- priv->name = fops_sarg(const char __user *, regs, 1);
- break;
- case 's': /* file name: absolute or relative to CWD */
- priv->dfd = AT_FDCWD;
- priv->name = fops_sarg(const char __user *, regs, 0);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- priv->ret = ret;
-
- return ret;
-}
-
-static int open_entry_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
-
- if (rp) {
- struct file_probe *fprobe = to_file_probe(rp);
- struct open_private *priv = (struct open_private *)ri->data;
-
- open_private_init(fprobe->args, regs, priv);
- /* FIXME entry event will be sent in open_ret_handler: cannot
- * perform a file lookup in atomic context */
- }
-
- return 0;
-}
-
-static int open_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
- struct open_private *priv = (struct open_private *)ri->data;
-
- if (rp && priv->ret == 0) {
- struct file_probe *fprobe = to_file_probe(rp);
- int fd = fops_ret(int, regs);
- struct file *file = __fops_fget(fd);
-
- if (fops_fcheck(current, file) == 0) {
- char *buf = fops_path_buf();
- const char *path = fops_fpath(file, buf, PATH_LEN);
-
- ksf_msg_file_entry_open(fd, fprobe->subtype,
- path, priv->name);
- ksf_msg_file_exit(regs, 'x');
- }
-
- if (file)
- __fops_fput(file);
- }
-
- return 0;
-}
-
-/* wrapper for 'struct flock*' data */
-struct lock_arg {
- int type;
- int whence;
- s64 start;
- s64 len;
-};
-
-/* TODO copy_from_user */
-#define __lock_arg_init(_type, _regs, _arg) \
- do { \
- _type __user *flock = fops_sarg(_type __user *, _regs, 2); \
- _arg->type = flock->l_type; \
- _arg->whence = flock->l_whence; \
- _arg->start = flock->l_start; \
- _arg->len = flock->l_len; \
- } while (0)
-
-static int lock_arg_init(int id, struct pt_regs *regs, struct lock_arg *arg)
-{
- unsigned int cmd = fops_sarg(unsigned int, regs, 1);
- int ret = 0;
-
- switch (id) {
- case id_sys_fcntl:
- if (cmd == F_SETLK || cmd == F_SETLKW)
- __lock_arg_init(struct flock, regs, arg);
- else
- ret = -EINVAL;
- break;
- case id_sys_fcntl64:
- if (cmd == F_SETLK64 || cmd == F_SETLKW64)
- __lock_arg_init(struct flock64, regs, arg);
- else if (cmd == F_SETLK || cmd == F_SETLKW)
- __lock_arg_init(struct flock, regs, arg);
- else
- ret = -EINVAL;
- break;
- case id_sys_flock: /* TODO is it really needed? */
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int lock_entry_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
-
- if (rp) {
- struct file_probe *fprobe = to_file_probe(rp);
- struct flock_private *priv = (struct flock_private *)ri->data;
- int fd = fops_sarg(int, regs, 0);
- struct file *file = __fops_fget(fd);
-
- if (fops_fcheck(current, file) == 0) {
- int subtype = fprobe->subtype;
- struct lock_arg arg;
- char *buf, *filepath;
-
- buf = fops_path_buf();
- filepath = fops_fpath(file, buf, PATH_LEN);
-
- if (lock_arg_init(fprobe->id, regs, &arg) == 0) {
- subtype = arg.type == F_UNLCK ?
- FOPS_LOCK_RELEASE :
- FOPS_LOCK_START;
- ksf_msg_file_entry_lock(fd, subtype, filepath,
- arg.type, arg.whence,
- arg.start, arg.len);
- } else {
- ksf_msg_file_entry(fd, subtype, filepath);
- }
-
- priv->dentry = file->f_path.dentry;
- priv->subtype = subtype;
- } else {
- priv->dentry = NULL;
- }
-
- if (file)
- __fops_fput(file);
- }
-
- return 0;
-}
-
-static int lock_ret_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
- struct flock_private *priv = (struct flock_private *)ri->data;
-
- if (rp && priv->dentry)
- ksf_msg_file_exit(regs, 'x');
-
- return 0;
-}
-
-static int filp_close_entry_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
- struct file *file = fops_karg(struct file *, regs, 0);
-
- if (rp && file && file_count(file)) {
- struct dentry *dentry = file->f_path.dentry;
-
- /* release the file if it is going to be removed soon */
- if (dentry && fops_dcount(dentry) == 2)
- fops_dremove(dentry);
- }
-
- return 0;
-}
-
-static int filp_close_ret_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- return 0;
-}
-
-static void fops_unregister_probes(struct file_probe *fprobes, int cnt)
-{
- int i = cnt;
-
- /* probes are unregistered in reverse order */
- while (--i >= 0) {
- struct kretprobe *rp = &fprobes[i].rp;
-
- swap_unregister_kretprobe(rp);
- printk(FOPS_PREFIX "'%s/%08lx' kretprobe unregistered (%d)\n",
- rp->kp.symbol_name, F_ADDR(rp), i);
- }
-
- /* unregister helper probes */
- swap_unregister_kretprobe(&filp_close_krp);
-}
-
-static int fops_register_probes(struct file_probe *fprobes, int cnt)
-{
- struct kretprobe *rp = &filp_close_krp;
- int ret, i = 0;
-
- /* register helper probes */
- ret = swap_register_kretprobe(rp);
- if (ret)
- goto fail;
-
- /* register syscalls */
- for (i = 0; i < cnt; i++) {
- rp = &fprobes[i].rp;
-
- if (!rp->entry_handler)
- rp->entry_handler = generic_entry_handler;
-
- if (!rp->handler)
- rp->handler = generic_ret_handler;
-
- ret = swap_register_kretprobe(rp);
- if (ret)
- goto fail_unreg;
-
- printk(FOPS_PREFIX "'%s/%08lx' kretprobe registered (%d)\n",
- rp->kp.symbol_name, F_ADDR(rp), i);
- }
-
- return 0;
-
-fail_unreg:
- fops_unregister_probes(fprobes, i);
-
-fail:
- printk(FOPS_PREFIX "Failed to register probe: %s\n",
- rp->kp.symbol_name);
-
- return ret;
-}
-
-static char *__fops_dpath(struct dentry *dentry, char *buf, int buflen)
-{
- static const char *NA = "N/A";
- char *filename = dentry_path_raw(dentry, buf, buflen);
-
- if (IS_ERR_OR_NULL(filename)) {
- printk(FOPS_PREFIX "dentry_path_raw FAILED: %ld\n",
- PTR_ERR(filename));
- strncpy(buf, NA, buflen);
- filename = buf;
- }
-
- return filename;
-}
-
-/* just a simple wrapper for passing to clear function */
-static int __fops_dput_wrapper(void *data, void *arg)
-{
- static char buf[PATH_LEN]; /* called under write lock => static is ok */
- struct dentry *dentry = data;
- struct inode *inode = dentry->d_inode;
-
- printk(FOPS_PREFIX "Releasing dentry(%p/%p/%d): %s\n",
- dentry, inode, inode ? inode->i_nlink : 0,
- __fops_dpath(dentry, buf, PATH_LEN));
- __fops_dput(dentry);
-
- return 0;
-}
-
-bool file_ops_is_init(void)
-{
- return fops_enabled;
-}
-
-int file_ops_init(void)
-{
- int ret = -EINVAL;
-
- mutex_lock(&fops_lock);
-
- if (fops_enabled) {
- printk(FOPS_PREFIX "Handlers already enabled\n");
- goto unlock;
- }
-
- ret = fops_register_probes(fprobes, ARRAY_SIZE(fprobes));
- if (ret == 0)
- fops_enabled = 1;
-
-unlock:
- mutex_unlock(&fops_lock);
-
- return ret;
-}
-
-void file_ops_exit(void)
-{
- struct map *map;
-
- mutex_lock(&fops_lock);
-
- if (!fops_enabled) {
- printk(FOPS_PREFIX "Handlers not enabled\n");
- goto unlock;
- }
-
- /* 1. unregister probes */
- fops_unregister_probes(fprobes, ARRAY_SIZE(fprobes));
-
- /* 2. clear the map */
- map = get_map_write();
- printk(FOPS_PREFIX "Clearing map: entries(%d)\n", map->size);
- clear(map, __fops_dput_wrapper, NULL);
- WARN(file_get_put_balance, "File GET/PUT balance: %d\n",
- file_get_put_balance);
- WARN(dentry_get_put_balance, "Dentry GET/PUT balance: %d\n",
- dentry_get_put_balance);
- put_map_write(map);
-
- /* 3. drop the flag */
- fops_enabled = 0;
-
-unlock:
- mutex_unlock(&fops_lock);
-}
-
-#endif /* CONFIG_SWAP_HOOK_SYSCALL */
+++ /dev/null
-#ifndef __FILE_OPS__
-#define __FILE_OPS__
-
-#include <linux/types.h>
-#include <linux/printk.h>
-
-
-#define FOPS_PREFIX "[FILE_OPS] "
-
-
-/* TODO: add support CONFIG_SWAP_HOOK_SYSCALL */
-#ifdef CONFIG_SWAP_HOOK_SYSCALL
-
-static inline bool file_ops_is_init(void)
-{
- return 0;
-}
-
-static inline bool file_ops_init(void)
-{
- pr_info(FOPS_PREFIX "file_ops is not supported\n");
- return 0;
-}
-
-void file_ops_exit(void)
-{
-}
-
-#else /* CONFIG_SWAP_HOOK_SYSCALL */
-
-bool file_ops_is_init(void);
-int file_ops_init(void);
-void file_ops_exit(void);
-
-#endif /* CONFIG_SWAP_HOOK_SYSCALL */
-
-#endif /* __FILE_OPS__ */
+++ /dev/null
-/**
- * @author Vyacheslav Cherkashin: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features
- */
-
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <ksyms/ksyms.h>
-#include <master/swap_initializer.h>
-#include <writer/event_filter.h>
-#include <swap/hook_syscall.h>
-#include <asm/syscall.h>
-#include "ksf_msg.h"
-#include "ks_features.h"
-#include "syscall_list.h"
-#include "features_data.c"
-#include "writer/kernel_operations.h"
-#include "ks_features_data.h"
-
-
-static void syscall_entry_hendler(struct hook_syscall *self,
- struct pt_regs *regs)
-{
- if (check_event(current)) {
- struct ks_probe *ksp = container_of(self, struct ks_probe, hook);
- const char *fmt = ksp->args;
- const unsigned long func_addr = ksp->sys_addr;
- enum probe_t type = ksp->type;
-
- ksf_msg_entry(regs, func_addr, type, fmt);
- }
-}
-
-static void syscall_exit_hendler(struct hook_syscall *self,
- struct pt_regs *regs)
-{
- if (check_event(current)) {
- struct ks_probe *ksp = container_of(self, struct ks_probe, hook);
- const unsigned long func_addr = ksp->sys_addr;
- const unsigned long ret_addr = get_regs_ret_func(regs);
- enum probe_t type = ksp->type;
-
- ksf_msg_exit(regs, func_addr, ret_addr, type, 'x');
- }
-}
-
-static int register_syscall(size_t id)
-{
- int ret = 0;
-
- if (id >= syscall_cnt)
- return -EINVAL;
-
- ksp[id].hook.entry = syscall_entry_hendler;
- ksp[id].hook.exit = syscall_exit_hendler;
-#ifdef CONFIG_COMPAT
- /* FIXME: add hook_syscall_reg() */
- ret = hook_syscall_reg_compat(&ksp[id].hook, ksp[id].id);
-#else
- ret = hook_syscall_reg(&ksp[id].hook, ksp[id].id);
-#endif
-
- if (ret) {
- pr_err("ERROR: cannot register hook '%s' id=%zd sysid=%u\n",
- get_sys_name(id), id, ksp[id].id);
- }
-
- return ret;
-}
-
-static int unregister_syscall(size_t id)
-{
- if (id >= syscall_cnt)
- return -EINVAL;
-
-#ifdef CONFIG_COMPAT
- /* FIXME: add hook_syscall_unreg() */
- hook_syscall_unreg_compat(&ksp[id].hook);
-#else
- hook_syscall_unreg(&ksp[id].hook);
-#endif
-
- return 0;
-}
-
-static int unregister_multiple_syscalls(size_t *id_p, size_t cnt)
-{
- size_t i;
- int ret = 0;
-
- for (i = 0; i < cnt; i++) {
- ret = unregister_syscall(id_p[i]);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int init_syscalls(void)
-{
- size_t i, sys_id;
- unsigned long addr;
- const char *name;
- const char *sys_call_table_name;
- unsigned long *syscall_table;
-
-#ifdef CONFIG_COMPAT
- sys_call_table_name = "compat_sys_call_table";
-#else
- sys_call_table_name = "sys_call_table";
-#endif
-
- syscall_table = (unsigned long *)swap_ksyms(sys_call_table_name);
- if (syscall_table == NULL) {
- pr_warn("WARN: '%s' not found\n", sys_call_table_name);
- return 0;
- }
-
- for (i = 0; i < syscall_cnt; ++i) {
- name = get_sys_name(i);
- sys_id = ksp[i].id;
- addr = syscall_table[sys_id];
- if (addr == 0)
- pr_warn("WARN: %s() not found\n", name);
-
- ksp[i].sys_addr = addr;
- }
-
- return 0;
-}
+++ /dev/null
-/**
- * @author Vyacheslav Cherkashin: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features
- */
-
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <ksyms/ksyms.h>
-#include <kprobe/swap_kprobes.h>
-#include <master/swap_initializer.h>
-#include <writer/event_filter.h>
-#include "ksf_msg.h"
-#include "ks_features.h"
-#include "features_data.c"
-#include "ks_features_data.h"
-
-
-/* ========================= HANDLERS ========================= */
-static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
-
- if (rp && check_event(current)) {
- struct ks_probe *ksp = container_of(rp, struct ks_probe, rp);
- const char *fmt = ksp->args;
- const unsigned long addr = ksp->rp.kp.addr;
- enum probe_t type = ksp->type;
-
- ksf_msg_entry(regs, addr, type, fmt);
- }
-
- return 0;
-}
-
-static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct kretprobe *rp = ri->rp;
-
- if (rp && check_event(current)) {
- struct ks_probe *ksp = container_of(rp, struct ks_probe, rp);
- const unsigned long func_addr = rp->kp.addr;
- const unsigned long ret_addr = (unsigned long)ri->ret_addr;
- enum probe_t type = ksp->type;
-
- ksf_msg_exit(regs, func_addr, ret_addr, type, 'x');
- }
-
- return 0;
-}
-
-/* ========================= HANDLERS ========================= */
-
-
-
-
-
-static int register_syscall(size_t id)
-{
- int ret;
-
- if (ksp[id].rp.kp.addr == 0)
- return 0;
-
- ksp[id].rp.entry_handler = entry_handler;
- ksp[id].rp.handler = ret_handler;
-
- ret = swap_register_kretprobe(&ksp[id].rp);
-
- return ret;
-}
-
-static int unregister_syscall(size_t id)
-{
- if (ksp[id].rp.kp.addr == 0)
- return 0;
-
- swap_unregister_kretprobe(&ksp[id].rp);
-
- return 0;
-}
-
-static int unregister_multiple_syscalls(size_t *id_p, size_t cnt)
-{
- struct kretprobe **rpp;
- const size_t end = ((size_t) 0) - 1;
- size_t i = 0, id;
- int ret = 0;
-
- if (cnt == 1)
- return unregister_syscall(id_p[0]);
-
- rpp = kmalloc(sizeof(*rpp) * cnt, GFP_KERNEL);
- --cnt;
- if (rpp == NULL) {
- for (; cnt != end; --cnt) {
- ret = unregister_syscall(id_p[cnt]);
- if (ret)
- return ret;
- }
- return ret;
- }
-
- for (; cnt != end; --cnt) {
- id = id_p[cnt];
- if (ksp[id].rp.kp.addr) {
- rpp[i] = &ksp[id].rp;
- ++i;
- }
- }
-
- swap_unregister_kretprobes(rpp, i);
- kfree(rpp);
-
- return 0;
-}
-
-static int init_syscalls(void)
-{
- size_t i;
- unsigned long addr, ni_syscall;
- const char *name;
-
- ni_syscall = swap_ksyms("sys_ni_syscall");
-
- for (i = 0; i < syscall_cnt; ++i) {
- name = get_sys_name(i);
- addr = swap_ksyms(name);
- if (addr == 0) {
- pr_err("ERROR: %s() not found\n", name);
- } else if (ni_syscall == addr) {
- pr_err("WARN: %s is not install\n", name);
- addr = 0;
- }
-
- ksp[i].rp.kp.addr = addr;
- }
-
- return 0;
-}
+++ /dev/null
-/**
- * ks_features/ks_features.c
- * @author Vyacheslav Cherkashin: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features
- */
-
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <ksyms/ksyms.h>
-#include <master/swap_initializer.h>
-#include <writer/event_filter.h>
-#include "ksf_msg.h"
-#include "ks_features.h"
-#include "file_ops.h"
-
-#ifdef CONFIG_SWAP_HOOK_SYSCALL
-# include "ks_feature_hook.c"
-#else /* CONFIG_SWAP_HOOK_SYSCALL */
-# include "ks_feature_kprobe.c"
-#endif /* CONFIG_SWAP_HOOK_SYSCALL */
-
-
-
-/* ====================== SWITCH_CONTEXT ======================= */
-static DEFINE_MUTEX(mutex_sc_enable);
-static int sc_enable;
-
-
-#ifdef CONFIG_SWAP_HOOK_SWITCH_TO
-
-#include <swap/hook_switch_to.h>
-
-
-static void ksf_switch(struct task_struct *prev, struct task_struct *next)
-{
- if (check_event(prev))
- ksf_switch_entry(prev);
- if (check_event(next))
- ksf_switch_exit(next);
-}
-
-static struct swap_hook_ctx hook_ctx = {
- .hook = ksf_switch,
-};
-
-/**
- * @brief Get scheduler address.
- *
- * @return 0 on success, negative error code on error.
- */
-int init_switch_context(void)
-{
- return 0;
-}
-
-static int register_ctx_handler(void)
-{
- return swap_hook_ctx_reg(&hook_ctx);
-}
-
-static void unregister_ctx_handler(void)
-{
- swap_hook_ctx_unreg(&hook_ctx);
-}
-
-#else /* CONFIG_SWAP_HOOK_SWITCH_TO */
-
-static int switch_entry_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- if (check_event(current))
- ksf_switch_entry(current);
-
- return 0;
-}
-
-static int switch_ret_handler(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- if (check_event(current))
- ksf_switch_exit(current);
-
- return 0;
-}
-
-/**
- * @var switch_rp
- * Kretprobe for scheduler.
- */
-struct kretprobe switch_rp = {
- .entry_handler = switch_entry_handler,
- .handler = switch_ret_handler
-};
-
-/**
- * @brief Get scheduler address.
- *
- * @return 0 on success, negative error code on error.
- */
-int init_switch_context(void)
-{
- switch_rp.kp.addr = swap_ksyms("__switch_to");
- if (switch_rp.kp.addr == 0) {
- printk(KERN_INFO "ERROR: not found '__switch_to'\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int register_ctx_handler(void)
-{
- return swap_register_kretprobe(&switch_rp);
-}
-
-static void unregister_ctx_handler(void)
-{
- swap_unregister_kretprobe(&switch_rp);
-}
-#endif /* CONFIG_SWAP_HOOK_SWITCH_TO */
-
-
-/**
- * @brief Unregisters probe on context switching.
- *
- * @return Void.
- */
-void exit_switch_context(void)
-{
- if (sc_enable)
- unregister_ctx_handler();
-}
-
-static int register_switch_context(void)
-{
- int ret = -EINVAL;
-
- mutex_lock(&mutex_sc_enable);
- if (sc_enable) {
- printk(KERN_INFO "switch context profiling is already run!\n");
- goto unlock;
- }
-
- ret = register_ctx_handler();
- if (ret == 0)
- sc_enable = 1;
-
-unlock:
- mutex_unlock(&mutex_sc_enable);
-
- return ret;
-}
-
-static int unregister_switch_context(void)
-{
- int ret = 0;
-
- mutex_lock(&mutex_sc_enable);
- if (sc_enable == 0) {
- printk(KERN_INFO "switch context profiling is not running!\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- unregister_ctx_handler();
-
- sc_enable = 0;
-unlock:
- mutex_unlock(&mutex_sc_enable);
-
- return ret;
-}
-/* ====================== SWITCH_CONTEXT ======================= */
-
-
-
-
-static void set_pst(struct feature *f, size_t id)
-{
- ksp[id].type |= f->type;
-}
-
-static void unset_pst(struct feature *f, size_t id)
-{
- ksp[id].type &= !f->type;
-}
-
-static void do_uninstall_features(struct feature *f, size_t i)
-{
- int ret;
- size_t *id_p;
- size_t id;
- size_t cnt = 0;
- const size_t end = ((size_t) 0) - 1;
-
- id_p = kmalloc(sizeof(id) * (i + 1), GFP_KERNEL);
- /* NULL check is below in loop */
-
- for (; i != end; --i) {
- id = f->feature_list[i];
-
- if (get_counter(id) == 0) {
- printk(KERN_INFO "syscall %s not installed\n",
- get_sys_name(id));
- kfree(id_p);
- BUG();
- }
-
- dec_counter(id);
-
- if (get_counter(id) == 0) {
- if (id_p != NULL) {
- id_p[cnt] = id;
- ++cnt;
- } else {
- ret = unregister_syscall(id);
- if (ret)
- printk(KERN_INFO "syscall %s uninstall error, ret=%d\n",
- get_sys_name(id), ret);
- }
- }
-
- unset_pst(f, id);
- }
-
- if (id_p != NULL) {
- unregister_multiple_syscalls(id_p, cnt);
- kfree(id_p);
- }
-}
-
-static int do_install_features(struct feature *f)
-{
- int ret;
- size_t i, id;
-
- for (i = 0; i < f->cnt; ++i) {
- id = f->feature_list[i];
- set_pst(f, id);
-
- if (get_counter(id) == 0) {
- ret = register_syscall(id);
- if (ret) {
- printk(KERN_INFO "syscall %s install error, ret=%d\n",
- get_sys_name(id), ret);
-
- do_uninstall_features(f, --i);
- return ret;
- }
- }
-
- inc_counter(id);
- }
-
- return 0;
-}
-
-static DEFINE_MUTEX(mutex_features);
-
-static int install_features(struct feature *f)
-{
- int ret = 0;
-
- mutex_lock(&mutex_features);
- if (f->enable) {
- printk(KERN_INFO "energy profiling is already run!\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- ret = do_install_features(f);
-
- f->enable = 1;
-unlock:
- mutex_unlock(&mutex_features);
- return ret;
-}
-
-static int uninstall_features(struct feature *f)
-{
- int ret = 0;
-
- mutex_lock(&mutex_features);
- if (f->enable == 0) {
- printk(KERN_INFO "feature[%d] is not running!\n",
- feature_index(f));
- ret = -EINVAL;
- goto unlock;
- }
- do_uninstall_features(f, f->cnt - 1);
- f->enable = 0;
-unlock:
- mutex_unlock(&mutex_features);
-
- return ret;
-}
-
-static struct feature *get_feature(enum feature_id id)
-{
- if (id < 0 || id >= (int)feature_cnt)
- return NULL;
-
- return &features[id];
-}
-
-/**
- * @brief Sets probes related to specified feature.
- *
- * @param id Feature id.
- * @return 0 on success, negative error code on error.
- */
-int set_feature(enum feature_id id)
-{
- struct feature *f;
- int ret;
-
- switch (id) {
- case FID_SYSFILE_ACTIVITY:
- ret = file_ops_init();
- break;
- case FID_SWITCH:
- ret = register_switch_context();
- break;
- default:
- f = get_feature(id);
- ret = f ? install_features(f) : -EINVAL;
- break;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(set_feature);
-
-/**
- * @brief Unsets probes related to specified feature.
- *
- * @param id Feature id.
- * @return 0 on success, negative error code on error.
- */
-int unset_feature(enum feature_id id)
-{
- struct feature *f;
- int ret = 0;
-
- switch (id) {
- case FID_SYSFILE_ACTIVITY:
- file_ops_exit();
- break;
- case FID_SWITCH:
- ret = unregister_switch_context();
- break;
- default:
- f = get_feature(id);
- ret = f ? uninstall_features(f) : -EINVAL;
- break;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(unset_feature);
-
-static int init_syscall_features(void)
-{
- return init_syscalls();
-}
-
-static void uninit_syscall_features(void)
-{
- size_t id;
-
- for (id = 0; id < syscall_cnt; ++id) {
- if (get_counter(id) > 0)
- unregister_syscall(id);
- }
-}
-
-
-static int once(void)
-{
- int ret;
-
- ret = init_switch_context();
- if (ret)
- return ret;
-
- ret = init_syscall_features();
-
- return ret;
-}
-
-static void core_uninit(void)
-{
- uninit_syscall_features();
- exit_switch_context();
-
- if (file_ops_is_init())
- file_ops_exit();
-}
-
-SWAP_LIGHT_INIT_MODULE(once, NULL, core_uninit, NULL, NULL);
-
-MODULE_LICENSE("GPL");
-
-/* debug */
-static void print_feature(struct feature *f)
-{
- size_t i;
-
- for (i = 0; i < f->cnt; ++i)
- printk(KERN_INFO " feature[%3zu]: %s\n", i,
- get_sys_name(f->feature_list[i]));
-}
-
-/**
- * @brief Prints features.
- *
- * @return Void.
- */
-void print_features(void)
-{
- int i;
-
- printk(KERN_INFO "print_features:\n");
- for (i = 0; i < feature_cnt; ++i) {
- printk(KERN_INFO "feature: %d\n", i);
- print_feature(&features[i]);
- }
-}
-
-/**
- * @brief Prints all syscalls.
- *
- * @return Void.
- */
-void print_all_syscall(void)
-{
- int i;
-
- printk(KERN_INFO "SYSCALL:\n");
- for (i = 0; i < syscall_cnt; ++i)
- printk(KERN_INFO " [%2d] %s\n",
- get_counter(i), get_sys_name(i));
-}
-/* debug */
+++ /dev/null
-/**
- * @file ks_features/ks_features.h
- * @author Vyacheslav Cherkashin: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features interface declaration.
- */
-
-
-#ifndef _KS_FEATURES_H
-#define _KS_FEATURES_H
-
-/**
- * @enum feature_id
- * Features ids
- */
-enum feature_id {
- FID_FILE = 1, /**< File probes */
- FID_IPC = 2, /**< Hz probes */
- FID_PROCESS = 3, /**< Process probes */
- FID_SIGNAL = 4, /**< Signal probes */
- FID_NET = 5, /**< Network probes */
- FID_DESC = 6, /**< Description probes */
- FID_SWITCH = 7, /**< Switch context probes */
- FID_SYSFILE_ACTIVITY = 8 /**< System file activity */
-};
-
-int set_feature(enum feature_id id);
-int unset_feature(enum feature_id id);
-
-/* debug */
-void print_features(void);
-void print_all_syscall(void);
-/* debug */
-
-#endif /* _KS_FEATURES_H */
+++ /dev/null
-/**
- * @author Vitaliy Cherepanov: SWAP ks_features_data implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features
- */
-
-#include "syscall_list.h"
-#include "ks_features_data.h"
-#include "ksf_msg.h"
-
-
-/**
- * @struct ks_probe
- * @brief Kernel-space probe. Struct used as a container of syscall probes.
- * @var ks_probe::rp
- * Pointer to kretprobe.
- * @var ks_probe::counter
- * Installed probes counter.
- * @var ks_probe::args
- * Pointer to args format string.
- * @var ks_probe::type
- * Probe sub type.
- */
-
-#define CREATE_RP(name) \
-{ \
- .entry_handler = NULL, \
- .handler = NULL \
-}
-
-#define CREATE_HOOK_SYSCALL(name) \
-{ \
- .entry = NULL, \
- .exit = NULL \
-}
-
-#define SYSCALL_NAME_STR(name) #name
-
-#ifdef CONFIG_SWAP_HOOK_SYSCALL
-
-#include <asm/unistd32.h> /* FIXME: for only arm64 compat mode */
-
-#define X(name__, args__) \
-{ \
- .hook = CREATE_HOOK_SYSCALL(name__), \
- .sys_addr = 0xdeadbeef, \
- .counter = 0, \
- .args = #args__, \
- .type = PT_KS_NONE, \
- .name = SYSCALL_NAME_STR(sys_ ## name__), \
- .id = __NR_ ## name__, \
-},
-#else /* !CONFIG_SWAP_HOOK_SYSCALL */
-#define X(name__, args__) \
-{ \
- .rp = CREATE_RP(name__), \
- .counter = 0, \
- .args = #args__, \
- .type = PT_KS_NONE, \
- .name = SYSCALL_NAME_STR(sys_ ## name__), \
-},
-#endif /* CONFIG_SWAP_HOOK_SYSCALL */
-
-struct ks_probe ksp[syscall_cnt] = {
- SYSCALL_LIST
-};
-
-#undef X
-
-const char *get_sys_name(size_t id)
-{
- return ksp[id].name;
-}
-
-int get_counter(size_t id)
-{
- return ksp[id].counter;
-}
-
-void inc_counter(size_t id)
-{
- ++ksp[id].counter;
-}
-
-void dec_counter(size_t id)
-{
- --ksp[id].counter;
-}
-
+++ /dev/null
-/**
- * ks_features/ks_features_data.h
- * @author Vitaliy Cherepanov: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * @section DESCRIPTION
- *
- * SWAP kernel features
- */
-
-/**
- * @struct ks_probe
- * @brief Kernel-space probe. Struct used as a container of syscall probes.
- * @var ks_probe::rp
- * Pointer to kretprobe.
- * @var ks_probe::counter
- * Installed probes counter.
- * @var ks_probe::args
- * Pointer to args format string.
- * @var ks_probe::type
- * Probe sub type.
- */
-#ifndef __KS_FEATURE_DATA_H__
-#define __KS_FEATURE_DATA_H__
-
-#include "syscall_list.h"
-
-#ifdef CONFIG_SWAP_HOOK_SYSCALL
-# include <swap/hook_syscall.h>
-#else
-# include "kprobe/swap_kprobes.h"
-#endif
-
-struct ks_probe {
-#ifdef CONFIG_SWAP_HOOK_SYSCALL
- struct hook_syscall hook;
- u64 sys_addr;
-#else /* CONFIG_SWAP_HOOK_SYSCALL */
- struct kretprobe rp;
-#endif /* CONFIG_SWAP_HOOK_SYSCALL */
-
- int counter;
- char *args;
- int type;
-
- const char *name;
- unsigned int id;
-};
-
-/**
- * @enum
- * Syscall name count defenition
- */
-#define X(name__, args__) + 1
-enum {
- syscall_cnt = 0 SYSCALL_LIST
-};
-#undef X
-
-extern struct ks_probe ksp[syscall_cnt];
-
-const char *get_sys_name(size_t id);
-int get_counter(size_t id);
-void inc_counter(size_t id);
-void dec_counter(size_t id);
-
-#endif /* __KS_FEATURE_DATA_H__ */
+++ /dev/null
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include "ks_map.h"
-
-struct entry {
- struct rb_node node;
- void *data;
-};
-
-static inline void *entry_data(struct entry *entry)
-{
- return entry->data;
-}
-
-static struct entry *alloc_entry(struct map *map, void *data)
-{
- struct entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
-
- if (entry) {
- entry->data = data;
- RB_CLEAR_NODE(&entry->node);
- }
-
- return entry;
-}
-
-static void *free_entry(struct map *map, struct entry *entry)
-{
- void *data = entry_data(entry);
-
- kfree(entry);
-
- return data;
-}
-
-static struct entry *__search(struct map *map, void *key)
-{
- struct rb_root *root = &map->root;
- struct rb_node *node = root->rb_node;
- key_func_t key_f = map->key_f;
- cmp_func_t cmp_f = map->cmp_f;
-
- while (node) {
- struct entry *entry = rb_entry(node, struct entry, node);
- int result = cmp_f(key_f(entry_data(entry)), key);
-
- if (result < 0)
- node = node->rb_left;
- else if (result > 0)
- node = node->rb_right;
- else
- return entry;
- }
-
- return NULL;
-}
-
-void *search(struct map *map, void *key)
-{
- struct entry *entry = __search(map, key);
-
- return entry ? entry_data(entry) : NULL;
-}
-
-static void *__remove(struct map *map, struct entry *entry)
-{
- struct rb_root *root = &map->root;
-
- rb_erase(&entry->node, root);
- RB_CLEAR_NODE(&entry->node);
- map->size--;
-
- return free_entry(map, entry);
-}
-
-void *remove(struct map *map, void *key)
-{
- struct entry *entry = __search(map, key);
-
- /* Removes entry from the tree but does not free the data */
- return entry ? __remove(map, entry) : NULL;
-}
-
-static void *__replace(struct map *map, struct entry *old, struct entry *new)
-{
- struct rb_root *root = &map->root;
-
- rb_replace_node(&old->node, &new->node, root);
-
- return free_entry(map, old);
-}
-
-void *replace(struct map *map, void *data)
-{
- struct entry *old, *new;
-
- old = __search(map, map->key_f(data));
- if (old) {
- new = alloc_entry(map, data);
- if (!new)
- return ERR_PTR(-ENOMEM);
-
- return __replace(map, old, new);
- }
-
- return ERR_PTR(-ESRCH);
-}
-
-int insert(struct map *map, void *data)
-{
- struct rb_root *root = &map->root;
- struct rb_node **new = &(root->rb_node), *parent = NULL;
- key_func_t key_f = map->key_f;
- cmp_func_t cmp_f = map->cmp_f;
- void *key = key_f(data);
- struct entry *entry;
-
- /* Figure out where to put new node */
- while (*new) {
- struct entry *this = rb_entry(*new, struct entry, node);
- int result = cmp_f(key_f(entry_data(this)), key);
-
- parent = *new;
- if (result < 0)
- new = &((*new)->rb_left);
- else if (result > 0)
- new = &((*new)->rb_right);
- else /* entry already inserted */
- return -EEXIST;
- }
-
- entry = alloc_entry(map, data);
- if (!entry)
- return -ENOMEM;
-
- /* Add new node and rebalance tree. */
- rb_link_node(&entry->node, parent, new);
- rb_insert_color(&entry->node, root);
- map->size++;
-
- return 0;
-}
-
-int for_each_entry(struct map *map, act_func_t func, void *arg)
-{
- struct rb_root *root = &map->root;
- struct rb_node *node = rb_first(root);
- int ret = 0;
-
- while (node) {
- struct entry *entry = rb_entry(node, struct entry, node);
-
- /* Stop iteration if actor returns non zero */
- ret = func(entry_data(entry), arg);
- if (ret)
- break;
-
- node = rb_next(node);
- }
-
- return ret;
-}
-
-int for_each_entry_reverse(struct map *map, act_func_t func, void *arg)
-{
- struct rb_root *root = &map->root;
- struct rb_node *node = rb_last(root);
- int ret = 0;
-
- while (node) {
- struct entry *entry = rb_entry(node, struct entry, node);
-
- /* Stop iteration if actor returns non zero */
- ret = func(entry_data(entry), arg);
- if (ret)
- break;
-
- node = rb_prev(node);
- }
-
- return ret;
-}
-
-void clear(struct map *map, act_func_t destructor, void *arg)
-{
- struct rb_root *root = &map->root;
- struct rb_node *node = root->rb_node;
-
- while (node) {
- struct entry *entry = rb_entry(node, struct entry, node);
- void *data = __remove(map, entry);
-
- /* call the data 'destructor' if supplied */
- if (destructor)
- destructor(data, arg);
-
- node = root->rb_node;
- }
-
- WARN(map->size, "ks_map size: %d\n", map->size);
- map->root = RB_ROOT;
-}
+++ /dev/null
-#ifndef __KS_MAP__
-#define __KS_MAP__
-
-#include <linux/rbtree.h>
-
-typedef void *(*key_func_t)(void *);
-typedef int (*cmp_func_t)(void *, void *);
-typedef int (*act_func_t)(void *, void *);
-
-struct map {
- struct rb_root root;
- int size;
- key_func_t key_f;
- cmp_func_t cmp_f;
-};
-
-#define __MAP_INITIALIZER(_key_f, _cmp_f) \
- { \
- .root = RB_ROOT, \
- .size = 0, \
- .key_f = _key_f, \
- .cmp_f = _cmp_f \
- }
-
-#define DEFINE_MAP(_name, _key_f, _cmp_f) \
- struct map _name = __MAP_INITIALIZER(_key_f, _cmp_f)
-
-void *search(struct map *map, void *key);
-void *remove(struct map *map, void *key);
-void *replace(struct map *map, void *data);
-int insert(struct map *map, void *data);
-int for_each_entry(struct map *map, act_func_t func, void *arg);
-int for_each_entry_reverse(struct map *map, act_func_t act, void *arg);
-void clear(struct map *map, act_func_t destructor, void *arg);
-
-#endif /* __KS_MAP__ */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/sched.h>
-#include <writer/swap_msg.h>
-#include <writer/kernel_operations.h>
-#include "ksf_msg.h"
-
-
-#define KSF_PREFIX KERN_INFO "[KSF] "
-
-
-
-
-
-/* ============================================================================
- * = MSG_SYSCALL_* (ENTRY/EXIT) =
- * ============================================================================
- */
-struct msg_sys_header {
- u32 pid;
- u32 tid;
- u32 probe_type;
- u64 pc_addr;
- u64 caller_pc_addr;
- u32 cpu_num;
-} __packed;
-
-struct msg_sys_entry {
- struct msg_sys_header h;
- u32 cnt_args;
- char args[0];
-} __packed;
-
-struct msg_sys_exit {
- struct msg_sys_header h;
- char ret_val[0];
-} __packed;
-
-
-static void pack_header(struct msg_sys_header *h, unsigned long func_addr,
- unsigned long ret_addr, enum probe_t type)
-{
- struct task_struct *task = current;
-
- h->pid = task->tgid;
- h->tid = task->pid;
- h->probe_type = (u32)type;
- h->pc_addr = func_addr;
- h->caller_pc_addr = ret_addr;
- h->cpu_num = raw_smp_processor_id();
-}
-
-static void pack_entry_header(struct msg_sys_entry *e, struct pt_regs *regs,
- unsigned long func_addr, enum probe_t type,
- const char *fmt)
-{
- pack_header(&e->h, func_addr, get_regs_ret_func(regs), type);
- e->cnt_args = strlen(fmt);
-}
-
-static void pack_exit_header(struct msg_sys_exit *e, unsigned long func_addr,
- unsigned long ret_addr, enum probe_t type)
-{
- pack_header(&e->h, func_addr, ret_addr, type);
-}
-
-void ksf_msg_entry(struct pt_regs *regs, unsigned long func_addr,
- enum probe_t type, const char *fmt)
-{
- int ret;
- struct swap_msg *m;
- struct msg_sys_entry *ent;
- size_t size;
-
- m = swap_msg_get(MSG_SYSCALL_ENTRY);
-
- ent = swap_msg_payload(m);
- pack_entry_header(ent, regs, func_addr, type, fmt);
-
- size = swap_msg_size(m) - sizeof(*ent);
- ret = swap_msg_pack_args(ent->args, size, fmt, regs);
- if (ret < 0) {
- printk(KSF_PREFIX "ERROR: arguments packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, sizeof(*ent) + ret);
-
-put_msg:
- swap_msg_put(m);
-}
-
-void ksf_msg_exit(struct pt_regs *regs, unsigned long func_addr,
- unsigned long ret_addr, enum probe_t type, char ret_type)
-{
- int ret;
- struct swap_msg *m;
- struct msg_sys_exit *ext;
- size_t size;
-
- m = swap_msg_get(MSG_SYSCALL_EXIT);
-
- ext = swap_msg_payload(m);
- pack_exit_header(ext, func_addr, ret_addr, type);
-
- size = swap_msg_size(m) - sizeof(*ext);
- ret = swap_msg_pack_ret_val(ext->ret_val, size, ret_type, regs);
- if (ret < 0) {
- printk(KSF_PREFIX "ERROR: ret value packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, sizeof(*ext) + ret);
-
-put_msg:
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_FILE_FUNCTION_* (ENTRY/EXIT) =
- * ============================================================================
- */
-struct msg_file_entry {
- u32 pid;
- u32 tid;
- u32 fd;
- u32 event_type;
- char file_path[0];
-} __packed;
-
-enum file_info {
- FI_GENIRAL = 0,
- FI_OPEN = 1,
- FI_LOCK = 2
-};
-
-static int pack_file_entry_head(void *data, size_t size, enum file_info info,
- int fd, enum file_api_t api, const char *path)
-{
- struct msg_file_entry *ent = (struct msg_file_entry *)data;
- struct task_struct *task = current;
- size_t len, old_size = size;
-
- ent->pid = task->tgid;
- ent->tid = task->pid;
- ent->fd = fd;
- ent->event_type = api;
-
- size -= sizeof(*ent);
- len = strlen(path);
- if (size < len + 1)
- return -ENOMEM;
-
- memcpy(ent->file_path, path, len);
- ent->file_path[len] = '\0';
-
- size -= len + 1;
- data += old_size - size;
-
- if (size < 4)
- return -ENOMEM;
-
- *((u32 *)data) = (u32)info;
- size -= 4;
-
- return old_size - size;
-}
-
-
-
-void ksf_msg_file_entry(int fd, enum file_api_t api, const char *path)
-{
- int ret;
- void *p;
- size_t size;
- struct swap_msg *m;
-
- m = swap_msg_get(MSG_FILE_FUNCTION_ENTRY);
- p = swap_msg_payload(m);
- size = swap_msg_size(m);
-
- ret = pack_file_entry_head(p, size, FI_GENIRAL, fd, api, path);
- if (ret < 0) {
- printk(KSF_PREFIX "buffer is too small\n");
- goto put_msg;
- }
-
- swap_msg_flush(m, ret);
-
-put_msg:
- swap_msg_put(m);
-}
-
-void ksf_msg_file_entry_open(int fd, enum file_api_t api, const char *path,
- const char __user *ofile)
-{
- long n;
- int ret;
- void *p;
- size_t size;
- struct swap_msg *m;
-
- m = swap_msg_get(MSG_FILE_FUNCTION_ENTRY);
- p = swap_msg_payload(m);
- size = swap_msg_size(m);
-
- ret = pack_file_entry_head(p, size, FI_OPEN, fd, api, path);
- if (ret < 0) {
- printk(KSF_PREFIX "buffer is too small\n");
- goto put_msg;
- }
-
- size -= ret;
- p += ret;
-
- n = strncpy_from_user(p, ofile, size);
- if (n < 0) {
- printk(KSF_PREFIX "cannot copy ofile\n");
- goto put_msg;
- }
-
- swap_msg_flush(m, ret + n + 1);
-
-put_msg:
- swap_msg_put(m);
-}
-
-struct lock_arg {
- u32 type;
- u32 whence;
- u64 start;
- u64 len;
-} __packed;
-
-void ksf_msg_file_entry_lock(int fd, enum file_api_t api, const char *path,
- int type, int whence, s64 start, s64 len)
-{
- int ret;
- void *p;
- size_t size;
- struct swap_msg *m;
- struct lock_arg *arg;
-
- m = swap_msg_get(MSG_FILE_FUNCTION_ENTRY);
- p = swap_msg_payload(m);
- size = swap_msg_size(m);
-
- ret = pack_file_entry_head(p, size, FI_LOCK, fd, api, path);
- if (ret < 0) {
- printk(KSF_PREFIX "buffer is too small\n");
- goto put_msg;
- }
-
- size -= ret;
- p += ret;
-
- if (size < sizeof(*arg)) {
- printk(KSF_PREFIX "buffer is too small\n");
- goto put_msg;
- }
-
- arg = (struct lock_arg *)p;
- arg->type = (u32)type;
- arg->whence = (u32)whence;
- arg->start = (u64)start;
- arg->len = (u64)len;
-
- swap_msg_flush(m, ret + sizeof(*arg));
-
-put_msg:
- swap_msg_put(m);
-}
-
-
-struct msg_file_exit {
- u32 pid;
- u32 tid;
- char ret_val[0];
-} __packed;
-
-void ksf_msg_file_exit(struct pt_regs *regs, char ret_type)
-{
- struct task_struct *task = current;
- int ret;
- struct swap_msg *m;
- struct msg_file_exit *ext;
- size_t size;
-
- m = swap_msg_get(MSG_FILE_FUNCTION_EXIT);
-
- ext = swap_msg_payload(m);
- ext->pid = task->tgid;
- ext->tid = task->pid;
-
- size = swap_msg_size(m) - sizeof(*ext);
- ret = swap_msg_pack_ret_val(ext->ret_val, size, ret_type, regs);
- if (ret < 0) {
- printk(KSF_PREFIX "ERROR: ret value packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, sizeof(*ext) + ret);
-
-put_msg:
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_FILE_FUNCTION_* (ENTRY/EXIT) =
- * ============================================================================
- */
-struct msg_context_switch {
- u64 pc_addr;
- u32 pid;
- u32 tid;
- u32 cpu_num;
-} __packed;
-
-static void context_switch(struct task_struct *task, enum swap_msg_id id)
-{
- struct swap_msg *m;
- struct msg_context_switch *mcs;
- void *p;
-
- m = swap_msg_get(id);
- p = swap_msg_payload(m);
-
- mcs = p;
- mcs->pc_addr = 0;
- mcs->pid = task->tgid;
- mcs->tid = task->pid;
- mcs->cpu_num = raw_smp_processor_id();
-
- swap_msg_flush_wakeupoff(m, sizeof(*mcs));
- swap_msg_put(m);
-}
-
-void ksf_switch_entry(struct task_struct *task)
-{
- context_switch(task, MSG_CONTEXT_SWITCH_ENTRY);
-}
-
-void ksf_switch_exit(struct task_struct *task)
-{
- context_switch(task, MSG_CONTEXT_SWITCH_EXIT);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _KSF_MSG_H
-#define _KSF_MSG_H
-
-
-enum probe_t {
- PT_KS_NONE = 0x00,
- PT_KS_FILE = 0x01,
- PT_KS_IPC = 0x02,
- PT_KS_PROCESS = 0x04,
- PT_KS_SIGNAL = 0x08,
- PT_KS_NETWORK = 0x10,
- PT_KS_DESC = 0x20
-};
-
-
-enum file_api_t {
- FOPS_OPEN = 0,
- FOPS_CLOSE = 1,
- FOPS_READ_BEGIN = 2,
- FOPS_READ_END = 3,
- FOPS_READ = FOPS_READ_BEGIN,
- FOPS_WRITE_BEGIN = 4,
- FOPS_WRITE_END = 5,
- FOPS_WRITE = FOPS_WRITE_BEGIN,
- FOPS_DIRECTORY = 6,
- FOPS_PERMS = 7,
- FOPS_OTHER = 8,
- FOPS_SEND = 9,
- FOPS_RECV = 10,
- FOPS_OPTION = 11,
- FOPS_MANAGE = 12,
- FOPS_LOCK_START = 14, /* 13 */
- FOPS_LOCK_END = 15,
- FOPS_LOCK_RELEASE = 16
-};
-
-
-struct pt_regs;
-
-
-void ksf_msg_entry(struct pt_regs *regs, unsigned long func_addr,
- enum probe_t type, const char *fmt);
-void ksf_msg_exit(struct pt_regs *regs, unsigned long func_addr,
- unsigned long ret_addr, enum probe_t type, char ret_type);
-
-void ksf_msg_file_entry(int fd, enum file_api_t api, const char *path);
-void ksf_msg_file_entry_open(int fd, enum file_api_t api, const char *path,
- const char __user *ofile);
-void ksf_msg_file_entry_lock(int fd, enum file_api_t api, const char *path,
- int type, int whence, s64 start, s64 len);
-void ksf_msg_file_exit(struct pt_regs *regs, char ret_type);
-
-void ksf_switch_entry(struct task_struct *task);
-void ksf_switch_exit(struct task_struct *task);
-
-
-#endif /* _KSF_MSG_H */
+++ /dev/null
-/**
- * @file ks_features/syscall_list.h
- * @author Vyacheslav Cherkashin: SWAP ks_features implement
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Syscalls list.
- */
-
-
-#ifndef _SYSCALL_LIST_H
-#define _SYSCALL_LIST_H
-
-#define SYSCALL_LIST \
- X(accept4, dpdd) \
- X(accept, dpd) \
- X(access, sd) \
- X(acct, s) \
- X(bind, dpd) \
- X(chdir, s) \
- X(chmod, sd) \
-/* TODO: X(chown16, sdd) */ \
- X(chown, sdd) \
- X(chroot, s) \
- X(clone, ddddd) \
- X(connect, dpd) \
- X(creat, sd) \
- X(dup3, ddd) \
- X(epoll_create1, d) \
- X(epoll_ctl, dddp) \
- X(epoll_pwait, dpddpx) \
- X(epoll_wait, dpdd) \
- X(eventfd2, dd) \
- X(eventfd, d) \
- X(execve, spp) \
- X(exit, d) \
- X(exit_group, d) \
- X(faccessat, dsd) \
-/* TODO: X(fadvise64_64, dxxd) */ \
- X(fallocate, ddxx) \
- X(fanotify_init, dd) \
- X(fanotify_mark, ddxds) \
- X(fchmodat, dsd) \
- X(fchownat, dsddd) \
- X(fgetxattr, dspx) \
- X(flistxattr, dpx) \
- X(fork, /* empty */) \
- X(fremovexattr, ds) \
- X(fstat64, xp) \
- X(ftruncate64, dx) \
- X(futimesat, dsp) \
- X(getcwd, px) \
- X(getpeername, dpd) \
- X(getsockname, dpd) \
- X(getsockopt, dddpd) \
- X(getxattr, sspx) \
- X(inotify_add_watch, dsd) \
- X(inotify_init, /* empty */) \
- X(inotify_init1, d) \
- X(inotify_rm_watch, dd) \
-/* TODO: X(ipc, ddxxpx) */\
- X(kill, dd) \
- X(linkat, dsdsd) \
- X(link, ss) \
- X(listen, dd) \
- X(listxattr, spx) \
- X(lstat64, sp) \
-/* TODO: X(lstat, sp) */ \
- X(mkdirat, dsd) \
- X(mkdir, sd) \
- X(mknodat, dsdd) \
- X(mknod, sdd) \
-/* TODO: X(mmap_pgoff, xxxxxx) */ \
- X(mount, pppxp) \
- X(msgctl, ddp) \
- X(msgget, dd) \
- X(msgrcv, dpxxd) \
- X(msgsnd, dpxd) \
- X(name_to_handle_at, dspdd) \
-/* TODO: X(newfstatat, dspd) */ \
-/* TODO: X(old_mmap, p) */ \
- X(openat, dsdd) \
- X(open_by_handle_at, dpd) \
- X(open, sdd) \
- X(pause, /* empty */) \
- X(pipe2, dd) \
- X(ppoll, pdpp) \
- X(pread64, dpxx) \
- X(preadv, xpxxx) \
- X(pselect6, dxxxpp) \
- X(pwrite64, dsxx) \
- X(pwritev, xpxxx) \
- X(readlinkat, dspd) \
- X(readlink, spd) \
- X(recv, dpxd) \
- X(recvfrom, dpxdpd) \
- X(recvmmsg, dpddp) \
- X(recvmsg, dpd) \
- X(removexattr, ss) \
- X(renameat, dsds) \
- X(rename, ss) \
- X(rmdir, s) \
- X(rt_sigaction, dpp) \
- X(rt_sigprocmask, dppx) \
- X(rt_sigsuspend, px) \
- X(rt_sigtimedwait, pppx) \
- X(rt_tgsigqueueinfo, dddp) \
- X(semctl, dddx) \
- X(semget, ddd) \
- X(semop, dpd) \
- X(semtimedop, dpdp) \
- X(send, dpxd) \
- X(sendfile64, ddlxx) \
- X(sendfile, ddxx) \
- X(sendmmsg, dpdd) \
- X(sendmsg, dpd) \
- X(sendto, dpxdpd) \
- X(setns, dd) \
- X(setsockopt, dddpd) \
- X(setxattr, sspxd) \
- X(shmat, dpd) \
- X(shmctl, ddp) \
- X(shmdt, p) \
- X(shmget, dxd) \
- X(shutdown, dd) \
- X(sigaction, dpp) \
-/* TODO: X(sigaltstack, pp) */ \
-/* TODO: X(signal, dp) */ \
- X(signalfd4, dpxd) \
- X(signalfd, dpx) \
- X(sigpending, p) \
- X(sigprocmask, dpp) \
-/* TODO: X(sigsuspend, ddp) */ \
-/* TODO: X(sigsuspend, p) */ \
-/* TODO: X(socketcall, dx) */\
- X(socket, ddd) \
- X(socketpair, dddd) \
- X(splice, dxdxxd) \
- X(stat64, sp) \
- X(statfs64, sxp) \
- X(statfs, sp) \
-/* TODO: X(stat, sp) */ \
- X(swapoff, s) \
- X(swapon, sd) \
- X(symlinkat, sds) \
- X(symlink, ss) \
- X(syncfs, d) \
- X(tee, ddxd) \
- X(tgkill, ddd) \
- X(timerfd_create, dd) \
- X(timerfd_gettime, dp) \
- X(timerfd_settime, ddpp) \
- X(truncate64, sx) \
- X(truncate, sx) \
-/* TODO: X(umount, pd) */\
- X(unlinkat, dsd) \
- X(unlink, s) \
- X(unshare, x) \
- X(uselib, s) \
- X(utimensat, dspd) \
-/* TODO: X(utime, pp) */\
- X(utimes, pp) \
- X(vfork, /* empty */) \
- X(vmsplice, dpxd) \
- X(wait4, dddp) \
- X(waitid, ddpdp)
-/* TODO: X(waitpid, ddd) */
-
-#endif /* _SYSCALL_LIST_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
-
-obj-m := swap_ks_manager.o
-swap_ks_manager-y := ks_manager.o
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/ks_manager/ks_manager.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <kprobe/swap_kprobes.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include "ks_manager.h"
-
-struct probe {
- struct hlist_node hlist;
- struct kern_probe p;
-};
-
-static HLIST_HEAD(list_probes);
-
-static struct probe *create_probe(unsigned long addr, void *pre_handler,
- void *jp_handler, void *rp_handler)
-{
- struct probe *p = kzalloc(sizeof(*p), GFP_KERNEL);
-
- if (p == NULL)
- return NULL;
-
- p->p.jp.kp.addr = p->p.rp.kp.addr = addr;
- p->p.jp.pre_entry = pre_handler;
- p->p.jp.entry = jp_handler;
- p->p.rp.handler = rp_handler;
- INIT_HLIST_NODE(&p->hlist);
-
- return p;
-}
-
-static void free_probe(struct probe *p)
-{
- kfree(p);
-}
-
-static void add_probe_to_list(struct probe *p)
-{
- hlist_add_head(&p->hlist, &list_probes);
-}
-
-static void remove_probe_to_list(struct probe *p)
-{
- hlist_del(&p->hlist);
-}
-
-static struct probe *find_probe(unsigned long addr)
-{
- struct probe *p;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- /* check if such probe does exist */
- swap_hlist_for_each_entry(p, node, &list_probes, hlist)
- if ((unsigned long)p->p.jp.kp.addr == addr)
- return p;
-
- return NULL;
-}
-
-int ksm_register_probe(unsigned long addr, void *pre_handler,
- void *jp_handler, void *rp_handler)
-{
- int ret;
- struct probe *p;
-
- p = create_probe(addr, pre_handler, jp_handler, rp_handler);
- if (!p)
- return -ENOMEM;
-
- ret = swap_register_jprobe(&p->p.jp);
- if (ret)
- goto free;
-
- ret = swap_register_kretprobe(&p->p.rp);
- if (ret)
- goto unregister_jprobe;
-
- add_probe_to_list(p);
- return 0;
-
-unregister_jprobe:
- swap_unregister_jprobe(&p->p.jp);
-free:
- free_probe(p);
- return ret;
-}
-EXPORT_SYMBOL_GPL(ksm_register_probe);
-
-static void do_ksm_unregister_probe(struct probe *p)
-{
- remove_probe_to_list(p);
- swap_unregister_kretprobe(&p->p.rp);
- swap_unregister_jprobe(&p->p.jp);
- free_probe(p);
-}
-
-int ksm_unregister_probe(unsigned long addr)
-{
- struct probe *p;
-
- p = find_probe(addr);
- if (p == NULL)
- return -EINVAL;
-
- do_ksm_unregister_probe(p);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(ksm_unregister_probe);
-
-int ksm_unregister_probe_all(void)
-{
- struct probe *p;
- struct hlist_node *n;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry_safe(p, node, n, &list_probes, hlist) {
- do_ksm_unregister_probe(p);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(ksm_unregister_probe_all);
-
-static int __init init_ks_manager(void)
-{
- return 0;
-}
-
-static void __exit exit_ks_manager(void)
-{
- ksm_unregister_probe_all();
-}
-
-module_init(init_ks_manager);
-module_exit(exit_ks_manager);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-#ifndef _KS_MANAGER_H
-#define _KS_MANAGER_H
-
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/ks_manager/ks_manager.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include <kprobe/swap_kprobes.h>
-
-struct kern_probe {
- struct jprobe jp;
- struct kretprobe rp;
-};
-
-int ksm_register_probe(unsigned long addr, void *pre_handler,
- void *jp_handler, void *rp_handler);
-int ksm_unregister_probe(unsigned long addr);
-
-int ksm_unregister_probe_all(void);
-
-#endif /* _KS_MANAGER_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_ksyms.o
-swap_ksyms-y := ksyms_module.o
-
-ifeq ($(CONFIG_KALLSYMS),y)
- swap_ksyms-y += ksyms.o
-else
- swap_ksyms-y += no_ksyms.o
-endif
+++ /dev/null
-/**
- * @file ksyms/ksyms.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * SWAP ksyms module.
- */
-
-
-#include "ksyms.h"
-#include "ksyms_init.h"
-#include <linux/kallsyms.h>
-#include <linux/module.h>
-#include <linux/percpu.h>
-
-/**
- * @struct symbol_data
- * @brief Stores symbols data.
- * @var symbol_data::name
- * Pointer to symbol name string.
- * @var symbol_data::len
- * Symbol name length.
- * @var symbol_data::addr
- * Symbol address.
- */
-struct symbol_data {
- const char *name;
- size_t len;
- unsigned long addr;
-};
-
-static int symbol_cb(void *data, const char *sym, struct module *mod,
- unsigned long addr)
-{
- struct symbol_data *sym_data_p = (struct symbol_data *)data;
-
- /* We expect that real symbol name should have at least the same
- * length as symbol name we are looking for. */
- if (strncmp(sym_data_p->name, sym, sym_data_p->len) == 0) {
- sym_data_p->addr = addr;
- /* Return != 0 to stop loop over the symbols */
- return 1;
- }
-
- return 0;
-}
-
-/**
- * @brief Search of symbol address based on substring.
- *
- * @param name Pointer to the substring.
- * @return Symbol address.
- */
-unsigned long swap_ksyms_substr(const char *name)
-{
- struct symbol_data sym_data = {
- .name = name,
- .len = strlen(name),
- .addr = 0
- };
- kallsyms_on_each_symbol(symbol_cb, (void *)&sym_data);
-
- return sym_data.addr;
-}
-EXPORT_SYMBOL_GPL(swap_ksyms_substr);
+++ /dev/null
-/**
- * @file ksyms/ksyms.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @sectoin DESCRIPTION
- *
- * SWAP symbols searching module.
- */
-
-#ifndef __KSYMS_H__
-#define __KSYMS_H__
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
-#include <linux/autoconf.h>
-#else
-#include <generated/autoconf.h>
-#endif
-
-#include <linux/kallsyms.h>
-
-#ifdef CONFIG_KALLSYMS
-
-static inline int swap_get_ksyms(void)
-{
- return 0;
-}
-
-static inline void swap_put_ksyms(void)
-{
-}
-
-static inline unsigned long swap_ksyms(const char *name)
-{
- return kallsyms_lookup_name(name);
-}
-
-#else /* !CONFIG_KALLSYMS */
-
-int swap_get_ksyms(void);
-void swap_put_ksyms(void);
-unsigned long swap_ksyms(const char *name);
-
-#endif /*CONFIG_KALLSYMS*/
-
-unsigned long swap_ksyms_substr(const char *name);
-
-#endif /*__KSYMS_H__*/
+++ /dev/null
-/**
- * @file ksyms/ksyms_init.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section LICENSE
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * SWAP symbols searching module initialization interface.
- */
-
-#ifndef __KSYMS_INIT_H__
-#define __KSYMS_INIT_H__
-
-#ifdef CONFIG_KALLSYMS
-
-static inline int ksyms_init(void)
-{
- return 0;
-}
-
-static inline void ksyms_exit(void)
-{
-}
-
-#else /* CONFIG_KALLSYMS */
-
-int ksyms_init(void);
-void ksyms_exit(void);
-
-#endif /* CONFIG_KALLSYMS */
-
-#endif /* __KSYMS_INIT_H__ */
+++ /dev/null
-/**
- * @file ksyms/ksyms_module.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * SWAP symbols searching module initialization implementation.
- */
-
-#include "ksyms_init.h"
-
-#include <linux/module.h>
-
-/**
- * @brief Init ksyms module.
- *
- * @return 0 on success.
- */
-int __init swap_ksyms_init(void)
-{
- int ret = ksyms_init();
-
- printk(KERN_INFO "SWAP_KSYMS: Module initialized\n");
-
- return ret;
-}
-
-/**
- * @brief Exit ksyms module.
- *
- * @return Void.
- */
-void __exit swap_ksyms_exit(void)
-{
- ksyms_exit();
-
- printk(KERN_INFO "SWAP_KSYMS: Module uninitialized\n");
-}
-
-module_init(swap_ksyms_init);
-module_exit(swap_ksyms_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP ksyms module");
-MODULE_AUTHOR("Vyacheslav Cherkashin <v.cherkashin@samaung.com>");
+++ /dev/null
-/**
- * @file ksyms/no_ksyms.c
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * SWAP symbols searching implementation.
- */
-
-#include "ksyms.h"
-#include "ksyms_init.h"
-#include <linux/types.h>
-#include <linux/vmalloc.h>
-#include <linux/semaphore.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/fcntl.h>
-
-/**
- * @def KSYMS_ERR
- * Error message define.
- */
-#define KSYMS_ERR(format, args...) \
- do { \
- char *f = __FILE__; \
- char *n = strrchr(f, '/'); \
- printk(KERN_INFO "%s:%u \'%s\' ERROR: " format "\n" , \
- (n) ? n+1 : f, __LINE__, __func__, ##args); \
- } while (0)
-
-/**
- * @struct sys_map_item
- * @brief System map list item info.
- * @var sys_map_item::list
- * List pointer.
- * @var sys_map_item::addr
- * Symbol address.
- * @var sys_map_item::name
- * Symbol name.
- */
-struct sys_map_item {
- struct list_head list;
-
- unsigned long addr;
- char *name;
-};
-
-static char *sm_path;
-module_param(sm_path, charp, 0);
-
-/**
- * @var smi_list
- * List of sys_map_item.
- */
-LIST_HEAD(smi_list);
-static struct file *file;
-
-static int cnt_init_sm;
-
-/**
- * @var cnt_init_sm_lock
- * System map items list lock.
- */
-DEFINE_SEMAPHORE(cnt_init_sm_lock);
-
-static int file_open(void)
-{
- struct file *f = filp_open(sm_path, O_RDONLY, 0);
-
- if (IS_ERR(f)) {
- KSYMS_ERR("cannot open file \'%s\'", sm_path);
- return PTR_ERR(f);
- }
-
- file = f;
-
- return 0;
-}
-
-static void file_close(void)
-{
- if (file) {
- int ret = filp_close(file, NULL);
- file = NULL;
-
- if (ret) {
- KSYMS_ERR("while closing file \'%s\' err=%d",
- sm_path, ret);
- }
- }
-}
-
-static int file_check(void)
-{
- int ret = file_open();
- if (ret == 0)
- file_close();
-
- return ret;
-}
-
-static long file_size(struct file *file)
-{
- struct kstat st;
- if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
- return -1;
-
- if (!S_ISREG(st.mode))
- return -1;
-
- if (st.size != (long)st.size)
- return -1;
-
- return st.size;
-}
-
-static struct sys_map_item *create_smi(unsigned long addr, const char *name)
-{
- struct sys_map_item *smi = kmalloc(sizeof(*smi), GFP_KERNEL);
-
- if (smi == NULL) {
- KSYMS_ERR("not enough memory");
- return NULL;
- }
-
- smi->name = kmalloc(strlen(name) + 1, GFP_KERNEL);
- if (smi->name == NULL) {
- kfree(smi);
- KSYMS_ERR("not enough memory");
- return NULL;
- }
-
- INIT_LIST_HEAD(&smi->list);
- smi->addr = addr;
- strcpy(smi->name, name);
-
- return smi;
-}
-
-static void free_smi(struct sys_map_item *smi)
-{
- kfree(smi->name);
- kfree(smi);
-}
-
-static void add_smi(struct sys_map_item *smi)
-{
- list_add_tail(&smi->list, &smi_list);
-}
-
-static int is_endline(char c)
-{
- return c == '\n' || c == '\r' || c == '\0';
-}
-
-static int is_symbol_attr(char c)
-{
- return c == 't' || c == 'T';
-}
-
-static struct sys_map_item *get_sys_map_item(char *begin, char *end)
-{
- struct sys_map_item *smi = NULL;
- int n, len = end - begin;
- unsigned long addr;
- char attr, name[128], *line;
-
- line = kmalloc(len + 1, GFP_KERNEL);
- memcpy(line, begin, len);
- line[len] = '\0';
-
- n = sscanf(line, "%lx %c %127s", &addr, &attr, name);
- name[127] = '\0';
-
- if (n != 3) {
- KSYMS_ERR("parsing line: \"%s\"", line);
- attr = '\0';
- }
-
- kfree(line);
-
- if (is_symbol_attr(attr))
- smi = create_smi(addr, name);
-
- return smi;
-}
-
-
-static void parsing(char *buf, int size)
-{
- struct sys_map_item *smi;
- char *start, *end, *c;
-
- start = buf;
- end = buf + size;
-
- for (c = start; c < end; ++c) {
- if (is_endline(*c)) {
- smi = get_sys_map_item(start, c);
- if (smi)
- add_smi(smi);
-
- for (start = c; c < end; ++c) {
- if (!is_endline(*c)) {
- start = c;
- break;
- }
- }
- }
- }
-}
-
-static int create_sys_map(void)
-{
- char *data;
- long size;
- int ret = file_open();
-
- if (ret)
- return ret;
-
- size = file_size(file);
- if (size < 0) {
- KSYMS_ERR("cannot get file size");
- ret = size;
- goto close;
- }
-
- data = vmalloc(size);
- if (data == NULL) {
- KSYMS_ERR("not enough memory");
- ret = -1;
- goto close;
- }
-
- if (kernel_read(file, 0, data, size) != size) {
- KSYMS_ERR("reading file %s", sm_path);
- ret = -1;
- goto free;
- }
-
- parsing(data, size);
-
-free:
- vfree(data);
-
-close:
- file_close();
-
- return 0;
-}
-
-static void free_sys_map(void)
-{
- struct sys_map_item *smi, *n;
- list_for_each_entry_safe(smi, n, &smi_list, list) {
- list_del(&smi->list);
- free_smi(smi);
- }
-}
-
-/**
- * @brief Generates symbols list.
- *
- * @return 0 on success.
- */
-int swap_get_ksyms(void)
-{
- int ret = 0;
-
- down(&cnt_init_sm_lock);
- if (cnt_init_sm == 0)
- ret = create_sys_map();
-
- ++cnt_init_sm;
- up(&cnt_init_sm_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_get_ksyms);
-
-/**
- * @brief Frees symbols list.
- *
- * @return Void.
- */
-void swap_put_ksyms(void)
-{
- down(&cnt_init_sm_lock);
- --cnt_init_sm;
- if (cnt_init_sm == 0)
- free_sys_map();
-
- if (cnt_init_sm < 0) {
- KSYMS_ERR("cnt_init_sm=%d", cnt_init_sm);
- cnt_init_sm = 0;
- }
-
- up(&cnt_init_sm_lock);
-}
-EXPORT_SYMBOL_GPL(swap_put_ksyms);
-
-/**
- * @brief Searches for symbol by its exact name.
- *
- * @param name Pointer the name string.
- * @return Symbol's address.
- */
-unsigned long swap_ksyms(const char *name)
-{
- struct sys_map_item *smi;
-
- list_for_each_entry(smi, &smi_list, list) {
- if (strcmp(name, smi->name) == 0)
- return smi->addr;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(swap_ksyms);
-
-/**
- * @brief Searches for symbol by substring of its name.
- *
- * @param name Pointer to the name substring.
- * @return Symbol's address.
- */
-unsigned long swap_ksyms_substr(const char *name)
-{
- struct sys_map_item *smi;
- size_t len = strlen(name);
-
- list_for_each_entry(smi, &smi_list, list) {
- if (strncmp(name, smi->name, len) == 0)
- return smi->addr;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(swap_ksyms_substr);
-
-/**
- * @brief SWAP ksyms module initialization.
- *
- * @return 0 on success, negative error code on error.
- */
-int ksyms_init(void)
-{
- int ret = 0;
-
- if (sm_path == NULL) {
- KSYMS_ERR("sm_path=NULL");
- return -EINVAL;
- }
-
- ret = file_check();
- if (ret)
- return -EINVAL;
-
- /* TODO: calling func 'swap_get_ksyms' in
- * module used func 'swap_ksyms' */
- swap_get_ksyms();
-
- return 0;
-}
-
-/**
- * @brief SWAP ksyms module deinitialization.
- *
- * @return Void.
- */
-void ksyms_exit(void)
-{
- down(&cnt_init_sm_lock);
-
- if (cnt_init_sm > 0)
- free_sys_map();
-
- up(&cnt_init_sm_lock);
-}
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_loader.o
-swap_loader-y := loader_module.o \
- loader_debugfs.o \
- loader_storage.o \
- loader_pd.o
+++ /dev/null
-#ifndef __LOADER__
-#define __LOADER__
-
-struct pd_t;
-struct hd_t;
-struct uretprobe_instance;
-struct task_struct;
-
-/* process loader states */
-enum ps_t {
- NOT_LOADED,
- LOADING,
- LOADED,
- FAILED,
- ERROR
-};
-
-void loader_module_prepare_ujump(struct uretprobe_instance *ri,
- struct pt_regs *regs, unsigned long addr);
-
-unsigned long loader_not_loaded_entry(struct uretprobe_instance *ri,
- struct pt_regs *regs, struct pd_t *pd,
- struct hd_t *hd);
-void loader_loading_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd);
-void loader_failed_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd);
-
-void loader_set_rp_data_size(struct uretprobe *rp);
-void loader_set_priv_origin(struct uretprobe_instance *ri, unsigned long addr);
-unsigned long loader_get_priv_origin(struct uretprobe_instance *ri);
-int loader_add_handler(const char *path);
-
-
-struct pd_t *lpd_get(struct sspt_proc *proc);
-struct pd_t *lpd_get_by_task(struct task_struct *task);
-struct hd_t *lpd_get_hd(struct pd_t *pd, struct dentry *dentry);
-
-bool lpd_get_init_state(struct pd_t *pd);
-void lpd_set_init_state(struct pd_t *pd, bool state);
-
-struct dentry *lpd_get_dentry(struct hd_t *hd);
-struct pd_t *lpd_get_parent_pd(struct hd_t *hd);
-enum ps_t lpd_get_state(struct hd_t *hd);
-unsigned long lpd_get_handlers_base(struct hd_t *hd);
-void *lpd_get_handle(struct hd_t *hd);
-long lpd_get_attempts(struct hd_t *hd);
-void lpd_dec_attempts(struct hd_t *hd);
-
-
-#endif /* __LOADER__ */
+++ /dev/null
-#include <linux/kernel.h>
-#include <linux/debugfs.h>
-#include <linux/err.h>
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/limits.h>
-#include <asm/uaccess.h>
-#include <master/swap_debugfs.h>
-#include <us_manager/us_common_file.h>
-#include "loader_defs.h"
-#include "loader_debugfs.h"
-#include "loader_module.h"
-#include "loader_storage.h"
-
-static const char LOADER_FOLDER[] = "loader";
-static const char LOADER_LOADER[] = "loader";
-static const char LOADER_LOADER_OFFSET[] = "loader_offset";
-static const char LOADER_LOADER_PATH[] = "loader_path";
-static const char LOADER_LINKER_DATA[] = "linker";
-static const char LOADER_LINKER_PATH[] = "linker_path";
-static const char LOADER_LINKER_R_STATE_OFFSET[] = "r_state_offset";
-
-struct loader_info {
- char *path;
- unsigned long offset;
- struct dentry *dentry;
-};
-
-static struct dentry *loader_root;
-static struct loader_info __loader_info;
-
-static unsigned long r_state_offset = 0;
-static DEFINE_SPINLOCK(__dentry_lock);
-
-static inline void dentry_lock(void)
-{
- spin_lock(&__dentry_lock);
-}
-
-static inline void dentry_unlock(void)
-{
- spin_unlock(&__dentry_lock);
-}
-
-
-static void set_loader_file(char *path)
-{
- __loader_info.path = path;
- dentry_lock();
- __loader_info.dentry = swap_get_dentry(__loader_info.path);
- dentry_unlock();
-}
-
-struct dentry *ld_get_loader_dentry(void)
-{
- struct dentry *dentry;
-
- dentry_lock();
- dentry = __loader_info.dentry;
- dentry_unlock();
-
- return dentry;
-}
-
-unsigned long ld_get_loader_offset(void)
-{
- /* TODO Think about sync */
- return __loader_info.offset;
-}
-
-static void clean_loader_info(void)
-{
- if (__loader_info.path != NULL)
- kfree(__loader_info.path);
- __loader_info.path = NULL;
-
- dentry_lock();
- if (__loader_info.dentry != NULL)
- swap_put_dentry(__loader_info.dentry);
-
- __loader_info.dentry = NULL;
- __loader_info.offset = 0;
-
- dentry_unlock();
-}
-
-struct dentry *swap_debugfs_create_ptr(const char *name, mode_t mode,
- struct dentry *parent,
- unsigned long *value)
-{
- struct dentry *dentry;
-
-#if BITS_PER_LONG == 32
- dentry = swap_debugfs_create_x32(name, mode, parent, (u32 *)value);
-#elif BITS_PER_LONG == 64
- dentry = swap_debugfs_create_x64(name, mode, parent, (u64 *)value);
-#else
-#error Unsupported BITS_PER_LONG value
-#endif
-
- return dentry;
-}
-
-
-/* ===========================================================================
- * = LOADER PATH =
- * ===========================================================================
- */
-
-static ssize_t loader_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *path;
-
- if (loader_module_is_running())
- return -EBUSY;
-
- clean_loader_info();
-
- path = kmalloc(len, GFP_KERNEL);
- if (path == NULL) {
- return -ENOMEM;
- }
-
- if (copy_from_user(path, buf, len)) {
- ret = -EINVAL;
- goto err;
- }
-
- path[len - 1] = '\0';
- set_loader_file(path);
-
- ret = len;
-
- return ret;
-err:
- kfree(path);
- return ret;
-}
-
-
-static const struct file_operations loader_path_file_ops = {
- .owner = THIS_MODULE,
- .write = loader_path_write,
-};
-
-
-/* ===========================================================================
- * = LINKER PATH =
- * ===========================================================================
- */
-
-
-static ssize_t linker_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *path;
-
- path = kmalloc(len, GFP_KERNEL);
- if (path == NULL) {
- ret = -ENOMEM;
- goto linker_path_write_out;
- }
-
- if (copy_from_user(path, buf, len)) {
- ret = -EINVAL;
- goto linker_path_write_out;
- }
-
- path[len - 1] = '\0';
-
- if (ls_set_linker_info(path) != 0) {
- printk(LOADER_PREFIX "Cannot set linker path %s\n", path);
- ret = -EINVAL;
- goto linker_path_write_out;
- }
-
- ret = len;
-
-linker_path_write_out:
- kfree(path);
-
- return ret;
-}
-
-static const struct file_operations linker_path_file_ops = {
- .owner = THIS_MODULE,
- .write = linker_path_write,
-};
-
-
-
-
-
-unsigned long ld_r_state_offset(void)
-{
- return r_state_offset;
-}
-
-int ld_init(void)
-{
- struct dentry *swap_dentry, *root, *loader, *open_p, *lib_path,
- *linker_dir, *linker_path, *r_state_path;
- int ret;
-
- ret = -ENODEV;
- if (!debugfs_initialized())
- goto fail;
-
- ret = -ENOENT;
- swap_dentry = swap_debugfs_getdir();
- if (!swap_dentry)
- goto fail;
-
- ret = -ENOMEM;
- root = swap_debugfs_create_dir(LOADER_FOLDER, swap_dentry);
- if (IS_ERR_OR_NULL(root))
- goto fail;
-
- loader_root = root;
-
- loader = swap_debugfs_create_dir(LOADER_LOADER, root);
- if (IS_ERR_OR_NULL(root)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- open_p = swap_debugfs_create_ptr(LOADER_LOADER_OFFSET,
- LOADER_DEFAULT_PERMS, loader,
- &__loader_info.offset);
- if (IS_ERR_OR_NULL(open_p)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- lib_path = swap_debugfs_create_file(LOADER_LOADER_PATH,
- LOADER_DEFAULT_PERMS, loader, NULL,
- &loader_path_file_ops);
- if (IS_ERR_OR_NULL(lib_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- linker_dir = swap_debugfs_create_dir(LOADER_LINKER_DATA, root);
- if (IS_ERR_OR_NULL(linker_dir)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- linker_path = swap_debugfs_create_file(LOADER_LINKER_PATH,
- LOADER_DEFAULT_PERMS, linker_dir,
- NULL, &linker_path_file_ops);
- if (IS_ERR_OR_NULL(linker_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- r_state_path = swap_debugfs_create_ptr(LOADER_LINKER_R_STATE_OFFSET,
- LOADER_DEFAULT_PERMS, linker_dir,
- &r_state_offset);
- if (IS_ERR_OR_NULL(r_state_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- return 0;
-
-remove:
-
- debugfs_remove_recursive(root);
-
-fail:
- printk(LOADER_PREFIX "Debugfs initialization failure: %d\n", ret);
-
- return ret;
-}
-
-void ld_exit(void)
-{
- if (loader_root)
- debugfs_remove_recursive(loader_root);
- loader_root = NULL;
-
- loader_module_set_not_ready();
- clean_loader_info();
-}
+++ /dev/null
-#ifndef __LOADER_DEBUGFS_H__
-#define __LOADER_DEBUGFS_H__
-
-struct dentry;
-
-int ld_init(void);
-void ld_exit(void);
-
-struct dentry *ld_get_loader_dentry(void);
-unsigned long ld_get_loader_offset(void);
-
-unsigned long ld_r_state_offset(void);
-
-#endif /* __LOADER_DEBUGFS_H__ */
+++ /dev/null
-#ifndef __LOADER_DEFS_H__
-#define __LOADER_DEFS_H__
-
-#define LOADER_PREFIX "SWAP_LOADER: "
-#define LOADER_MAX_ATTEMPTS 10
-#define LOADER_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
-
-#endif /* __LOADER_DEFS_H__ */
+++ /dev/null
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <linux/dcache.h>
-#include <linux/namei.h>
-#include <linux/mman.h>
-#include <linux/err.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/callbacks.h>
-#include <us_manager/usm_hook.h>
-#include <writer/kernel_operations.h>
-#include <master/swap_initializer.h>
-#include "loader_defs.h"
-#include "loader_debugfs.h"
-#include "loader_module.h"
-#include "loader.h"
-#include "loader_storage.h"
-#include "loader_pd.h"
-
-
-struct us_priv {
- struct pt_regs regs;
- unsigned long arg0;
- unsigned long arg1;
- unsigned long raddr;
- unsigned long origin;
-};
-
-static atomic_t dentry_balance = ATOMIC_INIT(0);
-
-enum loader_status_t {
- SWAP_LOADER_NOT_READY = 0,
- SWAP_LOADER_READY = 1,
- SWAP_LOADER_RUNNING = 2
-};
-
-static enum loader_status_t __loader_status = SWAP_LOADER_NOT_READY;
-
-static int __loader_cbs_start_h = -1;
-static int __loader_cbs_stop_h = -1;
-
-
-
-bool loader_module_is_running(void)
-{
- if (__loader_status == SWAP_LOADER_RUNNING)
- return true;
-
- return false;
-}
-
-bool loader_module_is_ready(void)
-{
- if (__loader_status == SWAP_LOADER_READY)
- return true;
-
- return false;
-}
-
-bool loader_module_is_not_ready(void)
-{
- if (__loader_status == SWAP_LOADER_NOT_READY)
- return true;
-
- return false;
-}
-
-void loader_module_set_ready(void)
-{
- __loader_status = SWAP_LOADER_READY;
-}
-
-void loader_module_set_running(void)
-{
- __loader_status = SWAP_LOADER_RUNNING;
-}
-
-void loader_module_set_not_ready(void)
-{
- __loader_status = SWAP_LOADER_NOT_READY;
-}
-
-static inline void __prepare_ujump(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- unsigned long vaddr)
-{
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- ri->preload.use = true;
- ri->preload.thumb = !!thumb_mode(regs);
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-
- swap_set_upc(regs, vaddr);
-}
-
-static inline void __save_uregs(struct uretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct us_priv *priv = (struct us_priv *)ri->data;
-
- priv->regs = *regs;
- priv->arg0 = swap_get_uarg(regs, 0);
- priv->arg1 = swap_get_uarg(regs, 1);
- priv->raddr = swap_get_uret_addr(regs);
-}
-
-static inline void __restore_uregs(struct uretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct us_priv *priv = (struct us_priv *)ri->data;
-
- *regs = priv->regs;
- swap_put_uarg(regs, 0, priv->arg0);
- swap_put_uarg(regs, 1, priv->arg1);
- swap_set_uret_addr(regs, priv->raddr);
-#ifdef CONFIG_X86_32
- /* need to do it only on x86 */
- regs->EREG(ip) -= 1;
-#endif /* CONFIG_X86_32 */
- /* we have just restored the registers => no need to do it in
- * trampoline_uprobe_handler */
- ri->ret_addr = NULL;
-}
-
-static inline void print_regs(const char *prefix, struct pt_regs *regs,
- struct uretprobe_instance *ri, struct hd_t *hd)
-{
- struct dentry *dentry = lpd_get_dentry(hd);
-
-#if defined(CONFIG_ARM)
- printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%08lx), "
- "r0(%08lx), r1(%08lx), r2(%08lx), r3(%08lx), "
- "r4(%08lx), r5(%08lx), r6(%08lx), r7(%08lx), "
- "sp(%08lx), lr(%08lx), pc(%08lx)\n",
- current->comm, current->tgid, current->pid,
- dentry != NULL ? (char *)(dentry->d_name.name) :
- (char *)("NULL"),
- (int)lpd_get_state(hd),
- prefix, (unsigned long)ri->rp->up.addr,
- regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3,
- regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7,
- regs->ARM_sp, regs->ARM_lr, regs->ARM_pc);
-#elif defined(CONFIG_X86_32)
- printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%08lx), "
- "ip(%08lx), arg0(%08lx), arg1(%08lx), raddr(%08lx)\n",
- current->comm, current->tgid, current->pid,
- dentry != NULL ? (char *)(dentry->d_name.name) :
- (char *)("NULL"),
- (int)lpd_get_state(hd),
- prefix, (unsigned long)ri->rp->up.addr,
- regs->EREG(ip), swap_get_uarg(regs, 0), swap_get_uarg(regs, 1),
- swap_get_uret_addr(regs));
-#elif defined(CONFIG_ARM64)
- printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%016lx), "
- "x0(%016lx), x1(%016lx), x2(%016lx), x3(%016lx), "
- "x4(%016lx), x5(%016lx), x6(%016lx), x7(%016lx), "
- "sp(%016lx), lr(%016lx), pc(%016lx)\n",
- current->comm, current->tgid, current->pid,
- dentry != NULL ? (char *)(dentry->d_name.name) :
- (char *)("NULL"),
- (int)lpd_get_state(hd),
- prefix, (unsigned long)ri->rp->up.addr,
- (long)regs->regs[0], (long)regs->regs[1],
- (long)regs->regs[2], (long)regs->regs[3],
- (long)regs->regs[4], (long)regs->regs[5],
- (long)regs->regs[6], (long)regs->regs[7],
- (long)regs->sp, swap_get_uret_addr(regs), (long)regs->pc);
- (void)dentry;
-#else /* CONFIG_arch */
-# error "this architecture is not supported"
-#endif /* CONFIG_arch */
-}
-
-static inline unsigned long get_r_state_addr(struct vm_area_struct *linker_vma)
-{
- unsigned long start_addr;
- unsigned long offset = ld_r_state_offset();
-
- if (linker_vma == NULL)
- return 0;
-
- start_addr = linker_vma->vm_start;
-
- return (offset ? start_addr + offset : 0);
-}
-
-static struct vm_area_struct *__get_linker_vma(struct task_struct *task)
-{
- struct vm_area_struct *vma = NULL;
- struct bin_info *ld_info;
-
- ld_info = ls_get_linker_info();
- if (ld_info == NULL) {
- printk(LOADER_PREFIX "Cannot get linker info [%u %u %s]!\n",
- task->tgid, task->pid, task->comm);
- return NULL;
- }
-
- for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
- if (vma->vm_file && vma->vm_flags & VM_EXEC
- && vma->vm_file->f_path.dentry == ld_info->dentry) {
- ls_put_linker_info(ld_info);
- return vma;
- }
- }
-
- ls_put_linker_info(ld_info);
- return NULL;
-}
-
-
-
-
-
-
-
-
-
-static bool __is_proc_mmap_mappable(struct task_struct *task)
-{
- struct vm_area_struct *linker_vma = __get_linker_vma(task);
- struct sspt_proc *proc;
- unsigned long r_state_addr;
- unsigned int state;
-
- if (linker_vma == NULL)
- return false;
-
- r_state_addr = get_r_state_addr(linker_vma);
- if (r_state_addr == 0)
- return false;
-
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- proc->r_state_addr = r_state_addr;
- sspt_proc_put(proc);
- }
-
- if (get_user(state, (unsigned long *)r_state_addr))
- return false;
-
- return !state;
-}
-
-static bool __should_we_load_handlers(struct task_struct *task,
- struct pt_regs *regs)
-{
- if (!__is_proc_mmap_mappable(task))
- return false;
-
- return true;
-}
-
-
-
-
-
-static void mmap_handler(struct sspt_proc *proc, struct vm_area_struct *vma)
-{
- struct pd_t *pd;
- unsigned long vaddr = vma->vm_start;
- struct dentry *dentry = vma->vm_file->f_path.dentry;
-
- pd = lpd_get(proc);
- if (pd == NULL) {
- printk(LOADER_PREFIX "%d: No process data! Current %d %s\n",
- __LINE__, current->tgid, current->comm);
- return;
- }
-
- if (dentry == ld_get_loader_dentry()) {
- lpd_set_loader_base(pd, vaddr);
- } else {
- struct hd_t *hd;
-
- hd = lpd_get_hd(pd, dentry);
- if (hd)
- lpd_set_handlers_base(hd, vaddr);
- }
-}
-
-
-static struct usm_hook usm_hook = {
- .owner = THIS_MODULE,
- .mmap = mmap_handler,
-};
-
-static bool mmap_rp_inst = false;
-static DEFINE_MUTEX(mmap_rp_mtx);
-
-static void loader_start_cb(void)
-{
- int res;
-
- mutex_lock(&mmap_rp_mtx);
- res = usm_hook_reg(&usm_hook);
- if (res != 0)
- pr_err(LOADER_PREFIX "Cannot register usm_hook\n");
- else
- mmap_rp_inst = true;
- mutex_unlock(&mmap_rp_mtx);
-}
-
-static void loader_stop_cb(void)
-{
- mutex_lock(&mmap_rp_mtx);
- if (mmap_rp_inst) {
- usm_hook_unreg(&usm_hook);
- mmap_rp_inst = false;
- }
- mutex_unlock(&mmap_rp_mtx);
-}
-
-static unsigned long __not_loaded_entry(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd)
-{
- char __user *path = NULL;
- unsigned long vaddr = 0;
- unsigned long base;
-
- /* if linker is still doing its work, we do nothing */
- if (!__should_we_load_handlers(current, regs))
- return 0;
-
- base = lpd_get_loader_base(pd);
- if (base == 0)
- return 0; /* loader isn't mapped */
-
- /* jump to loader code if ready */
- vaddr = base + ld_get_loader_offset();
- if (vaddr) {
- /* save original regs state */
- __save_uregs(ri, regs);
- print_regs("ORIG", regs, ri, hd);
-
- path = lpd_get_path(pd, hd);
-
- /* set dlopen args: filename, flags */
- swap_put_uarg(regs, 0, (unsigned long)path);
- swap_put_uarg(regs, 1, 2 /* RTLD_NOW */);
-
- /* do the jump to dlopen */
- __prepare_ujump(ri, regs, vaddr);
- /* set new state */
- lpd_set_state(hd, LOADING);
- }
-
- return vaddr;
-}
-
-static void __loading_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd)
-{
- struct us_priv *priv = (struct us_priv *)ri->data;
- unsigned long vaddr = 0;
-
- /* check if loading has been completed */
- vaddr = lpd_get_loader_base(pd) +
- ld_get_loader_offset();
- if (vaddr && (priv->origin == vaddr)) {
- lpd_set_handle(hd,
- (void __user *)regs_return_value(regs));
-
- /* restore original regs state */
- __restore_uregs(ri, regs);
- print_regs("REST", regs, ri, hd);
- /* check if loading done */
-
- if (lpd_get_handle(hd)) {
- lpd_set_state(hd, LOADED);
- } else {
- lpd_dec_attempts(hd);
- lpd_set_state(hd, FAILED);
- }
- }
-}
-
-static void __failed_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd)
-{
- if (lpd_get_attempts(hd))
- lpd_set_state(hd, NOT_LOADED);
-}
-
-
-
-unsigned long loader_not_loaded_entry(struct uretprobe_instance *ri,
- struct pt_regs *regs, struct pd_t *pd,
- struct hd_t *hd)
-{
- return __not_loaded_entry(ri, regs, pd, hd);
-}
-EXPORT_SYMBOL_GPL(loader_not_loaded_entry);
-
-void loader_loading_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd)
-{
- __loading_ret(ri, regs, pd, hd);
-}
-EXPORT_SYMBOL_GPL(loader_loading_ret);
-
-void loader_failed_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
- struct pd_t *pd, struct hd_t *hd)
-{
- __failed_ret(ri, regs, pd, hd);
-}
-EXPORT_SYMBOL_GPL(loader_failed_ret);
-
-void loader_module_prepare_ujump(struct uretprobe_instance *ri,
- struct pt_regs *regs, unsigned long addr)
-{
- __prepare_ujump(ri, regs, addr);
-}
-EXPORT_SYMBOL_GPL(loader_module_prepare_ujump);
-
-void loader_set_rp_data_size(struct uretprobe *rp)
-{
- rp->data_size = sizeof(struct us_priv);
-}
-EXPORT_SYMBOL_GPL(loader_set_rp_data_size);
-
-void loader_set_priv_origin(struct uretprobe_instance *ri, unsigned long addr)
-{
- struct us_priv *priv = (struct us_priv *)ri->data;
-
- priv->origin = addr;
-}
-EXPORT_SYMBOL_GPL(loader_set_priv_origin);
-
-unsigned long loader_get_priv_origin(struct uretprobe_instance *ri)
-{
- struct us_priv *priv = (struct us_priv *)ri->data;
-
- return priv->origin;
-}
-EXPORT_SYMBOL_GPL(loader_get_priv_origin);
-
-
-int loader_set(void)
-{
- if (loader_module_is_running())
- return -EBUSY;
-
- return 0;
-}
-
-void loader_unset(void)
-{
- mutex_lock(&mmap_rp_mtx);
- if (mmap_rp_inst) {
- usm_hook_unreg(&usm_hook);
- mmap_rp_inst = false;
- }
- mutex_unlock(&mmap_rp_mtx);
-
- /*module_put(THIS_MODULE);*/
- loader_module_set_not_ready();
-}
-
-int loader_add_handler(const char *path)
-{
- return ls_add_handler(path);
-}
-EXPORT_SYMBOL_GPL(loader_add_handler);
-
-
-static int loader_module_init(void)
-{
- int ret;
-
- ret = ld_init();
- if (ret)
- goto out_err;
-
- ret = ls_init();
- if (ret)
- goto exit_debugfs;
-
- ret = lpd_init();
- if (ret)
- goto exit_storage;
-
- /* TODO do not forget to remove set (it is just for debugging) */
- ret = loader_set();
- if (ret)
- goto exit_pd;
-
- __loader_cbs_start_h = us_manager_reg_cb(START_CB, loader_start_cb);
- if (__loader_cbs_start_h < 0)
- goto exit_start_cb;
-
- __loader_cbs_stop_h = us_manager_reg_cb(STOP_CB, loader_stop_cb);
- if (__loader_cbs_stop_h < 0)
- goto exit_stop_cb;
-
- return 0;
-
-exit_stop_cb:
- us_manager_unreg_cb(__loader_cbs_start_h);
-
-exit_start_cb:
- loader_unset();
-
-exit_pd:
- lpd_uninit();
-
-exit_storage:
- ls_exit();
-
-exit_debugfs:
- ld_exit();
-
-out_err:
- return ret;
-}
-
-static void loader_module_exit(void)
-{
- int balance;
-
- us_manager_unreg_cb(__loader_cbs_start_h);
- us_manager_unreg_cb(__loader_cbs_stop_h);
- loader_unset();
- lpd_uninit();
- ls_exit();
- ld_exit();
-
- balance = atomic_read(&dentry_balance);
- atomic_set(&dentry_balance, 0);
-
- WARN(balance, "Bad GET/PUT dentry balance: %d\n", balance);
-}
-
-SWAP_LIGHT_INIT_MODULE(NULL, loader_module_init, loader_module_exit,
- NULL, NULL);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP Loader Module");
-MODULE_AUTHOR("Vasiliy Ulyanov <v.ulyanov@samsung.com>"
- "Alexander Aksenov <a.aksenov@samsung.com>");
+++ /dev/null
-#ifndef __LOADER_MODULE_H__
-#define __LOADER_MODULE_H__
-
-#include <linux/types.h>
-
-struct dentry;
-
-bool loader_module_is_ready(void);
-bool loader_module_is_running(void);
-bool loader_module_is_not_ready(void);
-void loader_module_set_ready(void);
-void loader_module_set_running(void);
-void loader_module_set_not_ready(void);
-
-struct dentry *get_dentry(const char *filepath);
-void put_dentry(struct dentry *dentry);
-
-
-
-#endif /* __LOADER_MODULE_H__ */
+++ /dev/null
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/module.h>
-#include <linux/hardirq.h>
-#include <linux/list.h>
-#include <us_manager/us_manager_common.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include "loader_pd.h"
-#include "loader.h"
-#include "loader_debugfs.h"
-#include "loader_storage.h"
-#include "loader_defs.h"
-
-
-struct pd_t {
- unsigned long loader_base;
- unsigned long data_page;
- struct list_head handlers;
- bool is_pthread_init;
-};
-
-struct hd_t {
- struct list_head list;
- struct dentry *dentry;
- enum ps_t state;
- struct pd_t *parent;
- unsigned long base;
- unsigned long offset;
- void __user *handle;
- long attempts;
-};
-
-
-static inline bool check_vma(struct vm_area_struct *vma, struct dentry *dentry)
-{
- struct file *file = vma->vm_file;
-
- return (file && (vma->vm_flags & VM_EXEC) &&
- (file->f_path.dentry == dentry));
-}
-
-static inline unsigned long __get_loader_base(struct pd_t *pd)
-{
- return pd->loader_base;
-}
-
-static inline void __set_loader_base(struct pd_t *pd,
- unsigned long addr)
-{
- pd->loader_base = addr;
-}
-
-static inline unsigned long __get_data_page(struct pd_t *pd)
-{
- return pd->data_page;
-}
-
-static inline void __set_data_page(struct pd_t *pd, unsigned long page)
-{
- pd->data_page = page;
-}
-
-
-
-static inline enum ps_t __get_state(struct hd_t *hd)
-{
- return hd->state;
-}
-
-static inline void __set_state(struct hd_t *hd, enum ps_t state)
-{
- hd->state = state;
-}
-
-static inline unsigned long __get_handlers_base(struct hd_t *hd)
-{
- return hd->base;
-}
-
-static inline void __set_handlers_base(struct hd_t *hd,
- unsigned long addr)
-{
- hd->base = addr;
-}
-
-static inline unsigned long __get_offset(struct hd_t *hd)
-{
- return hd->offset;
-}
-
-static inline void *__get_handle(struct hd_t *hd)
-{
- return hd->handle;
-}
-
-static inline void __set_handle(struct hd_t *hd, void __user *handle)
-{
- hd->handle = handle;
-}
-
-static inline long __get_attempts(struct hd_t *hd)
-{
- return hd->attempts;
-}
-
-static inline void __set_attempts(struct hd_t *hd, long attempts)
-{
- hd->attempts = attempts;
-}
-
-
-
-static struct vm_area_struct *find_vma_by_dentry(struct mm_struct *mm,
- struct dentry *dentry)
-{
- struct vm_area_struct *vma;
-
- for (vma = mm->mmap; vma; vma = vma->vm_next)
- if (check_vma(vma, dentry))
- return vma;
-
- return NULL;
-}
-
-static struct pd_t *__create_pd(void)
-{
- struct pd_t *pd;
- unsigned long page;
-
- pd = kzalloc(sizeof(*pd), GFP_ATOMIC);
- if (pd == NULL)
- return NULL;
-
- down_write(¤t->mm->mmap_sem);
- page = __swap_do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0);
- up_write(¤t->mm->mmap_sem);
- if (IS_ERR_VALUE(page)) {
- printk(KERN_ERR LOADER_PREFIX
- "Cannot alloc page for %u\n", current->tgid);
- goto create_pd_fail;
- }
-
- __set_data_page(pd, page);
- pd->is_pthread_init = true;
- INIT_LIST_HEAD(&pd->handlers);
-
- return pd;
-
-create_pd_fail:
- kfree(pd);
-
- return NULL;
-}
-
-static size_t __copy_path(char *src, unsigned long page, unsigned long offset)
-{
- unsigned long dest = page + offset;
- size_t len = strnlen(src, PATH_MAX);
-
- /* set handler path */
- if (copy_to_user((void __user *)dest, src, len) != 0) {
- printk(KERN_ERR LOADER_PREFIX
- "Cannot copy string to user!\n");
- return 0;
- }
-
- return len;
-}
-
-static void __set_ld_mapped(struct pd_t *pd, struct mm_struct *mm)
-{
- struct vm_area_struct *vma;
- struct dentry *ld = ld_get_loader_dentry();
-
- down_read(&mm->mmap_sem);
- if (ld) {
- vma = find_vma_by_dentry(mm, ld);
- if (vma)
- __set_loader_base(pd, vma->vm_start);
- }
- up_read(&mm->mmap_sem);
-}
-
-static void __set_handler_mapped(struct hd_t *hd, struct mm_struct *mm)
-{
- struct vm_area_struct *vma;
- struct dentry *handlers = hd->dentry;
-
- down_read(&mm->mmap_sem);
- if (handlers) {
- vma = find_vma_by_dentry(mm, handlers);
- if (vma) {
- __set_handlers_base(hd, vma->vm_start);
- __set_state(hd, LOADED);
- goto set_handler_mapped_out;
- }
- }
- __set_state(hd, NOT_LOADED);
-
-set_handler_mapped_out:
- up_read(&mm->mmap_sem);
-}
-
-
-static int __get_handlers(struct pd_t *pd, struct task_struct *task)
-{
- struct list_head *handlers = NULL;
- struct bin_info_el *bin;
- struct hd_t *hd;
- unsigned long offset = 0;
- size_t len;
- int ret = 0;
-
- handlers = ls_get_handlers();
- if (handlers == NULL)
- return -EINVAL;
-
- list_for_each_entry(bin, handlers, list) {
- len = __copy_path(bin->path, pd->data_page, offset);
- if (len == 0) {
- ret = -EINVAL;
- goto get_handlers_out;
- }
-
- hd = kzalloc(sizeof(*hd), GFP_ATOMIC);
- if (hd == NULL) {
- printk(KERN_ERR LOADER_PREFIX "No atomic mem!\n");
- ret = -ENOMEM;
- goto get_handlers_out;
- }
-
-
- INIT_LIST_HEAD(&hd->list);
- hd->parent = pd;
- hd->dentry = bin->dentry;
- hd->offset = offset;
- __set_handler_mapped(hd, task->mm);
- __set_attempts(hd, LOADER_MAX_ATTEMPTS);
- list_add_tail(&hd->list, &pd->handlers);
-
- /* inc handlers path's on page */
- offset += len + 1;
- }
-
-get_handlers_out:
- /* TODO Cleanup already created */
- ls_put_handlers();
-
- return ret;
-}
-
-
-
-enum ps_t lpd_get_state(struct hd_t *hd)
-{
- if (hd == NULL)
- return 0;
-
- return __get_state(hd);
-}
-EXPORT_SYMBOL_GPL(lpd_get_state);
-
-void lpd_set_state(struct hd_t *hd, enum ps_t state)
-{
- if (hd == NULL) {
- printk(LOADER_PREFIX "%d: No handler data! Current %d %s\n",
- __LINE__, current->tgid, current->comm);
- return;
- }
-
- __set_state(hd, state);
-}
-
-unsigned long lpd_get_loader_base(struct pd_t *pd)
-{
- if (pd == NULL)
- return 0;
-
- return __get_loader_base(pd);
-}
-
-void lpd_set_loader_base(struct pd_t *pd, unsigned long vaddr)
-{
- __set_loader_base(pd, vaddr);
-}
-
-unsigned long lpd_get_handlers_base(struct hd_t *hd)
-{
- if (hd == NULL)
- return 0;
-
- return __get_handlers_base(hd);
-}
-EXPORT_SYMBOL_GPL(lpd_get_handlers_base);
-
-void lpd_set_handlers_base(struct hd_t *hd, unsigned long vaddr)
-{
- __set_handlers_base(hd, vaddr);
-}
-
-char __user *lpd_get_path(struct pd_t *pd, struct hd_t *hd)
-{
- unsigned long page = __get_data_page(pd);
- unsigned long offset = __get_offset(hd);
-
- return (char __user *)(page + offset);
-}
-
-
-
-void *lpd_get_handle(struct hd_t *hd)
-{
- if (hd == NULL)
- return NULL;
-
- return __get_handle(hd);
-}
-
-void lpd_set_handle(struct hd_t *hd, void __user *handle)
-{
- if (hd == NULL) {
- printk(LOADER_PREFIX "%d: No handler data! Current %d %s\n",
- __LINE__, current->tgid, current->comm);
- return;
- }
-
- __set_handle(hd, handle);
-}
-
-long lpd_get_attempts(struct hd_t *hd)
-{
- if (hd == NULL)
- return -EINVAL;
-
- return __get_attempts(hd);
-}
-
-void lpd_dec_attempts(struct hd_t *hd)
-{
- long attempts;
-
- if (hd == NULL) {
- printk(LOADER_PREFIX "%d: No handler data! Current %d %s\n",
- __LINE__, current->tgid, current->comm);
- return;
- }
-
- attempts = __get_attempts(hd);
- attempts--;
- __set_attempts(hd, attempts);
-}
-
-struct dentry *lpd_get_dentry(struct hd_t *hd)
-{
- return hd->dentry;
-}
-
-struct pd_t *lpd_get_parent_pd(struct hd_t *hd)
-{
- return hd->parent;
-}
-EXPORT_SYMBOL_GPL(lpd_get_parent_pd);
-
-struct pd_t *lpd_get(struct sspt_proc *proc)
-{
- return (struct pd_t *)proc->private_data;
-}
-EXPORT_SYMBOL_GPL(lpd_get);
-
-struct pd_t *lpd_get_by_task(struct task_struct *task)
-{
- struct sspt_proc *proc = sspt_proc_by_task(task);
-
- return lpd_get(proc);
-}
-EXPORT_SYMBOL_GPL(lpd_get_by_task);
-
-struct hd_t *lpd_get_hd(struct pd_t *pd, struct dentry *dentry)
-{
- struct hd_t *hd;
-
- list_for_each_entry(hd, &pd->handlers, list) {
- if (hd->dentry == dentry)
- return hd;
- }
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(lpd_get_hd);
-
-/* TODO Move it to GOT patcher, only it uses this */
-bool lpd_get_init_state(struct pd_t *pd)
-{
- return pd->is_pthread_init;
-}
-EXPORT_SYMBOL_GPL(lpd_get_init_state);
-
-/* TODO Move it to GOT patcher, only it uses this */
-void lpd_set_init_state(struct pd_t *pd, bool state)
-{
- pd->is_pthread_init = state;
-}
-EXPORT_SYMBOL_GPL(lpd_set_init_state);
-
-static struct pd_t *do_create_pd(struct task_struct *task)
-{
- struct pd_t *pd;
- int ret;
-
- pd = __create_pd();
- if (pd == NULL) {
- ret = -ENOMEM;
- goto create_pd_exit;
- }
-
- ret = __get_handlers(pd, task);
- if (ret)
- goto free_pd;
-
- __set_ld_mapped(pd, task->mm);
-
- return pd;
-
-free_pd:
- kfree(pd);
-
-create_pd_exit:
- printk(KERN_ERR LOADER_PREFIX "do_pd_create_pd: error=%d\n", ret);
- return NULL;
-}
-
-static void *pd_create(struct sspt_proc *proc)
-{
- struct pd_t *pd;
-
- pd = do_create_pd(proc->leader);
-
- return (void *)pd;
-}
-
-static void pd_destroy(struct sspt_proc *proc, void *data)
-{
- /* FIXME: to be implemented */
-}
-
-struct sspt_proc_cb pd_cb = {
- .priv_create = pd_create,
- .priv_destroy = pd_destroy
-};
-
-int lpd_init(void)
-{
- int ret;
-
- ret = sspt_proc_cb_set(&pd_cb);
-
- return ret;
-}
-
-void lpd_uninit(void)
-{
- sspt_proc_cb_set(NULL);
-
- /* TODO Cleanup */
-}
+++ /dev/null
-#ifndef __LOADER_PD_H__
-#define __LOADER_PD_H__
-
-#include <loader/loader.h>
-
-struct pd_t;
-struct hd_t;
-struct sspt_proc;
-struct dentry;
-struct list_head;
-
-
-unsigned long lpd_get_loader_base(struct pd_t *pd);
-void lpd_set_loader_base(struct pd_t *pd, unsigned long vaddr);
-
-void lpd_set_state(struct hd_t *hd, enum ps_t state);
-void lpd_set_handlers_base(struct hd_t *hd, unsigned long vaddr);
-void lpd_set_handle(struct hd_t *hd, void __user *handle);
-
-char __user *lpd_get_path(struct pd_t *pd, struct hd_t *hd);
-
-int lpd_init(void);
-void lpd_uninit(void);
-
-
-#endif /* __LOADER_PD_H__*/
+++ /dev/null
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/list.h>
-#include <ks_features/ks_map.h>
-#include <us_manager/us_common_file.h>
-#include "loader_defs.h"
-#include "loader_module.h"
-#include "loader_storage.h"
-
-static struct bin_info __linker_info = { NULL, NULL };
-
-static LIST_HEAD(handlers_list);
-
-
-static bool __check_dentry_already_exist(struct dentry *dentry)
-{
- struct bin_info_el *bin;
- bool ret = false;
-
- list_for_each_entry(bin, &handlers_list, list) {
- if (bin->dentry == dentry) {
- ret = true;
- goto out;
- }
- }
-
-out:
- return ret;
-}
-
-static inline int __add_handler(const char *path)
-{
- struct dentry *dentry;
- size_t len = strnlen(path, PATH_MAX);
- struct bin_info_el *bin;
- int ret = 0;
-
- dentry = swap_get_dentry(path);
- if (!dentry) {
- ret = -ENOENT;
- goto add_handler_out;
- }
-
- if (__check_dentry_already_exist(dentry)) {
- ret = 1;
- goto add_handler_fail_release_dentry;
- }
-
- bin = kmalloc(sizeof(*bin), GFP_KERNEL);
- if (bin == NULL) {
- ret = -ENOMEM;
- goto add_handler_fail_release_dentry;
- }
-
- bin->path = kmalloc(len + 1, GFP_KERNEL);
- if (bin->path == NULL) {
- ret = -ENOMEM;
- goto add_handler_fail_free_bin;
- }
-
- INIT_LIST_HEAD(&bin->list);
- strncpy(bin->path, path, len);
- bin->path[len] = '\0';
- bin->dentry = dentry;
- list_add_tail(&bin->list, &handlers_list);
-
- return ret;
-
-add_handler_fail_free_bin:
- kfree(bin);
-
-add_handler_fail_release_dentry:
- swap_put_dentry(dentry);
-
-add_handler_out:
- return ret;
-}
-
-static inline void __remove_handler(struct bin_info_el *bin)
-{
- list_del(&bin->list);
- swap_put_dentry(bin->dentry);
- kfree(bin->path);
- kfree(bin);
-}
-
-static inline void __remove_handlers(void)
-{
- struct bin_info_el *bin, *tmp;
-
- list_for_each_entry_safe(bin, tmp, &handlers_list, list)
- __remove_handler(bin);
-}
-
-static inline struct bin_info *__get_linker_info(void)
-{
- return &__linker_info;
-}
-
-static inline bool __check_linker_info(void)
-{
- return (__linker_info.dentry != NULL); /* TODO */
-}
-
-static inline int __init_linker_info(char *path)
-{
- struct dentry *dentry;
- size_t len = strnlen(path, PATH_MAX);
- int ret = 0;
-
-
- __linker_info.path = kmalloc(len + 1, GFP_KERNEL);
- if (__linker_info.path == NULL) {
- ret = -ENOMEM;
- goto init_linker_fail;
- }
-
- dentry = swap_get_dentry(path);
- if (!dentry) {
- ret = -ENOENT;
- goto init_linker_fail_free;
- }
-
- strncpy(__linker_info.path, path, len);
- __linker_info.path[len] = '\0';
- __linker_info.dentry = dentry;
-
- return ret;
-
-init_linker_fail_free:
- kfree(__linker_info.path);
-
-init_linker_fail:
-
- return ret;
-}
-
-static inline void __drop_linker_info(void)
-{
- kfree(__linker_info.path);
- __linker_info.path = NULL;
-
- if (__linker_info.dentry)
- swap_put_dentry(__linker_info.dentry);
- __linker_info.dentry = NULL;
-}
-
-
-
-
-int ls_add_handler(const char *path)
-{
- int ret;
-
- /* If ret is positive - handler was not added, because it is
- * already exists */
- ret = __add_handler(path);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-struct list_head *ls_get_handlers(void)
-{
- /* TODO counter, syncs */
- return &handlers_list;
-}
-
-void ls_put_handlers(void)
-{
- /* TODO dec counter, release sync */
-}
-
-int ls_set_linker_info(char *path)
-{
- return __init_linker_info(path);
-}
-
-struct bin_info *ls_get_linker_info(void)
-{
- struct bin_info *info = __get_linker_info();
-
- if (__check_linker_info())
- return info;
-
- return NULL;
-}
-
-void ls_put_linker_info(struct bin_info *info)
-{
-}
-
-int ls_init(void)
-{
- return 0;
-}
-
-void ls_exit(void)
-{
- __drop_linker_info();
- __remove_handlers();
-}
+++ /dev/null
-#ifndef __LOADER_STORAGE_H__
-#define __LOADER_STORAGE_H__
-
-struct list_head;
-struct dentry;
-
-struct bin_info {
- char *path;
- /* ghot */
- struct dentry *dentry;
-};
-
-struct bin_info_el {
- struct list_head list;
- char *path;
- /* ghot */
- struct dentry *dentry;
-};
-
-
-
-int ls_add_handler(const char *path);
-struct list_head *ls_get_handlers(void);
-void ls_put_handlers(void);
-
-int ls_set_linker_info(char *path);
-struct bin_info *ls_get_linker_info(void);
-void ls_put_linker_info(struct bin_info *info);
-
-int ls_init(void);
-void ls_exit(void);
-
-#endif /* __LOADER_HANDLERS_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_master.o
-swap_master-y := master_module.o \
- swap_debugfs.o \
- swap_initializer.o \
- swap_deps.o \
- wait.o
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include "swap_debugfs.h"
-#include "swap_initializer.h"
-
-
-static int __init master_init(void)
-{
- return swap_debugfs_init();
-}
-
-static void __exit master_exit(void)
-{
- swap_debugfs_uninit();
-}
-
-module_init(master_init);
-module_exit(master_exit);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
-#include <linux/version.h>
-#include "swap_initializer.h"
-#include "swap_debugfs.h"
-
-
-static int change_permission(struct dentry *dentry)
-{
- const int system_fw = 202;
-
- /* set UNIX permissions */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
- dentry->d_inode->i_uid = KUIDT_INIT(system_fw);
- dentry->d_inode->i_gid = KGIDT_INIT(system_fw);
-#else
- dentry->d_inode->i_uid = system_fw;
- dentry->d_inode->i_gid = system_fw;
-#endif
-
- return 0;
-}
-
-struct dentry *swap_debugfs_create_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops)
-{
- struct dentry *dentry;
-
- dentry = debugfs_create_file(name, mode, parent, data, fops);
- if (dentry) {
- if (change_permission(dentry)) {
- debugfs_remove(dentry);
- return NULL;
- }
- }
-
- return dentry;
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_file);
-
-struct dentry *swap_debugfs_create_dir(const char *name, struct dentry *parent)
-{
- struct dentry *dentry;
-
- dentry = debugfs_create_dir(name, parent);
- if (dentry) {
- if (change_permission(dentry)) {
- debugfs_remove(dentry);
- return NULL;
- }
- }
-
- return dentry;
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_dir);
-
-struct dentry *swap_debugfs_create_x32(const char *name, umode_t mode,
- struct dentry *parent, u32 *value)
-{
- struct dentry *dentry;
-
- dentry = debugfs_create_x32(name, mode, parent, value);
- if (dentry) {
- if (change_permission(dentry)) {
- debugfs_remove(dentry);
- return NULL;
- }
- }
-
- return dentry;
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_x32);
-
-struct dentry *swap_debugfs_create_x64(const char *name, umode_t mode,
- struct dentry *parent, u64 *value)
-{
- struct dentry *dentry;
-
- dentry = debugfs_create_x64(name, mode, parent, value);
- if (dentry) {
- if (change_permission(dentry)) {
- debugfs_remove(dentry);
- return NULL;
- }
- }
-
- return dentry;
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_x64);
-
-struct dentry *swap_debugfs_create_u64(const char *name, umode_t mode,
- struct dentry *parent, u64 *value)
-{
- struct dentry *dentry;
-
- dentry = debugfs_create_u64(name, mode, parent, value);
- if (dentry) {
- if (change_permission(dentry)) {
- debugfs_remove(dentry);
- return NULL;
- }
- }
-
- return dentry;
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_u64);
-
-
-/* based on define DEFINE_SIMPLE_ATTRIBUTE */
-#define SWAP_DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \
-static int __fops ## _open(struct inode *inode, struct file *file) \
-{ \
- int ret; \
- \
- ret = swap_init_simple_open(inode, file); \
- if (ret) \
- return ret; \
- \
- __simple_attr_check_format(__fmt, 0ull); \
- ret = simple_attr_open(inode, file, __get, __set, __fmt); \
- if (ret) \
- swap_init_simple_release(inode, file); \
- \
- return ret; \
-} \
-static int __fops ## _release(struct inode *inode, struct file *file) \
-{ \
- simple_attr_release(inode, file); \
- swap_init_simple_release(inode, file); \
- \
- return 0; \
-} \
-static const struct file_operations __fops = { \
- .owner = THIS_MODULE, \
- .open = __fops ## _open, \
- .release = __fops ## _release, \
- .read = simple_attr_read, \
- .write = simple_attr_write, \
- .llseek = generic_file_llseek, \
-}
-
-
-static int fset_u64(void *data, u64 val)
-{
- struct dfs_setget_64 *setget = data;
-
- return setget->set(val);
-}
-
-static int fget_u64(void *data, u64 *val)
-{
- struct dfs_setget_64 *setget = data;
-
- *val = setget->get();
- return 0;
-}
-
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_u64, fget_u64, fset_u64, "%llu\n");
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_u64_ro, fget_u64, NULL, "%llu\n");
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_u64_wo, NULL, fset_u64, "%llu\n");
-
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_x64, fget_u64, fset_u64, "0x%08llx\n");
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_x64_ro, fget_u64, NULL, "0x%08llx\n");
-SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_x64_wo, NULL, fset_u64, "0x%08llx\n");
-
-static struct dentry *do_create_dfs_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops,
- const struct file_operations *fops_ro,
- const struct file_operations *fops_wo)
-{
- /* if there are no write bits set, make read only */
- if (!(mode & S_IWUGO))
- return swap_debugfs_create_file(name, mode, parent,
- data, fops_ro);
- /* if there are no read bits set, make write only */
- if (!(mode & S_IRUGO))
- return swap_debugfs_create_file(name, mode, parent,
- data, fops_wo);
-
- return swap_debugfs_create_file(name, mode, parent, data, fops);
-}
-
-struct dentry *swap_debugfs_create_setget_u64(const char *name, umode_t mode,
- struct dentry *parent,
- struct dfs_setget_64 *setget)
-{
- return do_create_dfs_file(name, mode, parent, setget,
- &fops_setget_u64,
- &fops_setget_u64_ro,
- &fops_setget_u64_wo);
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_setget_u64);
-
-struct dentry *swap_debugfs_create_setget_x64(const char *name, umode_t mode,
- struct dentry *parent,
- struct dfs_setget_64 *setget)
-{
- return do_create_dfs_file(name, mode, parent, setget,
- &fops_setget_x64,
- &fops_setget_x64_ro,
- &fops_setget_x64_wo);
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_create_setget_x64);
-
-
-static int set_enable(int enable)
-{
- int ret = 0, change, enable_current;
-
- enable_current = swap_init_stat_get();
-
- change = ((!!enable_current) << 1) | (!!enable);
- switch (change) {
- case 0b01: /* init */
- ret = swap_init_init();
- break;
- case 0b10: /* uninit */
- ret = swap_init_uninit();
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- swap_init_stat_put();
-
- return ret;
-}
-
-static ssize_t read_enable(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- char buf[3];
- int enable;
-
- enable = swap_init_stat_get();
- swap_init_stat_put();
-
- if (enable)
- buf[0] = '1';
- else
- buf[0] = '0';
- buf[1] = '\n';
- buf[2] = '\0';
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static int do_write_enable(const char *buf, size_t size)
-{
- if (size < 1)
- return -EINVAL;
-
- switch (buf[0]) {
- case '1':
- return set_enable(1);
- case '0':
- return set_enable(0);
- }
-
- return -EINVAL;
-}
-
-static ssize_t write_enable(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int ret;
- char buf[32];
- size_t buf_size;
-
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- ret = do_write_enable(buf, buf_size);
-
- return ret ? ret : count;
-}
-
-static const struct file_operations fops_enable = {
- .owner = THIS_MODULE,
- .read = read_enable,
- .write = write_enable,
- .llseek = default_llseek,
-};
-
-
-static struct dentry *swap_dir;
-
-/**
- * @brief Get debugfs dir.
- *
- * @return Pointer to dentry stuct.
- */
-struct dentry *swap_debugfs_getdir(void)
-{
- return swap_dir;
-}
-EXPORT_SYMBOL_GPL(swap_debugfs_getdir);
-
-static int debugfs_dir_init(void)
-{
- swap_dir = swap_debugfs_create_dir("swap", NULL);
- if (swap_dir == NULL)
- return -ENOMEM;
-
- return 0;
-}
-
-static void debugfs_dir_exit(void)
-{
- struct dentry *dir = swap_dir;
-
- swap_dir = NULL;
- debugfs_remove_recursive(dir);
-}
-
-/**
- * @brief Initializes SWAP debugfs.
- *
- * @return 0 on success, negative error code on error.
- */
-int swap_debugfs_init(void)
-{
- int ret;
- struct dentry *dentry;
-
- ret = debugfs_dir_init();
- if (ret)
- return ret;
-
- dentry = swap_debugfs_create_file("enable", 0600, swap_dir, NULL,
- &fops_enable);
- if (dentry == NULL) {
- debugfs_dir_exit();
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/**
- * @brief Deinitializes SWAP debugfs and recursively removes all its files.
- *
- * @return Void.
- */
-void swap_debugfs_uninit(void)
-{
- debugfs_dir_exit();
-}
+++ /dev/null
-/**
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * SWAP debugfs interface definition.
- */
-
-#ifndef _SWAP_DEBUGFS_H
-#define _SWAP_DEBUGFS_H
-
-
-#include <linux/types.h>
-
-
-struct dfs_setget_64 {
- int (*set)(u64 val);
- u64 (*get)(void);
-};
-
-struct dentry;
-struct file_operations;
-
-struct dentry *swap_debugfs_create_setget_u64(const char *name, umode_t mode,
- struct dentry *parent,
- struct dfs_setget_64 *setget);
-
-struct dentry *swap_debugfs_create_setget_x64(const char *name, umode_t mode,
- struct dentry *parent,
- struct dfs_setget_64 *setget);
-
-struct dentry *swap_debugfs_create_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops);
-struct dentry *swap_debugfs_create_dir(const char *name, struct dentry *parent);
-
-struct dentry *swap_debugfs_create_x32(const char *name, umode_t mode,
- struct dentry *parent, u32 *value);
-struct dentry *swap_debugfs_create_x64(const char *name, umode_t mode,
- struct dentry *parent, u64 *value);
-struct dentry *swap_debugfs_create_u64(const char *name, umode_t mode,
- struct dentry *parent, u64 *value);
-
-struct dentry *swap_debugfs_getdir(void);
-
-int swap_debugfs_init(void);
-void swap_debugfs_uninit(void);
-
-
-#endif /* _SWAP_DEBUGFS_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <ksyms/ksyms.h>
-#include "swap_deps.h"
-
-
-static struct files_struct *(*__get_files_struct)(struct task_struct *);
-static void (*__put_files_struct)(struct files_struct *fs);
-
-struct files_struct *swap_get_files_struct(struct task_struct *task)
-{
- return __get_files_struct(task);
-}
-EXPORT_SYMBOL_GPL(swap_get_files_struct);
-
-void swap_put_files_struct(struct files_struct *fs)
-{
- __put_files_struct(fs);
-}
-EXPORT_SYMBOL_GPL(swap_put_files_struct);
-
-
-int chef_once(void)
-{
- const char *sym;
- static unsigned once_flag = 0;
-
- if (once_flag)
- return 0;
-
- sym = "get_files_struct";
- __get_files_struct = (void *)swap_ksyms(sym);
- if (__get_files_struct == NULL)
- goto not_found;
-
- sym = "put_files_struct";
- __put_files_struct = (void *)swap_ksyms(sym);
- if (__put_files_struct == NULL)
- goto not_found;
-
- once_flag = 1;
- return 0;
-
-not_found:
- printk("ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_DEPS_H
-#define _SWAP_DEPS_H
-
-
-struct task_struct;
-struct files_struct;
-
-
-struct files_struct *swap_get_files_struct(struct task_struct *task);
-void swap_put_files_struct(struct files_struct *fs);
-
-
-int chef_once(void);
-
-
-#endif /* _SWAP_DEPS_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/types.h>
-#include <linux/spinlock.h>
-#include "swap_initializer.h"
-#include "swap_deps.h"
-
-
-enum init_level {
- IL_CORE,
- IL_FS
-};
-
-static swap_init_t sis_get_fn_init(struct swap_init_struct *init,
- enum init_level level)
-{
- switch (level) {
- case IL_CORE:
- return init->core_init;
- case IL_FS:
- return init->fs_init;
- default:
- return NULL;
- }
-}
-
-static swap_uninit_t sis_get_fn_uninit(struct swap_init_struct *init,
- enum init_level level)
-{
- switch (level) {
- case IL_CORE:
- return init->core_uninit;
- case IL_FS:
- return init->fs_uninit;
- }
-
- return NULL;
-}
-
-static void sis_set_flag(struct swap_init_struct *init,
- enum init_level level, bool val)
-{
- switch (level) {
- case IL_CORE:
- init->core_flag = val;
- break;
- case IL_FS:
- init->fs_flag = val;
- break;
- }
-}
-
-static bool sis_get_flag(struct swap_init_struct *init, enum init_level level)
-{
- switch (level) {
- case IL_CORE:
- return init->core_flag;
- case IL_FS:
- return init->fs_flag;
- }
-
- return false;
-}
-
-static int sis_once(struct swap_init_struct *init)
-{
- swap_init_t once;
-
- once = init->once;
- if (!init->once_flag && once) {
- int ret;
-
- ret = once();
- if (ret)
- return ret;
-
- init->once_flag = true;
- }
-
- return 0;
-}
-
-static int sis_init_level(struct swap_init_struct *init, enum init_level level)
-{
- int ret;
- swap_init_t fn;
-
- if (sis_get_flag(init, level))
- return -EPERM;
-
- fn = sis_get_fn_init(init, level);
- if (fn) {
- ret = fn();
- if (ret)
- return ret;
- }
-
- sis_set_flag(init, level, true);
- return 0;
-}
-
-static void sis_uninit_level(struct swap_init_struct *init,
- enum init_level level)
-{
- if (sis_get_flag(init, level)) {
- swap_uninit_t fn = sis_get_fn_uninit(init, level);
- if (fn)
- fn();
- sis_set_flag(init, level, false);
- }
-}
-
-static int sis_init(struct swap_init_struct *init)
-{
- int ret;
-
- ret = sis_once(init);
- if (ret)
- return ret;
-
- ret = sis_init_level(init, IL_CORE);
- if (ret)
- return ret;
-
- ret = sis_init_level(init, IL_FS);
- if (ret)
- sis_uninit_level(init, IL_CORE);
-
- return ret;
-}
-
-static void sis_uninit(struct swap_init_struct *init)
-{
- sis_uninit_level(init, IL_FS);
- sis_uninit_level(init, IL_CORE);
-}
-
-static LIST_HEAD(init_list);
-static DEFINE_MUTEX(inst_mutex);
-static unsigned init_flag;
-
-static int do_once(void)
-{
- int ret;
- struct swap_init_struct *init;
-
- ret = chef_once();
- if (ret)
- return ret;
-
- list_for_each_entry(init, &init_list, list) {
- ret = sis_once(init);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static void do_uninit_level(enum init_level level)
-{
- struct swap_init_struct *init;
-
- list_for_each_entry_reverse(init, &init_list, list)
- sis_uninit_level(init, level);
-}
-
-static int do_init_level(enum init_level level)
-{
- int ret;
- struct swap_init_struct *init;
-
- list_for_each_entry(init, &init_list, list) {
- ret = sis_init_level(init, level);
- if (ret) {
- do_uninit_level(level);
- return ret;
- }
- }
-
- return 0;
-}
-
-static int do_init(void)
-{
- int ret;
-
- ret = do_once();
- if (ret)
- return ret;
-
- ret = do_init_level(IL_CORE);
- if (ret)
- return ret;
-
- ret = do_init_level(IL_FS);
- if (ret)
- do_uninit_level(IL_CORE);
-
- init_flag = 1;
-
- return 0;
-}
-
-static void do_uninit(void)
-{
- do_uninit_level(IL_FS);
- do_uninit_level(IL_CORE);
-
- init_flag = 0;
-}
-
-
-static atomic_t init_use = ATOMIC_INIT(0);
-
-enum init_stat_t {
- IS_OFF,
- IS_SWITCHING,
- IS_ON,
-};
-
-static enum init_stat_t init_stat;
-static DEFINE_SPINLOCK(init_stat_lock);
-
-
-static bool swap_init_try_get(void)
-{
- spin_lock(&init_stat_lock);
- if (init_stat != IS_ON) {
- spin_unlock(&init_stat_lock);
- return false;
- }
- spin_unlock(&init_stat_lock);
-
- atomic_inc(&init_use);
-
- return true;
-}
-
-static void swap_init_put(void)
-{
- atomic_dec(&init_use);
-}
-
-int swap_init_simple_open(struct inode *inode, struct file *file)
-{
- if (swap_init_try_get() == false)
- return -EBUSY;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(swap_init_simple_open);
-
-int swap_init_simple_release(struct inode *inode, struct file *file)
-{
- swap_init_put();
- return 0;
-}
-EXPORT_SYMBOL_GPL(swap_init_simple_release);
-
-int swap_init_init(void)
-{
- int ret;
-
- spin_lock(&init_stat_lock);
- init_stat = IS_SWITCHING;
- spin_unlock(&init_stat_lock);
-
- ret = do_init();
-
- spin_lock(&init_stat_lock);
- init_stat = ret ? IS_OFF : IS_ON;
- spin_unlock(&init_stat_lock);
-
- return ret;
-}
-
-int swap_init_uninit(void)
-{
- spin_lock(&init_stat_lock);
- init_stat = IS_SWITCHING;
- if (atomic_read(&init_use)) {
- init_stat = IS_ON;
- spin_unlock(&init_stat_lock);
- return -EBUSY;
- }
- spin_unlock(&init_stat_lock);
-
- do_uninit();
-
- spin_lock(&init_stat_lock);
- init_stat = IS_OFF;
- spin_unlock(&init_stat_lock);
-
- return 0;
-}
-
-
-int swap_init_stat_get(void)
-{
- mutex_lock(&inst_mutex);
-
- return init_flag;
-}
-
-void swap_init_stat_put(void)
-{
- mutex_unlock(&inst_mutex);
-}
-
-int swap_init_register(struct swap_init_struct *init)
-{
- int ret = 0;
-
- mutex_lock(&inst_mutex);
- if (init_flag)
- ret = sis_init(init);
-
- if (ret == 0)
- list_add_tail(&init->list, &init_list);
- mutex_unlock(&inst_mutex);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_init_register);
-
-void swap_init_unregister(struct swap_init_struct *init)
-{
- mutex_lock(&inst_mutex);
- list_del(&init->list);
- sis_uninit(init);
- mutex_unlock(&inst_mutex);
-}
-EXPORT_SYMBOL_GPL(swap_init_unregister);
+++ /dev/null
-/**
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * SWAP event notification interface.
- */
-
-#ifndef _SWAP_INITIALIZER_H
-#define _SWAP_INITIALIZER_H
-
-
-#include <linux/list.h>
-#include <linux/types.h>
-#include <linux/module.h>
-
-
-struct file;
-struct inode;
-
-
-typedef int (*swap_init_t)(void);
-typedef void (*swap_uninit_t)(void);
-
-
-struct swap_init_struct {
- swap_init_t once; /* to call only on the first initialization */
-
- swap_init_t core_init;
- swap_uninit_t core_uninit;
-
- swap_init_t fs_init;
- swap_uninit_t fs_uninit;
-
- /* private fields */
- struct list_head list;
- unsigned once_flag:1;
- unsigned core_flag:1;
- unsigned fs_flag:1;
-};
-
-
-int swap_init_simple_open(struct inode *inode, struct file *file);
-int swap_init_simple_release(struct inode *inode, struct file *file);
-
-int swap_init_init(void);
-int swap_init_uninit(void);
-
-int swap_init_stat_get(void);
-void swap_init_stat_put(void);
-
-int swap_init_register(struct swap_init_struct *init);
-void swap_init_unregister(struct swap_init_struct *init);
-
-
-#define SWAP_LIGHT_INIT_MODULE(_once, _init, _uninit, _fs_init, _fs_uninit) \
- static struct swap_init_struct __init_struct = { \
- .once = _once, \
- .core_init = _init, \
- .core_uninit = _uninit, \
- .fs_init = _fs_init, \
- .fs_uninit = _fs_uninit, \
- .list = LIST_HEAD_INIT(__init_struct.list), \
- .once_flag = false, \
- .core_flag = false, \
- .fs_flag = false \
- }; \
- static int __init __init_mod(void) \
- { \
- return swap_init_register(&__init_struct); \
- } \
- static void __exit __exit_mod(void) \
- { \
- swap_init_unregister(&__init_struct); \
- } \
- module_init(__init_mod); \
- module_exit(__exit_mod)
-
-
-#endif /* _SWAP_INITIALIZER_H */
+++ /dev/null
-/*
- * This code copied from linux kernel 3.11+
- * commit cb65537ee1134d3cc55c1fa83952bc8eb1212833
- */
-
-
-#include "wait.h"
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
-
-#include <linux/wait.h>
-#include <linux/module.h>
-
-
-#define WAIT_ATOMIC_T_BIT_NR -1
-#define __WAIT_ATOMIC_T_KEY_INITIALIZER(p) \
- { .flags = p, .bit_nr = WAIT_ATOMIC_T_BIT_NR, }
-
-
-/*
- * Manipulate the atomic_t address to produce a better bit waitqueue table hash
- * index (we're keying off bit -1, but that would produce a horrible hash
- * value).
- */
-static inline wait_queue_head_t *atomic_t_waitqueue(atomic_t *p)
-{
- if (BITS_PER_LONG == 64) {
- unsigned long q = (unsigned long)p;
- return bit_waitqueue((void *)(q & ~1), q & 1);
- }
- return bit_waitqueue(p, 0);
-}
-
-static int wake_atomic_t_function(wait_queue_t *wait, unsigned mode, int sync,
- void *arg)
-{
- struct wait_bit_key *key = arg;
- struct wait_bit_queue *wait_bit
- = container_of(wait, struct wait_bit_queue, wait);
- atomic_t *val = key->flags;
-
- if (wait_bit->key.flags != key->flags ||
- wait_bit->key.bit_nr != key->bit_nr ||
- atomic_read(val) != 0)
- return 0;
- return autoremove_wake_function(wait, mode, sync, key);
-}
-
-/*
- * To allow interruptible waiting and asynchronous (i.e. nonblocking) waiting,
- * the actions of __wait_on_atomic_t() are permitted return codes. Nonzero
- * return codes halt waiting and return.
- */
-static int __wait_on_atomic_t(wait_queue_head_t *wq, struct wait_bit_queue *q,
- int (*action)(atomic_t *), unsigned mode)
-{
- atomic_t *val;
- int ret = 0;
-
- do {
- prepare_to_wait(wq, &q->wait, mode);
- val = q->key.flags;
- if (atomic_read(val) == 0)
- ret = (*action)(val);
- } while (!ret && atomic_read(val) != 0);
- finish_wait(wq, &q->wait);
- return ret;
-}
-
-#define DEFINE_WAIT_ATOMIC_T(name, p) \
- struct wait_bit_queue name = { \
- .key = __WAIT_ATOMIC_T_KEY_INITIALIZER(p), \
- .wait = { \
- .private = current, \
- .func = wake_atomic_t_function, \
- .task_list = \
- LIST_HEAD_INIT((name).wait.task_list), \
- }, \
- }
-
-int out_of_line_wait_on_atomic_t(atomic_t *p, int (*action)(atomic_t *),
- unsigned mode)
-{
- wait_queue_head_t *wq = atomic_t_waitqueue(p);
- DEFINE_WAIT_ATOMIC_T(wait, p);
-
- return __wait_on_atomic_t(wq, &wait, action, mode);
-}
-EXPORT_SYMBOL(out_of_line_wait_on_atomic_t);
-
-/**
- * wake_up_atomic_t - Wake up a waiter on a atomic_t
- * @word: The word being waited on, a kernel virtual address
- * @bit: The bit of the word being waited on
- *
- * Wake up anyone waiting for the atomic_t to go to zero.
- *
- * Abuse the bit-waker function and its waitqueue hash table set (the atomic_t
- * check is done by the waiter's wake function, not the by the waker itself).
- */
-void wake_up_atomic_t(atomic_t *p)
-{
- __wake_up_bit(atomic_t_waitqueue(p), p, WAIT_ATOMIC_T_BIT_NR);
-}
-EXPORT_SYMBOL(wake_up_atomic_t);
-
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) */
+++ /dev/null
-/*
- * This code copied from linux kernel 3.11+
- * commit cb65537ee1134d3cc55c1fa83952bc8eb1212833
- */
-
-
-#ifndef _SWAP_WAIT_H
-#define _SWAP_WAIT_H
-
-
-#include <linux/version.h>
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
-
-
-#include <linux/atomic.h>
-
-
-void wake_up_atomic_t(atomic_t *);
-int out_of_line_wait_on_atomic_t(atomic_t *, int (*)(atomic_t *), unsigned);
-
-static inline
-int wait_on_atomic_t(atomic_t *val, int (*action)(atomic_t *), unsigned mode)
-{
- if (atomic_read(val) == 0)
- return 0;
- return out_of_line_wait_on_atomic_t(val, action, mode);
-}
-
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) */
-
-#include <linux/wait.h>
-
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) */
-
-#endif /* _SWAP_WAIT_H */
--- /dev/null
+# Doxyfile 1.8.7
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single 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.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "SWAP Modules"
+
+# 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) path
+# into which the generated documentation will be written. 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 =
+
+# 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 causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = 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.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES 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.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES 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.
+# The default value is: YES.
+
+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 and 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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES 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
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# 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.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+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 list of 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.
+# The default value is: NO.
+
+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-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+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 Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+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 behavior. 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 behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+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.
+# The default value is: NO.
+
+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.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act 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 =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# 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.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+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 (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. 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 For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# 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);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) 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.
+# The default value is: NO.
+
+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 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.
+# The default value is: YES.
+
+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.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES 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.
+# The default value is: YES.
+
+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).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef 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, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag 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.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_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 respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+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. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+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 only methods in the interface are
+# included.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO 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.
+# The default value is: NO.
+
+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 these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+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 these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+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 these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+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 then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+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.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# 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.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES 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.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+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 constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: 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 group names will
+# appear in their defined order.
+# The default value is: NO.
+
+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 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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+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.
+# The default value is: YES.
+
+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.
+# The default value is: YES.
+
+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.
+# The default value is: YES.
+
+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.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have 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 value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+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.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# 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 value 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 value 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. For an example see the documentation.
+
+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. To 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.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This 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. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+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 to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag 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.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag 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.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This 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 doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+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)
+# The default value is: $file:$line: $text.
+
+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 standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is 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.
+# Note: If this tag is empty the current directory is searched.
+
+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. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+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 patterns (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, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# 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 the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+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
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */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.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be 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.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+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 information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none 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 also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+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 tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# 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 that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES 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.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = 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.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES 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.
+# See also: Section \class.
+# The default value is: YES.
+
+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.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+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 a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+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.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_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.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# 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 (see: http://developer.apple.com/tools/xcode/), 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset 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.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# 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.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_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.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# 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.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# 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.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# 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).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# 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. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# 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.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# 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. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 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.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# 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.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. 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.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# 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
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# 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. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+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.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# 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.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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 too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+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.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+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 some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+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.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+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. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# 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 value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+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.
+# The default value is: NO.
+
+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.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+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.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+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 only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set 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.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+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.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+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 e.g.
+# 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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES 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. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. 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. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: 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. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+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.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML 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.
+# The default value is: YES.
+
+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 =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_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.
+# The default value is: YES.
+
+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 (see:
+# http://www.graphviz.org/), 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 value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 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.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates 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.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is 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 CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH tag is 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: 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).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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.
+# Note: 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag 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.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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 DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_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.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
--- /dev/null
+extra_cflags := -I$(M) -I$(M)/kprobe/arch/$(LINKNAME)/ -I$(M)/uprobe/arch/$(LINKNAME)/
+extra_cflags += $(MCFLAGS)
+EXTRA_CFLAGS := $(extra_cflags)
+export extra_cflags
+
+obj-m := master/ \
+ buffer/ \
+ ksyms/ \
+ driver/ \
+ writer/ \
+ kprobe/ \
+ uprobe/ \
+ us_manager/ \
+ ks_features/ \
+ loader/ \
+ sampler/ \
+ energy/ \
+ parser/ \
+ retprobe/ \
+ preload/ \
+ fbiprobe/ \
+ wsp/ \
+ nsp/ \
+ task_ctx/ \
+ uihv/ \
+ got_patcher/
+
+ifneq ($(CONFIG_SWAP_KERNEL_IMMUTABLE), y)
+obj-m += ks_manager/
+endif # CONFIG_SWAP_KERNEL_IMMUTABLE
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_COMPAT_ARM64_H
+#define _SWAP_ASM_COMPAT_ARM64_H
+
+
+#ifdef CONFIG_ARM64
+
+# define PSR_T_BIT COMPAT_PSR_T_BIT
+
+# define ARM_r0 compat_usr(0)
+# define ARM_r1 compat_usr(1)
+# define ARM_r2 compat_usr(2)
+# define ARM_r3 compat_usr(3)
+# define ARM_r4 compat_usr(4)
+# define ARM_r5 compat_usr(5)
+# define ARM_r6 compat_usr(6)
+# define ARM_r7 compat_usr(7)
+# define ARM_r8 compat_usr(8)
+# define ARM_r9 compat_usr(9)
+# define ARM_r10 compat_usr(10)
+# define ARM_fp compat_fp
+# define ARM_ip compat_usr(12)
+# define ARM_sp compat_sp
+# define ARM_lr compat_lr
+# define ARM_pc pc
+# define ARM_cpsr pstate
+
+# define thumb_mode(regs) compat_thumb_mode(regs)
+
+#endif /* CONFIG_ARM64 */
+
+
+#endif /* _SWAP_ASM_COMPAT_ARM64_H */
+
+
+
+
+
+
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_DECODE_ARM_OLD_H
+#define _SWAP_ASM_DECODE_ARM_OLD_H
+
+
+#define ARM_INSN_MATCH(name, insn) \
+ ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name)
+#define ARM_INSN_REG_RN(insn) ((insn & 0x000F0000) >> 16)
+#define ARM_INSN_REG_SET_RN(insn, nreg) { insn &= ~0x000F0000; insn |= nreg << 16; }
+#define ARM_INSN_REG_RD(insn) ((insn & 0x0000F000) >> 12)
+#define ARM_INSN_REG_SET_RD(insn, nreg) { insn &= ~0x0000F000; insn |= nreg << 12; }
+#define ARM_INSN_REG_RS(insn) ((insn & 0x00000F00) >> 8)
+#define ARM_INSN_REG_SET_RS(insn, nreg) { insn &= ~0x00000F00; insn |= nreg << 8; }
+#define ARM_INSN_REG_RM(insn) (insn & 0x0000000F)
+#define ARM_INSN_REG_SET_RM(insn, nreg) { insn &= ~0x0000000F; insn |= nreg; }
+#define ARM_INSN_REG_MR(insn, nreg) (insn & (1 << nreg))
+#define ARM_INSN_REG_SET_MR(insn, nreg) { insn |= (1 << nreg); }
+#define ARM_INSN_REG_CLEAR_MR(insn, nreg) { insn &= ~(1 << nreg); }
+
+
+/* Undefined */
+#define MASK_ARM_INSN_UNDEF 0x0FF00000
+#define PTRN_ARM_INSN_UNDEF 0x03000000
+
+/* Architecturally undefined */
+#define MASK_ARM_INSN_AUNDEF 0x0FF000F0
+#define PTRN_ARM_INSN_AUNDEF 0x07F000F0
+
+/* Branches */
+#define MASK_ARM_INSN_B 0x0F000000
+#define PTRN_ARM_INSN_B 0x0A000000
+
+#define MASK_ARM_INSN_BL 0x0F000000
+#define PTRN_ARM_INSN_BL 0x0B000000
+
+#define MASK_ARM_INSN_BLX1 0xFE000000
+#define PTRN_ARM_INSN_BLX1 0xFA000000
+
+#define MASK_ARM_INSN_BLX2 0x0FF000F0
+#define PTRN_ARM_INSN_BLX2 0x01200030
+
+#define MASK_ARM_INSN_BX 0x0FF000F0
+#define PTRN_ARM_INSN_BX 0x01200010
+
+#define MASK_ARM_INSN_BXJ 0x0FF000F0
+#define PTRN_ARM_INSN_BXJ 0x01200020
+
+/* Software interrupts */
+#define MASK_ARM_INSN_SWI 0x0F000000
+#define PTRN_ARM_INSN_SWI 0x0F000000
+
+/* Break */
+#define MASK_ARM_INSN_BREAK 0xFFF000F0
+#define PTRN_ARM_INSN_BREAK 0xE1200070
+/* A8-56 ARM DDI 046B if cond != ‘1110’ then UNPREDICTABLE; */
+
+/* CLZ */
+#define MASK_ARM_INSN_CLZ 0x0FFF0FF0
+#define PTRN_ARM_INSN_CLZ 0x016F0F10
+
+/* Data processing immediate shift */
+#define MASK_ARM_INSN_DPIS 0x0E000010
+#define PTRN_ARM_INSN_DPIS 0x00000000
+
+/* Data processing register shift */
+#define MASK_ARM_INSN_DPRS 0x0E000090
+#define PTRN_ARM_INSN_DPRS 0x00000010
+
+/* Data processing immediate */
+#define MASK_ARM_INSN_DPI 0x0E000000
+#define PTRN_ARM_INSN_DPI 0x02000000
+
+/* Load immediate offset */
+#define MASK_ARM_INSN_LIO 0x0E100000
+#define PTRN_ARM_INSN_LIO 0x04100000
+
+/* Store immediate offset */
+#define MASK_ARM_INSN_SIO MASK_ARM_INSN_LIO
+#define PTRN_ARM_INSN_SIO 0x04000000
+
+/* Load register offset */
+#define MASK_ARM_INSN_LRO 0x0E100010
+#define PTRN_ARM_INSN_LRO 0x06100000
+
+/* Store register offset */
+#define MASK_ARM_INSN_SRO MASK_ARM_INSN_LRO
+#define PTRN_ARM_INSN_SRO 0x06000000
+
+/* Load multiple */
+#define MASK_ARM_INSN_LM 0x0E100000
+#define PTRN_ARM_INSN_LM 0x08100000
+
+/* Store multiple */
+#define MASK_ARM_INSN_SM MASK_ARM_INSN_LM
+#define PTRN_ARM_INSN_SM 0x08000000
+
+
+/* Coprocessor load/store and double register transfers */
+#define MASK_ARM_INSN_CLS 0x0E000000
+#define PTRN_ARM_INSN_CLS 0x0C000000
+
+/* Coprocessor register transfers */
+#define MASK_ARM_INSN_CRT 0x0F000010
+#define PTRN_ARM_INSN_CRT 0x0E000010
+
+
+#endif /* _SWAP_ASM_DECODE_ARM_OLD_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include "decode_thumb.h"
+#include "tramps_thumb.h"
+#include "compat_arm64.h"
+
+
+#define GET_BIT(x, n) ((x >> n) & 0x1)
+#define GET_FIELD(x, s, l) ((x >> s) & ((1 << l) - 1))
+#define SET_FIELD(x, s, l, v) ({ \
+ typeof(x) mask = (((1 << l) - 1) << s); \
+ (x & ~mask) | ((v << s) & mask); \
+})
+
+
+typedef union thumb_insn {
+ u32 val;
+ struct {
+ u16 hw1;
+ u16 hw2;
+ } __packed;
+} thumb_insn_t;
+
+typedef int (*decode_handler_t)(thumb_insn_t insn, struct decode_info *info);
+
+
+static void make_def(void *tramp, u32 insn, u32 vaddr, bool t2)
+{
+ u32 ret_addr;
+ u16 *tr = tramp;
+
+ /*
+ * thumb - +2
+ * thumb2 - +4
+ */
+ ret_addr = vaddr + (2 << t2);
+ tr[4] = insn & 0x0000ffff;
+ if (t2)
+ tr[5] = insn >> 16;
+
+ tr[13] = RET_BREAK_THUMB;
+ tr[16] = (ret_addr & 0x0000ffff) | 0x1;
+ tr[17] = ret_addr >> 16;
+}
+
+static void tt_make_common(void *tramp, u32 insn, u32 vaddr, bool t2)
+{
+ memcpy(tramp, gen_insn_execbuf_thumb, sizeof(gen_insn_execbuf_thumb));
+ make_def(tramp, insn, vaddr, t2);
+}
+
+static void tt_make_pc_deps(void *tramp, u32 mod_insn, u32 vaddr, bool t2)
+{
+ u32 pc_val = vaddr + 4;
+ u16 *tr = tramp;
+
+ memcpy(tramp, pc_dep_insn_execbuf_thumb,
+ sizeof(pc_dep_insn_execbuf_thumb));
+ make_def(tramp, mod_insn, vaddr, t2);
+
+ /* save PC value */
+ tr[14] = pc_val & 0x0000ffff;
+ tr[15] = pc_val >> 16;
+}
+
+
+static bool bad_reg(int n)
+{
+ return n == 13 || n == 15;
+}
+
+static int thumb_not_implement(thumb_insn_t insn, struct decode_info *info)
+{
+ return -EFAULT;
+}
+
+static int thumb_unpredictable(thumb_insn_t insn, struct decode_info *info)
+{
+ return -EINVAL;
+}
+
+/* hw1[1110 100x x1xx ????] */
+static int t32_ldrd_strd(thumb_insn_t insn, struct decode_info *info)
+{
+ int w = GET_BIT(insn.hw1, 5);
+ int n = GET_FIELD(insn.hw1, 0, 4);
+ int t = GET_FIELD(insn.hw2, 12, 4);
+ int t2 = GET_FIELD(insn.hw2, 8, 4);
+
+ if (bad_reg(t) || bad_reg(t2))
+ return thumb_unpredictable(insn, info);
+
+ /* check load flag */
+ if (GET_BIT(insn.hw1, 4)) {
+ /* LDRD */
+ if ((w && (n == 15)) || t == t2)
+ return thumb_unpredictable(insn, info);
+
+ if (n == 15) {
+ /* change PC -> SP */
+ insn.hw1 = SET_FIELD(insn.hw1, 0, 4, 13);
+ tt_make_pc_deps(info->tramp, insn.val,
+ info->vaddr, true);
+
+ return 0;
+ }
+ } else {
+ /* STRD */
+ if ((w && t == n) || (w && t2 == n) || (n == 15))
+ return thumb_unpredictable(insn, info);
+ }
+
+ tt_make_common(info->tramp, insn.val, info->vaddr, true);
+
+ return 0;
+}
+
+static int t32_b1110_100x_x1(thumb_insn_t insn, struct decode_info *info)
+{
+ /* check PW bits */
+ if (insn.hw1 & 0x120)
+ return t32_ldrd_strd(insn, info);
+
+ return thumb_not_implement(insn, info);
+}
+
+static int t32_b1110_100(thumb_insn_t insn, struct decode_info *info)
+{
+ if (GET_BIT(insn.hw1, 6))
+ return t32_b1110_100x_x1(insn, info);
+
+ return thumb_not_implement(insn, info);
+}
+
+static void t32_simulate_branch(u32 insn, struct arch_insn_arm *ainsn,
+ struct pt_regs *regs)
+{
+ u32 pc = regs->ARM_pc;
+ thumb_insn_t i = { .val = insn };
+
+ s32 offset = GET_FIELD(i.hw2, 0, 11); /* imm11 */
+ offset += GET_FIELD(i.hw1, 0, 10) << 11; /* imm10 */
+ offset += GET_BIT(i.hw2, 13) << 21; /* J1 */
+ offset += GET_BIT(i.hw2, 11) << 22; /* J2 */
+
+ /* check S bit */
+ if (GET_BIT(i.hw1, 10))
+ offset -= 0x00800000; /* Apply sign bit */
+ else
+ offset ^= 0x00600000; /* Invert J1 and J2 */
+
+ /* check link */
+ if (GET_BIT(i.hw2, 14)) {
+ /* BL or BLX */
+ regs->ARM_lr = regs->ARM_pc | 1;
+ if (!GET_BIT(i.hw2, 12)) {
+ /* BLX so switch to ARM mode */
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+ pc &= ~3;
+ }
+ }
+
+ regs->ARM_pc = pc + (offset * 2);
+}
+
+static int t32_branch(thumb_insn_t insn, struct decode_info *info)
+{
+ info->handeler = t32_simulate_branch;
+
+ return 0;
+}
+
+static decode_handler_t table_branches[8] = {
+ /* hw2[14 12 0] */
+ /* Bc 0 0 0 */ thumb_not_implement,
+ /* Bc 0 0 1 */ thumb_not_implement,
+ /* B 0 1 0 */ t32_branch,
+ /* B 0 1 1 */ t32_branch,
+ /* BLX 1 0 0 */ t32_branch,
+ /* res 1 0 1 */ thumb_unpredictable,
+ /* BL 1 1 0 */ t32_branch,
+ /* BL 1 1 1 */ t32_branch,
+};
+
+
+
+static int t32_b1111_0xxx_xxxx_xxxx_1(thumb_insn_t insn,
+ struct decode_info *info)
+{
+ u32 s = GET_BIT(insn.hw2, 14) << 2 |
+ GET_BIT(insn.hw2, 12) << 1 |
+ GET_BIT(insn.hw2, 0);
+
+ return table_branches[s](insn, info);
+}
+
+static int b111(thumb_insn_t insn, struct decode_info *info)
+{
+ /* hw1[111x xxx? ???? ????] */
+ switch (GET_FIELD(insn.hw1, 9, 4)) {
+ case 0b0100:
+ return t32_b1110_100(insn, info);
+ }
+
+ /* [1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx] */
+ if (GET_FIELD(insn.hw1, 11, 2) == 0b10 && GET_BIT(insn.hw2, 15) == 1)
+ return t32_b1111_0xxx_xxxx_xxxx_1(insn, info);
+
+ return thumb_not_implement(insn, info);
+}
+
+
+decode_handler_t table_xxx[8] = {
+ /* 000 */ thumb_not_implement,
+ /* 001 */ thumb_not_implement,
+ /* 010 */ thumb_not_implement,
+ /* 011 */ thumb_not_implement,
+ /* 100 */ thumb_not_implement,
+ /* 101 */ thumb_not_implement,
+ /* 110 */ thumb_not_implement,
+ /* 111 */ b111,
+};
+
+
+int decode_thumb(u32 insn, struct decode_info *info)
+{
+ thumb_insn_t tinsn = { .val = insn };
+
+ /* check first 3 bits hw1[xxx? ???? ???? ????] */
+ return table_xxx[GET_FIELD(tinsn.hw1, 13, 3)](tinsn, info);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _ARM_DECODE_THUMB_H
+#define _ARM_DECODE_THUMB_H
+
+
+#include "probes.h"
+
+
+struct decode_info {
+ u32 vaddr;
+ void *tramp;
+ probe_handler_arm_t handeler;
+};
+
+
+int decode_thumb(u32 insn, struct decode_info *info);
+
+
+#endif /* _ARM_DECODE_THUMB_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_DECODE_THUMB_OLD_H
+#define _SWAP_ASM_DECODE_THUMB_OLD_H
+
+
+/* == THUMB == */
+#define THUMB_INSN_MATCH(name, insn) \
+ (((insn & 0x0000FFFF) & MASK_THUMB_INSN_##name) == \
+ PTRN_THUMB_INSN_##name)
+
+
+/* Undefined */
+#define MASK_THUMB_INSN_UNDEF 0xFE00
+#define PTRN_THUMB_INSN_UNDEF 0xDE00
+
+/* Branches */
+#define MASK_THUMB_INSN_B1 0xF000
+#define PTRN_THUMB_INSN_B1 0xD000 /* b<cond> label */
+
+#define MASK_THUMB_INSN_B2 0xF800
+#define PTRN_THUMB_INSN_B2 0xE000 /* b label */
+
+#define MASK_THUMB_INSN_CBZ 0xF500
+#define PTRN_THUMB_INSN_CBZ 0xB100 /* CBZ/CBNZ */
+
+#define MASK_THUMB_INSN_BLX2 0xFF80 /* blx reg */
+#define PTRN_THUMB_INSN_BLX2 0x4780
+
+#define MASK_THUMB_INSN_BX 0xFF80
+#define PTRN_THUMB_INSN_BX 0x4700
+
+/* Software interrupts */
+#define MASK_THUMB_INSN_SWI 0xFF00
+#define PTRN_THUMB_INSN_SWI 0xDF00
+
+/* Break */
+#define MASK_THUMB_INSN_BREAK 0xFF00
+#define PTRN_THUMB_INSN_BREAK 0xBE00
+
+/* Data processing immediate */
+#define MASK_THUMB_INSN_DP 0xFC00
+#define PTRN_THUMB_INSN_DP 0x4000
+
+#define MASK_THUMB_INSN_APC 0xF800
+#define PTRN_THUMB_INSN_APC 0xA000 /* ADD Rd, [PC, #<imm8> * 4] */
+
+#define MASK_THUMB_INSN_MOV3 0xFF00
+#define PTRN_THUMB_INSN_MOV3 0x4600 /* MOV Rd, PC */
+
+/* Load immediate offset */
+#define MASK_THUMB_INSN_LIO1 0xF800
+#define PTRN_THUMB_INSN_LIO1 0x6800 /* LDR */
+
+#define MASK_THUMB_INSN_LIO2 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_LIO2 0x7800 /* LDRB */
+
+#define MASK_THUMB_INSN_LIO3 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_LIO3 0x8800 /* LDRH */
+
+#define MASK_THUMB_INSN_LIO4 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_LIO4 0x9800 /* LDR SP relative */
+
+/* Store immediate offset */
+#define MASK_THUMB_INSN_SIO1 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_SIO1 0x6000 /* STR */
+
+#define MASK_THUMB_INSN_SIO2 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_SIO2 0x7000 /* STRB */
+
+#define MASK_THUMB_INSN_SIO3 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_SIO3 0x8000 /* STRH */
+
+#define MASK_THUMB_INSN_SIO4 MASK_THUMB_INSN_LIO1
+#define PTRN_THUMB_INSN_SIO4 0x9000 /* STR SP relative */
+
+/* Load register offset */
+#define MASK_THUMB_INSN_LRO1 0xFE00
+#define PTRN_THUMB_INSN_LRO1 0x5600 /* LDRSB */
+
+#define MASK_THUMB_INSN_LRO2 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_LRO2 0x5800 /* LDR */
+
+#define MASK_THUMB_INSN_LRO3 0xf800
+#define PTRN_THUMB_INSN_LRO3 0x4800 /* LDR Rd, [PC, #<imm8> * 4] */
+
+#define MASK_THUMB_INSN_LRO4 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_LRO4 0x5A00 /* LDRH */
+
+#define MASK_THUMB_INSN_LRO5 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_LRO5 0x5C00 /* LDRB */
+
+#define MASK_THUMB_INSN_LRO6 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_LRO6 0x5E00 /* LDRSH */
+
+/* Store register offset */
+#define MASK_THUMB_INSN_SRO1 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_SRO1 0x5000 /* STR */
+
+#define MASK_THUMB_INSN_SRO2 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_SRO2 0x5200 /* STRH */
+
+#define MASK_THUMB_INSN_SRO3 MASK_THUMB_INSN_LRO1
+#define PTRN_THUMB_INSN_SRO3 0x5400 /* STRB */
+
+
+/* == THUMB2 == */
+#define THUMB2_INSN_MATCH(name, insn) \
+ ((insn & MASK_THUMB2_INSN_##name) == PTRN_THUMB2_INSN_##name)
+
+#define THUMB2_INSN_REG_RT(insn) ((insn & 0xf0000000) >> 28)
+#define THUMB2_INSN_REG_RT2(insn) ((insn & 0x0f000000) >> 24)
+#define THUMB2_INSN_REG_RN(insn) (insn & 0x0000000f)
+#define THUMB2_INSN_REG_RD(insn) ((insn & 0x0f000000) >> 24)
+#define THUMB2_INSN_REG_RM(insn) ((insn & 0x000f0000) >> 16)
+
+
+/* Branches */
+#define MASK_THUMB2_INSN_B1 0xD000F800
+#define PTRN_THUMB2_INSN_B1 0x8000F000
+
+#define MASK_THUMB2_INSN_B2 0xD000F800
+#define PTRN_THUMB2_INSN_B2 0x9000F000
+
+#define MASK_THUMB2_INSN_BL 0xD000F800
+#define PTRN_THUMB2_INSN_BL 0xD000F000 /* bl imm swapped */
+
+#define MASK_THUMB2_INSN_BLX1 0xD001F800
+#define PTRN_THUMB2_INSN_BLX1 0xC000F000
+
+#define MASK_THUMB2_INSN_BXJ 0xD000FFF0
+#define PTRN_THUMB2_INSN_BXJ 0x8000F3C0
+
+/* Data processing register shift */
+#define MASK_THUMB2_INSN_DPRS 0xFFE00000
+#define PTRN_THUMB2_INSN_DPRS 0xEA000000
+
+/* Data processing immediate */
+#define MASK_THUMB2_INSN_DPI 0xFBE08000
+#define PTRN_THUMB2_INSN_DPI 0xF2000000
+
+#define MASK_THUMB2_INSN_RSBW 0x8000fbe0
+#define PTRN_THUMB2_INSN_RSBW 0x0000f1c0 /* RSB{S}.W Rd,Rn,#<const> */
+
+#define MASK_THUMB2_INSN_RORW 0xf0f0ffe0
+#define PTRN_THUMB2_INSN_RORW 0xf000fa60 /* ROR{S}.W Rd, Rn, Rm */
+
+#define MASK_THUMB2_INSN_ROR 0x0030ffef
+#define PTRN_THUMB2_INSN_ROR 0x0030ea4f /* ROR{S} Rd, Rm, #<imm> */
+
+#define MASK_THUMB2_INSN_LSLW1 0xf0f0ffe0
+#define PTRN_THUMB2_INSN_LSLW1 0xf000fa00 /* LSL{S}.W Rd, Rn, Rm */
+
+#define MASK_THUMB2_INSN_LSLW2 0x0030ffef
+#define PTRN_THUMB2_INSN_LSLW2 0x0000ea4f /* LSL{S}.W Rd, Rm, #<imm5>*/
+
+#define MASK_THUMB2_INSN_LSRW1 0xf0f0ffe0
+#define PTRN_THUMB2_INSN_LSRW1 0xf000fa20 /* LSR{S}.W Rd, Rn, Rm */
+
+#define MASK_THUMB2_INSN_LSRW2 0x0030ffef
+#define PTRN_THUMB2_INSN_LSRW2 0x0010ea4f /* LSR{S}.W Rd, Rm, #<imm5> */
+
+#define MASK_THUMB2_INSN_TEQ1 0x8f00fbf0
+#define PTRN_THUMB2_INSN_TEQ1 0x0f00f090 /* TEQ Rn, #<const> */
+
+#define MASK_THUMB2_INSN_TEQ2 0x0f00fff0
+#define PTRN_THUMB2_INSN_TEQ2 0x0f00ea90 /* TEQ Rn, Rm{,<shift>} */
+
+#define MASK_THUMB2_INSN_TST1 0x8f00fbf0
+#define PTRN_THUMB2_INSN_TST1 0x0f00f010 /* TST Rn, #<const> */
+
+#define MASK_THUMB2_INSN_TST2 0x0f00fff0
+#define PTRN_THUMB2_INSN_TST2 0x0f00ea10 /* TST Rn, Rm{,<shift>} */
+
+/* Load immediate offset */
+#define MASK_THUMB2_INSN_LDRW 0x0000fff0
+#define PTRN_THUMB2_INSN_LDRW 0x0000f850 /* LDR.W Rt, [Rn, #-<imm12>] */
+
+#define MASK_THUMB2_INSN_LDRW1 MASK_THUMB2_INSN_LDRW
+#define PTRN_THUMB2_INSN_LDRW1 0x0000f8d0 /* LDR.W Rt, [Rn, #<imm12>] */
+
+#define MASK_THUMB2_INSN_LDRBW MASK_THUMB2_INSN_LDRW
+#define PTRN_THUMB2_INSN_LDRBW 0x0000f810 /* LDRB.W Rt, [Rn, #-<imm8>] */
+
+#define MASK_THUMB2_INSN_LDRBW1 MASK_THUMB2_INSN_LDRW
+#define PTRN_THUMB2_INSN_LDRBW1 0x0000f890 /* LDRB.W Rt, [Rn, #<imm12>] */
+
+#define MASK_THUMB2_INSN_LDRHW MASK_THUMB2_INSN_LDRW
+#define PTRN_THUMB2_INSN_LDRHW 0x0000f830 /* LDRH.W Rt, [Rn, #-<imm8>] */
+
+#define MASK_THUMB2_INSN_LDRHW1 MASK_THUMB2_INSN_LDRW
+#define PTRN_THUMB2_INSN_LDRHW1 0x0000f8b0 /* LDRH.W Rt, [Rn, #<imm12>] */
+
+#define MASK_THUMB2_INSN_LDRD 0x0000fed0
+#define PTRN_THUMB2_INSN_LDRD 0x0000e850 /* LDRD Rt, Rt2, [Rn, #-<imm8>] */
+
+#define MASK_THUMB2_INSN_LDRD1 MASK_THUMB2_INSN_LDRD
+#define PTRN_THUMB2_INSN_LDRD1 0x0000e8d0 /* LDRD Rt, Rt2, [Rn, #<imm8>] */
+
+#define MASK_THUMB2_INSN_LDRWL 0x0fc0fff0
+#define PTRN_THUMB2_INSN_LDRWL 0x0000f850 /* LDR.W Rt, [Rn,Rm,LSL #<imm2>] */
+
+#define MASK_THUMB2_INSN_LDREX 0x0f00ffff
+#define PTRN_THUMB2_INSN_LDREX 0x0f00e85f /* LDREX Rt, [PC, #<imm8>] */
+
+#define MASK_THUMB2_INSN_MUL 0xf0f0fff0
+#define PTRN_THUMB2_INSN_MUL 0xf000fb00 /* MUL Rd, Rn, Rm */
+
+#define MASK_THUMB2_INSN_DP 0x0000ff00
+#define PTRN_THUMB2_INSN_DP 0x0000eb00 /* ADD/SUB/SBC/...Rd,Rn,Rm{,<shift>} */
+
+/* Store immediate offset */
+#define MASK_THUMB2_INSN_STRW 0x0fc0fff0
+#define PTRN_THUMB2_INSN_STRW 0x0000f840 /* STR.W Rt,[Rn,Rm,{LSL #<imm2>}] */
+
+#define MASK_THUMB2_INSN_STRW1 0x0000fff0
+#define PTRN_THUMB2_INSN_STRW1 0x0000f8c0 /* STR.W Rt, [Rn, #imm12]
+ * STR.W Rt, [PC, #imm12] shall be
+ * skipped, because it hangs
+ * on Tegra. WTF */
+
+#define MASK_THUMB2_INSN_STRHW MASK_THUMB2_INSN_STRW
+#define PTRN_THUMB2_INSN_STRHW 0x0000f820 /* STRH.W Rt,[Rn,Rm,{LSL #<imm2>}] */
+
+#define MASK_THUMB2_INSN_STRHW1 0x0000fff0
+#define PTRN_THUMB2_INSN_STRHW1 0x0000f8a0 /* STRH.W Rt, [Rn, #<imm12>] */
+
+#define MASK_THUMB2_INSN_STRHT 0x0f00fff0 /* strht r1, [pc, #imm] illegal
+ * instruction on Tegra. WTF */
+#define PTRN_THUMB2_INSN_STRHT 0x0e00f820 /* STRHT Rt, [Rn, #<imm8>] */
+
+#define MASK_THUMB2_INSN_STRT 0x0f00fff0
+#define PTRN_THUMB2_INSN_STRT 0x0e00f840 /* STRT Rt, [Rn, #<imm8>] */
+
+#define MASK_THUMB2_INSN_STRBW MASK_THUMB2_INSN_STRW
+#define PTRN_THUMB2_INSN_STRBW 0x0000f800 /* STRB.W Rt,[Rn,Rm,{LSL #<imm2>}] */
+
+#define MASK_THUMB2_INSN_STRBW1 0x0000fff0
+#define PTRN_THUMB2_INSN_STRBW1 0x0000f880 /* STRB.W Rt, [Rn, #<imm12>]
+ * STRB.W Rt, [PC, #imm12] shall be
+ * skipped, because it hangs
+ * on Tegra. WTF */
+
+#define MASK_THUMB2_INSN_STRBT 0x0f00fff0
+#define PTRN_THUMB2_INSN_STRBT 0x0e00f800 /* STRBT Rt, [Rn, #<imm8>}] */
+
+#define MASK_THUMB2_INSN_STRD 0x0000fe50
+#define PTRN_THUMB2_INSN_STRD 0x0000e840 /* STR{D,EX,EXB,EXH,EXD} Rt, Rt2, [Rn, #<imm8>] */
+
+/* Load register offset */
+#define MASK_THUMB2_INSN_ADR 0x8000fa1f
+#define PTRN_THUMB2_INSN_ADR 0x0000f20f
+
+/* Load multiple */
+#define MASK_THUMB2_INSN_LDMIA 0x8000ffd0
+#define PTRN_THUMB2_INSN_LDMIA 0x8000e890 /* LDMIA(.W) Rn(!),{Rx-PC} */
+
+#define MASK_THUMB2_INSN_LDMDB 0x8000ffd0
+#define PTRN_THUMB2_INSN_LDMDB 0x8000e910 /* LDMDB(.W) Rn(!), {Rx-PC} */
+
+
+#endif /* _SWAP_ASM_DECODE_THUMB_OLD_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include "probes.h"
+#include "probes_arm.h"
+#include "probes_thumb.h"
+
+
+int make_tramp(struct arch_insn_arm *ainsn, u32 vaddr, u32 insn,
+ u32 *tramp, u32 tramp_len)
+{
+ int ret;
+ int thumb_mode = vaddr & 1;
+
+ vaddr &= ~1;
+ if (thumb_mode)
+ ret = make_trampoline_thumb(ainsn, vaddr, insn,
+ tramp, tramp_len);
+ else
+ ret = make_trampoline_arm(vaddr, insn, tramp);
+
+ return ret;
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_PROBES_H
+#define _SWAP_ASM_PROBES_H
+
+
+#include <linux/types.h>
+#include <swap-asm/swap_probes.h>
+
+
+struct pt_regs;
+struct arch_insn_arm;
+
+
+typedef void (*probe_handler_arm_t)(u32 insn, struct arch_insn_arm *ainsn,
+ struct pt_regs *regs);
+
+
+struct arch_insn_arm {
+ probe_handler_arm_t handler;
+};
+
+
+int make_tramp(struct arch_insn_arm *ainsn, u32 vaddr, u32 insn,
+ u32 *tramp, u32 tramp_len);
+
+
+#endif /* _SWAP_ASM_PROBES_H */
+
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ptrace.h> /* nedded for printk.h */
+#include <linux/linkage.h> /* needed for printk.h */
+#include <linux/printk.h>
+#include "probes.h"
+#include "tramps_arm.h"
+#include "decode_arm_old.h"
+
+
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+
+static u32 get_addr_b(u32 insn, u32 addr)
+{
+ /* real position less then PC by 8 */
+ return ((s32)addr + 8 + branch_displacement(insn));
+}
+
+static int prep_pc_dep_insn_execbuf(u32 *insns, u32 insn, int uregs)
+{
+ int i;
+
+ if (uregs & 0x10) {
+ int reg_mask = 0x1;
+ /* search in reg list */
+ for (i = 0; i < 13; i++, reg_mask <<= 1) {
+ if (!(insn & reg_mask))
+ break;
+ }
+ } else {
+ for (i = 0; i < 13; i++) {
+ if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i))
+ continue;
+ if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i))
+ continue;
+ if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i))
+ continue;
+ if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i))
+ continue;
+ break;
+ }
+ }
+
+ if (i == 13) {
+ pr_err("there are no free register %x in insn %x!",
+ uregs, insn);
+ return -EINVAL;
+ }
+
+ /* set register to save */
+ ARM_INSN_REG_SET_RD(insns[0], i);
+ /* set register to load address to */
+ ARM_INSN_REG_SET_RD(insns[1], i);
+ /* set instruction to execute and patch it */
+ if (uregs & 0x10) {
+ ARM_INSN_REG_CLEAR_MR(insn, 15);
+ ARM_INSN_REG_SET_MR(insn, i);
+ } else {
+ if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15))
+ ARM_INSN_REG_SET_RN(insn, i);
+ if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15))
+ ARM_INSN_REG_SET_RD(insn, i);
+ if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15))
+ ARM_INSN_REG_SET_RS(insn, i);
+ if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15))
+ ARM_INSN_REG_SET_RM(insn, i);
+ }
+
+ insns[PROBES_TRAMP_INSN_IDX] = insn;
+ /* set register to restore */
+ ARM_INSN_REG_SET_RD(insns[3], i);
+
+ return 0;
+}
+
+static int arch_check_insn_arm(u32 insn)
+{
+ /* check instructions that can change PC by nature */
+ if (
+ /* ARM_INSN_MATCH(UNDEF, insn) || */
+ ARM_INSN_MATCH(AUNDEF, insn) ||
+ ARM_INSN_MATCH(SWI, insn) ||
+ ARM_INSN_MATCH(BREAK, insn) ||
+ ARM_INSN_MATCH(BXJ, insn)) {
+ goto bad_insn;
+#ifndef CONFIG_CPU_V7
+ /* check instructions that can write result to PC */
+ } else if ((ARM_INSN_MATCH(DPIS, insn) ||
+ ARM_INSN_MATCH(DPRS, insn) ||
+ ARM_INSN_MATCH(DPI, insn) ||
+ ARM_INSN_MATCH(LIO, insn) ||
+ ARM_INSN_MATCH(LRO, insn)) &&
+ (ARM_INSN_REG_RD(insn) == 15)) {
+ goto bad_insn;
+#endif /* CONFIG_CPU_V7 */
+ /* check special instruction loads store multiple registers */
+ } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) &&
+ /* store PC or load to PC */
+ (ARM_INSN_REG_MR(insn, 15) ||
+ /* store/load with PC update */
+ ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) {
+ goto bad_insn;
+ }
+
+ return 0;
+
+bad_insn:
+ return -EFAULT;
+}
+
+static int make_branch_tarmpoline(u32 addr, u32 insn, u32 *tramp)
+{
+ int ok = 0;
+
+ /* B */
+ if (ARM_INSN_MATCH(B, insn) &&
+ !ARM_INSN_MATCH(BLX1, insn)) {
+ /* B check can be false positive on BLX1 instruction */
+ memcpy(tramp, b_cond_insn_execbuf, sizeof(b_cond_insn_execbuf));
+ tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
+ tramp[0] |= insn & 0xf0000000;
+ tramp[6] = get_addr_b(insn, addr);
+ tramp[7] = addr + 4;
+ ok = 1;
+ /* BX, BLX (Rm) */
+ } else if (ARM_INSN_MATCH(BX, insn) ||
+ ARM_INSN_MATCH(BLX2, insn)) {
+ memcpy(tramp, b_r_insn_execbuf, sizeof(b_r_insn_execbuf));
+ tramp[0] = insn;
+ tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
+ tramp[7] = addr + 4;
+ ok = 1;
+ /* BL, BLX (Off) */
+ } else if (ARM_INSN_MATCH(BLX1, insn)) {
+ memcpy(tramp, blx_off_insn_execbuf, sizeof(blx_off_insn_execbuf));
+ tramp[0] |= 0xe0000000;
+ tramp[1] |= 0xe0000000;
+ tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
+ tramp[6] = get_addr_b(insn, addr) +
+ 2 * (insn & 01000000) + 1; /* jump to thumb */
+ tramp[7] = addr + 4;
+ ok = 1;
+ /* BL */
+ } else if (ARM_INSN_MATCH(BL, insn)) {
+ memcpy(tramp, blx_off_insn_execbuf, sizeof(blx_off_insn_execbuf));
+ tramp[0] |= insn & 0xf0000000;
+ tramp[1] |= insn & 0xf0000000;
+ tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
+ tramp[6] = get_addr_b(insn, addr);
+ tramp[7] = addr + 4;
+ ok = 1;
+ }
+
+ return ok;
+}
+
+/**
+ * @brief Creates ARM trampoline.
+ *
+ * @param addr Probe address.
+ * @param insn Instuction at this address.
+ * @param tramp Pointer to memory for trampoline.
+ * @return 0 on success, error code on error.
+ */
+int make_trampoline_arm(u32 addr, u32 insn, u32 *tramp)
+{
+ int ret, uregs, pc_dep;
+
+ if (addr & 0x03) {
+ pr_err("Error in %s at %d: attempt to register probe "
+ "at an unaligned address\n", __FILE__, __LINE__);
+ return -EINVAL;
+ }
+
+ ret = arch_check_insn_arm(insn);
+ if (ret)
+ return ret;
+
+ if (make_branch_tarmpoline(addr, insn, tramp))
+ return 0;
+
+ uregs = pc_dep = 0;
+ /* Rm */
+ if (ARM_INSN_MATCH(CLZ, insn)) {
+ uregs = 0xa;
+ if (ARM_INSN_REG_RM(insn) == 15)
+ pc_dep = 1;
+ /* Rn, Rm ,Rd */
+ } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) ||
+ ARM_INSN_MATCH(SRO, insn)) {
+ uregs = 0xb;
+ if ((ARM_INSN_REG_RN(insn) == 15) ||
+ (ARM_INSN_REG_RM(insn) == 15) ||
+ (ARM_INSN_MATCH(SRO, insn) &&
+ (ARM_INSN_REG_RD(insn) == 15))) {
+ pc_dep = 1;
+ }
+ /* Rn ,Rd */
+ } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) ||
+ ARM_INSN_MATCH(SIO, insn)) {
+ uregs = 0x3;
+ if ((ARM_INSN_REG_RN(insn) == 15) ||
+ (ARM_INSN_MATCH(SIO, insn) &&
+ (ARM_INSN_REG_RD(insn) == 15))) {
+ pc_dep = 1;
+ }
+ /* Rn, Rm, Rs */
+ } else if (ARM_INSN_MATCH(DPRS, insn)) {
+ uregs = 0xd;
+ if ((ARM_INSN_REG_RN(insn) == 15) ||
+ (ARM_INSN_REG_RM(insn) == 15) ||
+ (ARM_INSN_REG_RS(insn) == 15)) {
+ pc_dep = 1;
+ }
+ /* register list */
+ } else if (ARM_INSN_MATCH(SM, insn)) {
+ uregs = 0x10;
+ if (ARM_INSN_REG_MR(insn, 15))
+ pc_dep = 1;
+ }
+
+ /* check instructions that can write result to SP and uses PC */
+ if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) {
+ pr_err("Error in %s at %d: instruction check failed (arm)\n",
+ __FILE__, __LINE__);
+ return -EFAULT;
+ }
+
+ if (unlikely(uregs && pc_dep)) {
+ memcpy(tramp, pc_dep_insn_execbuf, sizeof(pc_dep_insn_execbuf));
+ if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) {
+ pr_err("Error in %s at %d: failed "
+ "to prepare exec buffer for insn %x!",
+ __FILE__, __LINE__, insn);
+ return -EINVAL;
+ }
+
+ tramp[6] = addr + 8;
+ } else {
+ memcpy(tramp, gen_insn_execbuf, sizeof(gen_insn_execbuf));
+ tramp[PROBES_TRAMP_INSN_IDX] = insn;
+ }
+
+ /* TODO: remove for probe */
+ tramp[PROBES_TRAMP_RET_BREAK_IDX] = RET_BREAK_ARM;
+ tramp[7] = addr + 4;
+
+ return 0;
+}
+
+int noret_arm(u32 opcode)
+{
+ return !!(ARM_INSN_MATCH(BL, opcode) ||
+ ARM_INSN_MATCH(BLX1, opcode) ||
+ ARM_INSN_MATCH(BLX2, opcode));
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_PROBES_ARM_H
+#define _SWAP_ASM_PROBES_ARM_H
+
+
+int make_trampoline_arm(u32 addr, u32 insn, u32 *tramp);
+int noret_arm(u32 opcode);
+
+
+#endif /* _SWAP_ASM_PROBES_ARM_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "tramps_thumb.h"
+#include "decode_thumb.h"
+#include "decode_thumb_old.h"
+
+
+static inline s32 branch_t16_dest(u32 insn, unsigned int insn_addr)
+{
+ s32 offset = insn & 0x3ff;
+ offset -= insn & 0x400;
+ return insn_addr + 4 + offset * 2;
+}
+
+static inline s32 branch_cond_t16_dest(u32 insn, unsigned int insn_addr)
+{
+ s32 offset = insn & 0x7f;
+ offset -= insn & 0x80;
+ return insn_addr + 4 + offset * 2;
+}
+
+static inline s32 branch_t32_dest(u32 insn, unsigned int insn_addr)
+{
+ unsigned int poff = insn & 0x3ff;
+ unsigned int offset = (insn & 0x07fe0000) >> 17;
+
+ poff -= (insn & 0x400);
+
+ if (insn & (1 << 12))
+ return insn_addr + 4 + (poff << 12) + offset * 4;
+
+ return (insn_addr + 4 + (poff << 12) + offset * 4) & ~3;
+}
+
+static inline s32 cbz_t16_dest(u32 insn, unsigned int insn_addr)
+{
+ unsigned int i = (insn & 0x200) >> 3;
+ unsigned int offset = (insn & 0xf8) >> 2;
+ return insn_addr + 4 + i + offset;
+}
+
+/* is instruction Thumb2 and NOT a branch, etc... */
+static int is_thumb2(u32 insn)
+{
+ return ((insn & 0xf800) == 0xe800 ||
+ (insn & 0xf800) == 0xf000 ||
+ (insn & 0xf800) == 0xf800);
+}
+
+static int prep_pc_dep_insn_execbuf_thumb(u32 *insns, u32 insn, int uregs)
+{
+ unsigned char mreg = 0;
+ unsigned char reg = 0;
+
+ if (THUMB_INSN_MATCH(APC, insn) ||
+ THUMB_INSN_MATCH(LRO3, insn)) {
+ reg = ((insn & 0xffff) & uregs) >> 8;
+ } else if (THUMB_INSN_MATCH(MOV3, insn)) {
+ if (((((unsigned char)insn) & 0xff) >> 3) == 15)
+ reg = (insn & 0xffff) & uregs;
+ else
+ return 0;
+ } else if (THUMB2_INSN_MATCH(ADR, insn)) {
+ reg = ((insn >> 16) & uregs) >> 8;
+ if (reg == 15)
+ return 0;
+ } else if (THUMB2_INSN_MATCH(LDRW, insn) ||
+ THUMB2_INSN_MATCH(LDRW1, insn) ||
+ THUMB2_INSN_MATCH(LDRHW, insn) ||
+ THUMB2_INSN_MATCH(LDRHW1, insn) ||
+ THUMB2_INSN_MATCH(LDRWL, insn)) {
+ reg = ((insn >> 16) & uregs) >> 12;
+ if (reg == 15)
+ return 0;
+ /*
+ * LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped
+ */
+ } else if (THUMB2_INSN_MATCH(LDRBW, insn) ||
+ THUMB2_INSN_MATCH(LDRBW1, insn) ||
+ THUMB2_INSN_MATCH(LDREX, insn)) {
+ reg = ((insn >> 16) & uregs) >> 12;
+ } else if (THUMB2_INSN_MATCH(DP, insn)) {
+ reg = ((insn >> 16) & uregs) >> 12;
+ if (reg == 15)
+ return 0;
+ } else if (THUMB2_INSN_MATCH(RSBW, insn)) {
+ reg = ((insn >> 12) & uregs) >> 8;
+ if (reg == 15)
+ return 0;
+ } else if (THUMB2_INSN_MATCH(RORW, insn)) {
+ reg = ((insn >> 12) & uregs) >> 8;
+ if (reg == 15)
+ return 0;
+ } else if (THUMB2_INSN_MATCH(ROR, insn) ||
+ THUMB2_INSN_MATCH(LSLW1, insn) ||
+ THUMB2_INSN_MATCH(LSLW2, insn) ||
+ THUMB2_INSN_MATCH(LSRW1, insn) ||
+ THUMB2_INSN_MATCH(LSRW2, insn)) {
+ reg = ((insn >> 12) & uregs) >> 8;
+ if (reg == 15)
+ return 0;
+ } else if (THUMB2_INSN_MATCH(TEQ1, insn) ||
+ THUMB2_INSN_MATCH(TST1, insn)) {
+ reg = 15;
+ } else if (THUMB2_INSN_MATCH(TEQ2, insn) ||
+ THUMB2_INSN_MATCH(TST2, insn)) {
+ reg = THUMB2_INSN_REG_RM(insn);
+ }
+
+ if ((THUMB2_INSN_MATCH(STRW, insn) ||
+ THUMB2_INSN_MATCH(STRBW, insn) ||
+ THUMB2_INSN_MATCH(STRD, insn) ||
+ THUMB2_INSN_MATCH(STRHT, insn) ||
+ THUMB2_INSN_MATCH(STRT, insn) ||
+ THUMB2_INSN_MATCH(STRHW1, insn) ||
+ THUMB2_INSN_MATCH(STRHW, insn)) &&
+ THUMB2_INSN_REG_RT(insn) == 15) {
+ reg = THUMB2_INSN_REG_RT(insn);
+ }
+
+ if (reg == 6 || reg == 7) {
+ *((u16 *)insns + 0) =
+ (*((u16 *)insns + 0) & 0x00ff) |
+ ((1 << mreg) | (1 << (mreg + 1)));
+ *((u16 *)insns + 1) =
+ (*((u16 *)insns + 1) & 0xf8ff) | (mreg << 8);
+ *((u16 *)insns + 2) =
+ (*((u16 *)insns + 2) & 0xfff8) | (mreg + 1);
+ *((u16 *)insns + 3) =
+ (*((u16 *)insns + 3) & 0xffc7) | (mreg << 3);
+ *((u16 *)insns + 7) =
+ (*((u16 *)insns + 7) & 0xf8ff) | (mreg << 8);
+ *((u16 *)insns + 8) =
+ (*((u16 *)insns + 8) & 0xffc7) | (mreg << 3);
+ *((u16 *)insns + 9) =
+ (*((u16 *)insns + 9) & 0xffc7) | ((mreg + 1) << 3);
+ *((u16 *)insns + 10) =
+ (*((u16 *)insns + 10) & 0x00ff) |
+ ((1 << mreg) | (1 << (mreg + 1)));
+ }
+
+ if (THUMB_INSN_MATCH(APC, insn)) {
+ /* ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4 */
+ *((u16 *)insns + 4) = ((insn & 0xffff) | 0x800);
+ } else if (THUMB_INSN_MATCH(LRO3, insn)) {
+ /* LDR Rd, [PC, #immed_8*4] ->
+ * LDR Rd, [SP, #immed_8*4] */
+ *((u16 *)insns + 4) = ((insn & 0xffff) + 0x5000);
+ } else if (THUMB_INSN_MATCH(MOV3, insn)) {
+ /* MOV Rd, PC -> MOV Rd, SP */
+ *((u16 *)insns + 4) = ((insn & 0xffff) ^ 0x10);
+ } else if (THUMB2_INSN_MATCH(ADR, insn)) {
+ /* ADDW Rd,PC,#imm -> ADDW Rd,SP,#imm */
+ insns[2] = (insn & 0xfffffff0) | 0x0d;
+ } else if (THUMB2_INSN_MATCH(LDRW, insn) ||
+ THUMB2_INSN_MATCH(LDRBW, insn) ||
+ THUMB2_INSN_MATCH(LDRHW, insn)) {
+ /* LDR.W Rt, [PC, #-<imm_12>] ->
+ * LDR.W Rt, [SP, #-<imm_8>]
+ * !!!!!!!!!!!!!!!!!!!!!!!!
+ * !!! imm_12 vs. imm_8 !!!
+ * !!!!!!!!!!!!!!!!!!!!!!!! */
+ insns[2] = (insn & 0xf0fffff0) | 0x0c00000d;
+ } else if (THUMB2_INSN_MATCH(LDRW1, insn) ||
+ THUMB2_INSN_MATCH(LDRBW1, insn) ||
+ THUMB2_INSN_MATCH(LDRHW1, insn) ||
+ THUMB2_INSN_MATCH(LDRD, insn) ||
+ THUMB2_INSN_MATCH(LDRD1, insn) ||
+ THUMB2_INSN_MATCH(LDREX, insn)) {
+ /* LDRx.W Rt, [PC, #+<imm_12>] ->
+ * LDRx.W Rt, [SP, #+<imm_12>]
+ (+/-imm_8 for LDRD Rt, Rt2, [PC, #<imm_8>] */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ } else if (THUMB2_INSN_MATCH(MUL, insn)) {
+ /* MUL Rd, Rn, SP */
+ insns[2] = (insn & 0xfff0ffff) | 0x000d0000;
+ } else if (THUMB2_INSN_MATCH(DP, insn)) {
+ if (THUMB2_INSN_REG_RM(insn) == 15)
+ /* DP Rd, Rn, PC */
+ insns[2] = (insn & 0xfff0ffff) | 0x000d0000;
+ else if (THUMB2_INSN_REG_RN(insn) == 15)
+ /* DP Rd, PC, Rm */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ } else if (THUMB2_INSN_MATCH(LDRWL, insn)) {
+ /* LDRx.W Rt, [PC, #<imm_12>] ->
+ * LDRx.W Rt, [SP, #+<imm_12>]
+ * (+/-imm_8 for LDRD Rt, Rt2, [PC, #<imm_8>] */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ } else if (THUMB2_INSN_MATCH(RSBW, insn)) {
+ /* RSB{S}.W Rd, PC, #<const> -> RSB{S}.W Rd, SP, #<const> */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ } else if (THUMB2_INSN_MATCH(RORW, insn) ||
+ THUMB2_INSN_MATCH(LSLW1, insn) ||
+ THUMB2_INSN_MATCH(LSRW1, insn)) {
+ if ((THUMB2_INSN_REG_RM(insn) == 15) &&
+ (THUMB2_INSN_REG_RN(insn) == 15))
+ /* ROR.W Rd, PC, PC */
+ insns[2] = (insn & 0xfffdfffd);
+ else if (THUMB2_INSN_REG_RM(insn) == 15)
+ /* ROR.W Rd, Rn, PC */
+ insns[2] = (insn & 0xfff0ffff) | 0xd0000;
+ else if (THUMB2_INSN_REG_RN(insn) == 15)
+ /* ROR.W Rd, PC, Rm */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ } else if (THUMB2_INSN_MATCH(ROR, insn) ||
+ THUMB2_INSN_MATCH(LSLW2, insn) ||
+ THUMB2_INSN_MATCH(LSRW2, insn)) {
+ /* ROR{S} Rd, PC, #<const> -> ROR{S} Rd, SP, #<const> */
+ insns[2] = (insn & 0xfff0ffff) | 0xd0000;
+ }
+
+ if (THUMB2_INSN_MATCH(STRW, insn) ||
+ THUMB2_INSN_MATCH(STRBW, insn)) {
+ /* STRx.W Rt, [Rn, SP] */
+ insns[2] = (insn & 0xfff0ffff) | 0x000d0000;
+ } else if (THUMB2_INSN_MATCH(STRD, insn) ||
+ THUMB2_INSN_MATCH(STRHT, insn) ||
+ THUMB2_INSN_MATCH(STRT, insn) ||
+ THUMB2_INSN_MATCH(STRHW1, insn)) {
+ if (THUMB2_INSN_REG_RN(insn) == 15)
+ /* STRD/T/HT{.W} Rt, [SP, ...] */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ else
+ insns[2] = insn;
+ } else if (THUMB2_INSN_MATCH(STRHW, insn) &&
+ (THUMB2_INSN_REG_RN(insn) == 15)) {
+ if (THUMB2_INSN_REG_RN(insn) == 15)
+ /* STRH.W Rt, [SP, #-<imm_8>] */
+ insns[2] = (insn & 0xf0fffff0) | 0x0c00000d;
+ else
+ insns[2] = insn;
+ }
+
+ /* STRx PC, xxx */
+ if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn) ||
+ THUMB2_INSN_MATCH(STRBW, insn) ||
+ THUMB2_INSN_MATCH(STRD, insn) ||
+ THUMB2_INSN_MATCH(STRHT, insn) ||
+ THUMB2_INSN_MATCH(STRT, insn) ||
+ THUMB2_INSN_MATCH(STRHW1, insn) ||
+ THUMB2_INSN_MATCH(STRHW, insn))) {
+ insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000;
+ }
+
+ if (THUMB2_INSN_MATCH(TEQ1, insn) ||
+ THUMB2_INSN_MATCH(TST1, insn)) {
+ /* TEQ SP, #<const> */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ } else if (THUMB2_INSN_MATCH(TEQ2, insn) ||
+ THUMB2_INSN_MATCH(TST2, insn)) {
+ if ((THUMB2_INSN_REG_RN(insn) == 15) &&
+ (THUMB2_INSN_REG_RM(insn) == 15))
+ /* TEQ/TST PC, PC */
+ insns[2] = (insn & 0xfffdfffd);
+ else if (THUMB2_INSN_REG_RM(insn) == 15)
+ /* TEQ/TST Rn, PC */
+ insns[2] = (insn & 0xfff0ffff) | 0xd0000;
+ else if (THUMB2_INSN_REG_RN(insn) == 15)
+ /* TEQ/TST PC, Rm */
+ insns[2] = (insn & 0xfffffff0) | 0xd;
+ }
+
+ return 0;
+}
+
+static int arch_check_insn_thumb(u32 insn)
+{
+ int ret = 0;
+
+ /* check instructions that can change PC */
+ if (THUMB_INSN_MATCH(UNDEF, insn) ||
+ THUMB2_INSN_MATCH(BLX1, insn) ||
+ THUMB2_INSN_MATCH(BL, insn) ||
+ THUMB_INSN_MATCH(SWI, insn) ||
+ THUMB_INSN_MATCH(BREAK, insn) ||
+ THUMB2_INSN_MATCH(B1, insn) ||
+ THUMB2_INSN_MATCH(B2, insn) ||
+ THUMB2_INSN_MATCH(BXJ, insn) ||
+ (THUMB2_INSN_MATCH(ADR, insn) &&
+ THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RT(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRW1, insn) &&
+ THUMB2_INSN_REG_RT(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRHW, insn) &&
+ THUMB2_INSN_REG_RT(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRHW1, insn) &&
+ THUMB2_INSN_REG_RT(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRWL, insn) &&
+ THUMB2_INSN_REG_RT(insn) == 15) ||
+ THUMB2_INSN_MATCH(LDMIA, insn) ||
+ THUMB2_INSN_MATCH(LDMDB, insn) ||
+ (THUMB2_INSN_MATCH(DP, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(RSBW, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(RORW, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(ROR, insn) && THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LSLW1, insn) &&
+ THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LSLW2, insn) &&
+ THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LSRW1, insn) &&
+ THUMB2_INSN_REG_RD(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LSRW2, insn) &&
+ THUMB2_INSN_REG_RD(insn) == 15) ||
+ /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */
+ (THUMB2_INSN_MATCH(STRW1, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(STRBW1, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(STRHW1, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(STRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(STRHW, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRBW, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_MATCH(LDRHW, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */
+ (THUMB2_INSN_MATCH(LDRD, insn) || THUMB2_INSN_MATCH(LDRD1, insn) ||
+ THUMB2_INSN_MATCH(STRD, insn))) {
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static int do_make_trampoline_thumb(u32 vaddr, u32 insn,
+ u32 *tramp, size_t tramp_len)
+{
+ int ret;
+ int uregs = 0;
+ int pc_dep = 0;
+ unsigned int addr;
+
+ ret = arch_check_insn_thumb(insn);
+ if (ret)
+ return ret;
+
+ if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) {
+ uregs = 0x0700; /* 8-10 */
+ pc_dep = 1;
+ } else if (THUMB_INSN_MATCH(MOV3, insn) &&
+ (((((unsigned char)insn) & 0xff) >> 3) == 15)) {
+ /* MOV Rd, PC */
+ uregs = 0x07;
+ pc_dep = 1;
+ } else if THUMB2_INSN_MATCH(ADR, insn) {
+ uregs = 0x0f00; /* Rd 8-11 */
+ pc_dep = 1;
+ } else if (((THUMB2_INSN_MATCH(LDRW, insn) ||
+ THUMB2_INSN_MATCH(LDRW1, insn) ||
+ THUMB2_INSN_MATCH(LDRBW, insn) ||
+ THUMB2_INSN_MATCH(LDRBW1, insn) ||
+ THUMB2_INSN_MATCH(LDRHW, insn) ||
+ THUMB2_INSN_MATCH(LDRHW1, insn) ||
+ THUMB2_INSN_MATCH(LDRWL, insn)) &&
+ THUMB2_INSN_REG_RN(insn) == 15) ||
+ THUMB2_INSN_MATCH(LDREX, insn) ||
+ ((THUMB2_INSN_MATCH(STRW, insn) ||
+ THUMB2_INSN_MATCH(STRBW, insn) ||
+ THUMB2_INSN_MATCH(STRHW, insn) ||
+ THUMB2_INSN_MATCH(STRHW1, insn)) &&
+ (THUMB2_INSN_REG_RN(insn) == 15 ||
+ THUMB2_INSN_REG_RT(insn) == 15)) ||
+ ((THUMB2_INSN_MATCH(STRT, insn) ||
+ THUMB2_INSN_MATCH(STRHT, insn)) &&
+ (THUMB2_INSN_REG_RN(insn) == 15 ||
+ THUMB2_INSN_REG_RT(insn) == 15))) {
+ uregs = 0xf000; /* Rt 12-15 */
+ pc_dep = 1;
+ } else if ((THUMB2_INSN_MATCH(LDRD, insn) ||
+ THUMB2_INSN_MATCH(LDRD1, insn)) &&
+ (THUMB2_INSN_REG_RN(insn) == 15)) {
+ uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */
+ pc_dep = 1;
+ } else if (THUMB2_INSN_MATCH(MUL, insn) &&
+ THUMB2_INSN_REG_RM(insn) == 15) {
+ uregs = 0xf;
+ pc_dep = 1;
+ } else if (THUMB2_INSN_MATCH(DP, insn) &&
+ (THUMB2_INSN_REG_RN(insn) == 15 ||
+ THUMB2_INSN_REG_RM(insn) == 15)) {
+ uregs = 0xf000; /* Rd 12-15 */
+ pc_dep = 1;
+ } else if (THUMB2_INSN_MATCH(STRD, insn) &&
+ ((THUMB2_INSN_REG_RN(insn) == 15) ||
+ (THUMB2_INSN_REG_RT(insn) == 15) ||
+ THUMB2_INSN_REG_RT2(insn) == 15)) {
+ uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */
+ pc_dep = 1;
+ } else if (THUMB2_INSN_MATCH(RSBW, insn) &&
+ THUMB2_INSN_REG_RN(insn) == 15) {
+ uregs = 0x0f00; /* Rd 8-11 */
+ pc_dep = 1;
+ } else if (THUMB2_INSN_MATCH(RORW, insn) &&
+ (THUMB2_INSN_REG_RN(insn) == 15 ||
+ THUMB2_INSN_REG_RM(insn) == 15)) {
+ uregs = 0x0f00;
+ pc_dep = 1;
+ } else if ((THUMB2_INSN_MATCH(ROR, insn) ||
+ THUMB2_INSN_MATCH(LSLW2, insn) ||
+ THUMB2_INSN_MATCH(LSRW2, insn)) &&
+ THUMB2_INSN_REG_RM(insn) == 15) {
+ uregs = 0x0f00; /* Rd 8-11 */
+ pc_dep = 1;
+ } else if ((THUMB2_INSN_MATCH(LSLW1, insn) ||
+ THUMB2_INSN_MATCH(LSRW1, insn)) &&
+ (THUMB2_INSN_REG_RN(insn) == 15 ||
+ THUMB2_INSN_REG_RM(insn) == 15)) {
+ uregs = 0x0f00; /* Rd 8-11 */
+ pc_dep = 1;
+ } else if ((THUMB2_INSN_MATCH(TEQ1, insn) ||
+ THUMB2_INSN_MATCH(TST1, insn)) &&
+ THUMB2_INSN_REG_RN(insn) == 15) {
+ uregs = 0xf0000; /* Rn 0-3 (16-19) */
+ pc_dep = 1;
+ } else if ((THUMB2_INSN_MATCH(TEQ2, insn) ||
+ THUMB2_INSN_MATCH(TST2, insn)) &&
+ (THUMB2_INSN_REG_RN(insn) == 15 ||
+ THUMB2_INSN_REG_RM(insn) == 15)) {
+ uregs = 0xf0000; /* Rn 0-3 (16-19) */
+ pc_dep = 1;
+ }
+
+ if (unlikely(uregs && pc_dep)) {
+ memcpy(tramp, pc_dep_insn_execbuf_thumb, tramp_len);
+ prep_pc_dep_insn_execbuf_thumb(tramp, insn, uregs);
+
+ addr = vaddr + 4;
+ *((u16 *)tramp + 13) = RET_BREAK_THUMB;
+ *((u16 *)tramp + 14) = addr & 0x0000ffff;
+ *((u16 *)tramp + 15) = addr >> 16;
+ if (!is_thumb2(insn)) {
+ addr = vaddr + 2;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+ } else {
+ addr = vaddr + 4;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+ }
+ } else {
+ memcpy(tramp, gen_insn_execbuf_thumb, tramp_len);
+ *((u16 *)tramp + 13) = RET_BREAK_THUMB;
+ if (!is_thumb2(insn)) {
+ addr = vaddr + 2;
+ *((u16 *)tramp + 2) = insn;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+ } else {
+ addr = vaddr + 4;
+ tramp[1] = insn;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+ }
+ }
+
+ if (THUMB_INSN_MATCH(B2, insn)) {
+ memcpy(tramp, b_off_insn_execbuf_thumb, tramp_len);
+ *((u16 *)tramp + 13) = RET_BREAK_THUMB;
+ addr = branch_t16_dest(insn, vaddr);
+ *((u16 *)tramp + 14) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 15) = addr >> 16;
+ *((u16 *)tramp + 16) = 0;
+ *((u16 *)tramp + 17) = 0;
+
+ } else if (THUMB_INSN_MATCH(B1, insn)) {
+ memcpy(tramp, b_cond_insn_execbuf_thumb, tramp_len);
+ *((u16 *)tramp + 13) = RET_BREAK_THUMB;
+ *((u16 *)tramp + 0) |= (insn & 0xf00);
+ addr = branch_cond_t16_dest(insn, vaddr);
+ *((u16 *)tramp + 14) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 15) = addr >> 16;
+ addr = vaddr + 2;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+
+ } else if (THUMB_INSN_MATCH(BLX2, insn) ||
+ THUMB_INSN_MATCH(BX, insn)) {
+ memcpy(tramp, b_r_insn_execbuf_thumb, tramp_len);
+ *((u16 *)tramp + 13) = RET_BREAK_THUMB;
+ *((u16 *)tramp + 4) = insn;
+ addr = vaddr + 2;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+
+ } else if (THUMB_INSN_MATCH(CBZ, insn)) {
+ memcpy(tramp, cbz_insn_execbuf_thumb, tramp_len);
+ *((u16 *)tramp + 13) = RET_BREAK_THUMB;
+ /* zero out original branch displacement (imm5 = 0; i = 0) */
+ *((u16 *)tramp + 0) = insn & (~0x2f8);
+ /* replace it with 8 bytes offset in execbuf (imm5 = 0b00010) */
+ *((u16 *)tramp + 0) |= 0x20;
+ addr = cbz_t16_dest(insn, vaddr);
+ *((u16 *)tramp + 14) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 15) = addr >> 16;
+ addr = vaddr + 2;
+ *((u16 *)tramp + 16) = (addr & 0x0000ffff) | 0x1;
+ *((u16 *)tramp + 17) = addr >> 16;
+ }
+
+ return 0;
+}
+
+int make_trampoline_thumb(struct arch_insn_arm *ainsn,
+ u32 vaddr, u32 insn, u32 *tramp, size_t tramp_len)
+{
+ int ret;
+
+ ret = do_make_trampoline_thumb(vaddr, insn, tramp, tramp_len);
+ if (ret) {
+ struct decode_info info = {
+ .vaddr = vaddr,
+ .tramp = tramp,
+ .handeler = NULL,
+ };
+
+ ret = decode_thumb(insn, &info);
+ if (info.handeler) {
+ u16 *tr = (u16 *)tramp;
+ tr[13] = RET_BREAK_THUMB; /* bp for uretprobe */
+ ainsn->handler = info.handeler;
+ }
+ }
+
+ return ret;
+}
+
+int noret_thumb(u32 opcode)
+{
+ return !!(THUMB2_INSN_MATCH(BL, opcode) ||
+ THUMB2_INSN_MATCH(BLX1, opcode) ||
+ THUMB_INSN_MATCH(BLX2, opcode));
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_PROBES_THUMB_H
+#define _SWAP_ASM_PROBES_THUMB_H
+
+
+int make_trampoline_thumb(struct arch_insn_arm *ainsn, u32 vaddr, u32 insn,
+ u32 *tramp, size_t tramp_len);
+int noret_thumb(u32 opcode);
+
+
+#endif /* _SWAP_ASM_PROBES_THUMB_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+__asm(
+".text\n"
+".arm\n"
+".global gen_insn_execbuf\n"
+"gen_insn_execbuf:\n"
+" nop\n"
+" nop\n"
+" nop\n" /* original instruction */
+" nop\n"
+" ldr pc, [pc, #4]\n" /* ssbreak */
+" nop\n" /* retbreak */
+" nop\n"
+" nop\n" /* stored PC-4(next insn addr) */
+
+".global pc_dep_insn_execbuf\n"
+"pc_dep_insn_execbuf:\n"
+" str r0, [sp, #-4]\n"
+" ldr r0, [pc, #12]\n"
+" nop\n" /* instruction with replaced PC */
+" ldr r0, [sp, #-4]\n"
+" ldr pc, [pc, #4]\n" /* ssbreak */
+" nop\n" /* retbreak */
+" nop\n" /* stored PC */
+" nop\n" /* stored PC-4 (next insn addr) */
+
+".global b_r_insn_execbuf\n"
+"b_r_insn_execbuf:\n"
+" nop\n" /* bx, blx (Rm) */
+" ldr pc, np1\n"
+" nop\n"
+" nop\n"
+" nop\n"
+" nop\n" /* retbreak */
+" nop\n"
+"np1:\n"
+" nop\n" /* stored PC-4 (next insn addr) */
+
+".global b_cond_insn_execbuf\n"
+"b_cond_insn_execbuf:\n"
+" beq condway\n"
+" ldr pc, np2\n"
+"condway:\n"
+" ldr pc, bd2\n"
+" nop\n"
+" nop\n"
+" nop\n" /* retbreak */
+"bd2:\n"
+" nop\n" /* branch displacement */
+"np2:\n"
+" nop\n" /* stored PC-4 (next insn addr) */
+
+".global blx_off_insn_execbuf\n"
+"blx_off_insn_execbuf:\n"
+" ldreq lr, bd3\n"
+" blxeq lr\n"
+" ldr pc, np3\n"
+" nop\n"
+" nop\n"
+" nop\n" /* retbreak */
+"bd3:\n"
+" nop\n" /* branch displacement */
+"np3:\n"
+" nop\n" /* stored PC-4 (next insn addr) */
+);
--- /dev/null
+/**
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM/MIPS
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
+ * Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>:
+ * redesign module for separating core and arch parts
+ * @author Alexander Shirshikov <a.shirshikov@samsung.com>:
+ * initial implementation for Thumb
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ */
+
+#ifndef _SWAP_ASM_TRAMPS_ARM_H
+#define _SWAP_ASM_TRAMPS_ARM_H
+
+
+#include <linux/types.h>
+
+
+/*
+ * These arrays generated from tramps_arm.c
+ * using 32 bit compiler:
+ * $ gcc tramps_arm.c -c -o tramps_arm.o
+ * $ objdump -d tramps_arm.o
+ */
+
+static u32 __attribute__((unused)) gen_insn_execbuf[] = {
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe59ff004, // ldr pc, [pc, #4]
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+};
+
+static u32 __attribute__((unused)) pc_dep_insn_execbuf[] = {
+ 0xe50d0004, // str r0, [sp, #-4]
+ 0xe59f000c, // ldr r0, [pc, #12]
+ 0xe320f000, // nop
+ 0xe51d0004, // ldr r0, [sp, #-4]
+ 0xe59ff004, // ldr pc, [pc, #4]
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+};
+
+static u32 __attribute__((unused)) b_r_insn_execbuf[] = {
+ 0xe320f000, // nop
+ 0xe59ff010, // ldr pc, [pc, #16]
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+};
+
+static u32 __attribute__((unused)) b_cond_insn_execbuf[] = {
+ 0x0a000000, // beq 68 <condway>
+ 0xe59ff010, // ldr pc, [pc, #16]
+ 0xe59ff008, // ldr pc, [pc, #8]
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+};
+
+static u32 __attribute__((unused)) blx_off_insn_execbuf[] = {
+ 0x059fe010, // ldreq lr, [pc, #16]
+ 0x012fff3e, // blxeq lr
+ 0xe59ff00c, // ldr pc, [pc, #12]
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+ 0xe320f000, // nop
+};
+
+
+#endif /* _SWAP_ASM_TRAMPS_ARM_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+__asm(
+".text\n"
+".thumb\n"
+
+".global gen_insn_execbuf_thumb\n"
+"gen_insn_execbuf_thumb:\n"
+ "nop\n"
+ "nop\n"
+ "nop\n" /* original instruction */
+ "nop\n" /* original instruction */
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "sub sp, sp, #8\n"
+ "str r0, [sp, #0]\n"
+ "ldr r0, [pc, #12]\n"
+ "str r0, [sp, #4]\n"
+ "nop\n"
+ "pop {r0, pc}\n" /* ssbreak */
+ "nop\n" /* retbreak */
+ "nop\n"
+ "nop\n"
+ "nop\n" /* stored PC-4(next insn addr) hi */
+ "nop\n" /* stored PC-4(next insn addr) lo */
+
+ "nop\n"
+
+".global pc_dep_insn_execbuf_thumb\n"
+".align 4\n"
+"pc_dep_insn_execbuf_thumb:\n"
+ "push {r6, r7}\n"
+ "ldr r6, i1\n"
+ "mov r7, sp\n"
+ "mov sp, r6\n"
+ "nop\n" /* PC -> SP */
+ "nop\n" /* PC -> SP */
+ "mov sp, r7\n"
+ "pop {r6, r7}\n"
+ "push {r0, r1}\n"
+ "ldr r0, i2\n"
+ "nop\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0, pc}\n" /* ssbreak */
+ "nop\n" /* retbreak */
+"i1:\n"
+ "nop\n" /* stored PC hi */
+ "nop\n" /* stored PC lo */
+"i2:\n"
+ "nop\n" /* stored PC-4(next insn addr) hi */
+ "nop\n" /* stored PC-4(next insn addr) lo */
+
+".global b_r_insn_execbuf_thumb\n"
+".align 4\n"
+"b_r_insn_execbuf_thumb:\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n" /* bx,blx (Rm) */
+ "nop\n"
+ "push {r0,r1}\n"
+ "ldr r0, np\n"
+ "nop\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0,pc}\n"
+ "nop\n"
+ "nop\n" /* ssbreak */
+ "nop\n" /* retbreak */
+ "nop\n"
+ "nop\n"
+"np:\n"
+ "nop\n" /* stored PC-4(next insn addr) hi */
+ "nop\n" /* stored PC-4(next insn addr) lo */
+
+".global b_off_insn_execbuf_thumb\n"
+".align 4\n"
+"b_off_insn_execbuf_thumb:\n"
+ "push {r0,r1}\n"
+ "ldr r0, bd\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0, pc}\n"
+ "nop\n"
+ "nop\n"
+ "push {r0,r1}\n"
+ "ldr r0, np2\n"
+ "nop\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0,pc}\n"
+ "nop\n"
+ "nop\n" /* ssbreak */
+ "nop\n" /* retbreak */
+"bd:\n"
+ "nop\n" /* branch displacement hi */
+ "nop\n" /* branch displacement lo */
+"np2:\n"
+ "nop\n" /* stored PC-4(next insn addr) hi */
+ "nop\n" /* stored PC-4(next insn addr) lo */
+
+".global b_cond_insn_execbuf_thumb\n"
+".align 4\n"
+"b_cond_insn_execbuf_thumb:\n"
+ "beq condway\n"
+ "push {r0,r1}\n"
+ "ldr r0, np4\n"
+ "nop\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0,pc}\n"
+"condway:\n"
+ "push {r0,r1}\n"
+ "ldr r0, bd4\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0,pc}\n"
+ "nop\n"
+ "nop\n"
+ "nop\n" /* ssbreak */
+ "nop\n" /* retbreak */
+"bd4:\n"
+ "nop\n" /* branch displacement hi */
+ "nop\n" /* branch displacement lo */
+"np4:\n"
+ "nop\n" /* stored PC-4(next insn addr) hi */
+ "nop\n" /* stored PC-4(next insn addr) lo */
+
+".global cbz_insn_execbuf_thumb\n"
+".align 4\n"
+"cbz_insn_execbuf_thumb:\n"
+ "nop\n" /* cbz */
+ "push {r0,r1}\n"
+ "ldr r0, np5\n"
+ "nop\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0,pc}\n"
+ "push {r0,r1}\n"
+ "ldr r0, bd5\n"
+ "str r0, [sp, #4]\n"
+ "pop {r0,pc}\n"
+ "nop\n"
+ "nop\n"
+ "nop\n" /* ssbreak */
+ "nop\n" /* retbreak */
+"bd5:\n"
+ "nop\n" /* branch displacement hi */
+ "nop\n" /* branch displacement lo */
+"np5:\n"
+ "nop\n" /* stored PC-4(next insn addr) hi */
+ "nop\n" /* stored PC-4(next insn addr) lo */
+);
--- /dev/null
+/**
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ */
+
+
+#ifndef _SWAP_ASM_TRAMPS_THUMB_H
+#define _SWAP_ASM_TRAMPS_THUMB_H
+
+
+#include <linux/types.h>
+
+
+/*
+ * These arrays generated from tramps_thumb.c
+ * using 32 bit compiler:
+ * $ gcc tramps_thumb.c -c -o tramps_thumb.o
+ * $ objdump -d tramps_thumb.o
+ */
+
+static u16 __attribute__((unused)) gen_insn_execbuf_thumb[] = {
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xb082, // sub sp, #8
+ 0x9000, // str r0, [sp, #0]
+ 0x4803, // ldr r0, [pc, #12]
+ 0x9001, // str r0, [sp, #4]
+ 0xbf00, // nop
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+};
+
+static u16 __attribute__((unused)) pc_dep_insn_execbuf_thumb[] = {
+ 0xb4c0, // push {r6, r7}
+ 0x4e06, // ldr r6, [pc, #24]
+ 0x466f, // mov r7, sp
+ 0x46b5, // mov sp, r6
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0x46bd, // mov sp, r7
+ 0xbcc0, // pop {r6, r7}
+ 0xb403, // push {r0, r1}
+ 0x4803, // ldr r0, [pc, #12]
+ 0xbf00, // nop
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+};
+
+static u16 __attribute__((unused)) b_r_insn_execbuf_thumb[] = {
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xb403, // push {r0, r1}
+ 0x4804, // ldr r0, [pc, #16]
+ 0xbf00, // nop
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+};
+
+static u16 __attribute__((unused)) b_off_insn_execbuf_thumb[] = {
+ 0xb403, // push {r0, r1}
+ 0x4806, // ldr r0, [pc, #24]
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xb403, // push {r0, r1}
+ 0x4804, // ldr r0, [pc, #16]
+ 0xbf00, // nop
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+};
+
+static u16 __attribute__((unused)) b_cond_insn_execbuf_thumb[] = {
+ 0xf000, 0x8005, // beq.w ce <condway>
+ 0xb403, // push {r0, r1}
+ 0x4807, // ldr r0, [pc, #28]
+ 0xbf00, // nop
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xb403, // push {r0, r1}
+ 0xf8df, 0x000c, // ldr.w r0, [pc, #12]
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+};
+
+static u16 __attribute__((unused)) cbz_insn_execbuf_thumb[] = {
+ 0xbf00, // nop
+ 0xb403, // push {r0, r1}
+ 0x4806, // ldr r0, [pc, #24]
+ 0xbf00, // nop
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xb403, // push {r0, r1}
+ 0x4803, // ldr r0, [pc, #12]
+ 0x9001, // str r0, [sp, #4]
+ 0xbd01, // pop {r0, pc}
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+ 0xbf00, // nop
+};
+
+
+#endif /* _SWAP_ASM_TRAMPS_THUMB_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+
+#include <arch/arm/probes/probes.h>
+#include <arch/arm/probes/probes_arm.h>
+#include <arch/arm/probes/probes_thumb.h>
+#include <uprobe/swap_uprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <kprobe/swap_slots.h>
+#include "../probes/compat_arm64.h"
+
+
+#define PTR_TO_U32(x) ((u32)(unsigned long)(x))
+#define U32_TO_PTR(x) ((void *)(unsigned long)(x))
+
+#define flush_insns(addr, size) \
+ flush_icache_range((unsigned long)(addr), \
+ (unsigned long)(addr) + (size))
+
+
+/**
+ * @brief Prepares uprobe for ARM.
+ *
+ * @param up Pointer to the uprobe.
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_prepare_uprobe_arm(struct uprobe *p)
+{
+ int ret;
+ struct task_struct *task = p->task;
+ unsigned long vaddr = (unsigned long)p->addr;
+ u32 insn;
+ u32 tramp[UPROBES_TRAMP_LEN];
+ u32 __user *utramp;
+ enum { tramp_len = sizeof(tramp) };
+
+ if (!read_proc_vm_atomic(task, vaddr & ~1, &insn, sizeof(insn))) {
+ printk(KERN_ERR "failed to read memory %lx!\n", vaddr);
+ return -EINVAL;
+ }
+
+ ret = make_tramp(&p->ainsn.insn, vaddr, insn, tramp, tramp_len);
+ if (ret) {
+ pr_err("failed to make tramp, addr=%p\n", p->addr);
+ return ret;
+ }
+
+ utramp = swap_slot_alloc(p->sm);
+ if (utramp == NULL) {
+ printk(KERN_INFO "Error: swap_slot_alloc failed (%08lx)\n",
+ vaddr);
+ return -ENOMEM;
+ }
+
+ if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp,
+ tramp_len)) {
+ pr_err("failed to write memory tramp=%p!\n", utramp);
+ swap_slot_free(p->sm, utramp);
+ return -EINVAL;
+ }
+
+ flush_insns(utramp, tramp_len);
+ p->insn = utramp;
+ p->opcode = insn;
+
+ return 0;
+}
+
+int arch_arm_uprobe_arm(struct uprobe *p)
+{
+ int ret;
+ unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
+ int thumb_mode = (unsigned long)p->addr & 1;
+ int len = 4 >> thumb_mode; /* if thumb_mode then len = 2 */
+ unsigned long insn = thumb_mode ? BREAK_THUMB : BREAK_ARM;
+
+ ret = write_proc_vm_atomic(p->task, vaddr, &insn, len);
+ if (!ret) {
+ pr_err("failed to write memory tgid=%u addr=%08lx len=%d\n",
+ p->task->tgid, vaddr, len);
+
+ return -EACCES;
+ } else {
+ flush_insns(vaddr, len);
+ }
+
+ return 0;
+}
+
+void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task)
+{
+ int ret;
+
+ unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
+ int thumb_mode = (unsigned long)p->addr & 1;
+ int len = 4 >> thumb_mode; /* if thumb_mode then len = 2 */
+
+ ret = write_proc_vm_atomic(task, vaddr, &p->opcode, len);
+ if (!ret) {
+ pr_err("Failed to write memory tgid=%u addr=%08lx len=%d\n",
+ task->tgid, vaddr, len);
+ } else {
+ flush_insns(vaddr, len);
+ }
+}
+
+/**
+ * @brief Prepates uretprobe for ARM.
+ *
+ * @param ri Pointer to the uretprobe instance.
+ * @param regs Pointer to CPU register data.
+ * @return Error code.
+ */
+int prepare_uretprobe_arm(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ unsigned long thumb, bp_offset;
+
+ thumb = ri->preload.use ? ri->preload.thumb : thumb_mode(regs);
+ bp_offset = thumb ? 0x1b : 4 * PROBES_TRAMP_RET_BREAK_IDX;
+
+ /* save original return address */
+ ri->ret_addr = (uprobe_opcode_t *)regs->ARM_lr;
+
+ /* replace return address with break point adddress */
+ regs->ARM_lr = (unsigned long)(ri->rp->up.insn) + bp_offset;
+
+ /* save stack pointer address */
+ ri->sp = (uprobe_opcode_t *)regs->ARM_sp;
+
+ /* Set flag of current mode */
+ ri->sp = (uprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
+
+ return 0;
+}
+
+/**
+ * @brief Restores return address.
+ *
+ * @param orig_ret_addr Original return address.
+ * @param regs Pointer to CPU register data.
+ * @return Void.
+ */
+void set_orig_ret_addr_arm(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->ARM_lr = orig_ret_addr;
+ regs->ARM_pc = orig_ret_addr & ~0x1;
+
+ if (regs->ARM_lr & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+ else
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+}
+
+void arch_opcode_analysis_uretprobe_arm(struct uretprobe *rp)
+{
+ /* Remove retprobe if first insn overwrites lr */
+ rp->thumb_noret = noret_thumb(rp->up.opcode);
+ rp->arm_noret = noret_arm(rp->up.opcode);
+}
+
+unsigned long arch_get_trampoline_addr_arm(struct uprobe *p,
+ struct pt_regs *regs)
+{
+ return thumb_mode(regs) ?
+ PTR_TO_U32(p->insn) + 0x1b :
+ PTR_TO_U32(p->insn +
+ PROBES_TRAMP_RET_BREAK_IDX);
+}
+
+unsigned long arch_tramp_by_ri_arm(struct uretprobe_instance *ri)
+{
+ /* Understand function mode */
+ return (PTR_TO_U32(ri->sp) & 1) ?
+ PTR_TO_U32(ri->rp->up.insn) + 0x1b :
+ PTR_TO_U32(ri->rp->up.insn +
+ PROBES_TRAMP_RET_BREAK_IDX);
+}
+
+int arch_disarm_urp_inst_arm(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ struct pt_regs *uregs = task_pt_regs(ri->task);
+ u32 ra = uregs->ARM_lr;
+ u32 vaddr, tramp, found = 0;
+ u32 sp = PTR_TO_U32(ri->sp) & ~1;
+ u32 ret_addr = PTR_TO_U32(ri->ret_addr);
+ u32 stack = sp - 4 * (URETPROBE_STACK_DEPTH + 1);
+ u32 buf[URETPROBE_STACK_DEPTH];
+ int i, ret;
+
+ vaddr = PTR_TO_U32(ri->rp->up.addr);
+ tramp = arch_tramp_by_ri_arm(ri);
+
+ /* check stack */
+ ret = read_proc_vm_atomic(task, stack, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ pr_info("---> %s (%d/%d): failed to read stack from %08x\n",
+ task->comm, task->tgid, task->pid, stack);
+ ret = -EFAULT;
+ goto check_lr;
+ }
+
+ /* search the stack from the bottom */
+ for (i = URETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
+ if (buf[i] == tramp) {
+ found = stack + 4 * i;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret = -ESRCH;
+ goto check_lr;
+ }
+
+ pr_info("---> %s (%d/%d): trampoline found at "
+ "%08x (%08x /%+d) - %x, set ret_addr=%08x\n",
+ task->comm, task->tgid, task->pid,
+ found, sp,
+ found - sp, vaddr, ret_addr);
+ ret = write_proc_vm_atomic(task, found, &ret_addr, 4);
+ if (ret != 4) {
+ pr_info("---> %s (%d/%d): failed to write value to %08x",
+ task->comm, task->tgid, task->pid, found);
+ ret = -EFAULT;
+ } else {
+ ret = 0;
+ }
+
+check_lr: /* check lr anyway */
+ if (ra == tramp) {
+ pr_info("---> %s (%d/%d): trampoline found at "
+ "lr = %08x - %x, set ret_addr=%08x\n",
+ task->comm, task->tgid, task->pid, ra, vaddr,
+ ret_addr);
+
+ /* set ret_addr */
+ uregs->ARM_lr = ret_addr;
+ ret = 0;
+ } else if (ret) {
+ pr_info("---> %s (%d/%d): trampoline NOT found at "
+ "sp=%08x, lr=%08x - %x, ret_addr=%08x\n",
+ task->comm, task->tgid, task->pid,
+ sp, ra, vaddr, ret_addr);
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_ARM_UPROBE_H
+#define _SWAP_ASM_ARM_UPROBE_H
+
+
+#include <linux/printk.h>
+#include <linux/uaccess.h>
+#include "../probes/compat_arm64.h"
+
+
+struct pt_regs;
+struct uprobe;
+struct uretprobe;
+struct uretprobe_instance;
+
+
+static inline unsigned long swap_get_upc_arm(struct pt_regs *regs)
+{
+ return regs->ARM_pc | !!thumb_mode(regs);
+}
+
+static inline void swap_set_upc_arm(struct pt_regs *regs, unsigned long val)
+{
+ if (val & 1) {
+ regs->ARM_pc = val & ~1UL;
+ regs->ARM_cpsr |= PSR_T_BIT;
+ } else {
+ regs->ARM_pc = val;
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+ }
+}
+
+static inline unsigned long swap_get_uarg_arm(struct pt_regs *regs,
+ unsigned long n)
+{
+ u32 *ptr, val = 0;
+
+ switch (n) {
+ case 0:
+ return regs->ARM_r0;
+ case 1:
+ return regs->ARM_r1;
+ case 2:
+ return regs->ARM_r2;
+ case 3:
+ return regs->ARM_r3;
+ default:
+ ptr = (u32 *)regs->ARM_sp + n - 4;
+ if (get_user(val, ptr))
+ pr_err("Failed to dereference a pointer[%p]\n", ptr);
+ break;
+ }
+
+ return val;
+}
+
+static inline void swap_put_uarg_arm(struct pt_regs *regs, unsigned long n,
+ unsigned long val)
+{
+ u32 *ptr;
+
+ switch (n) {
+ case 0:
+ regs->ARM_r0 = val;
+ break;
+ case 1:
+ regs->ARM_r1 = val;
+ break;
+ case 2:
+ regs->ARM_r2 = val;
+ break;
+ case 3:
+ regs->ARM_r3 = val;
+ break;
+ default:
+ ptr = (u32 *)regs->ARM_sp + n - 4;
+ if (put_user(val, ptr))
+ pr_err("Failed to dereference a pointer[%p]\n", ptr);
+ }
+}
+
+static inline unsigned long swap_get_uret_addr_arm(struct pt_regs *regs)
+{
+ return regs->ARM_lr;
+}
+
+static inline void swap_set_uret_addr_arm(struct pt_regs *regs, unsigned long v)
+{
+ regs->ARM_lr = v;
+}
+
+int arch_prepare_uprobe_arm(struct uprobe *p);
+int arch_arm_uprobe_arm(struct uprobe *p);
+void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task);
+
+int prepare_uretprobe_arm(struct uretprobe_instance *ri, struct pt_regs *regs);
+void set_orig_ret_addr_arm(unsigned long orig_ret_addr, struct pt_regs *regs);
+void arch_opcode_analysis_uretprobe_arm(struct uretprobe *rp);
+unsigned long arch_get_trampoline_addr_arm(struct uprobe *p,
+ struct pt_regs *regs);
+unsigned long arch_tramp_by_ri_arm(struct uretprobe_instance *ri);
+int arch_disarm_urp_inst_arm(struct uretprobe_instance *ri,
+ struct task_struct *task);
+
+
+#endif /* _SWAP_ASM_ARM_UPROBE_H */
+
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_buffer.o
+swap_buffer-y := swap_buffer_module.o \
+ buffer_queue.o
+
--- /dev/null
+/**
+ * @file buffer/buffer_description.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * swap_subbuffer structure represents one buffers subbufer
+ */
+
+#ifndef __BUFFER_DESCRIPTION_H__
+#define __BUFFER_DESCRIPTION_H__
+
+#include "data_types.h"
+
+/**
+ * @struct swap_subbuffer
+ * @brief This structures are combined in array which represents the SWAP buffer.
+ * @var swap_subbuffer::next_in_queue
+ * Pointer to the next swap_subbufer in queue
+ * @var swap_subbuffer::full_buffer_part
+ * Currently occupied subbuffers size
+ * @var swap_subbuffer::data_buffer
+ * Pointer to subbuffers data itself of type swap_subbuffer_ptr
+ * @var swap_subbuffer::buffer_sync
+ * Subbuffers sync primitive
+ */
+struct swap_subbuffer {
+ /* Pointer to the next subbuffer in queue */
+ struct swap_subbuffer *next_in_queue;
+ /* Size of the filled part of a subbuffer */
+ size_t full_buffer_part;
+ /* Pointer to data buffer */
+ swap_subbuffer_ptr data_buffer;
+ /* Buffer rw sync */
+ struct sync_t buffer_sync;
+};
+
+#endif /* __BUFFER_DESCRIPTION_H__ */
--- /dev/null
+/**
+ * buffer/buffer_queue.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>: SWAP Buffer implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Implements buffers queues interface
+ */
+
+/* For all memory allocation/deallocation operations, except buffer memory
+ * allocation/deallocation should be used
+ * memory_allocation(size_t memory_size)
+ * memory_free(void *ptr)
+ * defines.
+ * For subbuffer allocation/deallocation operations should be used
+ * buffer_allocation(size_t subbuffer_size)
+ * buffer_free(void *ptr, size_t subbuffer_size)
+ * To get buffer pointer for any usage, EXCEPT ALLOCATION AND DEALLOCATION
+ * use the following define:
+ * buffer_pointer(void *ptr_to_buffer_element_of_swap_buffer_structure)
+ * DO NOT USE SUBBUFFER PTR IN STRUCT SWAP_BUFFER WITHOUT THIS DEFINE!
+ * It will be ok for user space, but fail in kernel space.
+ *
+ * See space_dep_types_and_def.h for details */
+
+
+
+#include "buffer_queue.h"
+#include "swap_buffer_to_buffer_queue.h"
+#include "swap_buffer_errors.h"
+#include "kernel_operations.h"
+
+/**
+ * @struct queue_t
+ * @brief Queue structure. Consist of pointers to the first and the last
+ * elements of queue.
+ * @var queue_t::start_ptr
+ * Pointer to the first subbuffer in queue
+ * @var queue_t::end_ptr
+ * Pointer to the last subbuffer in queue
+ * @var queue_t::subbuffers_count
+ * Subbuffers count in queue
+ * @var queue_t::queue_sync
+ * Queue access sync primitive
+ */
+struct queue_t {
+ struct swap_subbuffer *start_ptr;
+ struct swap_subbuffer *end_ptr;
+ unsigned int subbuffers_count;
+ struct sync_t queue_sync;
+};
+
+/**
+ * @var write_queue
+ * @brief Represents write queue.
+ */
+struct queue_t write_queue = {
+ .start_ptr = NULL,
+ .end_ptr = NULL,
+ .subbuffers_count = 0,
+ .queue_sync = {
+ .flags = 0x0
+ }
+};
+
+/**
+ * @var read_queue
+ * @brief Represents read queue.
+ */
+struct queue_t read_queue = {
+ .start_ptr = NULL,
+ .end_ptr = NULL,
+ .subbuffers_count = 0,
+ .queue_sync = {
+ .flags = 0x0
+ }
+};
+
+/* Pointers array. Points to busy buffers */
+static struct swap_subbuffer **queue_busy;
+
+/* Store last busy element */
+static unsigned int queue_busy_last_element;
+
+/* Subbuffers count */
+static unsigned int queue_subbuffer_count;
+
+/* One subbuffer size */
+static size_t queue_subbuffer_size;
+
+/* Busy list sync */
+static struct sync_t buffer_busy_sync = {
+ .flags = 0x0
+};
+
+/* Memory pages count in one subbuffer */
+static int pages_order_in_subbuffer;
+
+/**
+ * @brief Allocates memory for swap_subbuffer structures and subbuffers.
+ * Total allocated memory = subbuffer_size * subbuffers_count.
+ *
+ * @param subbuffer_size Size of each subbuffer.
+ * @param subbuffers_count Count of subbuffers.
+ * @return 0 on success, negative error code otherwise.
+ */
+int buffer_queue_allocation(size_t subbuffer_size,
+ unsigned int subbuffers_count)
+{
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int allocated_buffers = 0;
+ unsigned int allocated_structs = 0;
+ struct swap_subbuffer *clean_tmp_struct;
+ int result;
+
+ /* Static varibles initialization */
+ queue_subbuffer_size = subbuffer_size;
+ queue_subbuffer_count = subbuffers_count;
+ queue_busy_last_element = 0;
+
+ /* Set variable pages_in_subbuffer. It is used for allocation and
+ * deallocation memory pages and its value is returned from
+ * swap_buffer_get() and contains page count in one subbuffer.
+ * All this useful only in kernel space. In userspace it is dummy.*/
+ set_pages_order_in_subbuffer(queue_subbuffer_size);
+ /* Sync primitives initialization */
+ sync_init(&read_queue.queue_sync);
+ sync_init(&write_queue.queue_sync);
+ sync_init(&buffer_busy_sync);
+
+ /* Memory allocation for queue_busy */
+ queue_busy =
+ memory_allocation(sizeof(*queue_busy) * queue_subbuffer_count);
+
+ if (!queue_busy) {
+ result = -E_SB_NO_MEM_QUEUE_BUSY;
+ goto buffer_allocation_error_ret;
+ }
+
+ /* Memory allocation for swap_subbuffer structures */
+
+ /* Allocation for first structure. */
+ write_queue.start_ptr =
+ memory_allocation(sizeof(*write_queue.start_ptr));
+
+ if (!write_queue.start_ptr) {
+ result = -E_SB_NO_MEM_BUFFER_STRUCT;
+ goto buffer_allocation_queue_busy_free;
+ }
+ allocated_structs++;
+
+
+ write_queue.end_ptr = write_queue.start_ptr;
+
+ write_queue.end_ptr->next_in_queue = NULL;
+ write_queue.end_ptr->full_buffer_part = 0;
+ write_queue.end_ptr->data_buffer =
+ buffer_allocation(queue_subbuffer_size);
+ if (!write_queue.end_ptr->data_buffer) {
+ print_err("Cannot allocate memory for buffer 1\n");
+ result = -E_SB_NO_MEM_DATA_BUFFER;
+ goto buffer_allocation_error_free;
+ }
+ allocated_buffers++;
+
+ sync_init(&write_queue.end_ptr->buffer_sync);
+
+ /* Buffer initialization */
+ memset(buffer_address(write_queue.end_ptr->data_buffer), 0,
+ queue_subbuffer_size);
+
+ /* Allocation for other structures. */
+ for (i = 1; i < queue_subbuffer_count; i++) {
+ write_queue.end_ptr->next_in_queue =
+ memory_allocation(
+ sizeof(*write_queue.end_ptr->next_in_queue));
+ if (!write_queue.end_ptr->next_in_queue) {
+ result = -E_SB_NO_MEM_BUFFER_STRUCT;
+ goto buffer_allocation_error_free;
+ }
+ allocated_structs++;
+
+ /* Now next write_queue.end_ptr is next */
+ write_queue.end_ptr = write_queue.end_ptr->next_in_queue;
+
+ write_queue.end_ptr->next_in_queue = NULL;
+ write_queue.end_ptr->full_buffer_part = 0;
+ write_queue.end_ptr->data_buffer =
+ buffer_allocation(queue_subbuffer_size);
+ if (!write_queue.end_ptr->data_buffer) {
+ result = -E_SB_NO_MEM_DATA_BUFFER;
+ goto buffer_allocation_error_free;
+ }
+ allocated_buffers++;
+
+ sync_init(&write_queue.end_ptr->buffer_sync);
+
+ /* Buffer initialization */
+ memset(buffer_address(write_queue.end_ptr->data_buffer), 0,
+ queue_subbuffer_size);
+ }
+
+ /* All subbuffers are in write list */
+ write_queue.subbuffers_count = subbuffers_count;
+
+ return E_SB_SUCCESS;
+
+ /* In case of errors, this code is called */
+ /* Free all previously allocated memory */
+buffer_allocation_error_free:
+ clean_tmp_struct = write_queue.start_ptr;
+
+ for (j = 0; j < allocated_structs; j++) {
+ clean_tmp_struct = write_queue.start_ptr;
+ if (allocated_buffers) {
+ buffer_free(clean_tmp_struct->data_buffer,
+ queue_subbuffer_size);
+ allocated_buffers--;
+ }
+ if (write_queue.start_ptr != write_queue.end_ptr)
+ write_queue.start_ptr =
+ write_queue.start_ptr->next_in_queue;
+ memory_free(clean_tmp_struct);
+ }
+ write_queue.end_ptr = NULL;
+ write_queue.start_ptr = NULL;
+
+buffer_allocation_queue_busy_free:
+ memory_free(queue_busy);
+ queue_busy = NULL;
+
+buffer_allocation_error_ret:
+ return result;
+}
+
+/**
+ * @brief Resets all subbuffers for writing.
+ *
+ * @return 0 on success, negative error code otherwise.
+ */
+int buffer_queue_reset(void)
+{
+ struct swap_subbuffer *buffer = read_queue.start_ptr;
+
+ /* Check if there are some subbuffers in busy list.
+ * If so - return error */
+ if (get_busy_buffers_count())
+ return -E_SB_UNRELEASED_BUFFERS;
+
+ /* Lock read sync primitive */
+ sync_lock(&read_queue.queue_sync);
+
+ /* Set all subbuffers in read list to write list
+ * and reinitialize them */
+ while (read_queue.start_ptr) {
+
+ /* Lock buffer sync primitive to prevent writing to buffer if it
+ * had been selected for writing, but still wasn't wrote. */
+ sync_lock(&buffer->buffer_sync);
+
+ buffer = read_queue.start_ptr;
+
+ /* If we reached end of the list */
+ if (read_queue.start_ptr == read_queue.end_ptr)
+ read_queue.end_ptr = NULL;
+
+ read_queue.start_ptr = read_queue.start_ptr->next_in_queue;
+
+ /* Reinit full buffer part */
+ buffer->full_buffer_part = 0;
+
+ add_to_write_list(buffer);
+
+ /* Unlock buffer sync primitive */
+ sync_unlock(&buffer->buffer_sync);
+ }
+
+ /* Unlock read primitive */
+ sync_unlock(&read_queue.queue_sync);
+
+ return E_SB_SUCCESS;
+}
+
+/**
+ * @brief Free all allocated subbuffers.
+ *
+ * @return Void.
+ */
+void buffer_queue_free(void)
+{
+ struct swap_subbuffer *tmp = NULL;
+
+ /* Lock all sync primitives to prevet accessing free memory */
+ sync_lock(&write_queue.queue_sync);
+ sync_lock(&read_queue.queue_sync);
+ sync_lock(&buffer_busy_sync);
+
+ /* Free buffers and structures memory that are in read list */
+ while (read_queue.start_ptr) {
+ tmp = read_queue.start_ptr;
+ read_queue.start_ptr = read_queue.start_ptr->next_in_queue;
+ buffer_free(tmp->data_buffer, queue_subbuffer_size);
+ memory_free(tmp);
+ }
+
+ /* Free buffers and structures memory that are in read list */
+ while (write_queue.start_ptr) {
+ tmp = write_queue.start_ptr;
+ write_queue.start_ptr = write_queue.start_ptr->next_in_queue;
+ buffer_free(tmp->data_buffer, queue_subbuffer_size);
+ memory_free(tmp);
+ }
+
+ /* Free busy_list */
+ memory_free(queue_busy);
+ queue_busy = NULL;
+
+ queue_subbuffer_size = 0;
+ queue_subbuffer_count = 0;
+ read_queue.start_ptr = NULL;
+ read_queue.end_ptr = NULL;
+ write_queue.start_ptr = NULL;
+ write_queue.end_ptr = NULL;
+
+ /* Unlock all sync primitives */
+ sync_unlock(&buffer_busy_sync);
+ sync_unlock(&read_queue.queue_sync);
+ sync_unlock(&write_queue.queue_sync);
+}
+
+static unsigned int is_buffer_enough(struct swap_subbuffer *subbuffer,
+ size_t size)
+{
+ /* XXX Think about checking full_buffer_part for correctness
+ * (<queue_subbuffer_size). It should be true, but if isn't (due to
+ * sources chaning, etc.) this function should be true! */
+ return ((queue_subbuffer_size-subbuffer->full_buffer_part) >= size) ?
+ 1 : 0;
+}
+
+static void next_queue_element(struct queue_t *queue)
+{
+ /* If we reached the last elemenet, end pointer should point to NULL */
+ if (queue->start_ptr == queue->end_ptr)
+ queue->end_ptr = NULL;
+
+ queue->start_ptr = queue->start_ptr->next_in_queue;
+ --queue->subbuffers_count;
+}
+
+/**
+ * @brief Get first subbuffer from read list.
+ *
+ * @return Pointer to swap_subbuffer
+ */
+struct swap_subbuffer *get_from_read_list(void)
+{
+ struct swap_subbuffer *result = NULL;
+
+ /* Lock read sync primitive */
+ sync_lock(&read_queue.queue_sync);
+
+ if (read_queue.start_ptr == NULL) {
+ result = NULL;
+ goto get_from_read_list_unlock;
+ }
+
+ result = read_queue.start_ptr;
+
+ next_queue_element(&read_queue);
+
+get_from_read_list_unlock:
+ /* Unlock read sync primitive */
+ sync_unlock(&read_queue.queue_sync);
+
+ return result;
+}
+
+/**
+ * @brief Add subbuffer to read list.
+ *
+ * @param subbuffer Pointer to the subbuffer to add.
+ * @return Void.
+ */
+void add_to_read_list(struct swap_subbuffer *subbuffer)
+{
+ /* Lock read sync primitive */
+ sync_lock(&read_queue.queue_sync);
+
+ if (!read_queue.start_ptr)
+ read_queue.start_ptr = subbuffer;
+
+ if (read_queue.end_ptr) {
+ read_queue.end_ptr->next_in_queue = subbuffer;
+
+ read_queue.end_ptr = read_queue.end_ptr->next_in_queue;
+ } else {
+ read_queue.end_ptr = subbuffer;
+ }
+ read_queue.end_ptr->next_in_queue = NULL;
+ ++read_queue.subbuffers_count;
+
+ /* Unlock read sync primitive */
+ sync_unlock(&read_queue.queue_sync);
+}
+
+static int add_to_read_list_with_callback(struct swap_subbuffer *subbuffer,
+ bool wakeup)
+{
+ int result = 0;
+
+ add_to_read_list(subbuffer);
+ /* TODO Handle ret value */
+ result = swap_buffer_callback(subbuffer, wakeup);
+
+ return result;
+}
+
+/**
+ * @brief Returns subbuffers to read count.
+ *
+ * @return Count of subbuffers in read_queue.
+ */
+unsigned int get_readable_buf_cnt(void)
+{
+ return read_queue.subbuffers_count;
+}
+
+
+/**
+ * @brief Get first writable subbuffer from write list.
+ *
+ * @param size Minimum amount of free space in subbuffer.
+ * @param[out] ptr_to_write Pointer to the variable where pointer to the
+ * beginning of memory for writing should be stored.
+ * @return Found swap_subbuffer.
+ */
+struct swap_subbuffer *get_from_write_list(size_t size, void **ptr_to_write,
+ bool wakeup)
+{
+ struct swap_subbuffer *result = NULL;
+
+ /* Callbacks are called at the end of the function
+ * to prevent deadlocks */
+ struct queue_t callback_queue = {
+ .start_ptr = NULL,
+ .end_ptr = NULL,
+ .queue_sync = {
+ .flags = 0x0
+ }
+ };
+ struct swap_subbuffer *tmp_buffer = NULL;
+
+ /* Init pointer */
+ *ptr_to_write = NULL;
+
+ /* Lock write list sync primitive */
+ sync_lock(&write_queue.queue_sync);
+
+ while (write_queue.start_ptr) {
+
+ /* We're found subbuffer */
+ if (is_buffer_enough(write_queue.start_ptr, size)) {
+
+ result = write_queue.start_ptr;
+ *ptr_to_write =
+ (void *)((unsigned long)
+ (buffer_address(result->data_buffer)) +
+ result->full_buffer_part);
+
+ /* Add data size to full_buffer_part.
+ * Very important to do it in
+ * write_queue.queue_sync spinlock */
+ write_queue.start_ptr->full_buffer_part += size;
+
+ /* Lock rw sync.
+ * Should be unlocked in swap_buffer_write() */
+ sync_lock_no_flags(&result->buffer_sync);
+ break;
+ /* This subbuffer is not enough => it goes to read list */
+ } else {
+ result = write_queue.start_ptr;
+
+ next_queue_element(&write_queue);
+
+ /* Add to callback list */
+ if (!callback_queue.start_ptr)
+ callback_queue.start_ptr = result;
+
+ if (callback_queue.end_ptr)
+ callback_queue.end_ptr->next_in_queue = result;
+ callback_queue.end_ptr = result;
+ callback_queue.end_ptr->next_in_queue = NULL;
+ result = NULL;
+ }
+ }
+
+ /* Unlock write list sync primitive */
+ sync_unlock(&write_queue.queue_sync);
+
+ /* Adding buffers to read list and calling callbacks */
+ for (tmp_buffer = NULL; callback_queue.start_ptr; ) {
+ if (callback_queue.start_ptr == callback_queue.end_ptr)
+ callback_queue.end_ptr = NULL;
+
+ tmp_buffer = callback_queue.start_ptr;
+ callback_queue.start_ptr =
+ callback_queue.start_ptr->next_in_queue;
+
+ add_to_read_list_with_callback(tmp_buffer, wakeup);
+ }
+
+ return result;
+}
+
+/**
+ * @brief Add subbuffer to write list.
+ *
+ * @param subbuffer Pointer to the swap_subbuffer that should be stored.
+ * @return Void.
+ */
+void add_to_write_list(struct swap_subbuffer *subbuffer)
+{
+ sync_lock(&write_queue.queue_sync);
+
+ /* Reinitialize */
+ subbuffer->full_buffer_part = 0;
+
+ if (!write_queue.start_ptr)
+ write_queue.start_ptr = subbuffer;
+
+ if (write_queue.end_ptr) {
+ write_queue.end_ptr->next_in_queue = subbuffer;
+ write_queue.end_ptr = write_queue.end_ptr->next_in_queue;
+ } else {
+ write_queue.end_ptr = subbuffer;
+ }
+ write_queue.end_ptr->next_in_queue = NULL;
+ ++write_queue.subbuffers_count;
+
+ sync_unlock(&write_queue.queue_sync);
+}
+
+/**
+ * @brief Returns subbuffers to write count.
+ *
+ * @return Count of subbuffers in write queue.
+ */
+unsigned int get_writable_buf_cnt(void)
+{
+ return write_queue.subbuffers_count;
+}
+
+
+/**
+ * @brief Add subbuffer to busy list when it is read from out of the buffer.
+ *
+ * @param subbuffer Pointer to the swap_subbuffer that should be added.
+ * @return Void.
+ */
+void add_to_busy_list(struct swap_subbuffer *subbuffer)
+{
+ /* Lock busy sync primitive */
+ sync_lock(&buffer_busy_sync);
+
+ subbuffer->next_in_queue = NULL;
+ queue_busy[queue_busy_last_element] = subbuffer;
+ queue_busy_last_element += 1;
+
+ /* Unlock busy sync primitive */
+ sync_unlock(&buffer_busy_sync);
+}
+
+/**
+ * @brief Remove subbuffer from busy list when it is released.
+ *
+ * @param subbuffer Pointer to the swap_subbuffer that should be removed.
+ * @return 0 on success, negative error code otherwise.
+ */
+int remove_from_busy_list(struct swap_subbuffer *subbuffer)
+{
+ int result = -E_SB_NO_SUBBUFFER_IN_BUSY; /* For sanitization */
+ int i;
+
+ /* Lock busy list sync primitive */
+ sync_lock(&buffer_busy_sync);
+
+ /* Sanitization and removing */
+ for (i = 0; i < queue_busy_last_element; i++) {
+ if (queue_busy[i] == subbuffer) {
+ /* Last element goes here and length is down 1 */
+ queue_busy[i] = queue_busy[queue_busy_last_element - 1];
+ queue_busy_last_element -= 1;
+ result = E_SB_SUCCESS;
+ break;
+ }
+ }
+
+ /* Unlock busy list sync primitive */
+ sync_unlock(&buffer_busy_sync);
+
+ return result;
+}
+
+/**
+ * @brief Set all subbuffers in write list to read list.
+ *
+ * @return Void.
+ */
+void buffer_queue_flush(void)
+{
+ struct swap_subbuffer *buffer = write_queue.start_ptr;
+
+ /* Locking write sync primitive */
+ sync_lock(&write_queue.queue_sync);
+
+ while (write_queue.start_ptr &&
+ write_queue.start_ptr->full_buffer_part) {
+
+ /* Lock buffer sync primitive to prevent writing to buffer if it
+ * had been selected for writing, but still wasn't wrote. */
+ sync_lock(&buffer->buffer_sync);
+
+ buffer = write_queue.start_ptr;
+ next_queue_element(&write_queue);
+ add_to_read_list(buffer);
+
+ /* Unlock buffer sync primitive */
+ sync_unlock(&buffer->buffer_sync);
+ }
+
+ /* Unlock write primitive */
+ sync_unlock(&write_queue.queue_sync);
+}
+
+/**
+ * @brief Get subbuffers count in busy list.
+ *
+ * @return Count of swap_subbuffers in busy list.
+ */
+int get_busy_buffers_count(void)
+{
+ int result;
+
+ sync_lock(&buffer_busy_sync);
+ result = queue_busy_last_element;
+ sync_unlock(&buffer_busy_sync);
+
+ return result;
+}
+
+/**
+ * @brief Get memory pages count in subbuffer.
+ *
+ * @return Pages count in subbuffer.
+ */
+int get_pages_count_in_subbuffer(void)
+{
+/* Return 1 if pages order 0,
+ * or 2 of power pages_order_in_subbuffer otherwise */
+ return (pages_order_in_subbuffer) ?
+ 2 << (pages_order_in_subbuffer - 1) : 1;
+}
--- /dev/null
+/**
+ * @file buffer/buffer_queue.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Represents buffers queues interface
+ */
+
+/* SWAP Buffer queues interface */
+
+#ifndef __BUFFER_QUEUE_H__
+#define __BUFFER_QUEUE_H__
+
+#include <linux/types.h>
+#include "buffer_description.h"
+
+int buffer_queue_allocation(size_t subbuffer_size,
+ unsigned int subbuffers_count);
+void buffer_queue_free(void);
+int buffer_queue_reset(void);
+void buffer_queue_flush(void);
+struct swap_subbuffer *get_from_write_list(size_t size, void **ptr_to_write,
+ bool wakeup);
+struct swap_subbuffer *get_from_read_list(void);
+void add_to_write_list(struct swap_subbuffer *subbuffer);
+void add_to_read_list(struct swap_subbuffer *subbuffer);
+void add_to_busy_list(struct swap_subbuffer *subbuffer);
+int remove_from_busy_list(struct swap_subbuffer *subbuffer);
+
+unsigned int get_readable_buf_cnt(void);
+unsigned int get_writable_buf_cnt(void);
+int get_busy_buffers_count(void);
+int get_pages_count_in_subbuffer(void);
+
+#endif /* __BUFFER_QUEUE_H__ */
--- /dev/null
+/**
+ * @file buffer/data_types.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Declares data types for SWAP buffer.
+ */
+
+#ifndef __DATA_TYPES_H__
+#define __DATA_TYPES_H__
+
+
+#include <linux/spinlock.h>
+
+
+struct page;
+
+/**
+ * @struct sync_t
+ * @brief Using spinlocks as sync primitives.
+ * @var sync_t::spinlock
+ * Spinlock.
+ * @var sync_t::flags
+ * Flags for spinlock.
+ */
+struct sync_t {
+ spinlock_t spinlock;
+ unsigned long flags;
+};
+
+/**
+ * @brief swap_subbuffer_ptr points to the first memory page of the subbuffer.
+ */
+typedef struct page *swap_subbuffer_ptr;
+
+#endif /* __DATA_TYPES_H__ */
--- /dev/null
+/**
+ * @file buffer/kernel_operations.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Kernel functions wrap.
+ */
+
+#ifndef __KERNEL_OPERATIONS_H__
+#define __KERNEL_OPERATIONS_H__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
+
+#include "data_types.h"
+
+
+/* MESSAGES */
+/** Prints debug message.*/
+#define print_debug(msg, args...) \
+ printk(KERN_DEBUG "SWAP_BUFFER DEBUG : " msg, ##args)
+/** Prints info message.*/
+#define print_msg(msg, args...) \
+ printk(KERN_INFO "SWAP_BUFFER : " msg, ##args)
+/** Prints warning message.*/
+#define print_warn(msg, args...) \
+ printk(KERN_WARNING "SWAP_BUFFER WARNING : " msg, ##args)
+/** Prints error message.*/
+#define print_err(msg, args...) \
+ printk(KERN_ERR "SWAP_BUFFER ERROR : " msg, ##args)
+/** Prints critical error message.*/
+#define print_crit(msg, args...) \
+ printk(KERN_CRIT "SWAP_BUFFER CRITICAL : " msg, ##args)
+
+
+/**
+ * @brief struct sync_t initialization.
+ *
+ * @param buffer_sync Target sync primitive.
+ * @return Void.
+ */
+static inline void sync_init(struct sync_t *buffer_sync)
+{
+ spin_lock_init(&buffer_sync->spinlock);
+}
+
+/**
+ * @brief Lock sync_t with saving flags.
+ *
+ * @param buffer_sync Target sync primitive.
+ * @return Void.
+ */
+static inline void sync_lock(struct sync_t *buffer_sync)
+{
+ spin_lock_irqsave(&buffer_sync->spinlock, buffer_sync->flags);
+}
+
+/**
+ * @brief Unlock sync_t with restoring flags.
+ *
+ * @param buffer_sync Target sync primitive.
+ * @return Void.
+ */
+static inline void sync_unlock(struct sync_t *buffer_sync)
+{
+ spin_unlock_irqrestore(&buffer_sync->spinlock, buffer_sync->flags);
+}
+
+/**
+ * @brief Lock sync_t without saving flags.
+ *
+ * @param buffer_sync Target sync primitive.
+ * @return Void.
+ */
+static inline void sync_lock_no_flags(struct sync_t *buffer_sync)
+{
+ spin_lock(&buffer_sync->spinlock);
+}
+
+/**
+ * @brief Unlock sync_t without restoring flags.
+ *
+ * @param buffer_sync Target sync primitive.
+ * @return Void.
+ */
+static inline void sync_unlock_no_flags(struct sync_t *buffer_sync)
+{
+ spin_unlock(&buffer_sync->spinlock);
+}
+
+/**
+ * @brief Disable preemption and irqs.
+ *
+ * @param flags Variable to save flags to.
+ * @return Void.
+ */
+static inline void swap_irq_disable(unsigned long *flags)
+{
+ preempt_disable();
+ local_irq_save(*flags);
+}
+
+/**
+ * @brief Enable preemption and irqs.
+ *
+ * @param flags Variable to restore flags from.
+ * @return Void.
+ */
+static inline void swap_irq_enable(unsigned long *flags)
+{
+ local_irq_restore(*flags);
+ preempt_enable();
+}
+
+/* SWAP SUBBUFER */
+
+
+/* We alloc memory for swap_subbuffer structures with common kmalloc */
+/** Allocates memory for subbuffer structures.*/
+#define memory_allocation(memory_size) kmalloc(memory_size, GFP_KERNEL)
+/** Free subbuffer structures memory.*/
+#define memory_free(ptr) kfree(ptr)
+
+/** For subbuffers themselves, we allocate memory with alloc_pages, so, we have
+ * to evaluate required pages order */
+#define buffer_allocation(memory_size) \
+ alloc_pages(GFP_KERNEL, (pages_order_in_subbuffer >= 0) ? \
+ pages_order_in_subbuffer : \
+ get_order_for_alloc_pages(memory_size))
+
+/** Free buffer's memory.*/
+#define buffer_free(ptr, subbuf_size) \
+ __free_pages(ptr, (pages_order_in_subbuffer >= 0) ? \
+ pages_order_in_subbuffer : \
+ get_order_for_alloc_pages(subbuf_size))
+
+/** Returns buffer address.*/
+#define buffer_address(buffer_ptr) page_address(buffer_ptr)
+/** Sets page order in subbuffer.*/
+#define set_pages_order_in_subbuffer(memory_size) \
+ pages_order_in_subbuffer = get_order_for_alloc_pages(memory_size)
+
+/**
+ * @brief Functions for pages allocation.
+ *
+ * @param number Target number.
+ * @return Power of two.
+ */
+static inline unsigned int nearest_power_of_two(unsigned int number)
+{
+ unsigned int result = 0;
+ unsigned int two_to_the_power = 1;
+
+ /* If aligned_size == PAGE_SIZE we need only one page, so return 0 */
+ if (number == 1)
+ return result;
+
+ while (two_to_the_power < number) {
+ two_to_the_power <<= 1;
+ result++;
+ }
+
+ return result;
+}
+
+/**
+ * @brief Order for alloc pages.
+ *
+ * @param memory_size Wishful memory size.
+ * @return Pages order.
+ */
+static inline unsigned int get_order_for_alloc_pages(size_t memory_size)
+{
+ /* First evaluate remainder of the division memory_size by PAGE_SIZE.
+ * If memory_size is divisible by PAGE_SIZE, then remainder equals 0. */
+ size_t remainder = (memory_size % PAGE_SIZE) ?
+ (memory_size % PAGE_SIZE) : PAGE_SIZE;
+
+ /* Align memory_size to the PAGE_SIZE. aligned_size >= memory_size */
+ size_t aligned_size = memory_size + (PAGE_SIZE - remainder);
+
+ return nearest_power_of_two(aligned_size / PAGE_SIZE);
+}
+
+#endif /* __KERNEL_OPERATIONS_H__ */
--- /dev/null
+/**
+ * @file buffer/swap_buffer_errors.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP Buffer error codes enumeration.
+ */
+
+#ifndef __SWAP_BUFFER_ERRORS_H__
+#define __SWAP_BUFFER_ERRORS_H__
+
+/**
+ * @enum _swap_buffer_errors
+ * @brief SWAP buffer errors enumeration.
+ */
+enum _swap_buffer_errors {
+ /**
+ * @brief Success.
+ */
+ E_SB_SUCCESS = 0,
+ /**
+ * @brief There are some unreleased buffers.
+ * Mainly returned by swap_buffer_uninit.
+ */
+ E_SB_UNRELEASED_BUFFERS = 1,
+ /**
+ * @brief No buffers for writing.
+ */
+ E_SB_NO_WRITABLE_BUFFERS = 2,
+ /**
+ * @brief Wrong data size: size == 0 or size > subbuffer size.
+ */
+ E_SB_WRONG_DATA_SIZE = 3,
+ /**
+ * @brief Trying to write data after SWAP buffer has been stopped.
+ */
+ E_SB_IS_STOPPED = 4,
+ /**
+ * @brief Memory areas of data to be written and subbuffer itself
+ * are overlap.
+ */
+ E_SB_OVERLAP = 5,
+ /**
+ * @brief No buffers for reading.
+ */
+ E_SB_NO_READABLE_BUFFERS = 6,
+ /**
+ * @brief Callback function ptr == NULL.
+ */
+ E_SB_NO_CALLBACK = 7,
+ /**
+ * @brief Memory for queue_busy wasn't allocated.
+ */
+ E_SB_NO_MEM_QUEUE_BUSY = 8,
+ /**
+ * @brief Memory for one of struct swap_buffer wasn't allocated.
+ */
+ E_SB_NO_MEM_BUFFER_STRUCT = 9,
+ /**
+ * @brief Memort for data buffer itself wasn't allocated.
+ */
+ E_SB_NO_MEM_DATA_BUFFER = 10,
+ /**
+ * @brief No such subbuffer in busy_list.
+ */
+ E_SB_NO_SUBBUFFER_IN_BUSY = 11,
+ /**
+ * @brief Subbuffers aren't allocated.
+ */
+ E_SB_NOT_ALLOC = 12,
+ /**
+ * @brief Thresholds > 100, top < lower.
+ */
+ E_SB_WRONG_THRESHOLD = 13
+};
+
+#endif /* __SWAP_BUFFER_ERRORS_H__ */
--- /dev/null
+/**
+ * buffer/swap_buffer_module.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP Buffer interface implementation.
+ */
+
+#include "swap_buffer_module.h"
+#include "buffer_queue.h"
+#include "buffer_description.h"
+#include "swap_buffer_errors.h"
+#include "kernel_operations.h"
+
+/**
+ * @enum _swap_buffer_status_mask
+ * @brief Bitwise mask for buffer status.
+ */
+enum _swap_buffer_status_mask {
+ BUFFER_FREE = 0, /**< 000 - memory free. */
+ BUFFER_ALLOC = 1, /**< 001 - memory allocated. */
+ BUFFER_PAUSE = 2, /**< 010 - buffer overflow. */
+ BUFFER_WORK = 4 /**< @brief 100 - buffer work. */
+};
+
+static unsigned char swap_buffer_status = BUFFER_FREE;
+
+/**
+ * @brief Subbuffer callback type.
+ */
+typedef int(*subbuffer_callback_type)(bool wakeup);
+
+/* Callback that is called when full subbuffer appears */
+static subbuffer_callback_type subbuffer_callback;
+
+/* One subbuffer size */
+static size_t subbuffers_size;
+
+/* Subbuffers count */
+static unsigned int subbuffers_num;
+
+static unsigned int enough_writable_bufs;
+static unsigned int min_writable_bufs;
+static int (*low_mem_cb)(void);
+static int (*enough_mem_cb)(void);
+
+
+static inline int areas_overlap(const void *area1,
+ const void *area2,
+ size_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if ((area1 + i == area2) || (area2 + i == area1))
+ return 1;
+
+ return 0;
+}
+
+static inline unsigned int percent_to_count(unsigned char percent,
+ unsigned int cnt)
+{
+ return (percent * cnt) / 100;
+}
+
+/**
+ * @brief Initializes SWAP buffer and allocates memory.
+ *
+ * @param buf_init Pointer to the buffer_init_t structure which contains
+ * information about subbuffers count, subbuffers size and subbuffer-full-
+ * callback.
+ * @return 0 on success, negative error code otherwise.
+ */
+int swap_buffer_init(struct buffer_init_t *buf_init)
+{
+ int result = -1;
+
+ swap_buffer_status &= ~BUFFER_WORK;
+ print_debug("status buffer stop = %d\n", swap_buffer_status);
+
+ if ((buf_init->top_threshold > 100) ||
+ (buf_init->lower_threshold > 100) ||
+ (buf_init->top_threshold < buf_init->lower_threshold))
+ return -E_SB_WRONG_THRESHOLD;
+
+ min_writable_bufs = percent_to_count(buf_init->lower_threshold,
+ buf_init->nr_subbuffers);
+
+ enough_writable_bufs = percent_to_count(buf_init->top_threshold,
+ buf_init->nr_subbuffers);
+
+ low_mem_cb = buf_init->low_mem_cb;
+ enough_mem_cb = buf_init->enough_mem_cb;
+
+ if ((swap_buffer_status & BUFFER_ALLOC) &&
+ (subbuffers_size == buf_init->subbuffer_size) &&
+ (subbuffers_num == buf_init->nr_subbuffers) &&
+ ((subbuffer_callback_type)subbuffer_callback ==
+ buf_init->subbuffer_full_cb)) {
+ result = buffer_queue_reset();
+ goto swap_buffer_init_work;
+ }
+
+ subbuffer_callback = buf_init->subbuffer_full_cb;
+ subbuffers_size = buf_init->subbuffer_size;
+ subbuffers_num = buf_init->nr_subbuffers;
+
+ result = buffer_queue_allocation(subbuffers_size, subbuffers_num);
+ if (result < 0)
+ return result;
+
+ result = get_pages_count_in_subbuffer();
+
+ swap_buffer_status |= BUFFER_ALLOC;
+ print_debug("status buffer alloc = %d\n", swap_buffer_status);
+
+swap_buffer_init_work:
+ swap_buffer_status |= BUFFER_WORK;
+ print_debug("status buffer work = %d\n", swap_buffer_status);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(swap_buffer_init);
+
+/**
+ * @brief Uninitializes SWAP buffer, releases allocated memory.
+ *
+ * @return 0 on success, negative error code otherwise.
+ */
+int swap_buffer_uninit(void)
+{
+ /* Check whether buffer is allocated */
+ if (!(swap_buffer_status & BUFFER_ALLOC))
+ return -E_SB_NOT_ALLOC;
+
+ /* Stop buffer */
+ swap_buffer_status &= ~BUFFER_WORK;
+ print_debug("status buffer stop = %d\n", swap_buffer_status);
+
+ /* Check whether all buffers are released */
+ if (get_busy_buffers_count())
+ return -E_SB_UNRELEASED_BUFFERS;
+
+ /* Free */
+ buffer_queue_free();
+
+ subbuffer_callback = NULL;
+ subbuffers_size = 0;
+ subbuffers_num = 0;
+ min_writable_bufs = 0;
+ enough_writable_bufs = 0;
+ low_mem_cb = NULL;
+ enough_mem_cb = NULL;
+
+ swap_buffer_status &= ~BUFFER_ALLOC;
+ print_debug("status buffer dealloc = %d\n", swap_buffer_status);
+
+ return E_SB_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(swap_buffer_uninit);
+
+/**
+ * @brief Writes data to SWAP buffer.
+ *
+ * @param data Pointer to a data for writing.
+ * @param size Size of a data for writing.
+ * @return Size of written data on success, negative error code otherwise.
+ */
+ssize_t swap_buffer_write(void *data, size_t size, bool wakeup)
+{
+ int result = E_SB_SUCCESS;
+ struct swap_subbuffer *buffer_to_write = NULL;
+ void *ptr_to_write = NULL;
+ unsigned long flags = 0;
+
+ /* Size sanitization */
+ if ((size > subbuffers_size) || (size == 0))
+ return -E_SB_WRONG_DATA_SIZE;
+
+ /* Check buffer status */
+ if (!(swap_buffer_status & BUFFER_WORK))
+ return -E_SB_IS_STOPPED;
+
+ /* We're going to look for writable buffer, so disable irqs */
+ swap_irq_disable(&flags);
+
+ /* Get next write buffer and occupying semaphore */
+ buffer_to_write = get_from_write_list(size, &ptr_to_write, wakeup);
+ if (!buffer_to_write) {
+ swap_irq_enable(&flags);
+ return -E_SB_NO_WRITABLE_BUFFERS;
+ }
+
+ /* Check for overlapping */
+ if (areas_overlap(ptr_to_write, data, size)) {
+ result = -E_SB_OVERLAP;
+ goto buf_write_sem_post;
+ }
+
+ /* Copy data to buffer */
+ /* XXX Think of using memmove instead - useless, anyway overlapping
+ * means that something went wrong. */
+ memcpy(ptr_to_write, data, size);
+
+ result = size;
+
+ if ((get_writable_buf_cnt() < min_writable_bufs) &&
+ !(swap_buffer_status & BUFFER_PAUSE)) {
+ swap_buffer_status |= BUFFER_PAUSE;
+ if (low_mem_cb != NULL)
+ low_mem_cb();
+ }
+
+ /* Unlock sync (Locked in get_from_write_list()) and enable irqs */
+buf_write_sem_post:
+ sync_unlock_no_flags(&buffer_to_write->buffer_sync);
+ swap_irq_enable(&flags);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(swap_buffer_write);
+
+/**
+ * @brief Gets pointer to subbuffer for reading.
+ *
+ * @param[out] subbuffer Pointer to a variable which points on target subbuffer.
+ * @return 0 on success, negative error code otherwise.
+ */
+int swap_buffer_get(struct swap_subbuffer **subbuffer)
+{
+ int result = 0;
+ struct swap_subbuffer *buffer_to_read = NULL;
+
+ /* Check buffer status */
+ if (!(swap_buffer_status & BUFFER_WORK))
+ return -E_SB_IS_STOPPED;
+
+ /* Get next read buffer */
+ buffer_to_read = get_from_read_list();
+ if (!buffer_to_read)
+ return -E_SB_NO_READABLE_BUFFERS;
+
+ /* Add to busy list */
+ buffer_to_read->next_in_queue = NULL;
+ add_to_busy_list(buffer_to_read);
+
+ *subbuffer = buffer_to_read;
+
+ result = get_pages_count_in_subbuffer();
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(swap_buffer_get);
+
+/**
+ * @brief Releases subbuffer after reading.
+ *
+ * @param subbuffer Subbuffer that should be released.
+ * @return 0 on success, negative error code otherwise.
+ */
+int swap_buffer_release(struct swap_subbuffer **subbuffer)
+{
+ int result;
+
+ /* Remove from busy list (includes sanitization) */
+ result = remove_from_busy_list(*subbuffer);
+ if (result < 0)
+ return result;
+
+ /* Add to write list */
+ add_to_write_list(*subbuffer);
+
+ if ((swap_buffer_status & BUFFER_PAUSE) &&
+ (get_writable_buf_cnt() >= enough_writable_bufs)) {
+ swap_buffer_status &= ~BUFFER_PAUSE;
+ if (enough_mem_cb != NULL)
+ enough_mem_cb();
+ }
+
+ return E_SB_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(swap_buffer_release);
+
+/**
+ * @brief Sets all subbuffers for reading.
+ *
+ * @return Count of subbeffers for reading.
+ */
+unsigned int swap_buffer_flush(void)
+{
+ unsigned int result;
+
+ /* Set all non-empty write buffers to read list */
+ buffer_queue_flush();
+
+ /* Get count of all full buffers */
+ result = get_readable_buf_cnt();
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(swap_buffer_flush);
+
+/**
+ * @brief Executes subbuffer-full-callback.
+ *
+ * @param buffer Pointer to the full subbuffer.
+ * @return -E_SB_NO_CALLBACK if no callback is registered or callbacks ret
+ * value otherwise.
+ */
+int swap_buffer_callback(void *buffer, bool wakeup)
+{
+ int result;
+
+ if (!subbuffer_callback)
+ return -E_SB_NO_CALLBACK;
+
+ result = subbuffer_callback(wakeup);
+ if (result < 0)
+ print_err("Callback error! Error code: %d\n", result);
+
+ return result;
+}
+
+static int __init swap_buffer_module_init(void)
+{
+ printk(KERN_NOTICE "SWAP_BUFFER : Buffer module initialized\n");
+ return E_SB_SUCCESS;
+}
+
+static void __exit swap_buffer_module_exit(void)
+{
+ if (swap_buffer_status & BUFFER_ALLOC)
+ swap_buffer_uninit();
+ printk(KERN_NOTICE "SWAP_BUFFER : Buffer module unintialized\n");
+}
+
+module_init(swap_buffer_module_init);
+module_exit(swap_buffer_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP buffer module");
+MODULE_AUTHOR("Aksenov A.S.");
--- /dev/null
+/**
+ * @file buffer/swap_buffer_module.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP Buffer interface declaration.
+ */
+
+#ifndef __SWAP_BUFFER_MODULE_H__
+#define __SWAP_BUFFER_MODULE_H__
+
+#include <linux/types.h>
+
+struct swap_subbuffer;
+
+/**
+ * @struct buffer_init_t
+ * @brief Buffer init structure. Contains information necessary for SWAP buffer
+ * initialization.
+ * @var buffer_init_t::subbuffer_size
+ * Subbuffer size.
+ * @var buffer_init_t::nr_subbuffers
+ * Subbuffers count.
+ * @var buffer_init_t::subbuffer_full_cb
+ * Callback. Called when one of subbuffers is full.
+ * @var buffer_init_t::lower_threshold
+ * Lower threshold in percent. When buffers fall below this limit
+ * low_mem_cb is called and swap_buffer is suspended.
+ * @var buffer_init_t::low_mem_cb
+ * Callback that is called when count of free subbuffers falls below
+ * lower_threshold.
+ * @var buffer_init_t::top_threshold
+ * Top threshold in percent. When buffers exceed this limit
+ * enough_mem_cb is called.
+ * @var buffer_init_t::enough_mem_cb
+ * Callback that is called when count of free subbuffers exceeds top_threshold.
+ */
+
+struct buffer_init_t {
+ size_t subbuffer_size;
+ unsigned int nr_subbuffers;
+ int (*subbuffer_full_cb)(bool wakeup);
+
+ unsigned char lower_threshold;
+ int (*low_mem_cb)(void);
+
+ unsigned char top_threshold;
+ int (*enough_mem_cb)(void);
+};
+
+/* SWAP Buffer initialization function. Call it before using buffer.
+ * Returns memory pages count (>0) in one subbuffer on success, or error code
+ * (<0) otherwise. */
+int swap_buffer_init(struct buffer_init_t *buf_init);
+
+/* SWAP Buffer uninitialization function. Call it every time before removing
+ * this module.
+ * Returns E_SB_SUCCESS (0) on success, otherwise error code. */
+int swap_buffer_uninit(void);
+
+/* SWAP Buffer write function. Pass it size of the data and pointer to the data.
+ * On success returns number of bytes written (>=0) or error code (<0)
+ * otherwise */
+ssize_t swap_buffer_write(void *data, size_t size, bool wakeup);
+
+/* SWAP Buffer get. Put subbuffer pointer to the variable *subbuffer.
+ * Return pages count in subbuffer. */
+int swap_buffer_get(struct swap_subbuffer **subbuffer);
+
+/* SWAP Buffer release. All 'get' buffers must be released with this function.
+ * Just pass &subbuffer_ptr to it */
+int swap_buffer_release(struct swap_subbuffer **subbuffer);
+
+/* SWAP Buffer flush. Puts all buffers to read queue and returns their count. */
+unsigned int swap_buffer_flush(void);
+
+#endif /* __SWAP_BUFFER_MODULE_H__ */
--- /dev/null
+/**
+ * @file buffer/swap_buffer_to_buffer_queue.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ * SWAP Buffer interface for buffer queue.
+ */
+
+#ifndef __SWAP_BUFFER_TO_BUFFER_QUEUE_H__
+#define __SWAP_BUFFER_TO_BUFFER_QUEUE_H__
+
+#include <linux/types.h>
+
+int swap_buffer_callback(void *buffer, bool wakeup);
+
+#endif /* __SWAP_BUFFER_TO_BUFFER_QUEUE_H__ */
--- /dev/null
+# Config for build.sh
+
+kernel=<path-to-kernel-sourse>
+toolchain=<path-to-toolchain>
+arch=<architecture>
--- /dev/null
+#!/usr/bin/env bash
+# NOTE: This requires GNU getopt. On Mac OS X and FreeBSD, you have to install
+# this separately; see below.
+
+IDENT=${0}
+
+show_usage_and_exit () {
+ echo -e "Usage: ${IDENT} <options> <compile|check|clean>"
+ echo -e "\tOptions:"
+ echo -e "\t--verbose"
+ echo -e "\t--file <config file>"
+# echo -e "\t--debug"
+ echo -e "\t--kernel <kernel path>"
+ echo -e "\t--arch <arm|arm64|i386>"
+ echo -e "\t[--toolchain <cross compile path>]"
+ exit 1
+}
+
+TEMP=`getopt -o vk:t:a:f: --long verbose,kernel:,toolchain:,arch:,file: \
+ -n '${IDENT}' -- "$@"`
+
+if [ $? != 0 ] ; then
+ show_usage_and_exit
+fi
+
+# Note the quotes around `$TEMP': they are essential!
+eval set -- "$TEMP"
+
+MDIR=`pwd`
+VERBOSE=false
+CONFIG_FILE="build.config"
+KERNELDIR=""
+TOOLCHAIN=""
+ARCH=""
+
+while true; do
+ case "$1" in
+ -v | --verbose ) VERBOSE=true; shift ;;
+ -k | --kernel ) KERNELDIR="$2"; shift 2 ;;
+ -t | --toolchain ) TOOLCHAIN="$2"; shift 2;;
+ -a | --arch ) ARCH="$2"; shift 2;;
+ -f | --file ) CONFIG_FILE="$2"; shift 2;;
+ -- ) shift; break ;;
+ * ) break ;;
+ esac
+done
+
+if [ "${1}" != "compile" -a "${1}" != "clean" -a "${1}" != "check" ] ; then
+ ACTION="compile"
+ ARCH=${2}
+ KERNELDIR=${1}
+ if [ "${3}" != "" ] ; then
+ TOOLCHAIN=${3}
+ fi
+else
+ if [ -r ${CONFIG_FILE} ]; then
+ . ${CONFIG_FILE}
+ KERNELDIR=${kernel}
+ TOOLCHAIN=${toolchain}
+ ARCH=$arch
+ fi
+ ACTION="${1}"
+fi
+
+if [ "${KERNELDIR}" = "" ] ; then
+ show_usage_and_exit
+fi
+
+if [ "${ARCH}" = "arm" ] ; then
+ LINKNAME="arm"
+elif [ "${ARCH}" = "arm64" ] ; then
+ LINKNAME="arm64"
+elif [ "${ARCH}" = "i386" ] ; then
+ LINKNAME="x86"
+else
+ show_usage_and_exit
+fi
+
+MCFLAGS="-Werror"
+
+CMDLINE_ARGS=""
+CMDLINE_ARGS="CROSS_COMPILE=${TOOLCHAIN} ARCH=${ARCH} -C ${KERNELDIR}"
+CMDLINE_ARGS="${CMDLINE_ARGS} M=${MDIR} MCFLAGS=${MCFLAGS} LINKNAME=${LINKNAME}"
+
+if [ "${ACTION}" = "check" ] ; then
+ CMDLINE="make C=2 CF=\"-Wsparse-all\" ${CMDLINE_ARGS} modules"
+elif [ "${ACTION}" = "clean" ] ; then
+ CMDLINE="make ${CMDLINE_ARGS} clean"
+else
+ CMDLINE="make ${CMDLINE_ARGS} modules"
+fi
+
+if [ ${VERBOSE} = "true" ] ; then
+ CMDLINE="${CMDLINE} V=1"
+fi
+
+#echo -n "CMDLINE ${CMDLINE}\n"
+
+${CMDLINE} || exit 1
+
+# On arm64 gbs compiler fails on stripping arm64. This workaround is made to
+# make it buildable until the bug is fixed.
+if [ "${ARCH}" = "arm64" ] ; then
+ for i in */*.ko; do
+ echo "Stripping $i..."
+ strip -x -g $i
+ done
+fi
+
+exit 0
+
--- /dev/null
+#!/bin/bash
+
+model=$(sed -n "s|.*tizen.org/system/model_name[^>]*>\([^<]*\)<.*|\1|p" /etc/config/model-config.xml)
+modpath=/opt/swap/sdk
+
+case $model in
+# armv7l
+ "TM1")
+ mv $modpath/tm1_swap_modules/* $modpath
+ rm -r $modpath/tm1_swap_modules
+ rm -rf $modpath/odroid_swap_modules
+ rm -rf $modpath/tw1_swap_modules
+ ;;
+ "xu3")
+ mv $modpath/odroid_swap_modules/* $modpath
+ rm -r $modpath/odroid_swap_modules
+ rm -rf $modpath/tm1_swap_modules
+ rm -rf $modpath/tw1_swap_modules
+ ;;
+ "TW1")
+ mv $modpath/tw1_swap_modules/* $modpath
+ rm -r $modpath/tw1_swap_modules
+ rm -rf $modpath/odroid_swap_modules
+ rm -rf $modpath/tm1_swap_modules
+ ;;
+# x86
+ "Emulator")
+ mv $modpath/emul_swap_modules/* $modpath
+ rm -r $modpath/emul_swap_modules
+ ;;
+# aarch64
+ "TM2")
+ mv $modpath/tm2_swap_modules/* $modpath
+ rm -r $modpath/tm2_swap_modules
+ rm -rf $modpath/rpi3_swap_modules
+ rm -rf $modpath/tw2_swap_modules
+ ;;
+ "TW2")
+ mv $modpath/tw2_swap_modules/* $modpath
+ rm -r $modpath/tw2_swap_modules
+ rm -rf $modpath/rpi3_swap_modules
+ rm -rf $modpath/tm2_swap_modules
+ ;;
+ "rpi3")
+ mv $modpath/rpi3_swap_modules/* $modpath
+ rm -r $modpath/rpi3_swap_modules
+ rm -rf $modpath/tw2_swap_modules
+ rm -rf $modpath/tm2_swap_modules
+ ;;
+ *)
+ echo "Device isn't supported" > /tmp/swap-modules_install.log
+ ;;
+esac
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../buffer/Module.symvers
+
+obj-m := swap_driver.o
+swap_driver-y := swap_driver_module.o \
+ device_driver.o \
+ driver_to_buffer.o \
+ driver_debugfs.o
+
+ifeq ($(CONFIG_CONNECTOR),y)
+ swap_driver-y += us_interaction.o
+endif
--- /dev/null
+/**
+ * @file driver/app_manage.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * Driver user <-> kernel connect implement.
+ */
+
+#ifndef __APP_MANAGE_H__
+#define __APP_MANAGE_H__
+
+#include "us_interaction.h"
+#include "us_interaction_msg.h"
+
+/**
+ * @brief Sends pause message to kernel.
+ *
+ * @return us_interaction_send_msg result.
+ */
+static inline int app_manage_pause_apps(void)
+{
+ enum us_interaction_k2u_msg_t us_int_msg = US_INT_PAUSE_APPS;
+
+ return us_interaction_send_msg(&us_int_msg, sizeof(us_int_msg));
+}
+
+/**
+ * @brief Sends continue message to kernel.
+ *
+ * @return us_interaction_send_msg result.
+ */
+static inline int app_manage_cont_apps(void)
+{
+ enum us_interaction_k2u_msg_t us_int_msg = US_INT_CONT_APPS;
+
+ return us_interaction_send_msg(&us_int_msg, sizeof(us_int_msg));
+}
+
+#endif /* __APP_MANAGE_H__ */
--- /dev/null
+/**
+ * driver/device_driver.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Provides SWAP device.
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/splice.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#include <ksyms/ksyms.h>
+#include <master/swap_initializer.h>
+
+#include "device_driver.h"
+#include "swap_driver_errors.h"
+#include "driver_to_buffer.h"
+#include "swap_ioctl.h"
+#include "driver_defs.h"
+#include "device_driver_to_driver_to_buffer.h"
+#include "driver_to_buffer.h"
+#include "driver_to_msg.h"
+
+/** SWAP device name as it is in /dev/. */
+#define SWAP_DEVICE_NAME "swap_device"
+
+/* swap_device driver routines */
+static ssize_t swap_device_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos);
+static long swap_device_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg);
+static ssize_t swap_device_splice_read(struct file *filp, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags);
+
+#ifdef CONFIG_COMPAT
+static long swap_compat_device_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd & ~IOCSIZE_MASK) {
+ case SWAP_DRIVER_BUFFER_INITIALIZE & ~IOCSIZE_MASK:
+ case SWAP_DRIVER_MSG & ~IOCSIZE_MASK:
+ /* Fix up pointer size (usually 4 -> 8 in 32-on-64-bit case) */
+ if (_IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
+ cmd &= ~IOCSIZE_MASK;
+ cmd |= sizeof(void *) << IOCSIZE_SHIFT;
+ }
+ break;
+ }
+
+ return swap_device_ioctl(filp, cmd, arg);
+}
+#else /* CONFIG_COMPAT */
+#define swap_compat_device_ioctl NULL
+#endif /* CONFIG_COMPAT */
+
+
+/**
+ * @var swap_device_fops
+ * @brief SWAP device file operations.
+ */
+const struct file_operations swap_device_fops = {
+ .owner = THIS_MODULE,
+ .read = swap_device_read,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .unlocked_ioctl = swap_device_ioctl,
+ .compat_ioctl = swap_compat_device_ioctl,
+ .splice_read = swap_device_splice_read,
+};
+
+/* Typedefs for splice_* funcs. Prototypes are for linux-3.8.6 */
+/** Splice to pipe pointer type. */
+typedef ssize_t(*splice_to_pipe_p_t)(struct pipe_inode_info *pipe,
+ struct splice_pipe_desc *spd);
+/** Splice grow spd pointer type. */
+typedef int(*splice_grow_spd_p_t)(const struct pipe_inode_info *pipe,
+ struct splice_pipe_desc *spd);
+
+static splice_to_pipe_p_t splice_to_pipe_p;
+static splice_grow_spd_p_t splice_grow_spd_p;
+
+/* Device numbers */
+static dev_t swap_device_no;
+
+/* Device cdev struct */
+static struct cdev swap_device_cdev;
+
+/* Device class struct */
+static struct class *swap_device_class;
+
+/* Device device struct */
+static struct device *swap_device_device;
+
+/* Reading tasks queue */
+static DECLARE_WAIT_QUEUE_HEAD(swap_device_wait);
+
+
+static atomic_t flag_wake_up = ATOMIC_INIT(0);
+
+static void __bottom_wake_up(void)
+{
+ if (waitqueue_active(&swap_device_wait))
+ wake_up_interruptible(&swap_device_wait);
+}
+
+static void bottom_wake_up(struct work_struct *work)
+{
+ if (atomic_read(&flag_wake_up)) {
+ atomic_set(&flag_wake_up, 0);
+ __bottom_wake_up();
+ }
+}
+
+static DECLARE_WORK(w_wake_up, bottom_wake_up);
+
+static void exit_w_wake_up(void)
+{
+ flush_scheduled_work();
+ __bottom_wake_up();
+}
+
+
+/*
+ * Driver message handler
+ */
+static DECLARE_RWSEM(dmsg_handler_sem);
+static struct driver_msg_handler *dmsg_handler;
+
+static int driver_msg_handler_call(void __user *data)
+{
+ int ret;
+
+ down_read(&dmsg_handler_sem);
+ if (dmsg_handler) {
+ ret = dmsg_handler->handler(data);
+ } else {
+ print_warn("dmsg_handler() is not register\n");
+ ret = -EINVAL;
+ }
+ up_read(&dmsg_handler_sem);
+
+ return ret;
+}
+
+/**
+ * @brief Register message handler.
+ *
+ * @param msg_handler Pointer to message handler.
+ * @return Void.
+ */
+void driver_msg_handler_set(struct driver_msg_handler *msg_handler)
+{
+ down_write(&dmsg_handler_sem);
+ /* unregister dmsg_handler */
+ if (dmsg_handler) {
+ module_put(dmsg_handler->mod);
+ dmsg_handler = NULL;
+ }
+
+ /* register dmsg_handler */
+ if (msg_handler) {
+ BUG_ON(!try_module_get(msg_handler->mod));
+ dmsg_handler = msg_handler;
+ }
+ up_write(&dmsg_handler_sem);
+}
+EXPORT_SYMBOL_GPL(driver_msg_handler_set);
+
+
+/**
+ * @brief We need this realization of splice_shrink_spd() because its desing
+ * frequently changes in custom kernels.
+ *
+ * @param pipe Pointer to the pipe whereto splice data.
+ * @param spd Pointer to the splice_pipe_desc structure.
+ * @return Void.
+ */
+void swap_device_splice_shrink_spd(struct pipe_inode_info *pipe,
+ struct splice_pipe_desc *spd)
+{
+ if (pipe->buffers <= PIPE_DEF_BUFFERS)
+ return;
+
+ kfree(spd->pages);
+ kfree(spd->partial);
+}
+
+
+/* TODO Think of permanent major */
+
+/**
+ * @brief Register device.
+ *
+ * @return 0 on success, negative error code otherwise.
+ */
+int swap_device_init(void)
+{
+ int result;
+
+ /* Allocating device major and minor nums for swap_device */
+ result = alloc_chrdev_region(&swap_device_no, 0, 1, SWAP_DEVICE_NAME);
+ if (result < 0) {
+ print_crit("Major number allocation has failed\n");
+ result = -E_SD_ALLOC_CHRDEV_FAIL;
+ goto init_fail;
+ }
+
+ /* Creating device class. Using IS_ERR, because class_create
+ * returns ERR_PTR on error. */
+ swap_device_class = class_create(THIS_MODULE, SWAP_DEVICE_NAME);
+ if (IS_ERR(swap_device_class)) {
+ print_crit("Class creation has failed\n");
+ result = -E_SD_CLASS_CREATE_FAIL;
+ goto init_fail;
+ }
+
+ /* Cdev intialization and setting file operations */
+ cdev_init(&swap_device_cdev, &swap_device_fops);
+
+ /* Adding cdev to system */
+ result = cdev_add(&swap_device_cdev, swap_device_no, 1);
+ if (result < 0) {
+ print_crit("Device adding has failed\n");
+ result = -E_SD_CDEV_ADD_FAIL;
+ goto init_fail;
+ }
+
+ /* Create device struct */
+ swap_device_device = device_create(swap_device_class, NULL,
+ swap_device_no,
+ "%s", SWAP_DEVICE_NAME);
+ if (IS_ERR(swap_device_device)) {
+ print_crit("Device struct creating has failed\n");
+ result = -E_SD_DEVICE_CREATE_FAIL;
+ goto init_fail;
+ }
+
+ /* Find splice_* funcs addresses */
+ splice_to_pipe_p = (splice_to_pipe_p_t)swap_ksyms("splice_to_pipe");
+ if (!splice_to_pipe_p) {
+ print_err("splice_to_pipe() not found!\n");
+ result = -E_SD_NO_SPLICE_FUNCS;
+ goto init_fail;
+ }
+
+ splice_grow_spd_p = (splice_grow_spd_p_t)swap_ksyms("splice_grow_spd");
+ if (!splice_grow_spd_p) {
+ print_err("splice_grow_spd() not found!\n");
+ result = -E_SD_NO_SPLICE_FUNCS;
+ goto init_fail;
+ }
+
+ return 0;
+
+init_fail:
+ cdev_del(&swap_device_cdev);
+ if (swap_device_class)
+ class_destroy(swap_device_class);
+ if (swap_device_no)
+ unregister_chrdev_region(swap_device_no, 1);
+ return result;
+}
+
+/* TODO Check wether driver is registered */
+
+/**
+ * @brief Unregister device.
+ *
+ * @return Void.
+ */
+void swap_device_exit(void)
+{
+ exit_w_wake_up();
+
+ splice_to_pipe_p = NULL;
+ splice_grow_spd_p = NULL;
+
+ device_destroy(swap_device_class, swap_device_no);
+ cdev_del(&swap_device_cdev);
+ class_destroy(swap_device_class);
+ unregister_chrdev_region(swap_device_no, 1);
+}
+
+static ssize_t swap_device_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ /* Wait queue item that consists current task. It is used to be added in
+ * swap_device_wait queue if there is no data to be read. */
+ DEFINE_WAIT(wait);
+ int result;
+
+ /* TODO : Think about spin_locks to prevent reading race condition. */
+ while ((result =
+ driver_to_buffer_next_buffer_to_read()) != E_SD_SUCCESS) {
+
+ /* Add process to the swap_device_wait queue and set the current
+ * task state TASK_INTERRUPTIBLE. If there is any data to be
+ * read, then the current task is removed from the
+ * swap_device_wait queue and its state is changed to this. */
+ prepare_to_wait(&swap_device_wait, &wait, TASK_INTERRUPTIBLE);
+
+ if (result < 0) {
+ result = 0;
+ goto swap_device_read_error;
+ } else if (result == E_SD_NO_DATA_TO_READ) {
+ /* Yes, E_SD_NO_DATA_TO_READ should be positive,
+ * cause it's not really an error */
+ if (filp->f_flags & O_NONBLOCK) {
+ result = -EAGAIN;
+ goto swap_device_read_error;
+ }
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ goto swap_device_read_error;
+ }
+ schedule();
+ finish_wait(&swap_device_wait, &wait);
+ }
+ }
+
+ result = driver_to_buffer_read(buf, count);
+ /* If there is an error - return 0 */
+ if (result < 0)
+ result = 0;
+
+
+ return result;
+
+swap_device_read_error:
+ finish_wait(&swap_device_wait, &wait);
+
+ return result;
+}
+
+static long swap_device_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int result;
+
+ switch (cmd) {
+ case SWAP_DRIVER_BUFFER_INITIALIZE:
+ {
+ struct buffer_initialize initialize_struct;
+
+ result = copy_from_user(&initialize_struct, (void *)arg,
+ sizeof(struct buffer_initialize));
+ if (result)
+ break;
+
+ result = driver_to_buffer_set_size(initialize_struct.size);
+ if (result < 0) {
+ print_err("Wrong subbuffer size=%u\n",
+ initialize_struct.size);
+ break;
+ }
+
+ result = driver_to_buffer_set_count(initialize_struct.count);
+ if (result < 0) {
+ print_err("Wrong subbuffer count=%u\n",
+ initialize_struct.count);
+ break;
+ }
+
+ result = driver_to_buffer_initialize();
+ if (result < 0) {
+ print_err("Buffer initialization failed %d\n", result);
+ break;
+ }
+ result = E_SD_SUCCESS;
+
+ break;
+ }
+ case SWAP_DRIVER_BUFFER_UNINITIALIZE:
+ {
+ result = driver_to_buffer_uninitialize();
+ if (result < 0)
+ print_err("Buffer uninitialization failed %d\n",
+ result);
+ break;
+ }
+ case SWAP_DRIVER_NEXT_BUFFER_TO_READ:
+ {
+ /* Use this carefully */
+ result = driver_to_buffer_next_buffer_to_read();
+ if (result == E_SD_NO_DATA_TO_READ) {
+ /* TODO Do what we usually do when there are no
+ * subbuffers to read (make daemon sleep ?) */
+ }
+ break;
+ }
+ case SWAP_DRIVER_FLUSH_BUFFER:
+ {
+ result = driver_to_buffer_flush();
+ break;
+ }
+ case SWAP_DRIVER_MSG:
+ {
+ result = driver_msg_handler_call((void __user *)arg);
+ break;
+ }
+ case SWAP_DRIVER_WAKE_UP:
+ {
+ swap_device_wake_up_process();
+ result = E_SD_SUCCESS;
+ break;
+ }
+ default:
+ print_warn("Unknown command %d\n", cmd);
+ result = -EINVAL;
+ break;
+
+ }
+ return result;
+}
+
+static void swap_device_pipe_buf_release(struct pipe_inode_info *inode,
+ struct pipe_buffer *pipe)
+{
+ __free_page(pipe->page);
+}
+
+static void swap_device_page_release(struct splice_pipe_desc *spd,
+ unsigned int i)
+{
+ __free_page(spd->pages[i]);
+}
+
+static const struct pipe_buf_operations swap_device_pipe_buf_ops = {
+ .can_merge = 0,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0)
+ .map = generic_pipe_buf_map,
+ .unmap = generic_pipe_buf_unmap,
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0) */
+ .confirm = generic_pipe_buf_confirm,
+ .release = swap_device_pipe_buf_release,
+ .steal = generic_pipe_buf_steal,
+ .get = generic_pipe_buf_get
+};
+
+static ssize_t swap_device_splice_read(struct file *filp, loff_t *ppos,
+ struct pipe_inode_info *pipe,
+ size_t len, unsigned int flags)
+{
+ /* Wait queue item that consists current task. It is used to be added in
+ * swap_device_wait queue if there is no data to be read. */
+ DEFINE_WAIT(wait);
+
+ int result;
+ struct page *pages[PIPE_DEF_BUFFERS];
+ struct partial_page partial[PIPE_DEF_BUFFERS];
+ struct splice_pipe_desc spd = {
+ .pages = pages,
+ .partial = partial,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 5))
+ .nr_pages_max = PIPE_DEF_BUFFERS,
+#endif
+ .nr_pages = 0,
+ .flags = flags,
+ .ops = &swap_device_pipe_buf_ops,
+ .spd_release = swap_device_page_release,
+ };
+
+ /* Get next buffer to read */
+ /* TODO : Think about spin_locks to prevent reading race condition.*/
+ while ((result =
+ driver_to_buffer_next_buffer_to_read()) != E_SD_SUCCESS) {
+
+ /* Add process to the swap_device_wait queue and set the current
+ * task state TASK_INTERRUPTIBLE. If there is any data to be
+ * read, then the current task is removed from the
+ * swap_device_wait queue and its state is changed. */
+ prepare_to_wait(&swap_device_wait, &wait, TASK_INTERRUPTIBLE);
+ if (result < 0) {
+ print_err("driver_to_buffer_next_buffer_to_read error "
+ "%d\n", result);
+ /* TODO Error return to OS */
+ result = 0;
+ goto swap_device_splice_read_error;
+ } else if (result == E_SD_NO_DATA_TO_READ) {
+ if (filp->f_flags & O_NONBLOCK) {
+ result = -EAGAIN;
+ goto swap_device_splice_read_error;
+ }
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ goto swap_device_splice_read_error;
+ }
+ schedule();
+ finish_wait(&swap_device_wait, &wait);
+ }
+ }
+
+ if (splice_grow_spd_p(pipe, &spd)) {
+ result = -ENOMEM;
+ goto swap_device_splice_read_out;
+ }
+
+ result = driver_to_buffer_fill_spd(&spd);
+ if (result != 0) {
+ print_err("Cannot fill spd for splice\n");
+ goto swap_device_shrink_spd;
+ }
+
+ result = splice_to_pipe_p(pipe, &spd);
+
+swap_device_shrink_spd:
+ swap_device_splice_shrink_spd(pipe, &spd);
+
+swap_device_splice_read_out:
+ return result;
+
+swap_device_splice_read_error:
+ finish_wait(&swap_device_wait, &wait);
+
+ return result;
+}
+
+/**
+ * @brief Wakes up daemon that splicing data from driver.
+ *
+ * @return Void.
+ */
+void swap_device_wake_up_process(void)
+{
+ if (atomic_read(&flag_wake_up) == 0) {
+ atomic_set(&flag_wake_up, 1);
+ schedule_work(&w_wake_up);
+ }
+}
--- /dev/null
+/**
+ * @file driver/device_driver.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP device driver interface declaration.
+ */
+
+#ifndef __SWAP_DRIVER_DEVICE_DRIVER_H__
+#define __SWAP_DRIVER_DEVICE_DRIVER_H__
+
+/* Create and register device */
+int swap_device_init(void);
+
+/* Delete device */
+void swap_device_exit(void);
+
+#endif /* __SWAP_DRIVER_DEVICE_DRIVER_H__ */
--- /dev/null
+/**
+ * @file device_driver_to_driver_to_buffer.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ * SWAP device interface for driver_to_buffer.
+ */
+
+
+#ifndef __DEVICE_DRIVER_TO_DRIVER_TO_BUFFER_H__
+#define __DEVICE_DRIVER_TO_DRIVER_TO_BUFFER_H__
+
+void swap_device_wake_up_process(void);
+
+#endif /* __DEVICE_DRIVER_TO_DRIVER_TO_BUFFER_H__ */
--- /dev/null
+/**
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP debugfs interface definition.
+ */
+
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/debugfs.h>
+#include <master/swap_debugfs.h>
+#include "driver_to_buffer.h"
+#include "swap_driver_errors.h"
+
+
+#define WRITER_DBG_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
+
+
+static int buf_enabled_set(u64 val)
+{
+ int ret = -EINVAL;
+
+ switch (val) {
+ case 0:
+ ret = driver_to_buffer_uninitialize();
+ break;
+ case 1:
+ ret = driver_to_buffer_initialize();
+ break;
+ }
+
+ return ret;
+}
+
+static u64 buf_enabled_get(void)
+{
+ return driver_to_buffer_enabled();
+}
+
+static struct dfs_setget_64 dfs_enabled = {
+ .set = buf_enabled_set,
+ .get = buf_enabled_get,
+};
+
+static int subbuf_size_set(u64 val)
+{
+
+ if (driver_to_buffer_set_size(val) != E_SD_SUCCESS)
+ return -EINVAL;
+
+ return 0;
+}
+
+static u64 subbuf_size_get(void)
+{
+ return driver_to_buffer_get_size();
+}
+
+static struct dfs_setget_64 dfs_subbuf_size = {
+ .set = subbuf_size_set,
+ .get = subbuf_size_get,
+};
+
+static int subbuf_count_set(u64 val)
+{
+ if (driver_to_buffer_set_count(val) != E_SD_SUCCESS)
+ return -EINVAL;
+
+ return 0;
+}
+
+static u64 subbuf_count_get(void)
+{
+ return driver_to_buffer_get_count();
+}
+
+static struct dfs_setget_64 dfs_subbuf_count = {
+ .set = subbuf_count_set,
+ .get = subbuf_count_get,
+};
+
+
+struct dbgfs_data {
+ const char *name;
+ struct dfs_setget_64 *setget;
+};
+
+static struct dbgfs_data dbgfs[] = {
+ {
+ .name = "buffer_enabled",
+ .setget = &dfs_enabled,
+ }, {
+ .name = "subbuf_size",
+ .setget = &dfs_subbuf_size,
+ }, {
+ .name = "subbuf_conunt",
+ .setget = &dfs_subbuf_count,
+ }
+};
+
+
+static struct dentry *driver_dir;
+
+void driver_debugfs_uninit(void)
+{
+ debugfs_remove_recursive(driver_dir);
+ driver_dir = NULL;
+}
+
+int driver_debugfs_init(void)
+{
+ int i;
+ struct dentry *swap_dir, *dentry;
+
+ swap_dir = swap_debugfs_getdir();
+ if (swap_dir == NULL)
+ return -ENOENT;
+
+ driver_dir = swap_debugfs_create_dir("driver", swap_dir);
+ if (driver_dir == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dbgfs); ++i) {
+ struct dbgfs_data *data = &dbgfs[i];
+ dentry = swap_debugfs_create_setget_u64(data->name,
+ WRITER_DBG_PERMS,
+ driver_dir,
+ data->setget);
+ if (!dentry)
+ goto fail;
+ }
+
+ return 0;
+fail:
+ driver_debugfs_uninit();
+ return -ENOMEM;
+}
--- /dev/null
+/**
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP debugfs interface definition.
+ */
+
+#ifndef DRIVER_DEBUGFS_H
+#define DRIVER_DEBUGFS_H
+
+
+int driver_debugfs_init(void);
+void driver_debugfs_uninit(void);
+
+#endif // DRIVER_DEBUGFS_H
--- /dev/null
+/**
+ * @file driver/driver_defs.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ * Device driver defs.
+ */
+
+#ifndef __SWAP_DRIVER_DEVICE_DEFS_H__
+#define __SWAP_DRIVER_DEVICE_DEFS_H__
+
+#include <linux/kernel.h>
+
+/** Prints debug message.*/
+#define print_debug(msg, args...) \
+ printk(KERN_DEBUG "SWAP_DRIVER DEBUG : " msg, ##args)
+/** Prints info message.*/
+#define print_msg(msg, args...) \
+ printk(KERN_INFO "SWAP_DRIVER : " msg, ##args)
+/** Prints warning message.*/
+#define print_warn(msg, args...) \
+ printk(KERN_WARNING "SWAP_DRIVER WARNING : " msg, ##args)
+/** Prints error message.*/
+#define print_err(msg, args...) \
+ printk(KERN_ERR "SWAP_DRIVER ERROR : " msg, ##args)
+/** Prints critical error message.*/
+#define print_crit(msg, args...) \
+ printk(KERN_CRIT "SWAP_DRIVER CRITICAL : " msg, ##args)
+
+#endif /* __SWAP_DRIVER_DEVICE_DEFS_H__ */
--- /dev/null
+/**
+ * driver/driver_to_buffer.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Driver and buffer interaction interface implementation.
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/splice.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include <buffer/swap_buffer_module.h>
+#include <buffer/swap_buffer_errors.h>
+#include <buffer/buffer_description.h>
+
+#include "driver_defs.h"
+#include "swap_driver_errors.h"
+#include "device_driver_to_driver_to_buffer.h"
+#include "app_manage.h"
+
+/** Maximum subbuffer size. Used for sanitization checks. */
+#define MAXIMUM_SUBBUFFER_SIZE (64 * 1024)
+
+/* Current busy buffer */
+static struct swap_subbuffer *busy_buffer;
+
+/* Buffers count ready to be read */
+static int buffers_to_read;
+
+/* Pages count in one subbuffer */
+static int pages_per_buffer;
+
+/* Used to sync changes of the buffers_to_read var */
+static spinlock_t buf_to_read;
+
+
+static inline void init_buffers_to_read(void)
+{
+ spin_lock_init(&buf_to_read);
+ buffers_to_read = 0;
+}
+
+static inline void inc_buffers_to_read(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&buf_to_read, flags);
+ buffers_to_read++;
+ spin_unlock_irqrestore(&buf_to_read, flags);
+}
+
+static inline void dec_buffers_to_read(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&buf_to_read, flags);
+ buffers_to_read--;
+ spin_unlock_irqrestore(&buf_to_read, flags);
+}
+
+static inline void set_buffers_to_read(int count)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&buf_to_read, flags);
+ buffers_to_read = count;
+ spin_unlock_irqrestore(&buf_to_read, flags);
+}
+
+static inline int something_to_read(void)
+{
+ unsigned long flags;
+ int result;
+
+ spin_lock_irqsave(&buf_to_read, flags);
+ result = buffers_to_read;
+ spin_unlock_irqrestore(&buf_to_read, flags);
+
+ return result;
+}
+
+/* TODO Get subbuffer for reading */
+static size_t driver_to_buffer_get(void)
+{
+ int result;
+
+ /* If there is no readable buffers, return error */
+ result = swap_buffer_get(&busy_buffer);
+ if (result == -E_SB_NO_READABLE_BUFFERS) {
+ busy_buffer = NULL;
+ return -E_SD_NO_DATA_TO_READ;
+ } else if (result < 0) {
+ print_err("swap_buffer_get unhandle error %d\n", result);
+ return -E_SD_BUFFER_ERROR;
+ }
+
+ return busy_buffer->full_buffer_part;
+}
+
+/* TODO Release subbuffer */
+static int driver_to_buffer_release(void)
+{
+ int result;
+
+ if (!busy_buffer)
+ return -E_SD_NO_BUSY_SUBBUFFER;
+
+ result = swap_buffer_release(&busy_buffer);
+ if (result == -E_SB_NO_SUBBUFFER_IN_BUSY) {
+ return -E_SD_WRONG_SUBBUFFER_PTR;
+ } else if (result < 0) {
+ print_err("swap_buffer_release unhandle error %d\n", result);
+ return -E_SD_BUFFER_ERROR;
+ }
+
+ busy_buffer = NULL;
+
+ return E_SD_SUCCESS;
+}
+
+static int driver_to_buffer_callback(bool wakeup)
+{
+ /* Increment buffers_to_read counter */
+ inc_buffers_to_read();
+ if (wakeup)
+ swap_device_wake_up_process();
+
+ return E_SD_SUCCESS;
+}
+
+/**
+ * @brief Copies data from subbuffer to userspace.
+ *
+ * @param[out] buf Pointer to userspace memory area whereto copy data from
+ * subbuffer.
+ * @param count Size of data to be read.
+ * @return Read data size on success, negative error code on error.
+ */
+ssize_t driver_to_buffer_read(char __user *buf, size_t count)
+{
+ size_t bytes_to_copy;
+ size_t bytes_to_read = 0;
+ int page_counter = 0;
+
+ /* Reading from swap_device means reading only current busy_buffer.
+ * So, if there is no busy_buffer, we don't get next to read, we just
+ * read nothing. In this case, or if there is nothing to read from
+ * busy_buffer - return -E_SD_NO_DATA_TO_READ. It should be correctly
+ * handled in device_driver */
+ if (!busy_buffer || !busy_buffer->full_buffer_part)
+ return -E_SD_NO_DATA_TO_READ;
+
+ /* Bytes count that we're going to copy to user buffer is equal to user
+ * buffer size or to subbuffer readable size whichever is less */
+ bytes_to_copy = (count > busy_buffer->full_buffer_part) ?
+ busy_buffer->full_buffer_part : count;
+
+ /* Copy data from each page to buffer */
+ while (bytes_to_copy > 0) {
+ /* Get size that should be copied from current page */
+ size_t read_from_this_page =
+ (bytes_to_copy > PAGE_SIZE) ? PAGE_SIZE
+ : bytes_to_copy;
+
+ /* Copy and add size to copied bytes count */
+
+ /* TODO Check with more than one page */
+ bytes_to_read += read_from_this_page -
+ copy_to_user(
+ buf, page_address(busy_buffer->data_buffer) +
+ (sizeof(struct page *) *
+ page_counter),
+ read_from_this_page);
+ bytes_to_copy -= read_from_this_page;
+ page_counter++;
+ }
+
+ return bytes_to_read;
+}
+
+/**
+ * @brief Flushes SWAP buffer.
+ *
+ * @return 0.
+ */
+int driver_to_buffer_flush(void)
+{
+ unsigned int flushed;
+
+ flushed = swap_buffer_flush();
+ set_buffers_to_read(flushed);
+ swap_device_wake_up_process();
+
+ return E_SD_SUCCESS;
+}
+
+/**
+ * @brief Fills spd structure.
+ *
+ * @param[out] spd Pointer to the splice_pipe_desc struct that should be filled.
+ * @return 0 on success, negative error code on error.
+ */
+int driver_to_buffer_fill_spd(struct splice_pipe_desc *spd)
+{
+ size_t data_to_splice = busy_buffer->full_buffer_part;
+ struct page **pages = spd->pages;
+ struct partial_page *partial = spd->partial;
+
+ while (data_to_splice) {
+ size_t read_from_current_page = min(data_to_splice,
+ (size_t)PAGE_SIZE);
+
+ pages[spd->nr_pages] = alloc_page(GFP_KERNEL);
+ if (!pages[spd->nr_pages]) {
+ print_err("Cannot alloc page for splice\n");
+ return -ENOMEM;
+ }
+
+ /* FIXME: maybe there is more efficient way */
+ memcpy(page_address(pages[spd->nr_pages]),
+ page_address(&busy_buffer->data_buffer[spd->nr_pages]),
+ read_from_current_page);
+
+ /* Always beginning of the page */
+ partial[spd->nr_pages].offset = 0;
+ partial[spd->nr_pages].len = read_from_current_page;
+
+ /* Private is not used */
+ partial[spd->nr_pages].private = 0;
+
+ spd->nr_pages++;
+ data_to_splice -= read_from_current_page;
+
+ /* TODO: add check for pipe->buffers exceeding */
+ /* if (spd->nr_pages == pipe->buffers) { */
+ /* break; */
+ /* } */
+ }
+ return 0;
+}
+
+/**
+ * @brief Check for subbuffer ready to be read.
+ *
+ * @return 1 if there is subbuffer to be read, 0 - if there isn't.
+ */
+int driver_to_buffer_buffer_to_read(void)
+{
+ return busy_buffer ? 1 : 0;
+}
+
+static size_t subbuf_size;
+static unsigned int subbuf_count;
+static bool buffer_enabled;
+static DEFINE_MUTEX(buffer_mtx);
+
+bool driver_to_buffer_enabled(void)
+{
+ return buffer_enabled;
+}
+
+enum _swap_driver_errors driver_to_buffer_set_size(size_t size)
+{
+ enum _swap_driver_errors ret = E_SD_SUCCESS;
+
+ if (!size || size > MAXIMUM_SUBBUFFER_SIZE)
+ return -E_SD_WRONG_ARGS;
+
+ mutex_lock(&buffer_mtx);
+ if (buffer_enabled) {
+ ret = -E_SD_BUFFER_ENABLED;
+ goto unlock;
+ }
+
+ subbuf_size = size;
+unlock:
+ mutex_unlock(&buffer_mtx);
+ return ret;
+}
+
+size_t driver_to_buffer_get_size(void)
+{
+ return subbuf_size;
+}
+
+enum _swap_driver_errors driver_to_buffer_set_count(unsigned int count)
+{
+ enum _swap_driver_errors ret = E_SD_SUCCESS;
+
+ if (!count)
+ return -E_SD_WRONG_ARGS;
+
+ mutex_lock(&buffer_mtx);
+ if (buffer_enabled) {
+ ret = -E_SD_BUFFER_ENABLED;
+ goto unlock;
+ }
+ subbuf_count = count;
+
+unlock:
+ mutex_unlock(&buffer_mtx);
+ return ret;
+}
+
+unsigned int driver_to_buffer_get_count(void)
+{
+ return subbuf_count;
+}
+
+/**
+ * @brief Initializes SWAP buffer.
+ *
+ * @param size Size of one subbuffer.
+ * @param count Count of subbuffers.
+ * @return 0 on success, negative error code on error.
+ */
+int driver_to_buffer_initialize(void)
+{
+ enum _swap_driver_errors result;
+ struct buffer_init_t buf_init = {
+ .subbuffer_size = subbuf_size,
+ .nr_subbuffers = subbuf_count,
+ .subbuffer_full_cb = driver_to_buffer_callback,
+ .lower_threshold = 20,
+ .low_mem_cb = app_manage_pause_apps,
+ .top_threshold = 80,
+ .enough_mem_cb = app_manage_cont_apps,
+ };
+
+ mutex_lock(&buffer_mtx);
+ if (buffer_enabled) {
+ result = -E_SD_BUFFER_ENABLED;
+ goto unlock;
+ }
+
+ result = swap_buffer_init(&buf_init);
+ if (result == -E_SB_NO_MEM_QUEUE_BUSY
+ || result == -E_SB_NO_MEM_BUFFER_STRUCT) {
+ result = -E_SD_NO_MEMORY;
+ goto unlock;
+ }
+
+ /* TODO Race condition: buffer can be used in other thread till */
+ /* we're in this func */
+ /* Initialize driver_to_buffer variables */
+ pages_per_buffer = result;
+ busy_buffer = NULL;
+ init_buffers_to_read();
+ result = E_SD_SUCCESS;
+ buffer_enabled = true;
+
+unlock:
+ mutex_unlock(&buffer_mtx);
+ return result;
+}
+
+/**
+ * @brief Uninitializes buffer.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int driver_to_buffer_uninitialize(void)
+{
+ int result;
+
+ mutex_lock(&buffer_mtx);
+ if (!buffer_enabled) {
+ result = -E_SD_BUFFER_DISABLED;
+ goto unlock;
+ }
+
+ /* Release occupied buffer */
+ if (busy_buffer) {
+ result = driver_to_buffer_release();
+ /* TODO Maybe release anyway */
+ if (result < 0)
+ goto unlock;
+ busy_buffer = NULL;
+ }
+
+ result = swap_buffer_uninit();
+ if (result == -E_SB_UNRELEASED_BUFFERS) {
+ print_err("Can't uninit buffer! There are busy subbuffers!\n");
+ result = -E_SD_BUFFER_ERROR;
+ } else if (result < 0) {
+ print_err("swap_buffer_uninit error %d\n", result);
+ result = -E_SD_BUFFER_ERROR;
+ } else {
+ result = E_SD_SUCCESS;
+ buffer_enabled = false;
+ }
+
+ /* Reinit driver_to_buffer vars */
+ init_buffers_to_read();
+ pages_per_buffer = 0;
+
+unlock:
+ mutex_unlock(&buffer_mtx);
+ return result;
+}
+
+/**
+ * @brief Get next buffer to read.
+ *
+ * @return 0 on success, negative error code on error, E_SD_NO_DATA_TO_READ if
+ * there is nothing to be read.
+ */
+int driver_to_buffer_next_buffer_to_read(void)
+{
+ int result;
+
+ /* If there is busy_buffer first release it */
+ if (busy_buffer) {
+ result = driver_to_buffer_release();
+ if (result)
+ return result;
+ }
+
+ /* If there is no buffers to read, return E_SD_NO_DATA_TO_READ.
+ * SHOULD BE POSITIVE, cause there is no real error. */
+ if (!something_to_read())
+ return E_SD_NO_DATA_TO_READ;
+
+ /* Get next buffer to read */
+ result = driver_to_buffer_get();
+ if (result < 0) {
+ print_err("buffer_to_reads > 0, but there are no buffers to read\n");
+ return result;
+ }
+
+ /* Decrement buffers_to_read counter */
+ dec_buffers_to_read();
+
+ return E_SD_SUCCESS;
+}
--- /dev/null
+/**
+ * @file driver/driver_to_buffer.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Driver and buffer interaction interface declaration.
+ */
+
+#ifndef __SWAP_DRIVER_DRIVER_TO_BUFFER__
+#define __SWAP_DRIVER_DRIVER_TO_BUFFER__
+
+
+#include <linux/types.h>
+
+struct splice_pipe_desc;
+
+
+bool driver_to_buffer_enabled(void);
+
+enum _swap_driver_errors driver_to_buffer_set_size(size_t size);
+size_t driver_to_buffer_get_size(void);
+
+enum _swap_driver_errors driver_to_buffer_set_count(unsigned int count);
+unsigned int driver_to_buffer_get_count(void);
+
+int driver_to_buffer_initialize(void);
+int driver_to_buffer_uninitialize(void);
+ssize_t driver_to_buffer_read(char __user *buf, size_t count);
+int driver_to_buffer_fill_spd(struct splice_pipe_desc *spd);
+int driver_to_buffer_buffer_to_read(void);
+int driver_to_buffer_next_buffer_to_read(void);
+int driver_to_buffer_flush(void);
+
+
+#endif /* __SWAP_DRIVER_DRIVER_TO_BUFFER__ */
--- /dev/null
+/**
+ * @file driver/driver_to_msg.h
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Driver and parser interaction interface declaration.
+ */
+
+#ifndef __SWAP_DRIVER_DRIVER_TO_MSG__
+#define __SWAP_DRIVER_DRIVER_TO_MSG__
+
+#include <linux/compiler.h>
+
+struct module;
+
+struct driver_msg_handler {
+ struct module *mod;
+ int (*handler)(void __user *data);
+};
+
+/* Set the message handler */
+void driver_msg_handler_set(struct driver_msg_handler *msg_handler);
+
+#endif /* __SWAP_DRIVER_DRIVER_TO_MSG__ */
--- /dev/null
+/**
+ * @file driver/swap_driver_errors.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP driver error codes.
+ */
+
+#ifndef __SWAP_DRIVER_ERRORS_H__
+#define __SWAP_DRIVER_ERRORS_H__
+
+
+/**
+ * @enum _swap_driver_errors
+ * @brief SWAP driver errors enumeration.
+ */
+enum _swap_driver_errors {
+ /**
+ * @brief Success.
+ */
+ E_SD_SUCCESS = 0,
+ /**
+ * @brief Alloc_chrdev_region failed.
+ */
+ E_SD_ALLOC_CHRDEV_FAIL = 1,
+ /**
+ * @brief cdev_alloc failed.
+ */
+ E_SD_CDEV_ALLOC_FAIL = 2,
+ /**
+ * @brief cdev_add failed.
+ */
+ E_SD_CDEV_ADD_FAIL = 3,
+ /**
+ * @brief class_create failed.
+ */
+ E_SD_CLASS_CREATE_FAIL = 4,
+ /**
+ * @brief device_create failed.
+ */
+ E_SD_DEVICE_CREATE_FAIL = 5,
+ /**
+ * @brief splice_* funcs not found.
+ */
+ E_SD_NO_SPLICE_FUNCS = 6,
+ /**
+ * @brief swap_buffer_get tells us that there is no readable subbuffers.
+ */
+ E_SD_NO_DATA_TO_READ = 7,
+ /**
+ * @brief No busy subbuffer.
+ */
+ E_SD_NO_BUSY_SUBBUFFER = 8,
+ /**
+ * @brief Wrong subbuffer pointer passed to swap_buffer module.
+ */
+ E_SD_WRONG_SUBBUFFER_PTR = 9,
+ /**
+ * @brief Unhandled swap_buffer error.
+ */
+ E_SD_BUFFER_ERROR = 10,
+ /**
+ * @brief Write to subbuffer error.
+ */
+ E_SD_WRITE_ERROR = 11,
+ /**
+ * @brief Arguments, been passed to the func, doesn't pass sanity check.
+ */
+ E_SD_WRONG_ARGS = 12,
+ /**
+ * @brief No memory to allocate.
+ */
+ E_SD_NO_MEMORY = 13,
+ /**
+ * @brief swap_buffer uninitialization error.
+ */
+ E_SD_UNINIT_ERROR = 14,
+ /**
+ * @brief Netlink init error.
+ */
+ E_SD_NL_INIT_ERR = 15,
+ /**
+ * @brief Netlink message send error.
+ */
+ E_SD_NL_MSG_ERR = 16,
+ /**
+ * @brief No daemon pid in us_interaction.
+ */
+ E_SD_NO_DAEMON_PID = 17,
+ /**
+ * @brief Buffer already enabled
+ */
+ E_SD_BUFFER_ENABLED = 18,
+ /**
+ * @brief Buffer already disabled
+ */
+ E_SD_BUFFER_DISABLED = 19,
+};
+
+#endif /* __SWAP_DRIVER_ERRORS_H__ */
--- /dev/null
+/**
+ * driver/swap_driver_module.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP drive module interface implementation.
+ */
+
+#include <linux/module.h>
+#include <master/swap_initializer.h>
+#include "driver_defs.h"
+#include "device_driver.h"
+#include "us_interaction.h"
+#include "driver_debugfs.h"
+
+static int fs_init(void)
+{
+ int ret;
+
+ ret = swap_device_init();
+ if (ret)
+ goto dev_init_fail;
+
+ ret = us_interaction_create();
+ if (ret)
+ print_err("Cannot initialize netlink socket\n");
+
+ ret = driver_debugfs_init();
+ if (ret)
+ goto us_int_destroy;
+
+ print_msg("Driver module initialized\n");
+
+ return ret;
+
+us_int_destroy:
+ us_interaction_destroy();
+dev_init_fail:
+ swap_device_exit();
+
+ return ret;
+}
+
+static void fs_uninit(void)
+{
+ driver_debugfs_uninit();
+ us_interaction_destroy();
+ swap_device_exit();
+ print_msg("Driver module uninitialized\n");
+}
+
+SWAP_LIGHT_INIT_MODULE(NULL, NULL, NULL, fs_init, fs_uninit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP device driver");
+MODULE_AUTHOR("Aksenov A.S.");
--- /dev/null
+/**
+ * @file driver/swap_ioctl.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Provides ioctl commands and recources for SWAP driver.
+ */
+
+#ifndef __SWAP_IOCTL_H__
+#define __SWAP_IOCTL_H__
+
+#include <linux/ioctl.h>
+
+/** SWAP device magic number. */
+#define SWAP_DRIVER_IOC_MAGIC 0xAF
+
+/**
+ * @struct buffer_initialize
+ * @brief SWAP buffer initialization struct.
+ * @var buffer_initialize::size
+ * Size of one subbuffer.
+ * @var buffer_initialize::count
+ * Count of subbuffers in the buffer.
+ */
+struct buffer_initialize {
+ u32 size;
+ u32 count;
+} __packed;
+
+/* SWAP Device ioctl commands */
+
+/** Initialize buffer message. */
+#define SWAP_DRIVER_BUFFER_INITIALIZE _IOW(SWAP_DRIVER_IOC_MAGIC, 1, \
+ struct buffer_initialize *)
+/** Uninitialize buffer message. */
+#define SWAP_DRIVER_BUFFER_UNINITIALIZE _IO(SWAP_DRIVER_IOC_MAGIC, 2)
+/** Set next buffer to read. */
+#define SWAP_DRIVER_NEXT_BUFFER_TO_READ _IO(SWAP_DRIVER_IOC_MAGIC, 3)
+/** Flush buffers. */
+#define SWAP_DRIVER_FLUSH_BUFFER _IO(SWAP_DRIVER_IOC_MAGIC, 4)
+/** Custom message. */
+#define SWAP_DRIVER_MSG _IOW(SWAP_DRIVER_IOC_MAGIC, 5, \
+ void *)
+/** Force wake up daemon. */
+#define SWAP_DRIVER_WAKE_UP _IO(SWAP_DRIVER_IOC_MAGIC, 6)
+
+#endif /* __SWAP_IOCTL_H__ */
--- /dev/null
+/**
+ * driver/us_interaction.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * Kernel-to-user interface implementation.
+ */
+
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/connector.h>
+#include <linux/slab.h>
+
+#include "us_interaction.h"
+#include "us_interaction_msg.h"
+#include "swap_driver_errors.h"
+#include "driver_defs.h"
+
+
+/* Connector id struct */
+static struct cb_id cn_swap_id = {CN_SWAP_IDX, CN_SWAP_VAL};
+
+/* Swap connector name */
+static const char cn_swap_name[] = "cn_swap";
+
+/* Send messages counter */
+static u32 msg_counter;
+
+/**
+ * @brief Sends message to userspace via netlink.
+ *
+ * @param data Pointer to the data to be send.
+ * @param size Size of the data to be send.
+ * @return 0 on success, error code on error.
+ */
+int us_interaction_send_msg(const void *data, size_t size)
+{
+ struct cn_msg *msg;
+ int ret;
+
+ msg = kzalloc(sizeof(*msg) + size, GFP_ATOMIC);
+ if (msg == NULL)
+ return -E_SD_NO_MEMORY;
+
+ memcpy(&msg->id, &cn_swap_id, sizeof(msg->id));
+ msg->seq = msg_counter;
+ msg->len = size;
+ memcpy(msg->data, data, msg->len);
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 25)
+ ret = cn_netlink_send(msg, CN_DAEMON_GROUP, GFP_ATOMIC);
+#else /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0) */
+ ret = cn_netlink_send(msg, 0, CN_DAEMON_GROUP, GFP_ATOMIC);
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 0) */
+ if (ret < 0)
+ goto fail_send;
+ kfree(msg);
+
+ msg_counter++;
+
+ return E_SD_SUCCESS;
+
+fail_send:
+ kfree(msg);
+
+ return ret;
+}
+
+static void us_interaction_recv_msg(struct cn_msg *msg,
+ struct netlink_skb_parms *nsp)
+{
+}
+
+/**
+ * @brief Creates netlink connection.
+ *
+ * @return 0 on success, error code on error.
+ */
+int us_interaction_create(void)
+{
+ int res;
+
+ res = cn_add_callback(&cn_swap_id,
+ cn_swap_name,
+ us_interaction_recv_msg);
+ if (res)
+ return -E_SD_NL_INIT_ERR;
+
+ return E_SD_SUCCESS;
+}
+
+/**
+ * @brief Destroy netlink connection.
+ *
+ * @return Void.
+ */
+void us_interaction_destroy(void)
+{
+ cn_del_callback(&cn_swap_id);
+}
--- /dev/null
+/**
+ * @file driver/us_interaction.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * Kernel-to-user interface definition.
+ */
+
+#ifndef __US_INTERACTION_H__
+#define __US_INTERACTION_H__
+
+#include <linux/version.h>
+
+#ifdef CONFIG_CONNECTOR
+
+int us_interaction_create(void);
+void us_interaction_destroy(void);
+int us_interaction_send_msg(const void *data, size_t size);
+
+#else /* CONFIG_CONNECTOR */
+
+static inline int us_interaction_create(void)
+{
+ return -EPERM;
+}
+
+static inline void us_interaction_destroy(void)
+{
+}
+
+static inline int us_interaction_send_msg(const void *data, size_t size)
+{
+ return -EPERM;
+}
+
+#endif /* CONFIG_CONNECTOR */
+
+#endif /* __US_INTERACTION_H__ */
--- /dev/null
+/**
+ * @file driver/us_interaction_msg.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * Netlink messages declaration.
+ */
+
+#ifndef __US_INTERACTION_MSG_H__
+#define __US_INTERACTION_MSG_H__
+
+#define CN_SWAP_IDX 0x22 /**< Should be unique throughout the system */
+#define CN_SWAP_VAL 0x1 /**< Just the same in kernel and user */
+#define CN_DAEMON_GROUP 0x1 /**< Listener group. Connector works a bit
+ * faster when using one */
+
+/**
+ * @enum us_interaction_k2u_msg_t
+ * @brief Kernel-to-user netlink messages headers.
+ */
+enum us_interaction_k2u_msg_t {
+ /**
+ * @brief Make daemon pause apps.
+ */
+ US_INT_PAUSE_APPS = 1,
+ /**
+ * @brief Make daemon continue apps.
+ */
+ US_INT_CONT_APPS = 2
+};
+
+#endif /* __US_INTERACTION_MSG_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers \
+ $(src)/../us_manager/Module.symvers \
+ $(src)/../driver/Module.symvers
+
+
+
+
+
+###############################################################################
+### swap energy module description ###
+###############################################################################
+obj-m := swap_energy.o
+swap_energy-y := energy_module.o \
+ energy.o \
+ rational_debugfs.o \
+ debugfs_energy.o \
+ lcd/lcd_base.o \
+ lcd/lcd_debugfs.o
+
+
+
+
+
+###############################################################################
+### math support ###
+###############################################################################
+# S6E8AA0:
+ifeq ($(CONFIG_LCD_S6E8AA0), y)
+ swap_energy-y += lcd/s6e8aa0.o
+ LCD_FUNC_LIST += s6e8aa0
+endif
+
+
+# PANEL_S6E8AA0:
+ifeq ($(CONFIG_DISPLAY_PANEL_S6E8AA0), y)
+ swap_energy-y += lcd/s6e8aa0_panel.o
+ LCD_FUNC_LIST += s6e8aa0_panel
+endif
+
+
+# MARU:
+ifeq ($(CONFIG_MARU_BACKLIGHT), y)
+ swap_energy-y += lcd/maru.o
+ LCD_FUNC_LIST += maru
+endif
+
+
+
+
+
+###############################################################################
+### description functions ###
+###############################################################################
+LCD_FUNC_ARGS := void
+LCD_FUNC_RET := struct lcd_ops *
+
+
+
+
+
+###############################################################################
+### generate defines ###
+###############################################################################
+LCD_PREFIX := lcd_energy_
+
+# add prefix
+TMP := $(foreach it, $(LCD_FUNC_LIST), $(LCD_PREFIX)$(it))
+LCD_FUNC_LIST := $(TMP)
+
+# generate DEFINITION_LCD_FUNC
+TMP := ($(LCD_FUNC_ARGS));
+DEFINITION_LCD_FUNC := DEFINITION_LCD_FUNC=\
+$(foreach it, $(LCD_FUNC_LIST), "extern" $(LCD_FUNC_RET) $(it)$(TMP))
+
+
+# generate DEFINITION_LCD_ARRAY
+COMMA := ,
+AND := &
+DEFINITION_LCD_ARRAY := DEFINITION_LCD_ARRAY=\
+"{" $(foreach it, $(LCD_FUNC_LIST), &$(it),) "}"
+
+
+# generate LCD_MAKE_FNAME
+LCD_MAKE_FNAME := LCD_MAKE_FNAME(name)=$(LCD_PREFIX)\#\#name
+
+
+
+
+
+###############################################################################
+### add generate defines to EXTRA_CFLAGS ###
+###############################################################################
+EXTRA_CFLAGS += -D"$(DEFINITION_LCD_FUNC)" \
+ -D"$(DEFINITION_LCD_ARRAY)" \
+ -D"$(LCD_MAKE_FNAME)"
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * energy/debugfs_energy.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/math64.h>
+#include <master/swap_debugfs.h>
+#include "energy.h"
+#include "debugfs_energy.h"
+#include "rational_debugfs.h"
+#include "lcd/lcd_debugfs.h"
+#include "lcd/lcd_base.h"
+
+
+/* CPU running */
+static DEFINE_RATIONAL(cpu0_running_coef); /* boot core uses distinct coeff */
+static DEFINE_RATIONAL(cpuN_running_coef);
+
+static u64 __energy_cpu0(enum parameter_energy pe)
+{
+ u64 times[NR_CPUS] = { 0 };
+ u64 val = 0;
+
+ /* TODO: make for only cpu0 */
+ if (get_parameter_energy(pe, times, sizeof(times)) == 0) {
+ val = div_u64(times[0] * cpu0_running_coef.num,
+ cpu0_running_coef.denom);
+ }
+
+ return val;
+}
+
+static u64 __energy_cpuN(enum parameter_energy pe)
+{
+ u64 times[NR_CPUS] = { 0 };
+ u64 val = 0;
+
+ if (get_parameter_energy(pe, times, sizeof(times)) == 0) {
+ int i;
+
+ for (i = 1; i < NR_CPUS; i++)
+ val += div_u64(times[i] * cpuN_running_coef.num,
+ cpuN_running_coef.denom);
+ }
+
+ return val;
+}
+
+static u64 cpu0_system(void)
+{
+ return __energy_cpu0(PE_TIME_SYSTEM);
+}
+
+static u64 cpuN_system(void)
+{
+ return __energy_cpuN(PE_TIME_SYSTEM);
+}
+
+static u64 cpu0_apps(void)
+{
+ return __energy_cpu0(PE_TIME_APPS);
+}
+
+static u64 cpuN_apps(void)
+{
+ return __energy_cpuN(PE_TIME_APPS);
+}
+
+
+/* CPU idle */
+static DEFINE_RATIONAL(cpu_idle_coef);
+
+static u64 cpu_idle_system(void)
+{
+ u64 time = 0;
+
+ get_parameter_energy(PE_TIME_IDLE, &time, sizeof(time));
+ return div_u64(time * cpu_idle_coef.num, cpu_idle_coef.denom);
+}
+
+
+/* flash read */
+static DEFINE_RATIONAL(fr_coef);
+
+static u64 fr_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_READ_SYSTEM, &byte, sizeof(byte));
+ return div_u64(byte * fr_coef.num, fr_coef.denom);
+}
+
+static u64 fr_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_READ_APPS, &byte, sizeof(byte));
+ return div_u64(byte * fr_coef.num, fr_coef.denom);
+}
+
+
+/* flash write */
+static DEFINE_RATIONAL(fw_coef);
+
+static u64 fw_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_WRITE_SYSTEM, &byte, sizeof(byte));
+ return div_u64(byte * fw_coef.num, fw_coef.denom);
+}
+
+static u64 fw_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_WRITE_APPS, &byte, sizeof(byte));
+ return div_u64(byte * fw_coef.num, fw_coef.denom);
+}
+
+
+/* wifi recv */
+static DEFINE_RATIONAL(wf_recv_coef);
+
+static u64 wf_recv_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_WF_RECV_SYSTEM, &byte, sizeof(byte));
+
+ return div_u64(byte * wf_recv_coef.num, wf_recv_coef.denom);
+}
+
+static u64 wf_recv_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_WF_RECV_APPS, &byte, sizeof(byte));
+
+ return div_u64(byte * wf_recv_coef.num, wf_recv_coef.denom);
+}
+
+/* wifi send */
+static DEFINE_RATIONAL(wf_send_coef);
+
+static u64 wf_send_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_WF_SEND_SYSTEM, &byte, sizeof(byte));
+
+ return div_u64(byte * wf_send_coef.num, wf_send_coef.denom);
+}
+
+static u64 wf_send_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_WF_SEND_APPS, &byte, sizeof(byte));
+
+ return div_u64(byte * wf_send_coef.num, wf_send_coef.denom);
+}
+
+/* l2cap_recv_acldata */
+static DEFINE_RATIONAL(l2cap_recv_acldata_coef);
+
+static u64 l2cap_recv_acldata_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_L2CAP_RECV_SYSTEM, &byte, sizeof(byte));
+
+ return div_u64(byte * l2cap_recv_acldata_coef.num,
+ l2cap_recv_acldata_coef.denom);
+}
+
+static u64 l2cap_recv_acldata_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_L2CAP_RECV_APPS, &byte, sizeof(byte));
+
+ return div_u64(byte * l2cap_recv_acldata_coef.num,
+ l2cap_recv_acldata_coef.denom);
+}
+
+/* sco_recv_scodata */
+static DEFINE_RATIONAL(sco_recv_scodata_coef);
+
+static u64 sco_recv_scodata_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_SCO_RECV_SYSTEM, &byte, sizeof(byte));
+
+ return div_u64(byte * sco_recv_scodata_coef.num,
+ sco_recv_scodata_coef.denom);
+}
+
+static u64 sco_recv_scodata_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PE_SCO_RECV_APPS, &byte, sizeof(byte));
+
+ return div_u64(byte * sco_recv_scodata_coef.num,
+ sco_recv_scodata_coef.denom);
+}
+
+/* hci_send_acl */
+static DEFINE_RATIONAL(hci_send_acl_coef);
+
+static u64 hci_send_acl_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PT_SEND_ACL_SYSTEM, &byte, sizeof(byte));
+
+ return div_u64(byte * hci_send_acl_coef.num, hci_send_acl_coef.denom);
+}
+
+static u64 hci_send_acl_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PT_SEND_ACL_APPS, &byte, sizeof(byte));
+
+ return div_u64(byte * hci_send_acl_coef.num, hci_send_acl_coef.denom);
+}
+
+/* hci_send_sco */
+static DEFINE_RATIONAL(hci_send_sco_coef);
+
+static u64 hci_send_sco_system(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PT_SEND_SCO_SYSTEM, &byte, sizeof(byte));
+
+ return div_u64(byte * hci_send_sco_coef.num, hci_send_sco_coef.denom);
+}
+
+static u64 hci_send_sco_apps(void)
+{
+ u64 byte = 0;
+
+ get_parameter_energy(PT_SEND_SCO_APPS, &byte, sizeof(byte));
+
+ return div_u64(byte * hci_send_sco_coef.num, hci_send_sco_coef.denom);
+}
+
+
+
+
+
+/* ============================================================================
+ * === PARAMETERS ===
+ * ============================================================================
+ */
+static int get_func_u64(void *data, u64 *val)
+{
+ u64 (*func)(void) = data;
+ *val = func();
+ return 0;
+}
+
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_get_u64, get_func_u64, NULL, "%llu\n");
+
+
+struct param_data {
+ char *name;
+ struct rational *coef;
+ u64 (*system)(void);
+ u64 (*apps)(void);
+};
+
+static struct dentry *create_parameter(struct dentry *parent,
+ struct param_data *param)
+{
+ struct dentry *name, *system, *apps = NULL;
+
+ name = swap_debugfs_create_dir(param->name, parent);
+ if (name == NULL)
+ return NULL;
+
+ system = swap_debugfs_create_file("system", 0600, name, param->system,
+ &fops_get_u64);
+ if (system == NULL)
+ goto rm_name;
+
+ if (param->apps) {
+ apps = swap_debugfs_create_file("apps", 0600, name, param->apps,
+ &fops_get_u64);
+ if (apps == NULL)
+ goto rm_system;
+ }
+
+ if (create_rational_files(name, param->coef,
+ "numerator", "denominator"))
+ goto rm_apps;
+
+ return name;
+
+rm_apps:
+ if (param->apps)
+ debugfs_remove(apps);
+rm_system:
+ debugfs_remove(system);
+rm_name:
+ debugfs_remove(name);
+
+ return NULL;
+}
+
+struct param_data parameters[] = {
+ {
+ .name = "cpu_running",
+ .coef = &cpu0_running_coef,
+ .system = cpu0_system,
+ .apps = cpu0_apps
+ },
+ {
+ .name = "cpuN_running",
+ .coef = &cpuN_running_coef,
+ .system = cpuN_system,
+ .apps = cpuN_apps
+ },
+ {
+ .name = "cpu_idle",
+ .coef = &cpu_idle_coef,
+ .system = cpu_idle_system,
+ .apps = NULL
+ },
+ {
+ .name = "flash_read",
+ .coef = &fr_coef,
+ .system = fr_system,
+ .apps = fr_apps
+ },
+ {
+ .name = "flash_write",
+ .coef = &fw_coef,
+ .system = fw_system,
+ .apps = fw_apps
+ },
+ {
+ .name = "wf_recv",
+ .coef = &wf_recv_coef,
+ .system = wf_recv_system,
+ .apps = wf_recv_apps
+ },
+ {
+ .name = "wf_send",
+ .coef = &wf_send_coef,
+ .system = wf_send_system,
+ .apps = wf_send_apps
+ },
+ {
+ .name = "sco_recv_scodata",
+ .coef = &sco_recv_scodata_coef,
+ .system = sco_recv_scodata_system,
+ .apps = sco_recv_scodata_apps
+ },
+ {
+ .name = "l2cap_recv_acldata",
+ .coef = &l2cap_recv_acldata_coef,
+ .system = l2cap_recv_acldata_system,
+ .apps = l2cap_recv_acldata_apps
+ },
+ {
+ .name = "hci_send_acl",
+ .coef = &hci_send_acl_coef,
+ .system = hci_send_acl_system,
+ .apps = hci_send_acl_apps
+ },
+ {
+ .name = "hci_send_sco",
+ .coef = &hci_send_sco_coef,
+ .system = hci_send_sco_system,
+ .apps = hci_send_sco_apps
+ }
+};
+
+enum {
+ parameters_cnt = sizeof(parameters) / sizeof(struct param_data)
+};
+
+
+
+
+
+/* ============================================================================
+ * === INIT/EXIT ===
+ * ============================================================================
+ */
+static struct dentry *energy_dir;
+
+/**
+ * @brief Destroy debugfs for LCD
+ *
+ * @return Dentry of energy debugfs
+ */
+struct dentry *get_energy_dir(void)
+{
+ return energy_dir;
+}
+
+/**
+ * @brief Destroy debugfs for energy
+ *
+ * @return Void
+ */
+void exit_debugfs_energy(void)
+{
+ lcd_exit();
+ exit_lcd_debugfs();
+
+ if (energy_dir)
+ debugfs_remove_recursive(energy_dir);
+
+ energy_dir = NULL;
+}
+
+/**
+ * @brief Create debugfs for energy
+ *
+ * @return Error code
+ */
+int init_debugfs_energy(void)
+{
+ int i;
+ struct dentry *swap_dir, *dentry;
+
+ swap_dir = swap_debugfs_getdir();
+ if (swap_dir == NULL)
+ return -ENOENT;
+
+ energy_dir = swap_debugfs_create_dir("energy", swap_dir);
+ if (energy_dir == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < parameters_cnt; ++i) {
+ dentry = create_parameter(energy_dir, ¶meters[i]);
+ if (dentry == NULL)
+ goto fail;
+ }
+
+ if (init_lcd_debugfs(energy_dir))
+ goto fail;
+
+ /* Actually, the only goal of lcd_init() is to register lcd screen's
+ debugfs, so it is called here. */
+ if (lcd_init()) {
+ exit_lcd_debugfs();
+ }
+
+ return 0;
+
+fail:
+ exit_debugfs_energy();
+ return -ENOMEM;
+}
--- /dev/null
+#ifndef _DEBUGFS_ENERGY_H
+#define _DEBUGFS_ENERGY_H
+
+/**
+ * @file energy/debugfs_energy.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ * Debugfs for energy
+ */
+
+
+#include <linux/fs.h>
+#include <master/swap_initializer.h>
+
+
+struct dentry;
+
+
+/* based on define DEFINE_SIMPLE_ATTRIBUTE */
+#define SWAP_DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \
+static int __fops ## _open(struct inode *inode, struct file *file) \
+{ \
+ int ret; \
+ \
+ ret = swap_init_simple_open(inode, file); \
+ if (ret) \
+ return ret; \
+ \
+ __simple_attr_check_format(__fmt, 0ull); \
+ ret = simple_attr_open(inode, file, __get, __set, __fmt); \
+ if (ret) \
+ swap_init_simple_release(inode, file); \
+ \
+ return ret; \
+} \
+static int __fops ## _release(struct inode *inode, struct file *file) \
+{ \
+ simple_attr_release(inode, file); \
+ swap_init_simple_release(inode, file); \
+ \
+ return 0; \
+} \
+static const struct file_operations __fops = { \
+ .owner = THIS_MODULE, \
+ .open = __fops ## _open, \
+ .release = __fops ## _release, \
+ .read = simple_attr_read, \
+ .write = simple_attr_write, \
+ .llseek = generic_file_llseek, \
+}
+
+
+int init_debugfs_energy(void);
+void exit_debugfs_energy(void);
+
+struct dentry *get_energy_dir(void);
+
+
+#endif /* _DEBUGFS_ENERGY_H */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/energy/swap_energy.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vasiliy Ulyanov <v.ulyanov@samsung.com>
+ * Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/spinlock.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/fdtable.h>
+#include <net/sock.h>
+#include <ksyms/ksyms.h>
+#include <master/swap_deps.h>
+#include <swap-asm/swap_kprobes.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/sspt/sspt_feature.h>
+#include <linux/atomic.h>
+
+#ifdef CONFIG_SWAP_HOOK_SWITCH_TO
+# include <swap/hook_switch_to.h>
+#else /* CONFIG_SWAP_HOOK_SWITCH_TO */
+# include <kprobe/swap_kprobes.h>
+#endif /* CONFIG_SWAP_HOOK_SWITCH_TO */
+
+#ifdef CONFIG_SWAP_HOOK_ENERGY
+# include <swap/hook_syscall.h>
+# include <swap/hook_energy.h>
+# include <kprobe/swap_td_raw.h>
+#else /* CONFIG_SWAP_HOOK_ENERGY */
+# include <kprobe/swap_kprobes.h>
+#endif /* CONFIG_SWAP_HOOK_ENERGY */
+
+#include "energy.h"
+#include "lcd/lcd_base.h"
+#include "tm_stat.h"
+
+
+#ifndef CONFIG_SWAP_HOOK_ENERGY
+/* ============================================================================
+ * = ENERGY_XXX =
+ * ============================================================================
+ */
+struct kern_probe {
+ const char *name;
+ struct kretprobe *rp;
+};
+
+static int energy_xxx_once(struct kern_probe p[], int size)
+{
+ int i;
+ const char *sym;
+
+ for (i = 0; i < size; ++i) {
+ struct kretprobe *rp = p[i].rp;
+
+ sym = p[i].name;
+ rp->kp.addr = swap_ksyms(sym);
+ if (rp->kp.addr == 0)
+ goto not_found;
+ }
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
+static int energy_xxx_set(struct kern_probe p[], int size, int *flag)
+{
+ int i, ret;
+
+ for (i = 0; i < size; ++i) {
+ ret = swap_register_kretprobe(p[i].rp);
+ if (ret)
+ goto fail;
+ }
+
+ *flag = 1;
+ return 0;
+
+fail:
+ pr_err("swap_register_kretprobe(%s) ret=%d\n", p[i].name, ret);
+
+ for (--i; i != -1; --i)
+ swap_unregister_kretprobe(p[i].rp);
+
+ return ret;
+}
+
+static void energy_xxx_unset(struct kern_probe p[], int size, int *flag)
+{
+ int i;
+
+ if (*flag == 0)
+ return;
+
+ for (i = size - 1; i != -1; --i)
+ swap_unregister_kretprobe(p[i].rp);
+
+ *flag = 0;
+}
+#endif /* CONFIG_SWAP_HOOK_ENERGY */
+
+
+
+
+
+/* ============================================================================
+ * = CPUS_TIME =
+ * ============================================================================
+ */
+struct cpus_time {
+ spinlock_t lock; /* for concurrent access */
+ struct tm_stat tm[NR_CPUS];
+};
+
+#define cpus_time_lock(ct, flags) spin_lock_irqsave(&(ct)->lock, flags)
+#define cpus_time_unlock(ct, flags) spin_unlock_irqrestore(&(ct)->lock, flags)
+
+static void cpus_time_init(struct cpus_time *ct, u64 time)
+{
+ int cpu;
+
+ spin_lock_init(&ct->lock);
+
+ for (cpu = 0; cpu < NR_CPUS; ++cpu) {
+ tm_stat_init(&ct->tm[cpu]);
+ tm_stat_set_timestamp(&ct->tm[cpu], time);
+ }
+}
+
+static inline u64 cpu_time_get_running(struct cpus_time *ct, int cpu, u64 now)
+{
+ return tm_stat_current_running(&ct->tm[cpu], now);
+}
+
+static void *cpus_time_get_running_all(struct cpus_time *ct, u64 *buf, u64 now)
+{
+ int cpu;
+
+ for (cpu = 0; cpu < NR_CPUS; ++cpu)
+ buf[cpu] = tm_stat_current_running(&ct->tm[cpu], now);
+
+ return buf;
+}
+
+static void *cpus_time_sum_running_all(struct cpus_time *ct, u64 *buf, u64 now)
+{
+ int cpu;
+
+ for (cpu = 0; cpu < NR_CPUS; ++cpu)
+ buf[cpu] += tm_stat_current_running(&ct->tm[cpu], now);
+
+ return buf;
+}
+
+static void cpus_time_save_entry(struct cpus_time *ct, int cpu, u64 time)
+{
+ struct tm_stat *tm = &ct->tm[cpu];
+
+ if (unlikely(tm_stat_timestamp(tm))) /* should never happen */
+ printk(KERN_INFO "XXX %s[%d/%d]: WARNING tmstamp(%p) set on cpu(%d)\n",
+ current->comm, current->tgid, current->pid, tm, cpu);
+ tm_stat_set_timestamp(&ct->tm[cpu], time);
+}
+
+static void cpus_time_update_running(struct cpus_time *ct, int cpu, u64 now,
+ u64 start_time)
+{
+ struct tm_stat *tm = &ct->tm[cpu];
+
+ if (unlikely(tm_stat_timestamp(tm) == 0)) {
+ /* not initialized. should happen only once per cpu/task */
+ printk(KERN_INFO "XXX %s[%d/%d]: nnitializing tmstamp(%p) "
+ "on cpu(%d)\n",
+ current->comm, current->tgid, current->pid, tm, cpu);
+ tm_stat_set_timestamp(tm, start_time);
+ }
+
+ tm_stat_update(tm, now);
+ tm_stat_set_timestamp(tm, 0); /* set timestamp to 0 */
+}
+
+
+
+
+
+struct energy_data {
+ /* for __switch_to */
+ struct cpus_time ct;
+
+ /* for sys_read */
+ atomic64_t bytes_read;
+
+ /*for sys_write */
+ atomic64_t bytes_written;
+
+ /*for recvmsg*/
+ atomic64_t bytes_recv;
+
+ /* for sock_send */
+ atomic64_t bytes_send;
+
+ /* for l2cap_recv */
+ atomic64_t bytes_l2cap_recv_acldata;
+
+ /* for sco_recv_scodata */
+ atomic64_t bytes_sco_recv_scodata;
+
+ /* for hci_send_acl */
+ atomic64_t bytes_hci_send_acl;
+
+ /* for hci_send_sco */
+ atomic64_t bytes_hci_send_sco;
+};
+
+static sspt_feature_id_t feature_id = SSPT_FEATURE_ID_BAD;
+
+static void init_ed(struct energy_data *ed)
+{
+ /* instead of get_ntime(), CPU time is initialized to 0 here. Timestamp
+ * value will be properly set when the corresponding __switch_to event
+ * occurs */
+ cpus_time_init(&ed->ct, 0);
+ atomic64_set(&ed->bytes_read, 0);
+ atomic64_set(&ed->bytes_written, 0);
+ atomic64_set(&ed->bytes_recv, 0);
+ atomic64_set(&ed->bytes_send, 0);
+ atomic64_set(&ed->bytes_l2cap_recv_acldata, 0);
+ atomic64_set(&ed->bytes_sco_recv_scodata, 0);
+ atomic64_set(&ed->bytes_hci_send_acl, 0);
+ atomic64_set(&ed->bytes_hci_send_sco, 0);
+}
+
+static void uninit_ed(struct energy_data *ed)
+{
+ cpus_time_init(&ed->ct, 0);
+ atomic64_set(&ed->bytes_read, 0);
+ atomic64_set(&ed->bytes_written, 0);
+ atomic64_set(&ed->bytes_recv, 0);
+ atomic64_set(&ed->bytes_send, 0);
+ atomic64_set(&ed->bytes_l2cap_recv_acldata, 0);
+ atomic64_set(&ed->bytes_sco_recv_scodata, 0);
+ atomic64_set(&ed->bytes_hci_send_acl, 0);
+ atomic64_set(&ed->bytes_hci_send_sco, 0);
+}
+
+static void *create_ed(void)
+{
+ struct energy_data *ed;
+
+ ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
+ if (ed)
+ init_ed(ed);
+
+ return (void *)ed;
+}
+
+static void destroy_ed(void *data)
+{
+ struct energy_data *ed = (struct energy_data *)data;
+ kfree(ed);
+}
+
+
+static int init_feature(void)
+{
+ feature_id = sspt_register_feature(create_ed, destroy_ed);
+
+ if (feature_id == SSPT_FEATURE_ID_BAD)
+ return -EPERM;
+
+ return 0;
+}
+
+static void uninit_feature(void)
+{
+ sspt_unregister_feature(feature_id);
+ feature_id = SSPT_FEATURE_ID_BAD;
+}
+
+static struct energy_data *get_energy_data(struct task_struct *task)
+{
+ void *data = NULL;
+ struct sspt_proc *proc;
+
+ proc = sspt_proc_by_task(task);
+ if (proc)
+ data = sspt_get_feature_data(proc->feature, feature_id);
+
+ return (struct energy_data *)data;
+}
+
+static int check_fs(unsigned long magic)
+{
+ switch (magic) {
+ case EXT2_SUPER_MAGIC: /* == EXT3_SUPER_MAGIC == EXT4_SUPER_MAGIC */
+ case MSDOS_SUPER_MAGIC:
+ return 1;
+ }
+
+ return 0;
+}
+
+static int check_ftype(int fd)
+{
+ int err, ret = 0;
+ struct kstat kstat;
+
+ err = vfs_fstat(fd, &kstat);
+ if (err == 0 && S_ISREG(kstat.mode))
+ ret = 1;
+
+ return ret;
+}
+
+static int check_file(int fd)
+{
+ struct file *file;
+
+ file = fget(fd);
+ if (file) {
+ int magic = 0;
+ if (file->f_path.dentry && file->f_path.dentry->d_sb)
+ magic = file->f_path.dentry->d_sb->s_magic;
+
+ fput(file);
+
+ if (check_fs(magic) && check_ftype(fd))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+static struct cpus_time ct_idle;
+static struct energy_data ed_system;
+static u64 start_time;
+
+static void init_data_energy(void)
+{
+ start_time = get_ntime();
+ init_ed(&ed_system);
+ cpus_time_init(&ct_idle, 0);
+}
+
+static void uninit_data_energy(void)
+{
+ start_time = 0;
+ uninit_ed(&ed_system);
+ cpus_time_init(&ct_idle, 0);
+}
+
+
+
+
+
+/* ============================================================================
+ * = __switch_to =
+ * ============================================================================
+ */
+static void do_entry_handler_switch(struct task_struct *task)
+{
+ int cpu;
+ struct cpus_time *ct;
+ struct energy_data *ed;
+ unsigned long flags;
+
+ cpu = smp_processor_id();
+
+ ct = task->tgid ? &ed_system.ct : &ct_idle;
+ cpus_time_lock(ct, flags);
+ cpus_time_update_running(ct, cpu, get_ntime(), start_time);
+ cpus_time_unlock(ct, flags);
+
+ ed = get_energy_data(task);
+ if (ed) {
+ ct = &ed->ct;
+ cpus_time_lock(ct, flags);
+ cpus_time_update_running(ct, cpu, get_ntime(), start_time);
+ cpus_time_unlock(ct, flags);
+ }
+}
+
+static void do_ret_handler_switch(struct task_struct *task)
+{
+ int cpu;
+ struct cpus_time *ct;
+ struct energy_data *ed;
+ unsigned long flags;
+
+ cpu = smp_processor_id();
+
+ ct = task->tgid ? &ed_system.ct : &ct_idle;
+ cpus_time_lock(ct, flags);
+ cpus_time_save_entry(ct, cpu, get_ntime());
+ cpus_time_unlock(ct, flags);
+
+ ed = get_energy_data(task);
+ if (ed) {
+ ct = &ed->ct;
+ cpus_time_lock(ct, flags);
+ cpus_time_save_entry(ct, cpu, get_ntime());
+ cpus_time_unlock(ct, flags);
+ }
+}
+
+#ifndef CONFIG_SWAP_HOOK_SWITCH_TO
+static int ret_handler_switch(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ do_ret_handler_switch(current);
+ return 0;
+}
+
+static int entry_handler_switch(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ do_entry_handler_switch(current);
+ return 0;
+}
+
+static struct kretprobe switch_to_krp = {
+ .entry_handler = entry_handler_switch,
+ .handler = ret_handler_switch,
+};
+#endif /* !CONFIG_SWAP_HOOK_SWITCH_TO */
+
+
+
+
+
+/* ============================================================================
+ * = sys_read =
+ * ============================================================================
+ */
+struct sys_read_data {
+ int fd;
+};
+
+#ifndef CONFIG_SWAP_HOOK_ENERGY
+static int entry_handler_sys_read(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct sys_read_data *srd = (struct sys_read_data *)ri->data;
+
+ srd->fd = (int)swap_get_sarg(regs, 0);
+
+ return 0;
+}
+
+static int ret_handler_sys_read(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+
+ if (ret > 0) {
+ struct sys_read_data *srd;
+
+ srd = (struct sys_read_data *)ri->data;
+ if (check_file(srd->fd)) {
+ struct energy_data *ed;
+
+ ed = get_energy_data(current);
+ if (ed)
+ atomic64_add(ret, &ed->bytes_read);
+
+ atomic64_add(ret, &ed_system.bytes_read);
+ }
+ }
+
+ return 0;
+}
+
+static struct kretprobe sys_read_krp = {
+ .entry_handler = entry_handler_sys_read,
+ .handler = ret_handler_sys_read,
+ .data_size = sizeof(struct sys_read_data)
+};
+
+
+
+
+
+/* ============================================================================
+ * = sys_write =
+ * ============================================================================
+ */
+static int entry_handler_sys_write(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct sys_read_data *srd = (struct sys_read_data *)ri->data;
+
+ srd->fd = (int)swap_get_sarg(regs, 0);
+
+ return 0;
+}
+
+static int ret_handler_sys_write(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+
+ if (ret > 0) {
+ struct sys_read_data *srd;
+
+ srd = (struct sys_read_data *)ri->data;
+ if (check_file(srd->fd)) {
+ struct energy_data *ed;
+
+ ed = get_energy_data(current);
+ if (ed)
+ atomic64_add(ret, &ed->bytes_written);
+
+ atomic64_add(ret, &ed_system.bytes_written);
+ }
+ }
+
+ return 0;
+}
+
+static struct kretprobe sys_write_krp = {
+ .entry_handler = entry_handler_sys_write,
+ .handler = ret_handler_sys_write,
+ .data_size = sizeof(struct sys_read_data)
+};
+#endif /* !CONFIG_SWAP_HOOK_ENERGY */
+
+
+
+
+
+/* ============================================================================
+ * = wifi =
+ * ============================================================================
+ */
+static bool check_wlan0(struct socket *sock)
+{
+ /* FIXME: hardcode interface */
+ const char *name_intrf = "wlan0";
+
+ if (sock->sk->sk_dst_cache &&
+ sock->sk->sk_dst_cache->dev &&
+ !strcmp(sock->sk->sk_dst_cache->dev->name, name_intrf))
+ return true;
+
+ return false;
+}
+
+static bool check_socket(struct task_struct *task, struct socket *socket)
+{
+ bool ret = false;
+ unsigned int fd;
+ struct files_struct *files;
+
+ files = swap_get_files_struct(task);
+ if (files == NULL)
+ return false;
+
+ rcu_read_lock();
+ for (fd = 0; fd < files_fdtable(files)->max_fds; ++fd) {
+ if (fcheck_files(files, fd) == socket->file) {
+ ret = true;
+ goto unlock;
+ }
+ }
+
+unlock:
+ rcu_read_unlock();
+ swap_put_files_struct(files);
+ return ret;
+}
+
+static struct energy_data *get_energy_data_by_socket(struct task_struct *task,
+ struct socket *socket)
+{
+ struct energy_data *ed;
+
+ ed = get_energy_data(task);
+ if (ed)
+ ed = check_socket(task, socket) ? ed : NULL;
+
+ return ed;
+}
+
+#ifndef CONFIG_SWAP_HOOK_ENERGY
+static int wf_sock_eh(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct socket *socket = (struct socket *)swap_get_karg(regs, 0);
+
+ *(struct socket **)ri->data = socket;
+
+ return 0;
+}
+
+static int wf_sock_aio_eh(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct kiocb *iocb = (struct kiocb *)swap_get_karg(regs, 0);
+ struct socket *socket = iocb->ki_filp->private_data;
+
+ *(struct socket **)ri->data = socket;
+
+ return 0;
+}
+#endif /* CONFIG_SWAP_HOOK_ENERGY */
+
+static void calc_wifi_recv_energy(struct socket *sock, int len)
+{
+ struct energy_data *ed;
+
+ if (len <= 0 || !check_wlan0(sock))
+ return;
+
+ ed = get_energy_data_by_socket(current, sock);
+ if (ed)
+ atomic64_add(len, &ed->bytes_recv);
+ atomic64_add(len, &ed_system.bytes_recv);
+}
+
+static void calc_wifi_send_energy(struct socket *sock, int len)
+{
+ struct energy_data *ed;
+
+ if (len <= 0 || !check_wlan0(sock))
+ return;
+
+ ed = get_energy_data_by_socket(current, sock);
+ if (ed)
+ atomic64_add(len, &ed->bytes_send);
+ atomic64_add(len, &ed_system.bytes_send);
+}
+
+#ifndef CONFIG_SWAP_HOOK_ENERGY
+static int wf_sock_recv_rh(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+
+ calc_wifi_recv_energy(*(struct socket **)ri->data, ret);
+
+ return 0;
+}
+
+static int wf_sock_send_rh(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+
+ calc_wifi_send_energy(*(struct socket **)ri->data, ret);
+
+ return 0;
+}
+
+static struct kretprobe sock_recv_krp = {
+ .entry_handler = wf_sock_eh,
+ .handler = wf_sock_recv_rh,
+ .data_size = sizeof(struct socket *)
+};
+
+static struct kretprobe sock_send_krp = {
+ .entry_handler = wf_sock_eh,
+ .handler = wf_sock_send_rh,
+ .data_size = sizeof(struct socket *)
+};
+
+static struct kretprobe sock_aio_read_krp = {
+ .entry_handler = wf_sock_aio_eh,
+ .handler = wf_sock_recv_rh,
+ .data_size = sizeof(struct socket *)
+};
+
+static struct kretprobe sock_aio_write_krp = {
+ .entry_handler = wf_sock_aio_eh,
+ .handler = wf_sock_send_rh,
+ .data_size = sizeof(struct socket *)
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+static const char sock_aio_read[] = "sock_read_iter";
+static const char sock_aio_write[] = "sock_write_iter";
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
+static const char sock_aio_read[] = "sock_aio_read";
+static const char sock_aio_write[] = "sock_aio_write";
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
+
+static struct kern_probe wifi_probes[] = {
+ {
+ .name = "sock_recvmsg",
+ .rp = &sock_recv_krp,
+ },
+ {
+ .name = "sock_sendmsg",
+ .rp = &sock_send_krp,
+ },
+ {
+ .name = sock_aio_read,
+ .rp = &sock_aio_read_krp,
+ },
+ {
+ .name = sock_aio_write,
+ .rp = &sock_aio_write_krp,
+ }
+};
+
+enum { wifi_probes_cnt = ARRAY_SIZE(wifi_probes) };
+static int wifi_flag = 0;
+#endif /* !CONFIG_SWAP_HOOK_ENERGY */
+
+
+
+
+
+/* ============================================================================
+ * = bluetooth =
+ * ============================================================================
+ */
+
+struct swap_bt_data {
+ struct socket *socket;
+};
+
+static void calc_bt_recv_energy(struct socket *sock, int len)
+{
+ struct energy_data *ed;
+
+ if (len <= 0 || !sock)
+ return;
+
+ ed = get_energy_data_by_socket(current, sock);
+ if (ed)
+ atomic64_add(len, &ed->bytes_l2cap_recv_acldata);
+ atomic64_add(len, &ed_system.bytes_l2cap_recv_acldata);
+}
+
+static void calc_bt_send_energy(struct socket *sock, int len)
+{
+ struct energy_data *ed;
+
+ if (len <= 0 || !sock)
+ return;
+
+ ed = get_energy_data_by_socket(current, sock);
+ if (ed)
+ atomic64_add(len, &ed->bytes_hci_send_sco);
+ atomic64_add(len, &ed_system.bytes_hci_send_sco);
+}
+
+#ifndef CONFIG_SWAP_HOOK_ENERGY
+static int bt_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct swap_bt_data *data = (struct swap_bt_data *)ri->data;
+ struct socket *sock = (struct socket *)swap_get_sarg(regs, 1);
+
+ data->socket = sock ? sock : NULL;
+
+ return 0;
+}
+
+static int bt_recvmsg_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ struct swap_bt_data *data = (struct swap_bt_data *)ri->data;
+
+ calc_bt_recv_energy(data->socket, ret);
+
+ return 0;
+}
+
+static int bt_sendmsg_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ struct swap_bt_data *data = (struct swap_bt_data *)ri->data;
+
+ calc_bt_send_energy(data->socket, ret);
+
+ return 0;
+}
+
+static struct kretprobe rfcomm_sock_recvmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_recvmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kretprobe l2cap_sock_recvmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_recvmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kretprobe hci_sock_recvmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_recvmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kretprobe sco_sock_recvmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_recvmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+static struct kretprobe rfcomm_sock_sendmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_sendmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kretprobe l2cap_sock_sendmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_sendmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kretprobe hci_sock_sendmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_sendmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kretprobe sco_sock_sendmsg_krp = {
+ .entry_handler = bt_entry_handler,
+ .handler = bt_sendmsg_handler,
+ .data_size = sizeof(struct swap_bt_data)
+};
+
+static struct kern_probe bt_probes[] = {
+ {
+ .name = "rfcomm_sock_recvmsg",
+ .rp = &rfcomm_sock_recvmsg_krp,
+ },
+ {
+ .name = "l2cap_sock_recvmsg",
+ .rp = &l2cap_sock_recvmsg_krp,
+ },
+ {
+ .name = "hci_sock_recvmsg",
+ .rp = &hci_sock_recvmsg_krp,
+ },
+ {
+ .name = "sco_sock_recvmsg",
+ .rp = &sco_sock_recvmsg_krp,
+ },
+ {
+ .name = "rfcomm_sock_sendmsg",
+ .rp = &rfcomm_sock_sendmsg_krp,
+ },
+ {
+ .name = "l2cap_sock_sendmsg",
+ .rp = &l2cap_sock_sendmsg_krp,
+ },
+ {
+ .name = "hci_sock_sendmsg",
+ .rp = &hci_sock_sendmsg_krp,
+ },
+ {
+ .name = "sco_sock_sendmsg",
+ .rp = &sco_sock_sendmsg_krp,
+ }
+};
+
+enum { bt_probes_cnt = ARRAY_SIZE(bt_probes) };
+static int energy_bt_flag = 0;
+#endif /* CONFIG_SWAP_HOOK_ENERGY */
+
+enum parameter_type {
+ PT_CPU,
+ PT_READ,
+ PT_WRITE,
+ PT_WF_RECV,
+ PT_WF_SEND,
+ PT_L2CAP_RECV,
+ PT_SCO_RECV,
+ PT_SEND_ACL,
+ PT_SEND_SCO
+};
+
+struct cmd_pt {
+ enum parameter_type pt;
+ void *buf;
+ int sz;
+};
+
+static void callback_for_proc(struct sspt_proc *proc, void *data)
+{
+ void *f_data = sspt_get_feature_data(proc->feature, feature_id);
+ struct energy_data *ed = (struct energy_data *)f_data;
+
+ if (ed) {
+ unsigned long flags;
+ struct cmd_pt *cmdp = (struct cmd_pt *)data;
+ u64 *val = cmdp->buf;
+
+ switch (cmdp->pt) {
+ case PT_CPU:
+ cpus_time_lock(&ed->ct, flags);
+ cpus_time_sum_running_all(&ed->ct, val, get_ntime());
+ cpus_time_unlock(&ed->ct, flags);
+ break;
+ case PT_READ:
+ *val += atomic64_read(&ed->bytes_read);
+ break;
+ case PT_WRITE:
+ *val += atomic64_read(&ed->bytes_written);
+ break;
+ case PT_WF_RECV:
+ *val += atomic64_read(&ed->bytes_recv);
+ break;
+ case PT_WF_SEND:
+ *val += atomic64_read(&ed->bytes_send);
+ break;
+ case PT_L2CAP_RECV:
+ *val += atomic64_read(&ed->bytes_l2cap_recv_acldata);
+ break;
+ case PT_SCO_RECV:
+ *val += atomic64_read(&ed->bytes_sco_recv_scodata);
+ break;
+ case PT_SEND_ACL:
+ *val += atomic64_read(&ed->bytes_hci_send_acl);
+ break;
+ case PT_SEND_SCO:
+ *val += atomic64_read(&ed->bytes_hci_send_sco);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int current_parameter_apps(enum parameter_type pt, void *buf, int sz)
+{
+ struct cmd_pt cmdp;
+
+ cmdp.pt = pt;
+ cmdp.buf = buf;
+ cmdp.sz = sz;
+
+ on_each_proc(callback_for_proc, (void *)&cmdp);
+
+ return 0;
+}
+
+/**
+ * @brief Get energy parameter
+ *
+ * @param pe Type of energy parameter
+ * @param buf Buffer
+ * @param sz Buffer size
+ * @return Error code
+ */
+int get_parameter_energy(enum parameter_energy pe, void *buf, size_t sz)
+{
+ unsigned long flags;
+ u64 *val = buf; /* currently all parameters are u64 vals */
+ int ret = 0;
+
+ switch (pe) {
+ case PE_TIME_IDLE:
+ cpus_time_lock(&ct_idle, flags);
+ /* for the moment we consider only CPU[0] idle time */
+ *val = cpu_time_get_running(&ct_idle, 0, get_ntime());
+ cpus_time_unlock(&ct_idle, flags);
+ break;
+ case PE_TIME_SYSTEM:
+ cpus_time_lock(&ed_system.ct, flags);
+ cpus_time_get_running_all(&ed_system.ct, val, get_ntime());
+ cpus_time_unlock(&ed_system.ct, flags);
+ break;
+ case PE_TIME_APPS:
+ current_parameter_apps(PT_CPU, buf, sz);
+ break;
+ case PE_READ_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_read);
+ break;
+ case PE_WRITE_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_written);
+ break;
+ case PE_WF_RECV_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_recv);
+ break;
+ case PE_WF_SEND_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_send);
+ break;
+ case PE_L2CAP_RECV_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_l2cap_recv_acldata);
+ break;
+ case PE_SCO_RECV_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_sco_recv_scodata);
+ break;
+ case PT_SEND_ACL_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_hci_send_acl);
+ break;
+ case PT_SEND_SCO_SYSTEM:
+ *val = atomic64_read(&ed_system.bytes_hci_send_sco);
+ break;
+ case PE_READ_APPS:
+ current_parameter_apps(PT_READ, buf, sz);
+ break;
+ case PE_WRITE_APPS:
+ current_parameter_apps(PT_WRITE, buf, sz);
+ break;
+ case PE_WF_RECV_APPS:
+ current_parameter_apps(PT_WF_RECV, buf, sz);
+ break;
+ case PE_WF_SEND_APPS:
+ current_parameter_apps(PT_WF_SEND, buf, sz);
+ break;
+ case PE_L2CAP_RECV_APPS:
+ current_parameter_apps(PT_L2CAP_RECV, buf, sz);
+ break;
+ case PE_SCO_RECV_APPS:
+ current_parameter_apps(PT_SCO_RECV, buf, sz);
+ break;
+ case PT_SEND_ACL_APPS:
+ current_parameter_apps(PT_SEND_ACL, buf, sz);
+ break;
+ case PT_SEND_SCO_APPS:
+ current_parameter_apps(PT_SEND_SCO, buf, sz);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_SWAP_HOOK_ENERGY
+static struct swap_hook_energy hook_energy = {
+ .bt_recvmsg = calc_bt_recv_energy,
+ .bt_sendmsg = calc_bt_send_energy,
+ .wifi_recvmsg = calc_wifi_recv_energy ,
+ .wifi_sendmsg = calc_wifi_send_energy
+};
+
+
+static struct td_raw sys_call_tdraw;
+
+static void entry_hook_sys_rw(struct hook_syscall *self, struct pt_regs *regs)
+{
+ struct sys_read_data *srd =
+ (struct sys_read_data *)swap_td_raw(&sys_call_tdraw, current);
+ srd->fd = (int)swap_get_sarg(regs, 0);
+}
+
+static void return_hook_sys_read(struct hook_syscall *self,
+ struct pt_regs *regs)
+{
+ struct sys_read_data *srd;
+ struct energy_data *ed;
+ int ret = regs_return_value(regs);
+
+ if (ret <= 0)
+ return;
+
+ srd = (struct sys_read_data *)swap_td_raw(&sys_call_tdraw, current);
+ if (!check_file(srd->fd))
+ return;
+
+ ed = get_energy_data(current);
+ if (ed)
+ atomic64_add(ret, &ed->bytes_read);
+ atomic64_add(ret, &ed_system.bytes_read);
+}
+
+static void return_hook_sys_write(struct hook_syscall *self,
+ struct pt_regs *regs)
+{
+ struct sys_read_data *srd;
+ struct energy_data *ed;
+ int ret = regs_return_value(regs);
+
+ if (ret > 0)
+ return;
+
+ srd = (struct sys_read_data *)swap_td_raw(&sys_call_tdraw, current);
+ if (!check_file(srd->fd))
+ return;
+
+ ed = get_energy_data(current);
+ if (ed)
+ atomic64_add(ret, &ed->bytes_written);
+ atomic64_add(ret, &ed_system.bytes_written);
+}
+
+static struct hook_syscall sys_read_hook = {
+ .entry = entry_hook_sys_rw,
+ .exit = return_hook_sys_read
+};
+
+static struct hook_syscall sys_write_hook = {
+ .entry = entry_hook_sys_rw,
+ .exit = return_hook_sys_write
+};
+
+
+# ifdef CONFIG_SWAP_HOOK_SWITCH_TO
+static void handler_switch(struct task_struct *prev,
+ struct task_struct *next)
+{
+ do_entry_handler_switch(prev);
+ do_ret_handler_switch(next);
+}
+
+static struct swap_hook_ctx switch_to_hook = {
+ .hook = handler_switch
+};
+# endif /* CONFIG_SWAP_HOOK_SWITCH_TO */
+
+int do_set_energy(void)
+{
+ int ret = 0;
+
+ init_data_energy();
+
+ swap_hook_ctx_reg(&switch_to_hook);
+ ret = swap_td_raw_reg(&sys_call_tdraw, sizeof(struct sys_read_data));
+ if (ret)
+ return ret;
+
+ hook_syscall_reg(&sys_read_hook, __NR_read);
+ hook_syscall_reg(&sys_write_hook, __NR_write);
+
+ /* TODO: add compat mode support */
+
+ swap_hook_energy_set(&hook_energy);
+ /* TODO: init lcd */
+
+ return ret;
+}
+
+void do_unset_energy(void)
+{
+ /* TODO: uinit lcd */
+ swap_hook_energy_unset();
+ swap_hook_ctx_unreg(&switch_to_hook);
+ hook_syscall_unreg(&sys_write_hook);
+ hook_syscall_unreg(&sys_read_hook);
+
+ swap_td_raw_unreg(&sys_call_tdraw);
+ uninit_data_energy();
+}
+
+int energy_once(void)
+{
+ return 0;
+}
+#else /* CONFIG_SWAP_HOOK_ENERGY */
+
+int do_set_energy(void)
+{
+ int ret = 0;
+
+ init_data_energy();
+
+ ret = swap_register_kretprobe(&sys_read_krp);
+ if (ret) {
+ printk(KERN_INFO "swap_register_kretprobe(sys_read) "
+ "result=%d!\n", ret);
+ return ret;
+ }
+
+ ret = swap_register_kretprobe(&sys_write_krp);
+ if (ret != 0) {
+ printk(KERN_INFO "swap_register_kretprobe(sys_write) "
+ "result=%d!\n", ret);
+ goto unregister_sys_read;
+ }
+
+ ret = swap_register_kretprobe(&switch_to_krp);
+ if (ret) {
+ printk(KERN_INFO "swap_register_kretprobe(__switch_to) "
+ "result=%d!\n",
+ ret);
+ goto unregister_sys_write;
+ }
+
+ energy_xxx_set(bt_probes, bt_probes_cnt, &energy_bt_flag);
+ energy_xxx_set(wifi_probes, wifi_probes_cnt, &wifi_flag);
+
+ /* TODO: check return value */
+ lcd_set_energy();
+
+ return ret;
+
+unregister_sys_read:
+ swap_unregister_kretprobe(&sys_read_krp);
+
+unregister_sys_write:
+ swap_unregister_kretprobe(&sys_write_krp);
+
+ return ret;
+}
+
+void do_unset_energy(void)
+{
+ lcd_unset_energy();
+ energy_xxx_unset(wifi_probes, wifi_probes_cnt, &wifi_flag);
+ energy_xxx_unset(bt_probes, bt_probes_cnt, &energy_bt_flag);
+
+ swap_unregister_kretprobe(&switch_to_krp);
+ swap_unregister_kretprobe(&sys_write_krp);
+ swap_unregister_kretprobe(&sys_read_krp);
+
+ uninit_data_energy();
+}
+
+int energy_once(void)
+{
+ const char *sym;
+
+ sym = "__switch_to";
+ switch_to_krp.kp.addr = swap_ksyms(sym);
+ if (switch_to_krp.kp.addr == 0)
+ goto not_found;
+
+ sym = "sys_read";
+ sys_read_krp.kp.addr = swap_ksyms(sym);
+ if (sys_read_krp.kp.addr == 0)
+ goto not_found;
+
+ sym = "sys_write";
+ sys_write_krp.kp.addr = swap_ksyms(sym);
+ if (sys_write_krp.kp.addr == 0)
+ goto not_found;
+
+ energy_xxx_once(bt_probes, bt_probes_cnt);
+ energy_xxx_once(wifi_probes, wifi_probes_cnt);
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
+#endif /* CONFIG_SWAP_HOOK_ENERGY */
+
+static DEFINE_MUTEX(mutex_enable);
+static int energy_enable;
+
+/**
+ * @brief Start measuring the energy consumption
+ *
+ * @return Error code
+ */
+int set_energy(void)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&mutex_enable);
+ if (energy_enable) {
+ printk(KERN_INFO "energy profiling is already run!\n");
+ goto unlock;
+ }
+
+ ret = do_set_energy();
+ if (ret == 0)
+ energy_enable = 1;
+
+unlock:
+ mutex_unlock(&mutex_enable);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(set_energy);
+
+/**
+ * @brief Stop measuring the energy consumption
+ *
+ * @return Error code
+ */
+int unset_energy(void)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_enable);
+ if (energy_enable == 0) {
+ printk(KERN_INFO "energy profiling is not running!\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ do_unset_energy();
+
+ energy_enable = 0;
+unlock:
+ mutex_unlock(&mutex_enable);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(unset_energy);
+
+/**
+ * @brief Initialization energy
+ *
+ * @return Error code
+ */
+int energy_init(void)
+{
+ int ret;
+
+ ret = init_feature();
+ if (ret)
+ printk(KERN_INFO "Cannot init feature\n");
+
+ return ret;
+}
+
+/**
+ * @brief Deinitialization energy
+ *
+ * @return Void
+ */
+void energy_uninit(void)
+{
+ uninit_feature();
+
+ if (energy_enable)
+ do_unset_energy();
+}
--- /dev/null
+#ifndef _ENERGY_H
+#define _ENERGY_H
+
+/**
+ * @file energy/energy.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENCE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ */
+
+
+#include <linux/types.h>
+
+
+/** Description of parameters */
+enum parameter_energy {
+ PE_TIME_IDLE, /**< IDLE working time */
+ PE_TIME_SYSTEM, /**< system working time */
+ PE_TIME_APPS, /**< apps working time */
+ PE_READ_SYSTEM, /**< number of bytes are read by system */
+ PE_WRITE_SYSTEM, /**< number of bytes are write by system */
+ PE_READ_APPS, /**< number of bytes are read by apps */
+ PE_WRITE_APPS, /**< number of bytes are write by apps */
+ PE_WF_RECV_SYSTEM, /**< number of bytes are receive by system through wifi */
+ PE_WF_SEND_SYSTEM, /**< number of bytes are send by system through wifi */
+ PE_WF_RECV_APPS, /**< number of bytes are receive by apps through wifi */
+ PE_WF_SEND_APPS, /**< number of bytes are send by apps through wifi */
+ PE_L2CAP_RECV_SYSTEM, /**< number of bytes(ACL packets) are recv by system through bluetooth */
+ PE_L2CAP_RECV_APPS, /**< number of bytes(ACL packets) are recv by apps through bluetooth */
+ PE_SCO_RECV_SYSTEM, /**< number of bytes(SCO packets) are recv by system through bluetooth */
+ PE_SCO_RECV_APPS, /**< number of bytes(SCO packets) are recv by apps through bluetooth */
+ PT_SEND_ACL_SYSTEM, /**< number of bytes(ACL packets) are send by system through bluetooth */
+ PT_SEND_ACL_APPS, /**< number of bytes(ACL packets) are send by apps through bluetooth */
+ PT_SEND_SCO_SYSTEM, /**< number of bytes(SCO packets) are send by system through bluetooth */
+ PT_SEND_SCO_APPS, /**< number of bytes(SCO packets) are send by apps through bluetooth */
+};
+
+
+int energy_once(void);
+int energy_init(void);
+void energy_uninit(void);
+
+int set_energy(void);
+int unset_energy(void);
+
+int get_parameter_energy(enum parameter_energy pe, void *buf, size_t sz);
+
+#endif /* _ENERGY_H */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * energy/energy_mod.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <master/swap_initializer.h>
+#include "energy.h"
+#include "debugfs_energy.h"
+
+
+SWAP_LIGHT_INIT_MODULE(energy_once, energy_init, energy_uninit,
+ init_debugfs_energy, exit_debugfs_energy);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * energy/lcd/lcd_base.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/fb.h>
+#include <energy/tm_stat.h>
+#include <energy/debugfs_energy.h>
+#include "lcd_base.h"
+#include "lcd_debugfs.h"
+
+
+/**
+ * @brief Read the number of file
+ *
+ * @param path of the file
+ * @return Value or error(when negative)
+ */
+int read_val(const char *path)
+{
+ int ret;
+ struct file *f;
+ unsigned long val;
+ enum { buf_len = 32 };
+ char buf[buf_len];
+
+ f = filp_open(path, O_RDONLY, 0);
+ if (IS_ERR(f)) {
+ printk(KERN_INFO "cannot open file \'%s\'", path);
+ return PTR_ERR(f);
+ }
+
+ ret = kernel_read(f, 0, buf, sizeof(buf));
+ filp_close(f, NULL);
+ if (ret < 0)
+ return ret;
+
+ buf[ret >= buf_len ? buf_len - 1 : ret] = '\0';
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ return (int)val;
+}
+
+enum {
+ brt_no_init = -1,
+ brt_cnt = 10
+};
+
+enum power_t {
+ PW_ON,
+ PW_OFF
+};
+
+struct lcd_priv_data {
+ int min_brt;
+ int max_brt;
+
+ size_t tms_brt_cnt;
+ struct tm_stat *tms_brt;
+ spinlock_t lock_tms;
+ int brt_old;
+ enum power_t power;
+
+ u64 min_denom;
+ u64 min_num;
+ u64 max_denom;
+ u64 max_num;
+};
+
+static void *create_lcd_priv(struct lcd_ops *ops, size_t tms_brt_cnt)
+{
+ int i;
+ struct lcd_priv_data *lcd;
+
+ if (tms_brt_cnt <= 0) {
+ printk(KERN_INFO "error variable tms_brt_cnt=%zu\n",
+ tms_brt_cnt);
+ return NULL;
+ }
+
+ lcd = kmalloc(sizeof(*lcd) + sizeof(*lcd->tms_brt) * tms_brt_cnt,
+ GFP_KERNEL);
+ if (lcd == NULL) {
+ printk(KERN_INFO "error: %s - out of memory\n", __func__);
+ return NULL;
+ }
+
+ lcd->tms_brt = (void *)lcd + sizeof(*lcd);
+ lcd->tms_brt_cnt = tms_brt_cnt;
+
+ lcd->min_brt = ops->get(ops, LPD_MIN_BRIGHTNESS);
+ lcd->max_brt = ops->get(ops, LPD_MAX_BRIGHTNESS);
+
+ for (i = 0; i < tms_brt_cnt; ++i)
+ tm_stat_init(&lcd->tms_brt[i]);
+
+ spin_lock_init(&lcd->lock_tms);
+
+ lcd->brt_old = brt_no_init;
+ lcd->power = PW_OFF;
+
+ lcd->min_denom = 1;
+ lcd->min_num = 1;
+ lcd->max_denom = 1;
+ lcd->max_num = 1;
+
+ return (void *)lcd;
+}
+
+static void destroy_lcd_priv(void *data)
+{
+ kfree(data);
+}
+
+static struct lcd_priv_data *get_lcd_priv(struct lcd_ops *ops)
+{
+ return (struct lcd_priv_data *)ops->priv;
+}
+
+static void clean_brightness(struct lcd_ops *ops)
+{
+ struct lcd_priv_data *lcd = get_lcd_priv(ops);
+ int i;
+
+ spin_lock(&lcd->lock_tms);
+ for (i = 0; i < lcd->tms_brt_cnt; ++i)
+ tm_stat_init(&lcd->tms_brt[i]);
+
+ lcd->brt_old = brt_no_init;
+ spin_unlock(&lcd->lock_tms);
+}
+
+static int get_brt_num_of_array(struct lcd_priv_data *lcd, int brt)
+{
+ if (brt > lcd->max_brt || brt < lcd->min_brt) {
+ printk(KERN_INFO "LCD energy error: set brightness=%d, "
+ "when brightness[%d..%d]\n",
+ brt, lcd->min_brt, lcd->max_brt);
+ brt = brt > lcd->max_brt ? lcd->max_brt : lcd->min_brt;
+ }
+
+ return lcd->tms_brt_cnt * (brt - lcd->min_brt) /
+ (lcd->max_brt - lcd->min_brt + 1);
+}
+
+static void set_brightness(struct lcd_ops *ops, int brt)
+{
+ struct lcd_priv_data *lcd = get_lcd_priv(ops);
+ int n = get_brt_num_of_array(lcd, brt);
+
+ spin_lock(&lcd->lock_tms);
+
+ if (lcd->power == PW_ON && lcd->brt_old != n) {
+ u64 time = get_ntime();
+ if (lcd->brt_old != brt_no_init)
+ tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
+
+ tm_stat_set_timestamp(&lcd->tms_brt[n], time);
+ }
+ lcd->brt_old = n;
+
+ spin_unlock(&lcd->lock_tms);
+}
+
+static void set_power_on_set_brt(struct lcd_priv_data *lcd)
+{
+ if (lcd->brt_old != brt_no_init) {
+ u64 time = get_ntime();
+ tm_stat_set_timestamp(&lcd->tms_brt[lcd->brt_old], time);
+ }
+}
+
+static void set_power_on(struct lcd_priv_data *lcd)
+{
+ if (lcd->power == PW_OFF)
+ set_power_on_set_brt(lcd);
+
+ lcd->power = PW_ON;
+}
+
+static void set_power_off_update_brt(struct lcd_priv_data *lcd)
+{
+ if (lcd->brt_old != brt_no_init) {
+ u64 time = get_ntime();
+ tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
+ lcd->brt_old = brt_no_init;
+ }
+}
+
+static void set_power_off(struct lcd_priv_data *lcd)
+{
+ if (lcd->power == PW_ON)
+ set_power_off_update_brt(lcd);
+
+ lcd->power = PW_OFF;
+}
+
+static void set_power(struct lcd_ops *ops, int val)
+{
+ struct lcd_priv_data *lcd = get_lcd_priv(ops);
+
+ spin_lock(&lcd->lock_tms);
+
+ switch (val) {
+ case FB_BLANK_UNBLANK:
+ set_power_on(lcd);
+ break;
+ case FB_BLANK_POWERDOWN:
+ set_power_off(lcd);
+ break;
+ default:
+ printk(KERN_INFO "LCD energy error: set power=%d\n", val);
+ break;
+ }
+
+ spin_unlock(&lcd->lock_tms);
+}
+
+static int func_notifier_lcd(struct lcd_ops *ops, enum lcd_action_type action,
+ void *data)
+{
+ switch (action) {
+ case LAT_BRIGHTNESS:
+ set_brightness(ops, VOIDP2INT(data));
+ break;
+ case LAT_POWER:
+ set_power(ops, VOIDP2INT(data));
+ break;
+ default:
+ printk(KERN_INFO "LCD energy error: action=%d\n", action);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Get the array size of LCD
+ *
+ * @param ops LCD operations
+ * @return Array size
+ */
+size_t get_lcd_size_array(struct lcd_ops *ops)
+{
+ struct lcd_priv_data *lcd = get_lcd_priv(ops);
+
+ return lcd->tms_brt_cnt;
+}
+
+/**
+ * @brief Get an array of times
+ *
+ * @param ops LCD operations
+ * @param array_time[out] Array of times
+ * @return Void
+ */
+void get_lcd_array_time(struct lcd_ops *ops, u64 *array_time)
+{
+ struct lcd_priv_data *lcd = get_lcd_priv(ops);
+ int i;
+
+ spin_lock(&lcd->lock_tms);
+ for (i = 0; i < lcd->tms_brt_cnt; ++i)
+ array_time[i] = tm_stat_running(&lcd->tms_brt[i]);
+
+ if (lcd->power == PW_ON && lcd->brt_old != brt_no_init) {
+ int old = lcd->brt_old;
+ struct tm_stat *tm = &lcd->tms_brt[old];
+
+ array_time[old] += get_ntime() - tm_stat_timestamp(tm);
+ }
+ spin_unlock(&lcd->lock_tms);
+}
+
+static int register_lcd(struct lcd_ops *ops)
+{
+ int ret = 0;
+
+ ops->priv = create_lcd_priv(ops, brt_cnt);
+
+ /* TODO: create init_func() for 'struct rational' */
+ ops->min_coef.num = 1;
+ ops->min_coef.denom = 1;
+ ops->max_coef.num = 1;
+ ops->max_coef.denom = 1;
+
+ ops->notifier = func_notifier_lcd;
+
+ ret = register_lcd_debugfs(ops);
+ if (ret)
+ destroy_lcd_priv(ops->priv);
+
+ return ret;
+}
+
+static void unregister_lcd(struct lcd_ops *ops)
+{
+ unregister_lcd_debugfs(ops);
+ destroy_lcd_priv(ops->priv);
+}
+
+
+
+
+/* ============================================================================
+ * === LCD_INIT/LCD_EXIT ===
+ * ============================================================================
+ */
+typedef struct lcd_ops *(*get_ops_t)(void);
+
+DEFINITION_LCD_FUNC;
+
+get_ops_t lcd_ops[] = DEFINITION_LCD_ARRAY;
+enum { lcd_ops_cnt = sizeof(lcd_ops) / sizeof(get_ops_t) };
+
+enum ST_LCD_OPS {
+ SLO_REGISTER = 1 << 0,
+ SLO_SET = 1 << 1
+};
+
+static DEFINE_MUTEX(lcd_lock);
+static enum ST_LCD_OPS stat_lcd_ops[lcd_ops_cnt];
+
+static void do_lcd_exit(void)
+{
+ int i;
+ struct lcd_ops *ops;
+
+ mutex_lock(&lcd_lock);
+ for (i = 0; i < lcd_ops_cnt; ++i) {
+ ops = lcd_ops[i]();
+
+ if (stat_lcd_ops[i] & SLO_SET) {
+ ops->unset(ops);
+ stat_lcd_ops[i] &= ~SLO_SET;
+ }
+
+ if (stat_lcd_ops[i] & SLO_REGISTER) {
+ unregister_lcd(ops);
+ stat_lcd_ops[i] &= ~SLO_REGISTER;
+ }
+ }
+ mutex_unlock(&lcd_lock);
+}
+
+/**
+ * @brief LCD deinitialization
+ *
+ * @return Void
+ */
+void lcd_exit(void)
+{
+ do_lcd_exit();
+}
+
+static int do_lcd_init(void)
+{
+ int i, ret, count = 0;
+ struct lcd_ops *ops;
+
+ mutex_lock(&lcd_lock);
+ for (i = 0; i < lcd_ops_cnt; ++i) {
+ ops = lcd_ops[i]();
+ if (ops == NULL) {
+ printk(KERN_INFO "error %s [ops == NULL]\n", __func__);
+ continue;
+ }
+
+ if (0 == ops->check(ops)) {
+ printk(KERN_INFO "error checking %s\n", ops->name);
+ continue;
+ }
+
+ ret = register_lcd(ops);
+ if (ret) {
+ printk(KERN_INFO "error register_lcd %s\n", ops->name);
+ continue;
+ }
+
+ stat_lcd_ops[i] |= SLO_REGISTER;
+ ++count;
+ }
+ mutex_unlock(&lcd_lock);
+
+ return count ? 0 : -EPERM;
+}
+
+/**
+ * @brief LCD initialization
+ *
+ * @return Error code
+ */
+int lcd_init(void)
+{
+ int ret;
+
+ ret = do_lcd_init();
+ if (ret)
+ printk(KERN_INFO "LCD is not supported\n");
+
+ return ret;
+}
+
+
+
+/* ============================================================================
+ * === LCD_SET_ENERGY/LCD_UNSET_ENERGY ===
+ * ============================================================================
+ */
+
+/**
+ * @brief Start measuring the energy consumption of LСD
+ *
+ * @return Error code
+ */
+int lcd_set_energy(void)
+{
+ int i, ret, count = 0;
+ struct lcd_ops *ops;
+
+ mutex_lock(&lcd_lock);
+ for (i = 0; i < lcd_ops_cnt; ++i) {
+ ops = lcd_ops[i]();
+ if (stat_lcd_ops[i] & SLO_REGISTER) {
+ ret = ops->set(ops);
+ if (ret) {
+ printk(KERN_INFO "error %s set LCD energy",
+ ops->name);
+ continue;
+ }
+
+ set_brightness(ops, ops->get(ops, LPD_BRIGHTNESS));
+ set_power(ops, ops->get(ops, LPD_POWER));
+
+ stat_lcd_ops[i] |= SLO_SET;
+ ++count;
+ }
+ }
+ mutex_unlock(&lcd_lock);
+
+ return count ? 0 : -EPERM;
+}
+
+/**
+ * @brief Stop measuring the energy consumption of LСD
+ *
+ * @return Void
+ */
+void lcd_unset_energy(void)
+{
+ int i, ret;
+ struct lcd_ops *ops;
+
+ mutex_lock(&lcd_lock);
+ for (i = 0; i < lcd_ops_cnt; ++i) {
+ ops = lcd_ops[i]();
+ if (stat_lcd_ops[i] & SLO_SET) {
+ ret = ops->unset(ops);
+ if (ret)
+ printk(KERN_INFO "error %s unset LCD energy",
+ ops->name);
+
+ clean_brightness(ops);
+ stat_lcd_ops[i] &= ~SLO_SET;
+ }
+ }
+ mutex_unlock(&lcd_lock);
+}
--- /dev/null
+#ifndef _LCD_BASE_H
+#define _LCD_BASE_H
+
+/**
+ * @file energy/lcd/lcd_base.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ * Description of the interface for interacting with LСD
+ */
+
+
+#include <linux/errno.h>
+#include <energy/rational_debugfs.h>
+
+
+#define VOIDP2INT(x) ((int)(unsigned long)(x))
+#define INT2VOIDP(x) ((void *)(unsigned long)(x))
+
+
+/** Description of actions */
+enum lcd_action_type {
+ LAT_BRIGHTNESS, /**< LCD brightness */
+ LAT_POWER /**< LCD power */
+};
+
+
+/** Description of parameters */
+enum lcd_parameter_type {
+ LPD_MIN_BRIGHTNESS, /**< minimum brightness value */
+ LPD_MAX_BRIGHTNESS, /**< maximum brightness value */
+ LPD_BRIGHTNESS, /**< current brightness value */
+
+ LPD_POWER /**< current power value */
+};
+
+struct lcd_ops;
+
+/**
+ * @brief LCD callback type
+ *
+ * @param ops LCD operations
+ * @return Error code
+ */
+typedef int (*call_lcd)(struct lcd_ops *ops);
+
+/**
+ * @brief LCD notifier type
+ *
+ * @param ops LCD operations
+ * @param action Event type
+ * @param data Date
+ * @return Error code
+ */
+typedef int (*notifier_lcd)(struct lcd_ops *ops, enum lcd_action_type action,
+ void *data);
+
+/**
+ * @brief LCD parameter type
+ *
+ * @param ops LCD operations
+ * @param type Requested parameter type
+ * @return Requested parameter value
+ *
+ */
+typedef unsigned long (*get_parameter_lcd)(struct lcd_ops *ops,
+ enum lcd_parameter_type type);
+
+
+/**
+ * @struct lcd_ops
+ * @breaf set of operations available for LСD
+ */
+struct lcd_ops {
+ char *name; /**< LCD driver name */
+ notifier_lcd notifier; /**< Notifier */
+ get_parameter_lcd get; /**< Method to obtain the parameters */
+
+ call_lcd check; /**< LCD check on device */
+ call_lcd set; /**< LCD initialization */
+ call_lcd unset; /**< LCD deinitialization */
+
+ /* for debugfs */
+ struct dentry *dentry; /**< Dentry of debugfs for this LCD */
+ struct rational min_coef; /**< Minimum coefficient */
+ struct rational max_coef; /**< Maximum coefficient */
+
+ void *priv; /**< Private data */
+};
+
+size_t get_lcd_size_array(struct lcd_ops *ops);
+void get_lcd_array_time(struct lcd_ops *ops, u64 *array_time);
+
+int read_val(const char *path);
+
+int lcd_set_energy(void);
+void lcd_unset_energy(void);
+
+int lcd_init(void);
+void lcd_exit(void);
+
+#endif /* _LCD_BASE_H */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * energy/lcd/lcd_debugfs.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <master/swap_debugfs.h>
+#include <energy/lcd/lcd_base.h>
+#include <energy/debugfs_energy.h>
+#include <energy/rational_debugfs.h>
+
+
+static int get_system(void *data, u64 *val)
+{
+ struct lcd_ops *ops = (struct lcd_ops *)data;
+ const size_t size = get_lcd_size_array(ops);
+ const size_t size_1 = size - 1;
+ u64 i_max, j_min, t, e = 0;
+ u64 *array_time;
+ int i, j;
+
+ array_time = kmalloc(sizeof(*array_time) * size, GFP_KERNEL);
+ if (array_time == NULL)
+ return -ENOMEM;
+
+ get_lcd_array_time(ops, array_time);
+
+ for (i = 0; i < size; ++i) {
+ t = array_time[i];
+
+ /* e = (i * max + (k - i) * min) * t / k */
+ j = size_1 - i;
+ i_max = div_u64(i * ops->max_coef.num * t,
+ ops->max_coef.denom);
+ j_min = div_u64(j * ops->min_coef.num * t,
+ ops->min_coef.denom);
+ e += div_u64(i_max + j_min, size_1);
+ }
+
+ kfree(array_time);
+
+ *val = e;
+
+ return 0;
+}
+
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_get_system, get_system, NULL, "%llu\n");
+
+
+static struct dentry *lcd_dir;
+
+/**
+ * @brief Register LCD in debugfs
+ *
+ * @param ops LCD operations
+ * @return Error code
+ */
+int register_lcd_debugfs(struct lcd_ops *ops)
+{
+ int ret;
+ struct dentry *dentry, *system;
+
+ if (lcd_dir == NULL)
+ return -EINVAL;
+
+ dentry = swap_debugfs_create_dir(ops->name, lcd_dir);
+ if (dentry == NULL)
+ return -ENOMEM;
+
+ ret = create_rational_files(dentry, &ops->min_coef,
+ "min_num", "min_denom");
+ if (ret)
+ goto fail;
+
+ ret = create_rational_files(dentry, &ops->max_coef,
+ "max_num", "max_denom");
+ if (ret)
+ goto fail;
+
+ system = swap_debugfs_create_file("system", 0600, dentry, (void *)ops,
+ &fops_get_system);
+ if (system == NULL)
+ goto fail;
+
+ ops->dentry = dentry;
+
+ return 0;
+fail:
+ debugfs_remove_recursive(dentry);
+ return -ENOMEM;
+}
+
+/**
+ * @brief Unregister LCD in debugfs
+ *
+ * @param ops LCD operations
+ * @return Void
+ */
+void unregister_lcd_debugfs(struct lcd_ops *ops)
+{
+ debugfs_remove_recursive(ops->dentry);
+}
+
+/**
+ * @brief Destroy debugfs for LCD
+ *
+ * @return Void
+ */
+void exit_lcd_debugfs(void)
+{
+ if (lcd_dir)
+ debugfs_remove_recursive(lcd_dir);
+
+ lcd_dir = NULL;
+}
+
+/**
+ * @brief Create debugfs for LCD
+ *
+ * @param dentry Dentry
+ * @return Error code
+ */
+int init_lcd_debugfs(struct dentry *energy_dir)
+{
+ lcd_dir = swap_debugfs_create_dir("lcd", energy_dir);
+ if (lcd_dir == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
--- /dev/null
+#ifndef _LCD_DEBUGFS_H
+#define _LCD_DEBUGFS_H
+
+/**
+ * @file energy/lcd/lcd_debugfs.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ * Debugfs for LСD
+ *
+ */
+
+
+struct dentry;
+struct lcd_ops;
+
+int register_lcd_debugfs(struct lcd_ops *ops);
+void unregister_lcd_debugfs(struct lcd_ops *ops);
+
+int init_lcd_debugfs(struct dentry *energy_dir);
+void exit_lcd_debugfs(void);
+
+#endif /* _LCD_DEBUGFS_H */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * energy/lcd/maru.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <kprobe/swap_kprobes.h>
+#include <linux/backlight.h>
+#include "lcd_base.h"
+
+
+
+static const char path_backlight[] =
+ "/sys/class/backlight/emulator/brightness";
+static const char path_backlight_min[] =
+ "/sys/class/backlight/emulator/min_brightness";
+static const char path_backlight_max[] =
+ "/sys/class/backlight/emulator/max_brightness";
+static const char path_power[] =
+ "/sys/class/lcd/emulator/lcd_power";
+
+static const char * const all_path[] = {
+ path_backlight,
+ path_backlight_min,
+ path_backlight_max,
+ path_power
+};
+
+enum {
+ all_path_cnt = sizeof(all_path) / sizeof(char *)
+};
+
+
+static int maru_check(struct lcd_ops *ops)
+{
+ int i;
+
+ for (i = 0; i < all_path_cnt; ++i) {
+ int ret = read_val(all_path[i]);
+
+ if (IS_ERR_VALUE(ret))
+ return 0;
+ }
+
+ return 1;
+}
+
+static unsigned long maru_get_parameter(struct lcd_ops *ops,
+ enum lcd_parameter_type type)
+{
+ switch (type) {
+ case LPD_MIN_BRIGHTNESS:
+ return read_val(path_backlight_min);
+ case LPD_MAX_BRIGHTNESS:
+ return read_val(path_backlight_max);
+ case LPD_BRIGHTNESS:
+ return read_val(path_backlight);
+ case LPD_POWER:
+ return read_val(path_power);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+
+
+
+static int entry_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+static int ret_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+
+static struct kretprobe set_backlight_krp = {
+ .kp.symbol_name = "marubl_send_intensity",
+ .entry_handler = entry_handler_set_backlight,
+ .handler = ret_handler_set_backlight,
+ .data_size = sizeof(int)
+};
+
+
+
+
+
+static int maru_set(struct lcd_ops *ops)
+{
+ return swap_register_kretprobe(&set_backlight_krp);
+}
+
+static int maru_unset(struct lcd_ops *ops)
+{
+ swap_unregister_kretprobe(&set_backlight_krp);
+ return 0;
+}
+
+static struct lcd_ops maru_ops = {
+ .name = "maru",
+ .check = maru_check,
+ .set = maru_set,
+ .unset = maru_unset,
+ .get = maru_get_parameter
+};
+
+struct lcd_ops *LCD_MAKE_FNAME(maru)(void)
+{
+ return &maru_ops;
+}
+
+
+
+
+
+static int entry_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int *brightness = (int *)ri->data;
+ struct backlight_device *bd;
+
+ bd = (struct backlight_device *)swap_get_karg(regs, 0);
+ *brightness = bd->props.brightness;
+
+ return 0;
+}
+
+static int ret_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ int *brightness = (int *)ri->data;
+
+ if (!ret && maru_ops.notifier)
+ maru_ops.notifier(&maru_ops, LAT_BRIGHTNESS,
+ INT2VOIDP(*brightness));
+
+ return 0;
+}
--- /dev/null
+#include <kprobe/swap_kprobes.h>
+#include "lcd_base.h"
+
+
+static const char path_backlight[] =
+ "/sys/class/backlight/s6e8aa0-bl/brightness";
+static const char path_backlight_min[] =
+ "/sys/class/backlight/s6e8aa0-bl/min_brightness";
+static const char path_backlight_max[] =
+ "/sys/class/backlight/s6e8aa0-bl/max_brightness";
+static const char path_power[] =
+ "/sys/class/lcd/s6e8aa0/lcd_power";
+
+static const char * const all_path[] = {
+ path_backlight,
+ path_backlight_min,
+ path_backlight_max,
+ path_power
+};
+
+enum {
+ all_path_cnt = sizeof(all_path) / sizeof(char *)
+};
+
+
+
+static int s6e8aa0_check(struct lcd_ops *ops)
+{
+ int i;
+
+ for (i = 0; i < all_path_cnt; ++i) {
+ int ret = read_val(all_path[i]);
+
+ if (IS_ERR_VALUE(ret))
+ return 0;
+ }
+
+ return 1;
+}
+
+static unsigned long s6e8aa0_get_parameter(struct lcd_ops *ops,
+ enum lcd_parameter_type type)
+{
+ switch (type) {
+ case LPD_MIN_BRIGHTNESS:
+ return read_val(path_backlight_min);
+ case LPD_MAX_BRIGHTNESS:
+ return read_val(path_backlight_max);
+ case LPD_BRIGHTNESS:
+ return read_val(path_backlight);
+ case LPD_POWER:
+ return read_val(path_power);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+
+static int entry_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+static int ret_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+
+static struct kretprobe set_power_krp = {
+ .kp.symbol_name = "s6e8aa0_set_power",
+ .entry_handler = entry_handler_set_power,
+ .handler = ret_handler_set_power,
+ .data_size = sizeof(int)
+};
+
+
+static int entry_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+static int ret_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+
+static struct kretprobe set_backlight_krp = {
+ .kp.symbol_name = "s6e8aa0_gamma_ctrl",
+ .entry_handler = entry_handler_set_backlight,
+ .handler = ret_handler_set_backlight,
+ .data_size = sizeof(int)
+};
+
+int s6e8aa0_set(struct lcd_ops *ops)
+{
+ int ret;
+
+ ret = swap_register_kretprobe(&set_power_krp);
+ if (ret)
+ return ret;
+
+ ret = swap_register_kretprobe(&set_backlight_krp);
+ if (ret)
+ swap_unregister_kretprobe(&set_power_krp);
+
+ return ret;
+}
+
+int s6e8aa0_unset(struct lcd_ops *ops)
+{
+ swap_unregister_kretprobe(&set_backlight_krp);
+ swap_unregister_kretprobe(&set_power_krp);
+
+ return 0;
+}
+
+static struct lcd_ops s6e8aa0_ops = {
+ .name = "s6e8aa0",
+ .check = s6e8aa0_check,
+ .set = s6e8aa0_set,
+ .unset = s6e8aa0_unset,
+ .get = s6e8aa0_get_parameter
+};
+
+struct lcd_ops *LCD_MAKE_FNAME(s6e8aa0)(void)
+{
+ return &s6e8aa0_ops;
+}
+
+
+
+
+
+/* ============================================================================
+ * === POWER ===
+ * ============================================================================
+ */
+static int entry_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int *power = (int *)ri->data;
+
+ *power = (int)swap_get_karg(regs, 1);
+
+ return 0;
+}
+
+static int ret_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ int *power = (int *)ri->data;
+
+ if (!ret && s6e8aa0_ops.notifier)
+ s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER, INT2VOIDP(*power));
+
+ return 0;
+}
+
+
+
+
+
+/* ============================================================================
+ * === BACKLIGHT ===
+ * ============================================================================
+ */
+static int entry_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int *brightness = (int *)ri->data;
+ *brightness = (int)swap_get_karg(regs, 1);
+
+ return 0;
+}
+
+static int ret_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ int *brightness = (int *)ri->data;
+
+ if (!ret && s6e8aa0_ops.notifier)
+ s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_BRIGHTNESS,
+ INT2VOIDP(*brightness));
+
+ return 0;
+}
--- /dev/null
+#include <kprobe/swap_kprobes.h>
+#include <linux/backlight.h>
+#include "lcd_base.h"
+
+
+static const char path_backlight[] =
+ "/sys/class/backlight/s6e8aa0-bl/brightness";
+static const char path_backlight_max[] =
+ "/sys/class/backlight/s6e8aa0-bl/max_brightness";
+static const char path_power[] =
+ "/sys/class/lcd/s6e8aa0/lcd_power";
+
+static const char * const all_path[] = {
+ path_backlight,
+ path_backlight_max,
+ path_power
+};
+
+enum {
+ all_path_cnt = sizeof(all_path) / sizeof(char *)
+};
+
+
+
+static int s6e8aa0_check(struct lcd_ops *ops)
+{
+ int i;
+
+ for (i = 0; i < all_path_cnt; ++i) {
+ int ret = read_val(all_path[i]);
+
+ if (IS_ERR_VALUE(ret))
+ return 0;
+ }
+
+ return 1;
+}
+
+static unsigned long s6e8aa0_get_parameter(struct lcd_ops *ops,
+ enum lcd_parameter_type type)
+{
+ switch (type) {
+ case LPD_MIN_BRIGHTNESS:
+ return 0;
+ case LPD_MAX_BRIGHTNESS:
+ return read_val(path_backlight_max);
+ case LPD_BRIGHTNESS:
+ return read_val(path_backlight);
+ case LPD_POWER:
+ return read_val(path_power);
+ }
+
+ return -EINVAL;
+}
+
+
+
+static int entry_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+static int ret_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+
+static struct kretprobe set_power_krp = {
+ .kp.symbol_name = "s6e8aa0_set_power",
+ .entry_handler = entry_handler_set_power,
+ .handler = ret_handler_set_power,
+ .data_size = sizeof(int)
+};
+
+
+static int entry_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+static int ret_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+
+static struct kretprobe set_backlight_krp = {
+ .kp.symbol_name = "s6e8aa0_update_status",
+ .entry_handler = entry_handler_set_backlight,
+ .handler = ret_handler_set_backlight,
+ .data_size = sizeof(int)
+};
+
+int s6e8aa0_set(struct lcd_ops *ops)
+{
+ int ret;
+
+ ret = swap_register_kretprobe(&set_power_krp);
+ if (ret)
+ return ret;
+
+ ret = swap_register_kretprobe(&set_backlight_krp);
+ if (ret)
+ swap_unregister_kretprobe(&set_power_krp);
+
+ return ret;
+}
+
+int s6e8aa0_unset(struct lcd_ops *ops)
+{
+ swap_unregister_kretprobe(&set_backlight_krp);
+ swap_unregister_kretprobe(&set_power_krp);
+
+ return 0;
+}
+
+static struct lcd_ops s6e8aa0_ops = {
+ .name = "s6e8aa0_panel",
+ .check = s6e8aa0_check,
+ .set = s6e8aa0_set,
+ .unset = s6e8aa0_unset,
+ .get = s6e8aa0_get_parameter
+};
+
+struct lcd_ops *LCD_MAKE_FNAME(s6e8aa0_panel)(void)
+{
+ return &s6e8aa0_ops;
+}
+
+
+
+
+
+/* ============================================================================
+ * === POWER ===
+ * ============================================================================
+ */
+static int entry_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int *power = (int *)ri->data;
+
+ *power = (int)swap_get_karg(regs, 1);
+
+ return 0;
+}
+
+static int ret_handler_set_power(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ int *power = (int *)ri->data;
+
+ if (!ret && s6e8aa0_ops.notifier)
+ s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER,
+ INT2VOIDP(*power));
+
+ return 0;
+}
+
+
+
+
+
+/* ============================================================================
+ * === BACKLIGHT ===
+ * ============================================================================
+ */
+static int entry_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int *brightness = (int *)ri->data;
+ struct backlight_device *bd;
+
+ bd = (struct backlight_device *)swap_get_karg(regs, 0);
+ *brightness = bd->props.brightness;
+
+ return 0;
+}
+
+static int ret_handler_set_backlight(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret = regs_return_value(regs);
+ int *brightness = (int *)ri->data;
+
+ if (!ret && s6e8aa0_ops.notifier)
+ s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_BRIGHTNESS,
+ INT2VOIDP(*brightness));
+
+ return 0;
+}
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * energy/rational_debugfs.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <master/swap_debugfs.h>
+#include "debugfs_energy.h"
+#include "rational_debugfs.h"
+
+
+static int denom_set(void *data, u64 val)
+{
+ if (val == 0)
+ return -EINVAL;
+
+ *(u64 *)data = val;
+ return 0;
+}
+
+static int denom_get(void *data, u64 *val)
+{
+ *val = *(u64 *)data;
+ return 0;
+}
+
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_denom, denom_get, denom_set, "%llu\n");
+
+/**
+ * @brief Create file in debugfs for rational struct
+ *
+ * @param parent Dentry parent
+ * @param r Pointer to the rational struct
+ * @param num_name File name of numerator
+ * @param denom_name File name of denominator
+ * @return Error code
+ */
+int create_rational_files(struct dentry *parent, struct rational *r,
+ const char *num_name, const char *denom_name)
+{
+ struct dentry *d_num, *d_denom;
+
+ d_num = swap_debugfs_create_u64(num_name, 0600, parent, &r->num);
+ if (d_num == NULL)
+ return -ENOMEM;
+
+ d_denom = swap_debugfs_create_file(denom_name, 0600, parent, &r->denom,
+ &fops_denom);
+ if (d_denom == NULL) {
+ debugfs_remove(d_num);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
--- /dev/null
+#ifndef _RATIONAL_DEBUGFS_H
+#define _RATIONAL_DEBUGFS_H
+
+/**
+ * @file energy/rational_debugfs.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+
+#include <linux/types.h>
+
+
+/**
+ * @struct rational
+ * @brief Description of rational number
+ */
+struct rational {
+ u64 num; /**< Numerator */
+ u64 denom; /**< Denominator */
+};
+
+
+/**
+ * @def DEFINE_RATIONAL
+ * Initialize of rational struct @hideinitializer
+ */
+#define DEFINE_RATIONAL(rational_name) \
+ struct rational rational_name = { \
+ .num = 1, \
+ .denom = 1 \
+ }
+
+
+struct dentry;
+
+int create_rational_files(struct dentry *parent, struct rational *r,
+ const char *num_name, const char *denom_name);
+
+
+#endif /* _RATIONAL_DEBUGFS_H */
--- /dev/null
+#ifndef _TM_STAT_H
+#define _TM_STAT_H
+
+/**
+ * @file energy/tm_stat.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYFIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ */
+
+
+#include <linux/types.h>
+#include <linux/time.h>
+
+
+/**
+ * @struct tm_stat
+ * @brief Description of statistic time
+ */
+struct tm_stat {
+ u64 timestamp; /**< Time stamp */
+ u64 running; /**< Running time */
+};
+
+/**
+ * @def DEFINE_TM_STAT
+ * Initialize of tm_stat struct @hideinitializer
+ */
+#define DEFINE_TM_STAT(tm_name) \
+ struct tm_stat tm_name = { \
+ .timestamp = 0, \
+ .running = 0 \
+ }
+
+
+static inline u64 get_ntime(void)
+{
+ struct timespec ts;
+ getnstimeofday(&ts);
+ return timespec_to_ns(&ts);
+}
+
+static inline void tm_stat_init(struct tm_stat *tm)
+{
+ tm->timestamp = 0;
+ tm->running = 0;
+}
+
+static inline void tm_stat_set_timestamp(struct tm_stat *tm, u64 time)
+{
+ tm->timestamp = time;
+}
+
+static inline u64 tm_stat_timestamp(struct tm_stat *tm)
+{
+ return tm->timestamp;
+}
+
+static inline void tm_stat_update(struct tm_stat *tm, u64 time)
+{
+ tm->running += time - tm->timestamp;
+}
+
+static inline u64 tm_stat_running(struct tm_stat *tm)
+{
+ return tm->running;
+}
+
+static inline u64 tm_stat_current_running(struct tm_stat *tm, u64 now)
+{
+ if (unlikely(now < tm->timestamp))
+ printk(KERN_INFO "XXX %p WARNING now(%llu) < tmstmp(%llu)\n",
+ tm, now, tm->timestamp);
+ return tm->timestamp ? tm->running + now - tm->timestamp : tm->running;
+}
+
+#endif /* _TM_STAT_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_fbiprobe.o
+swap_fbiprobe-y := fbiprobe.o \
+ fbi_msg.o
--- /dev/null
+/**
+ * fbiprobe/fbi_msg.c
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Packing and writing data.
+ */
+
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <writer/swap_msg.h>
+
+struct msg_fbi {
+ u32 var_id;
+ u32 size;
+ char var_data[0];
+} __packed;
+
+
+static char *pack_fbi_info(char *payload, unsigned long var_id, size_t size,
+ char *msg_buf)
+{
+ struct msg_fbi *fbi_m = (struct msg_fbi *)payload;
+
+ fbi_m->var_id = var_id;
+ fbi_m->size = size;
+ if (size != 0) {
+ /* FIXME Possible out of buffer! */
+ memcpy(&fbi_m->var_data, msg_buf, size);
+ }
+
+ /*
+ * If size is 0 that mean we cannot get data for this probe.
+ * But we pack it like error code
+ */
+
+ return payload + sizeof(struct msg_fbi) + size;
+}
+
+void fbi_msg(unsigned long var_id, size_t size, char *msg_buf)
+{
+ struct swap_msg *m;
+ void *p;
+ void *buf_end;
+
+ m = swap_msg_get(MSG_FBI);
+ p = swap_msg_payload(m);
+
+ buf_end = pack_fbi_info(p, var_id, size, msg_buf);
+
+ swap_msg_flush(m, buf_end - p);
+
+ swap_msg_put(m);
+}
--- /dev/null
+/*
+ * @file fbiprobe/fbi_msg.h
+ *
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * Function body instrumetation
+ *
+ */
+
+#ifndef __FBI_MSG_H__
+#define __FBI_MSG_H__
+
+#include <linux/types.h>
+
+void fbi_msg(unsigned long var_id, size_t size, char *msg_buf);
+
+#endif /* __FBI_MSG_H__ */
--- /dev/null
+/*
+ * @file fbiprobe/fbi_probe.h
+ *
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov : FBI implement
+ * 2014 Vitaliy Cherepanov: FBI implement, portage
+ *
+ * @section DESCRIPTION
+ *
+ * Function body instrumentation.
+ *
+ */
+
+#ifndef __FBI_PROBE_MODULE_H__
+#define __FBI_PROBE_MODULE_H__
+
+#include <linux/kernel.h>
+
+/* MESSAGES */
+
+#define MODULE_NAME "SWAP_FBI_PROBE"
+
+/* FBI_DEBUG_ON:
+ * val | DEBUG | MSG | WARN | ERR | CRITICAL|
+ * ----+-------+-----+------+-----+---------|
+ * 0 | OFF | OFF | OFF | OFF | OFF |
+ * 1 | OFF | OFF | OFF | OFF | ON |
+ * 2 | OFF | OFF | OFF | ON | ON |
+ * 3 | OFF | OFF | ON | ON | ON |
+ * 4 | OFF | ON | ON | ON | ON |
+ * 5 | ON | ON | ON | ON | ON |
+ */
+
+#define FBI_DEBUG_LEVEL 3
+
+/** Prints debug message.*/
+#if (FBI_DEBUG_LEVEL >= 5)
+#define print_debug(msg, args...) \
+ printk(KERN_DEBUG MODULE_NAME " DEBUG : " msg, ##args)
+#else
+#define print_debug(msg, args...)
+#endif
+
+/** Prints info message.*/
+#if (FBI_DEBUG_LEVEL >= 4)
+#define print_msg(msg, args...) \
+ printk(KERN_INFO MODULE_NAME " : " msg, ##args)
+#else
+#define print_msg(msg, args...)
+#endif
+
+/** Prints warning message.*/
+#if (FBI_DEBUG_LEVEL >= 3)
+#define print_warn(msg, args...) \
+ printk(KERN_WARNING MODULE_NAME " WARNING : " msg, ##args)
+#else
+#define print_warn(msg, args...)
+#endif
+
+/** Prints error message.*/
+#if (FBI_DEBUG_LEVEL >= 2)
+#define print_err(msg, args...) \
+ printk(KERN_ERR MODULE_NAME " ERROR : " msg, ##args)
+#else
+#define print_err(msg, args...)
+#endif
+
+/** Prints critical error message.*/
+#if (FBI_DEBUG_LEVEL >= 1)
+#define print_crit(msg, args...) \
+ printk(KERN_CRIT MODULE_NAME " CRITICAL : " msg, ##args)
+#else
+#define print_crit(msg, args...)
+#endif
+
+#endif /* __FBI_PROBE_MODULE_H__ */
--- /dev/null
+/*
+ * @file fbiprobe/fbi_probe.c
+ *
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov : FBI implement
+ * 2014 Vitaliy Cherepanov: FBI implement, portage
+ *
+ * @section DESCRIPTION
+ *
+ * Function body instrumetation
+ *
+ */
+
+#include "fbiprobe.h"
+#include "fbi_probe_module.h"
+#include "fbi_msg.h"
+#include "regs.h"
+
+#include <us_manager/us_manager.h>
+#include <us_manager/probes/probes.h>
+#include <us_manager/probes/register_probes.h>
+
+#include <uprobe/swap_uprobes.h>
+#include <us_manager/sspt/sspt_ip.h>
+
+#include <kprobe/swap_kprobes_deps.h>
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/dcache.h>
+#include <linux/mm_types.h>
+
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+
+#define DIRECT_ADDR (0xFF)
+#define MAX_STRING_LEN (512)
+
+/* on fails. return NULL, set size to 0 */
+/* you shoud free allocated data buffer */
+static char *fbi_probe_alloc_and_read_from_addr(const struct fbi_var_data *fbid,
+ unsigned long addr,
+ uint32_t *size)
+{
+ uint8_t i, j;
+ char *buf = NULL;
+ struct fbi_step *step;
+
+ *size = 0;
+
+ /* get final variable address */
+ step = fbid->steps;
+ for (i = 0; i != fbid->steps_count; i++) {
+ /* dereference */
+ for (j = 0; j != step->ptr_order; j++) {
+ unsigned long new_addr;
+ /* equel to: addr = *addr */
+ if (!read_proc_vm_atomic(current, addr, &new_addr,
+ sizeof(new_addr))) {
+ print_warn("p = 0x%lx step #%d ptr_order #%d\n",
+ addr, i + 1, j + 1);
+ goto exit_fail;
+ }
+ addr = new_addr;
+ print_debug("dereference addr = 0x%lx;\n", addr);
+ }
+
+ /* offset */
+ addr += step->data_offset;
+ print_debug("addr + offset = 0x%lx;\n", addr);
+ step++;
+ }
+
+ /* calculate data size */
+ if (fbid->data_size == 0) {
+ /*
+ * that mean variable is string and
+ * we need to calculate string length
+ */
+
+ *size = strnlen_user((const char __user *)addr, MAX_STRING_LEN);
+ if (*size == 0) {
+ print_warn("Cannot get string from 0x%lx\n", addr);
+ goto exit_fail;
+ }
+ } else {
+ /* else use size from fbi struct */
+ *size = fbid->data_size;
+ }
+
+ buf = kmalloc(*size, GFP_KERNEL);
+ if (buf == NULL) {
+ print_warn("Not enough memory\n");
+ goto exit_fail_size_0;
+ }
+
+ if (!read_proc_vm_atomic(current, addr, buf, *size)) {
+ print_warn("Error reading data at 0x%lx, task %d\n",
+ addr, current->pid);
+ goto exit_fail_free_buf;
+ }
+
+ if (fbid->data_size == 0) {
+ /*
+ * that mean variable is string and
+ * we need to add terminate '\0'
+ */
+ buf[*size - 1] = '\0';
+ }
+
+ return buf;
+
+exit_fail_free_buf:
+ kfree(buf);
+ buf = NULL;
+exit_fail_size_0:
+ *size = 0;
+exit_fail:
+ return NULL;
+
+}
+
+static int fbi_probe_get_data_from_reg(const struct fbi_var_data *fbi_i,
+ struct pt_regs *regs)
+{
+ unsigned long *reg_ptr;
+
+ reg_ptr = get_ptr_by_num(regs, fbi_i->reg_n);
+ if (reg_ptr == NULL) {
+ print_err("fbi_probe_get_data_from_reg: Wrong register number!\n");
+ return 0;
+ }
+
+ fbi_msg(fbi_i->var_id, fbi_i->data_size, (char *)reg_ptr);
+
+ return 0;
+}
+
+static int fbi_probe_get_data_from_ptrs(const struct fbi_var_data *fbi_i,
+ struct pt_regs *regs)
+{
+ unsigned long *reg_ptr;
+ unsigned long addr;
+ uint32_t size = 0;
+ void *buf = NULL;
+
+ reg_ptr = get_ptr_by_num(regs, fbi_i->reg_n);
+ if (reg_ptr == NULL) {
+ print_err("fbi_probe_get_data_from_ptrs: Wrong register number!\n");
+ goto send_msg;
+ }
+
+ addr = *reg_ptr + fbi_i->reg_offset;
+ print_warn("reg = %p; off = 0x%llx; addr = 0x%lx!\n", reg_ptr,
+ fbi_i->reg_offset, addr);
+
+ buf = fbi_probe_alloc_and_read_from_addr(fbi_i, addr, &size);
+
+send_msg:
+ /* If buf is NULL size will be 0.
+ * That mean we cannot get data for this probe.
+ * But we should send probe message with packed data size 0
+ * as error message.
+ */
+ fbi_msg(fbi_i->var_id, size, buf);
+
+ if (buf != NULL)
+ kfree(buf);
+ else
+ print_err("cannot get data from ptrs\n");
+
+ return 0;
+}
+
+static struct vm_area_struct *find_vma_exe_by_dentry(struct mm_struct *mm,
+ struct dentry *dentry)
+{
+ struct vm_area_struct *vma;
+
+ /* FIXME: down_write(&mm->mmap_sem); up_write(&mm->mmap_sem); */
+ /* TODO FILTER vma */
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (vma->vm_file &&
+ (vma->vm_file->f_path.dentry == dentry))
+ /* found */
+ goto exit;
+ }
+
+ /* not found */
+ vma = NULL;
+exit:
+ return vma;
+}
+
+static int fbi_probe_get_data_from_direct_addr(const struct fbi_var_data *fbi_i,
+ struct sspt_ip *ip,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long addr;
+ uint32_t size = 0;
+ char *buf;
+
+ /* register offset is global address */
+ vma = find_vma_exe_by_dentry(current->mm, ip->page->file->dentry);
+ if (vma == NULL) {
+ print_warn("cannot locate dentry\n");
+ goto exit;
+ }
+
+ addr = vma->vm_start + fbi_i->reg_offset;
+
+ print_debug("DIRECT_ADDR reg_offset = %llx\n", fbi_i->reg_offset);
+ print_debug("DIRECT_ADDR vm_start = %lx\n", vma->vm_start);
+ print_debug("DIRECT_ADDR res_addr = %lx\n", addr);
+
+ buf = fbi_probe_alloc_and_read_from_addr(fbi_i, addr, &size);
+ /* If buf is NULL size will be 0.
+ * That mean we cannot get data for this probe.
+ * But we should send probe message with packed data size 0
+ * as error message.
+ */
+ fbi_msg(fbi_i->var_id, size, buf);
+
+ if (buf != NULL) {
+ kfree(buf);
+ } else {
+ print_warn("get data by direct addr failed (0x%lx :0x%llx)\n",
+ addr, fbi_i->reg_offset);
+ }
+exit:
+ return 0;
+}
+
+static int fbi_probe_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct sspt_ip *ip = container_of(p, struct sspt_ip, uprobe);
+ struct fbi_info *fbi_i = &ip->desc->info.fbi_i;
+ struct fbi_var_data *fbi_d = NULL;
+ uint8_t i;
+
+ if (ip->desc->type != SWAP_FBIPROBE) {
+ /* How this can occure? Doesn't matter, just print and go */
+ print_err("Not FBI probe in FBI handler!\n");
+ return 0;
+ }
+
+ for (i = 0; i != fbi_i->var_count; i++) {
+ fbi_d = &fbi_i->vars[i];
+ if (fbi_d->reg_n == DIRECT_ADDR) {
+ if (0 != fbi_probe_get_data_from_direct_addr(fbi_d, ip,
+ regs))
+ print_err("fbi_probe_get_data_from_direct_addr error\n");
+ } else if (fbi_d->steps_count == 0) {
+ if (0 != fbi_probe_get_data_from_reg(fbi_d, regs))
+ print_err("fbi_probe_get_data_from_reg error\n");
+ } else {
+ if (0 != fbi_probe_get_data_from_ptrs(fbi_d, regs))
+ print_err("fbi_probe_get_data_from_ptrs error\n");
+ }
+ }
+
+ return 0;
+}
+
+/* FBI probe interfaces */
+void fbi_probe_cleanup(struct probe_info *probe_i)
+{
+ uint8_t i;
+ struct fbi_info *fbi_i = &(probe_i->fbi_i);
+
+ for (i = 0; i != fbi_i->var_count; i++) {
+ if (fbi_i->vars[i].steps != NULL) {
+ if (fbi_i->vars[i].steps != NULL)
+ kfree(fbi_i->vars[i].steps);
+ fbi_i->vars[i].steps = NULL;
+ fbi_i->vars[i].steps_count = 0;
+ }
+ }
+
+ kfree(fbi_i->vars);
+ fbi_i->vars = NULL;
+}
+
+void fbi_probe_init(struct sspt_ip *ip)
+{
+ ip->uprobe.pre_handler = (uprobe_pre_handler_t)fbi_probe_handler;
+}
+
+void fbi_probe_uninit(struct sspt_ip *ip)
+{
+ if (ip != NULL)
+ fbi_probe_cleanup(&ip->desc->info);
+}
+
+static int fbi_probe_register_probe(struct sspt_ip *ip)
+{
+ return swap_register_uprobe(&ip->uprobe);
+}
+
+static void fbi_probe_unregister_probe(struct sspt_ip *ip, int disarm)
+{
+ __swap_unregister_uprobe(&ip->uprobe, disarm);
+}
+
+static struct uprobe *fbi_probe_get_uprobe(struct sspt_ip *ip)
+{
+ return &ip->uprobe;
+}
+
+int fbi_probe_copy(struct probe_info *dest, const struct probe_info *source)
+{
+ uint8_t steps_count;
+ size_t steps_size;
+ size_t vars_size;
+ struct fbi_var_data *vars;
+ struct fbi_step *steps_source;
+ struct fbi_step *steps_dest = NULL;
+ uint8_t i, n;
+ int ret = 0;
+
+ memcpy(dest, source, sizeof(*source));
+
+ vars_size = source->fbi_i.var_count * sizeof(*source->fbi_i.vars);
+ vars = kmalloc(vars_size, GFP_KERNEL);
+ if (vars == NULL)
+ return -ENOMEM;
+
+ memcpy(vars, source->fbi_i.vars, vars_size);
+
+ for (i = 0; i != source->fbi_i.var_count; i++) {
+ steps_dest = NULL;
+ steps_count = vars[i].steps_count;
+ steps_size = sizeof(*steps_source) * steps_count;
+ steps_source = vars[i].steps;
+
+ if (steps_size != 0 && steps_source != NULL) {
+ steps_dest = kmalloc(steps_size, GFP_KERNEL);
+ if (steps_dest == NULL) {
+ print_err("can not alloc data\n");
+ n = i;
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ memcpy(steps_dest, steps_source, steps_size);
+ }
+ vars[i].steps = steps_dest;
+ }
+
+ dest->fbi_i.vars = vars;
+
+ return ret;
+err:
+ for (i = 0; i < n; i++)
+ kfree(vars[i].steps);
+ kfree(vars);
+ return ret;
+}
+
+/* Register */
+static struct probe_iface fbi_probe_iface = {
+ .init = fbi_probe_init,
+ .uninit = fbi_probe_uninit,
+ .reg = fbi_probe_register_probe,
+ .unreg = fbi_probe_unregister_probe,
+ .get_uprobe = fbi_probe_get_uprobe,
+ .copy = fbi_probe_copy,
+ .cleanup = fbi_probe_cleanup
+};
+
+static int __init fbiprobe_module_init(void)
+{
+ int ret = 0;
+ ret = swap_register_probe_type(SWAP_FBIPROBE, &fbi_probe_iface);
+ print_debug("Init done. Result=%d\n", ret);
+ return ret;
+}
+
+static void __exit fbiprobe_module_exit(void)
+{
+ swap_unregister_probe_type(SWAP_FBIPROBE);
+}
+
+module_init(fbiprobe_module_init);
+module_exit(fbiprobe_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP fbiprobe");
+MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>; Vitaliy Cherepanov <v.cherepanov@samsung.com>");
+
--- /dev/null
+/*
+ * @file fbi_probe/fbi_probe.h
+ *
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov : FBI implement
+ * 2014 Vitaliy Cherepanov: FBI implement, portage
+ *
+ * @section DESCRIPTION
+ *
+ * Function body instrumentation.
+ *
+ */
+
+#ifndef __FBI_PROBE_H__
+#define __FBI_PROBE_H__
+
+#include <linux/types.h>
+
+/* FBI step */
+struct fbi_step {
+ uint8_t ptr_order; /* Specifies what is located on the address:
+ * ptr_order = 0 - variable
+ * ptr_order = 1 - pointer to variable
+ * ptr_order = 2 - pointer to pointer
+ * etc. */
+
+ uint64_t data_offset;
+} __packed;
+
+/* FBI var data */
+struct fbi_var_data {
+ /* Variable position is evaluated by the following rule:
+ * var_position = *(pointer_to_register) - reg_offset
+ * It is expected that the offset is not null only when we're taking
+ * var value from stack.
+ */
+ uint64_t var_id; /* Variable identifier
+ * Used to specify var */
+ uint64_t reg_offset; /* Offset relative to the registers value
+ * address, specified with reg_n */
+ uint8_t reg_n; /* Register number. Hope times of cpu
+ * with more than 2 million ones are very
+ * far from us */
+ uint32_t data_size; /* Data size to be read */
+
+ uint8_t steps_count; /* Count of steps to extract variable
+ * value */
+ struct fbi_step *steps; /* extract steps */
+};
+
+/* FBI info */
+struct fbi_info {
+ uint8_t var_count;
+ struct fbi_var_data *vars;
+};
+
+#endif /* __FBI_PROBE_H__ */
--- /dev/null
+/*
+ * @file fbiprobe/fbi_probe.h
+ *
+ * @author Aleksandr Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov : FBI implement
+ * 2014 Vitaliy Cherepanov: FBI portage
+ *
+ * @section DESCRIPTION
+ *
+ * Function body instrumetation
+ *
+ */
+
+#ifndef __REGS_H__
+#define __REGS_H__
+
+#include <linux/ptrace.h>
+
+#include "fbi_probe_module.h"
+/* This function is used to compare register number and its name on x86 arch.
+ * For ARM it is dumb.
+ * List of registers and their nums on x86:
+ * ax 0
+ * bx 1
+ * cx 2
+ * dx 3
+ * si 4
+ * di 5
+ * bp 6
+ * sp 7
+ */
+
+static inline unsigned long *get_ptr_by_num(struct pt_regs *regs,
+ unsigned char reg_num)
+{
+ unsigned long *reg = NULL;
+ /* FIXME: bad way to use "sizeof(long) " */
+ if (reg_num < sizeof(struct pt_regs) / sizeof(long)) {
+ reg = (unsigned long *)regs;
+ reg = ®[reg_num];
+ }
+
+ return reg;
+}
+
+#endif /* __REGS_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_gtp.o
+swap_gtp-y := gt_module.o \
+ gt_debugfs.o
--- /dev/null
+#ifndef __GT_H__
+#define __GT_H__
+
+#define GT_PREFIX "SWAP GOT PATCHER: "
+
+#endif /* __GT_H__ */
--- /dev/null
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <master/swap_debugfs.h>
+#include "gt.h"
+#include "gt_debugfs.h"
+#include "gt_module.h"
+
+#define GT_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
+
+static const char GT_FOLDER[] = "got_patcher";
+static const char GT_LINKER[] = "linker";
+static const char GT_PATH[] = "path";
+static const char GT_FIXUP_ADDR[] = "dl_fixup_addr";
+static const char GT_RELOC_ADDR[] = "dl_reloc_addr";
+static const char GT_ENABLE[] = "enable";
+static const char GT_BY_PATH[] = "by_path";
+static const char GT_BY_PID[] = "by_pid";
+static const char GT_BY_ID[] = "by_id";
+static const char GT_ADD[] = "add";
+static const char GT_DEL[] = "del";
+static const char GT_DEL_ALL[] = "del_all";
+static const char GT_LIST_TARGETS[] = "list_targets";
+static const char GT_HANDLER[] = "handler";
+static const char GT_HANDLER_FIXUP_OFF[] = "fixup_handler_off";
+static const char GT_HANDLER_RELOC_OFF[] = "reloc_handler_off";
+static const char GT_PROC_FEATURES_OFF[] = "proc_features_off";
+static const char GT_PTHREAD[] = "pthread";
+static const char GT_MINIMAL_INIT[] = "minimal_init_off";
+
+static struct dentry *gt_root;
+
+
+/* Type for function that handles string grabbed from userspace */
+typedef int (*sh_t)(char *);
+
+/* Type for function that handles unsigned long grabbed from userspace */
+typedef int (*ulh_t)(unsigned long);
+
+/* Type for function that handles pid grabbed from userspace */
+typedef int (*ph_t)(pid_t);
+
+
+static ssize_t get_string_and_call(const char __user *buf, size_t len,
+ sh_t cb)
+{
+ char *string;
+ ssize_t ret;
+
+ string = kmalloc(len, GFP_KERNEL);
+ if (string == NULL) {
+ ret = -ENOMEM;
+ goto get_string_write_out;
+ }
+
+ if (copy_from_user(string, buf, len)) {
+ ret = -EINVAL;
+ goto get_string_write_out;
+ }
+
+ string[len - 1] = '\0';
+
+ ret = cb(string);
+
+get_string_write_out:
+ kfree(string);
+
+ return ret == 0 ? len : ret;
+}
+
+static ssize_t get_ul_and_call(const char __user *buf, size_t len, ulh_t cb)
+{
+ ssize_t ret;
+ char *ulstring;
+ unsigned long ul;
+
+ ulstring = kmalloc(len, GFP_KERNEL);
+ if (ulstring == NULL) {
+ ret = -ENOMEM;
+ goto get_ul_write_out;
+ }
+
+ if (copy_from_user(ulstring, buf, len)) {
+ ret = -EINVAL;
+ goto get_ul_write_out;
+ }
+
+ ulstring[len - 1] = '\0';
+
+ ret = kstrtoul(ulstring, 16, &ul);
+ if (ret != 0)
+ goto get_ul_write_out;
+
+ ret = cb(ul);
+
+get_ul_write_out:
+ kfree(ulstring);
+
+ return ret == 0 ? len : ret;
+}
+
+static ssize_t get_pid_and_call(const char __user *buf, size_t len, ph_t cb)
+{
+ ssize_t ret;
+ char *pidstring;
+ pid_t pid;
+
+ pidstring = kmalloc(len, GFP_KERNEL);
+ if (pidstring == NULL) {
+ ret = -ENOMEM;
+ goto get_pid_write_out;
+ }
+
+ if (copy_from_user(pidstring, buf, len)) {
+ ret = -EINVAL;
+ goto get_pid_write_out;
+ }
+
+ pidstring[len - 1] = '\0';
+
+ ret = kstrtoul(pidstring, 10, (unsigned long *)&pid);
+ if (ret != 0)
+ goto get_pid_write_out;
+
+ ret = cb(pid);
+
+get_pid_write_out:
+ kfree(pidstring);
+
+ return ret == 0 ? len : ret;
+}
+
+/* ===========================================================================
+ * = TARGETS =
+ * ===========================================================================
+ */
+
+static ssize_t handler_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_set_handler_path);
+}
+
+static ssize_t handler_fixup_off_write(struct file *file,
+ const char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, gtm_set_handler_fixup_off);
+}
+
+static ssize_t handler_reloc_off_write(struct file *file,
+ const char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, gtm_set_handler_reloc_off);
+}
+
+static ssize_t proc_features_off_write(struct file *file,
+ const char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, gtm_set_proc_features_off);
+}
+
+static const struct file_operations handler_path_fops = {
+ .owner = THIS_MODULE,
+ .write = handler_path_write,
+};
+
+static const struct file_operations handler_fixup_off_fops = {
+ .owner = THIS_MODULE,
+ .write = handler_fixup_off_write,
+};
+
+static const struct file_operations handler_reloc_off_fops = {
+ .owner = THIS_MODULE,
+ .write = handler_reloc_off_write,
+};
+
+static const struct file_operations proc_features_off_fops = {
+ .owner = THIS_MODULE,
+ .write = proc_features_off_write,
+};
+
+/* ===========================================================================
+ * = TARGETS =
+ * ===========================================================================
+ */
+
+static ssize_t by_path_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_add_by_path);
+}
+
+static ssize_t by_path_del_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_del_by_path);
+}
+
+static ssize_t by_pid_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_pid_and_call(buf, len, gtm_add_by_pid);
+}
+
+static ssize_t by_pid_del_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_pid_and_call(buf, len, gtm_del_by_pid);
+}
+
+static ssize_t by_id_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_add_by_id);
+}
+
+static ssize_t by_id_del_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_del_by_id);
+}
+
+static ssize_t del_all_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+
+ ret = gtm_del_all();
+
+ return ret == 0 ? len : ret;
+}
+
+static ssize_t target_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *targets;
+
+ ret = gtm_get_targets(&targets);
+ if (ret < 0)
+ return ret;
+
+ ret = simple_read_from_buffer(buf, len, ppos, targets, ret);
+ kfree(targets);
+
+ return ret;
+}
+
+static const struct file_operations by_path_add_fops = {
+ .owner = THIS_MODULE,
+ .write = by_path_add_write,
+};
+
+static const struct file_operations by_path_del_fops = {
+ .owner = THIS_MODULE,
+ .write = by_path_del_write,
+};
+
+static const struct file_operations by_pid_add_fops = {
+ .owner = THIS_MODULE,
+ .write = by_pid_add_write,
+};
+
+static const struct file_operations by_pid_del_fops = {
+ .owner = THIS_MODULE,
+ .write = by_pid_del_write,
+};
+
+static const struct file_operations by_id_add_fops = {
+ .owner = THIS_MODULE,
+ .write = by_id_add_write,
+};
+
+static const struct file_operations by_id_del_fops = {
+ .owner = THIS_MODULE,
+ .write = by_id_del_write,
+};
+
+static const struct file_operations del_all_fops = {
+ .owner = THIS_MODULE,
+ .write = del_all_write,
+};
+
+static const struct file_operations target_fops = {
+ .owner = THIS_MODULE,
+ .read = target_read,
+};
+
+/* ===========================================================================
+ * = ENABLE =
+ * ===========================================================================
+ */
+
+static ssize_t enable_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ char val[2];
+
+ val[0] = (gtm_status() == GT_ON ? '1' : '0');
+ val[1] = '\0';
+
+ return simple_read_from_buffer(buf, len, ppos, val, 2);
+}
+
+static ssize_t enable_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char val[2];
+ size_t val_size;
+
+ val_size = min(len, (sizeof(val) - 1));
+ if (copy_from_user(val, buf, val_size))
+ return -EFAULT;
+
+ val[1] = '\0';
+ switch(val[0]) {
+ case '0':
+ ret = gtm_switch(GT_OFF);
+ break;
+ case '1':
+ ret = gtm_switch(GT_ON);
+ break;
+ default:
+ printk(GT_PREFIX "Invalid state!\n");
+ return -EINVAL;
+ }
+
+ return ret == 0 ? len : ret;
+}
+
+static const struct file_operations enable_fops = {
+ .owner = THIS_MODULE,
+ .write = enable_write,
+ .read = enable_read,
+};
+
+
+/* ===========================================================================
+ * = LINKER =
+ * ===========================================================================
+ */
+
+static ssize_t linker_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_set_linker_path);
+}
+
+static ssize_t fixup_off_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, gtm_set_fixup_off);
+}
+
+static ssize_t reloc_off_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, gtm_set_reloc_off);
+}
+
+static const struct file_operations linker_path_fops = {
+ .owner = THIS_MODULE,
+ .write = linker_path_write,
+};
+
+static const struct file_operations fixup_off_fops = {
+ .owner = THIS_MODULE,
+ .write = fixup_off_write,
+};
+
+static const struct file_operations reloc_off_fops = {
+ .owner = THIS_MODULE,
+ .write = reloc_off_write,
+};
+
+
+
+/* ===========================================================================
+ * = PTHREAD =
+ * ===========================================================================
+ */
+
+static ssize_t pthread_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, gtm_set_pthread_path);
+}
+
+static ssize_t init_off_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, gtm_set_init_off);
+}
+
+static const struct file_operations pthread_path_fops = {
+ .owner = THIS_MODULE,
+ .write = pthread_path_write,
+};
+
+static const struct file_operations pthread_init_off_fops = {
+ .owner = THIS_MODULE,
+ .write = init_off_write,
+};
+
+
+
+
+int gtd_init(void)
+{
+ struct dentry *swap_dentry, *root, *linker, *by_path, *by_pid,
+ *by_id, *handler, *pthread, *dentry;
+ int ret;
+
+ ret = -ENODEV;
+ if (!debugfs_initialized())
+ goto fail;
+
+ ret = -ENOENT;
+ swap_dentry = swap_debugfs_getdir();
+ if (!swap_dentry)
+ goto fail;
+
+ ret = -ENOMEM;
+ root = swap_debugfs_create_dir(GT_FOLDER, swap_dentry);
+ if (IS_ERR_OR_NULL(root))
+ goto fail;
+
+ gt_root = root;
+
+ linker = swap_debugfs_create_dir(GT_LINKER, root);
+ if (IS_ERR_OR_NULL(linker)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, linker,
+ NULL, &linker_path_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_FIXUP_ADDR, GT_DEFAULT_PERMS,
+ linker, NULL, &fixup_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_RELOC_ADDR, GT_DEFAULT_PERMS,
+ linker, NULL, &reloc_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ by_path = swap_debugfs_create_dir(GT_BY_PATH, root);
+ if (IS_ERR_OR_NULL(by_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_path,
+ NULL, &by_path_add_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_path,
+ NULL, &by_path_del_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ by_pid = swap_debugfs_create_dir(GT_BY_PID, root);
+ if (IS_ERR_OR_NULL(by_pid)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_pid,
+ NULL, &by_pid_add_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_pid,
+ NULL, &by_pid_del_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ by_id = swap_debugfs_create_dir(GT_BY_ID, root);
+ if (IS_ERR_OR_NULL(by_id)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_id,
+ NULL, &by_id_add_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_id,
+ NULL, &by_id_del_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_DEL_ALL, GT_DEFAULT_PERMS, root,
+ NULL, &del_all_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_LIST_TARGETS, GT_DEFAULT_PERMS, root,
+ NULL, &target_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_ENABLE, GT_DEFAULT_PERMS, root,
+ NULL, &enable_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ handler = swap_debugfs_create_dir(GT_HANDLER, root);
+ if (IS_ERR_OR_NULL(handler)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, handler,
+ NULL, &handler_path_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_HANDLER_FIXUP_OFF,
+ GT_DEFAULT_PERMS, handler,
+ NULL, &handler_fixup_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_HANDLER_RELOC_OFF,
+ GT_DEFAULT_PERMS, handler, NULL,
+ &handler_reloc_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_PROC_FEATURES_OFF,
+ GT_DEFAULT_PERMS, handler, NULL,
+ &proc_features_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ pthread = swap_debugfs_create_dir(GT_PTHREAD, root);
+ if (IS_ERR_OR_NULL(pthread)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, pthread,
+ NULL, &pthread_path_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(GT_MINIMAL_INIT, GT_DEFAULT_PERMS,
+ pthread, NULL,
+ &pthread_init_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ debugfs_remove_recursive(root);
+
+fail:
+ printk(GT_PREFIX "Debugfs initialization failure: %d\n", ret);
+
+ return ret;
+}
+
+void gtd_exit(void)
+{
+ if (gt_root)
+ debugfs_remove_recursive(gt_root);
+ gt_root = NULL;
+}
--- /dev/null
+#ifndef __GT_DEBUGFS_H__
+#define __GT_DEBUGFS_H__
+
+int gtd_init(void);
+void gtd_exit(void);
+
+#endif /* __GT_DEBUGFS_H__ */
--- /dev/null
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+#include <kprobe/swap_ktd.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/probes/probe_info_new.h>
+#include <us_manager/us_common_file.h>
+#include <loader/loader.h>
+#include <master/swap_initializer.h>
+#include "gt.h"
+#include "gt_module.h"
+#include "gt_debugfs.h"
+
+
+enum task_id_t {
+ GT_SET_BY_DENTRY,
+ GT_SET_BY_PID,
+ GT_SET_BY_ID,
+};
+
+struct l_probe_el {
+ struct list_head list;
+
+ struct pf_group *pfg;
+ enum task_id_t task_id;
+ union {
+ pid_t tgid;
+ struct dentry *dentry;
+ char *id;
+ };
+
+ struct probe_new p_fixup;
+ struct probe_new p_reloc;
+ struct probe_new p_init;
+};
+
+struct bin_data_t {
+ struct dentry *dentry;
+ unsigned long off;
+};
+
+typedef unsigned long (*rh_t)(struct uretprobe_instance *, struct pt_regs *,
+ struct hd_t *);
+
+/* Typedef for compare function that removes data from list */
+typedef bool (*cf_t)(struct l_probe_el*, void *);
+
+static LIST_HEAD(_reg_probes);
+static LIST_HEAD(_unreg_probes);
+static DEFINE_MUTEX(_linker_probes_lock);
+
+static enum gt_status _status = GT_OFF;
+static struct bin_data_t _linker_fixup;
+static struct bin_data_t _linker_reloc;
+static struct bin_data_t _handler_fixup;
+static struct bin_data_t _handler_reloc;
+static struct bin_data_t _proc_features;
+static struct bin_data_t _pthread_init;
+
+
+static inline void _lock_probes_list(void)
+{
+ mutex_lock(&_linker_probes_lock);
+}
+
+static inline void _unlock_probes_list(void)
+{
+ mutex_unlock(&_linker_probes_lock);
+}
+
+static inline bool _is_enable(void)
+{
+ return _status == GT_ON;
+}
+
+static inline bool _is_bin_data_available(struct bin_data_t *bin_data)
+{
+ return (bin_data->dentry && bin_data->off);
+}
+
+static inline bool _is_linker_data_available(void)
+{
+ return _is_bin_data_available(&_linker_fixup) &&
+ _is_bin_data_available(&_linker_reloc);
+}
+
+static inline bool _is_handler_data_available(void)
+{
+ return _is_bin_data_available(&_handler_fixup) &&
+ _is_bin_data_available(&_handler_reloc) &&
+ _is_bin_data_available(&_proc_features);
+}
+
+static inline bool _is_pthread_data_available(void)
+{
+ return _is_bin_data_available(&_pthread_init);
+}
+
+
+
+
+/* ===========================================================================
+ * = TASK DATA MANIPULATION =
+ * ===========================================================================
+ */
+
+static void gt_ktd_init(struct task_struct *task, void *data)
+{
+ bool *in_handler = (bool *)data;
+
+ *in_handler = false;
+}
+
+static void gt_ktd_exit(struct task_struct *task, void *data)
+{
+}
+
+static struct ktask_data gt_ktd = {
+ .init = gt_ktd_init,
+ .exit = gt_ktd_exit,
+ .size = sizeof(bool),
+};
+
+static inline bool _is_in_handler(void)
+{
+ return *(bool *)swap_ktd(>_ktd, current);
+}
+
+static inline void _set_in_handler(bool is_in_handler)
+{
+ *(bool *)swap_ktd(>_ktd, current) = is_in_handler;
+}
+
+static inline bool _check_by_dentry(struct l_probe_el *l_probe, void *data)
+{
+ struct dentry *dentry = (struct dentry *)data;
+
+ if (l_probe->task_id == GT_SET_BY_DENTRY &&
+ l_probe->dentry == dentry)
+ return true;
+
+ return false;
+}
+
+static inline bool _check_by_pid(struct l_probe_el *l_probe, void *data)
+{
+ pid_t tgid = *(pid_t *)data;
+
+ if (l_probe->task_id == GT_SET_BY_PID &&
+ l_probe->tgid == tgid)
+ return true;
+
+ return false;
+}
+
+static inline bool _check_by_id(struct l_probe_el *l_probe, void *data)
+{
+ char *id = (char *)data;
+
+ if (l_probe->task_id == GT_SET_BY_ID &&
+ strncmp(l_probe->id, id, PATH_MAX))
+ return true;
+
+ return false;
+}
+
+static inline bool _del_all(struct l_probe_el *l_probe, void *data)
+{
+ return true;
+}
+
+/* ===========================================================================
+ * = LINKER HANDLERS =
+ * ===========================================================================
+ */
+
+static unsigned long _redirect_to_handler(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ struct hd_t *hd, unsigned long off)
+{
+ unsigned long base;
+ unsigned long vaddr;
+
+ base = lpd_get_handlers_base(hd);
+ if (base == 0)
+ return 0;
+
+ vaddr = base + off;
+ loader_module_prepare_ujump(ri, regs, vaddr);
+
+ return vaddr;
+}
+
+static unsigned long _redirect_to_fixup_handler(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ struct hd_t *hd)
+{
+ return _redirect_to_handler(ri, regs, hd, _handler_fixup.off);
+}
+
+static unsigned long _redirect_to_reloc_handler(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ struct hd_t *hd)
+{
+ return _redirect_to_handler(ri, regs, hd, _handler_reloc.off);
+}
+
+static unsigned long _redirect_to_proc_features(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ struct hd_t *hd)
+{
+ return _redirect_to_handler(ri, regs, hd, _proc_features.off);
+}
+
+
+
+static int _process_eh(struct uretprobe_instance *ri, struct pt_regs *regs,
+ rh_t rh, struct dentry *dentry)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+ struct hd_t *hd;
+ unsigned long vaddr = 0;
+ unsigned long old_pc = swap_get_upc(regs);
+
+ if ((dentry == NULL) || _is_in_handler())
+ goto out_set_orig;
+
+ hd = lpd_get_hd(pd, dentry);
+ if (hd == NULL)
+ goto out_set_orig;
+
+ if ((lpd_get_state(hd) == NOT_LOADED || lpd_get_state(hd) == FAILED) &&
+ lpd_get_init_state(pd)) {
+ vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
+ } else if (lpd_get_state(hd) == LOADED) {
+ _set_in_handler(true);
+ vaddr = rh(ri, regs, hd);
+ }
+
+out_set_orig:
+ loader_set_priv_origin(ri, vaddr);
+
+ /* PC change check */
+ return old_pc != swap_get_upc(regs);
+}
+
+static int dl_fixup_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ return _process_eh(ri, regs, &_redirect_to_fixup_handler,
+ _handler_fixup.dentry);
+}
+
+
+
+static void _restore_exec(struct uretprobe_instance *ri, struct hd_t *hd)
+{
+ if (_is_in_handler())
+ _set_in_handler(false);
+}
+
+static int _process_rh(struct uretprobe_instance *ri, struct pt_regs *regs,
+ rh_t rh, struct dentry *dentry)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+ struct hd_t *hd;
+
+ if (dentry == NULL)
+ return 0;
+
+ hd = lpd_get_hd(pd, dentry);
+ if (hd == NULL)
+ return 0;
+
+ switch (lpd_get_state(hd)) {
+ case NOT_LOADED:
+ break;
+ case LOADING:
+ loader_loading_ret(ri, regs, pd, hd);
+ /* Patch all binaries */
+ if (lpd_get_state(hd))
+ rh(ri, regs, hd);
+ break;
+ case LOADED:
+ /* TODO Check does we need this if library is loaded
+ * with RTLD_NOW ? */
+ _restore_exec(ri, hd);
+ break;
+ case FAILED:
+ loader_failed_ret(ri, regs, pd, hd);
+ break;
+ case ERROR:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int common_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ return _process_rh(ri, regs, &_redirect_to_proc_features,
+ _proc_features.dentry);
+}
+
+/* TODO Make ordinary interface. Now real data_size is set in init, because
+ * it is unknown in this module during compile time. */
+static struct probe_desc pin_fixup = MAKE_URPROBE(dl_fixup_eh, common_rh, 0);
+
+
+static int dl_reloc_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ return _process_eh(ri, regs, &_redirect_to_reloc_handler,
+ _handler_reloc.dentry);
+}
+
+/* TODO Make ordinary interface. Now real data_size is set in init, because
+ * it is unknown in this module during compile time. */
+static struct probe_desc pin_reloc = MAKE_URPROBE(dl_reloc_eh, common_rh, 0);
+
+
+static int pthread_init_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+
+ /* Wait until pthread is inited, if it is going to be inited before
+ * loading our library */
+ lpd_set_init_state(pd, false);
+
+ return 0;
+}
+
+static int pthread_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+
+ lpd_set_init_state(pd, true);
+
+ return 0;
+}
+
+/* TODO Make ordinary interface. Now real data_size is set in init, because
+ * it is unknown in this module during compile time. */
+static struct probe_desc pin_pinit = MAKE_URPROBE(pthread_init_eh,
+ pthread_init_rh, 0);
+
+
+static int _register_probe_el_no_lock(struct l_probe_el *l_probe)
+{
+ int ret;
+
+ /* register 'fix_up' */
+ l_probe->p_fixup.desc = &pin_fixup;
+ l_probe->p_fixup.offset = _linker_fixup.off;
+ ret = pin_register(&l_probe->p_fixup, l_probe->pfg,
+ _linker_fixup.dentry);
+ if (ret != 0) {
+ printk(GT_PREFIX "Error register linker fixup probe. "
+ "Linker dentry %s, "
+ "dl_fixup() offset = 0x%lx\n",
+ _linker_fixup.dentry->d_name.name,
+ _linker_fixup.off);
+ return ret;
+ }
+
+ /* register 'reloc' */
+ l_probe->p_reloc.desc = &pin_reloc;
+ l_probe->p_reloc.offset = _linker_reloc.off;
+ ret = pin_register(&l_probe->p_reloc, l_probe->pfg,
+ _linker_reloc.dentry);
+ if (ret != 0) {
+ printk(GT_PREFIX "Error register linker reloc probe. "
+ "Linker dentry %s, "
+ "dl_reloc() offset = 0x%lx\n",
+ _linker_reloc.dentry->d_name.name,
+ _linker_reloc.off);
+
+ goto reg_probe_el_fail;
+ }
+
+ /* register 'init' */
+ l_probe->p_init.desc = &pin_pinit;
+ l_probe->p_init.offset = _pthread_init.off;
+ ret = pin_register(&l_probe->p_init, l_probe->pfg,
+ _pthread_init.dentry);
+ if (ret != 0) {
+ printk(GT_PREFIX "Error register pthread minimal init. "
+ "Pthread dentry %s, "
+ "__pthread_minimal_init() offset = 0x%lx\n",
+ _pthread_init.dentry->d_name.name,
+ _pthread_init.off);
+
+ goto reg_probe_pthread_fail;
+ }
+
+ return 0;
+
+reg_probe_pthread_fail:
+ pin_unregister(&l_probe->p_reloc, l_probe->pfg);
+
+reg_probe_el_fail:
+ pin_unregister(&l_probe->p_fixup, l_probe->pfg);
+
+ return ret;
+}
+
+static int _unregister_probe_el_no_lock(struct l_probe_el *l_probe)
+{
+ pin_unregister(&l_probe->p_init, l_probe->pfg);
+ pin_unregister(&l_probe->p_reloc, l_probe->pfg);
+ pin_unregister(&l_probe->p_fixup, l_probe->pfg);
+
+ return 0;
+}
+
+static int _disable_got_patcher(void)
+{
+ struct l_probe_el *l_probe;
+ int ret = 0;
+
+ if (!_is_linker_data_available())
+ return -EINVAL;
+
+ _lock_probes_list();
+
+ list_for_each_entry(l_probe, &_reg_probes, list) {
+ ret = _unregister_probe_el_no_lock(l_probe);
+ if (ret == 0)
+ list_move_tail(&l_probe->list, &_unreg_probes);
+ }
+
+ _unlock_probes_list();
+
+ _status = GT_OFF;
+
+ return ret;
+}
+
+static int _enable_got_patcher(void)
+{
+ struct l_probe_el *l_probe;
+ int ret;
+
+ if (!_is_linker_data_available() || !_is_handler_data_available() ||
+ !_is_pthread_data_available())
+ return -EINVAL;
+
+ _lock_probes_list();
+
+ list_for_each_entry(l_probe, &_unreg_probes, list) {
+ ret = _register_probe_el_no_lock(l_probe);
+ if (ret == 0)
+ list_move_tail(&l_probe->list, &_reg_probes);
+ else
+ goto enable_patcher_fail_unlock;
+ }
+
+ _unlock_probes_list();
+
+ _status = GT_ON;
+
+ return 0;
+enable_patcher_fail_unlock:
+ _unlock_probes_list();
+
+ printk(GT_PREFIX "Error registering linker probes, disabling...");
+ _disable_got_patcher();
+
+ return ret;
+}
+
+
+/* ===========================================================================
+ * = LIST MANIPULATIONS =
+ * ===========================================================================
+ */
+static struct l_probe_el *_create_linker_probe_el(void)
+{
+ struct l_probe_el *l_probe;
+
+ l_probe = kzalloc(sizeof(*l_probe), GFP_KERNEL);
+ if (l_probe == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&l_probe->list);
+
+ return l_probe;
+}
+
+static void _destroy_linker_probe_el_no_lock(struct l_probe_el *l_probe)
+{
+ if (l_probe->pfg)
+ put_pf_group(l_probe->pfg);
+
+ if (l_probe->task_id == GT_SET_BY_DENTRY && l_probe->dentry != NULL)
+ swap_put_dentry(l_probe->dentry);
+
+ if (l_probe->task_id == GT_SET_BY_ID && l_probe->id != NULL)
+ kfree(l_probe->id);
+
+ kfree(l_probe);
+}
+
+static void _clean_linker_probes(void)
+{
+ struct l_probe_el *l_probe, *n;
+ int ret;
+
+ _lock_probes_list();
+
+ list_for_each_entry_safe(l_probe, n, &_reg_probes, list) {
+ ret = _unregister_probe_el_no_lock(l_probe);
+ if (ret != 0)
+ printk(GT_PREFIX "Cannot remove linker probe!\n");
+ list_del(&l_probe->list);
+ _destroy_linker_probe_el_no_lock(l_probe);
+ }
+
+ list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) {
+ list_del(&l_probe->list);
+ _destroy_linker_probe_el_no_lock(l_probe);
+ }
+
+ _unlock_probes_list();
+}
+
+
+/* ===========================================================================
+ * = STRING MANIPULATIONS =
+ * ===========================================================================
+ */
+
+static size_t _get_dentry_len(struct dentry *dentry)
+{
+ return strnlen(dentry->d_name.name, PATH_MAX);
+}
+
+static size_t _get_pid_len(pid_t pid)
+{
+ /* FIXME Return constant - maximum digits for 10-based unsigned long on
+ * 64 bit architecture to avoid complicated evaluations. */
+ return 20;
+}
+
+static size_t _get_id_len(char *id)
+{
+ return strnlen(id, PATH_MAX);
+}
+
+static size_t _get_item_len(struct l_probe_el *l_probe)
+{
+ if (l_probe->task_id == GT_SET_BY_DENTRY)
+ return _get_dentry_len(l_probe->dentry);
+ else if (l_probe->task_id == GT_SET_BY_PID)
+ return _get_pid_len(l_probe->tgid);
+ else if (l_probe->task_id == GT_SET_BY_ID)
+ return _get_id_len(l_probe->id);
+
+ printk(GT_PREFIX "Error! Unknown process identificator!\n");
+ return 0;
+}
+
+static char *_copy_dentry_item(char *dest, struct dentry *dentry)
+{
+ size_t len;
+
+ len = strnlen(dentry->d_name.name, PATH_MAX);
+ memcpy(dest, dentry->d_name.name, len);
+
+ return (dest + len);
+}
+
+static char *_copy_pid_item(char *dest, pid_t pid)
+{
+ int ret;
+ size_t len;
+
+ len = _get_pid_len(pid);
+
+ ret = snprintf(dest, len, "%lu", (unsigned long)pid);
+ if (ret >= len)
+ printk(GT_PREFIX "PID string is truncated!\n");
+
+ return (dest + ret);
+}
+
+static char *_copy_id_item(char *dest, char *id)
+{
+ size_t len;
+
+ len = strnlen(id, PATH_MAX);
+ memcpy(dest, id, len);
+
+ return (dest + len);
+}
+
+static char *_copy_item(char *dest, struct l_probe_el *l_probe)
+{
+ if (l_probe->task_id == GT_SET_BY_DENTRY)
+ return _copy_dentry_item(dest, l_probe->dentry);
+ else if (l_probe->task_id == GT_SET_BY_PID)
+ return _copy_pid_item(dest, l_probe->tgid);
+ else if (l_probe->task_id == GT_SET_BY_ID)
+ return _copy_id_item(dest, l_probe->id);
+
+ printk(GT_PREFIX "Error! Unknown process identificator!\n");
+ return dest;
+}
+
+static char *_add_separator(char *dest)
+{
+ const char sep = ':';
+
+ *dest = sep;
+
+ return (dest + 1);
+}
+
+static int _add_to_list(struct l_probe_el *l_probe)
+{
+ int ret = 0;
+
+ _lock_probes_list();
+ if (_is_enable()) {
+ ret = _register_probe_el_no_lock(l_probe);
+ if (ret == 0)
+ list_add_tail(&l_probe->list, &_reg_probes);
+ } else {
+ list_add_tail(&l_probe->list, &_unreg_probes);
+ }
+
+ _unlock_probes_list();
+
+ return ret;
+}
+
+static int _remove_from_list(cf_t cb, void *data)
+{
+ struct l_probe_el *l_probe, *n;
+ int ret = 0;
+
+ _lock_probes_list();
+
+ list_for_each_entry_safe(l_probe, n, &_reg_probes, list) {
+ if (cb(l_probe, data)) {
+ ret = _unregister_probe_el_no_lock(l_probe);
+ if (ret == 0) {
+ list_del(&l_probe->list);
+ _destroy_linker_probe_el_no_lock(l_probe);
+ }
+ goto remove_from_list_unlock;
+ }
+ }
+
+ list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) {
+ if (cb(l_probe, data)) {
+ list_del(&l_probe->list);
+ _destroy_linker_probe_el_no_lock(l_probe);
+ goto remove_from_list_unlock;
+ }
+ }
+
+remove_from_list_unlock:
+ _unlock_probes_list();
+
+ return ret;
+}
+
+
+
+
+int gtm_set_linker_path(char *path)
+{
+ struct dentry *dentry;
+
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL)
+ return -EINVAL;
+
+ /* We use get_dentry only once, so use put dentry also only once */
+ if (_linker_fixup.dentry != NULL ||
+ _linker_reloc.dentry != NULL) {
+ if (_linker_fixup.dentry != NULL)
+ swap_put_dentry(_linker_fixup.dentry);
+ else
+ swap_put_dentry(_linker_reloc.dentry);
+ }
+
+ _linker_fixup.dentry = dentry;
+ _linker_reloc.dentry = dentry;
+
+ return 0;
+}
+
+int gtm_set_fixup_off(unsigned long offset)
+{
+ _linker_fixup.off = offset;
+
+ return 0;
+}
+
+int gtm_set_reloc_off(unsigned long offset)
+{
+ _linker_reloc.off = offset;
+
+ return 0;
+}
+
+int gtm_switch(enum gt_status stat)
+{
+ int ret;
+
+ switch (stat) {
+ case GT_ON:
+ ret = _enable_got_patcher();
+ break;
+ case GT_OFF:
+ ret = _disable_got_patcher();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+enum gt_status gtm_status(void)
+{
+ return _status;
+}
+
+int gtm_add_by_path(char *path)
+{
+ struct dentry *dentry;
+ struct pf_group *pfg;
+ struct l_probe_el *l_probe;
+ int ret;
+
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL)
+ return -EINVAL;
+
+ pfg = get_pf_group_by_dentry(dentry, dentry);
+ if (pfg == NULL) {
+ ret = -ENOMEM;
+ goto add_by_path_put_dentry;
+ }
+
+ l_probe = _create_linker_probe_el();
+ if (l_probe == NULL) {
+ ret = -ENOMEM;
+ goto add_by_path_put_pfg;
+ }
+
+ l_probe->pfg = pfg;
+ l_probe->task_id = GT_SET_BY_DENTRY;
+ l_probe->dentry = dentry;
+
+ ret = _add_to_list(l_probe);
+ if (ret != 0)
+ goto add_by_path_free;
+
+ return 0;
+
+add_by_path_free:
+ kfree(l_probe);
+
+add_by_path_put_pfg:
+ put_pf_group(pfg);
+
+add_by_path_put_dentry:
+ swap_put_dentry(dentry);
+
+ return ret;
+}
+
+int gtm_add_by_pid(pid_t pid)
+{
+ struct pf_group *pfg;
+ struct l_probe_el *l_probe;
+ int ret;
+
+ /* TODO Add dentry as private data */
+ pfg = get_pf_group_by_tgid(pid, NULL);
+ if (pfg == NULL)
+ return -ENOMEM;
+
+ l_probe = _create_linker_probe_el();
+ if (l_probe == NULL) {
+ ret = -ENOMEM;
+ goto add_by_pid_put_pfg;
+ }
+
+ l_probe->pfg = pfg;
+ l_probe->task_id = GT_SET_BY_PID;
+ l_probe->tgid = pid;
+
+ ret = _add_to_list(l_probe);
+ if (ret != 0)
+ goto add_by_pid_free;
+
+ return 0;
+
+add_by_pid_free:
+ kfree(l_probe);
+
+add_by_pid_put_pfg:
+ put_pf_group(pfg);
+
+ return ret;
+}
+
+int gtm_add_by_id(char *id)
+{
+ char *new_id;
+ struct pf_group *pfg;
+ struct l_probe_el *l_probe;
+ size_t len;
+ int ret;
+
+ len = strnlen(id, PATH_MAX);
+ new_id = kmalloc(len, GFP_KERNEL);
+ if (new_id == NULL)
+ return -ENOMEM;
+
+ /* TODO Add dentry as private data */
+ pfg = get_pf_group_by_comm(id, NULL);
+ if (pfg == NULL) {
+ ret = -ENOMEM;
+ goto add_by_id_free_id;
+ }
+
+ l_probe = _create_linker_probe_el();
+ if (l_probe == NULL) {
+ ret = -ENOMEM;
+ goto add_by_id_put_pfg;
+ }
+
+ l_probe->pfg = pfg;
+ l_probe->task_id = GT_SET_BY_ID;
+ l_probe->id = new_id;
+
+ ret = _add_to_list(l_probe);
+ if (ret != 0)
+ goto add_by_id_free;
+
+ return 0;
+
+add_by_id_free:
+ kfree(l_probe);
+
+add_by_id_put_pfg:
+ put_pf_group(pfg);
+
+add_by_id_free_id:
+ kfree(new_id);
+
+ return ret;
+}
+
+int gtm_del_by_path(char *path)
+{
+ struct dentry *dentry;
+ int ret = 0;
+
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL)
+ return -EINVAL;
+
+ ret = _remove_from_list(_check_by_dentry, dentry);
+
+ return ret;
+}
+
+int gtm_del_by_pid(pid_t pid)
+{
+ int ret = 0;
+
+ ret = _remove_from_list(_check_by_pid, &pid);
+
+ return ret;
+}
+
+int gtm_del_by_id(char *id)
+{
+ int ret = 0;
+
+ ret = _remove_from_list(_check_by_id, id);
+
+ return ret;
+}
+
+int gtm_del_all(void)
+{
+ int ret = 0;
+
+ ret = _remove_from_list(_del_all, NULL);
+
+ return ret;
+}
+
+ssize_t gtm_get_targets(char **targets)
+{
+ struct l_probe_el *l_probe;
+ char *ptr;
+ size_t len = 0;
+
+ _lock_probes_list();
+ list_for_each_entry(l_probe, &_reg_probes, list) {
+ /* Add separator */
+ len += _get_item_len(l_probe) + 1;
+ }
+
+ list_for_each_entry(l_probe, &_unreg_probes, list) {
+ /* Add separator */
+ len += _get_item_len(l_probe) + 1;
+ }
+ _unlock_probes_list();
+
+ *targets = kmalloc(len, GFP_KERNEL);
+ if (*targets == NULL)
+ return -ENOMEM;
+
+ ptr = *targets;
+
+ _lock_probes_list();
+
+ list_for_each_entry(l_probe, &_reg_probes, list) {
+ ptr = _copy_item(ptr, l_probe);
+ ptr = _add_separator(ptr);
+ }
+
+ list_for_each_entry(l_probe, &_unreg_probes, list) {
+ ptr = _copy_item(ptr, l_probe);
+ ptr = _add_separator(ptr);
+ }
+
+ _unlock_probes_list();
+
+ *targets[len - 1] = '\0';
+
+ return len;
+}
+
+int gtm_set_handler_path(char *path)
+{
+ struct dentry *dentry;
+ int ret;
+
+ /* We use get_dentry only once, so use put dentry also only once */
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL)
+ return -EINVAL;
+
+ if (_handler_fixup.dentry)
+ swap_put_dentry(_handler_fixup.dentry);
+
+ if (_handler_reloc.dentry)
+ swap_put_dentry(_handler_reloc.dentry);
+
+ if (_proc_features.dentry)
+ swap_put_dentry(_proc_features.dentry);
+
+ _handler_fixup.dentry = dentry;
+ _handler_reloc.dentry = dentry;
+ _proc_features.dentry = dentry;
+
+ /* TODO Do smth with this:
+ * make interface for loader to remove handlers
+ * and add/delete when gtm_set_handler() is called
+ * Check container_of() may be useful */
+ ret = loader_add_handler(path);
+
+ return ret;
+}
+
+int gtm_set_handler_fixup_off(unsigned long offset)
+{
+ _handler_fixup.off = offset;
+
+ return 0;
+}
+
+int gtm_set_handler_reloc_off(unsigned long offset)
+{
+ _handler_reloc.off = offset;
+
+ return 0;
+}
+
+int gtm_set_proc_features_off(unsigned long offset)
+{
+ _proc_features.off = offset;
+
+ return 0;
+}
+
+int gtm_set_pthread_path(char *path)
+{
+ struct dentry *dentry;
+
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL)
+ return -EINVAL;
+
+ if (_pthread_init.dentry != NULL)
+ swap_put_dentry(_pthread_init.dentry);
+
+ _pthread_init.dentry = dentry;
+
+ return 0;
+}
+
+int gtm_set_init_off(unsigned long offset)
+{
+ _pthread_init.off = offset;
+
+ return 0;
+}
+
+static int gtm_init(void)
+{
+ int ret;
+
+ ret = gtd_init();
+ if (ret)
+ goto gtd_init_err;
+
+ ret = swap_ktd_reg(>_ktd);
+ if (ret)
+ goto gtd_ktd_reg_err;
+
+ return 0;
+
+gtd_ktd_reg_err:
+ gtd_exit();
+
+gtd_init_err:
+ return ret;
+}
+
+static void gtm_exit(void)
+{
+ swap_ktd_unreg(>_ktd);
+ gtd_exit();
+
+ _clean_linker_probes();
+
+ /* We use get_dentry only once, so use put dentry also only once */
+ if (_handler_fixup.dentry != NULL ||
+ _handler_reloc.dentry != NULL) {
+ if (_handler_fixup.dentry != NULL)
+ swap_put_dentry(_handler_fixup.dentry);
+ else
+ swap_put_dentry(_handler_reloc.dentry);
+ _handler_reloc.dentry = NULL;
+ _handler_fixup.dentry = NULL;
+ }
+}
+
+SWAP_LIGHT_INIT_MODULE(NULL, gtm_init, gtm_exit, NULL, NULL);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP GOT Patcher Module");
+MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>");
--- /dev/null
+#ifndef __GT_MODULE_H__
+#define __GT_MODULE_H__
+
+#include <linux/types.h>
+
+enum gt_status {
+ GT_ON,
+ GT_OFF
+};
+
+/* Linker data */
+int gtm_set_linker_path(char *path);
+int gtm_set_fixup_off(unsigned long offset);
+int gtm_set_reloc_off(unsigned long offset);
+int gtm_switch(enum gt_status stat);
+enum gt_status gtm_status(void);
+
+/* Target processes data */
+int gtm_add_by_path(char *path);
+int gtm_add_by_pid(pid_t pid);
+int gtm_add_by_id(char *id);
+int gtm_del_by_path(char *path);
+int gtm_del_by_pid(pid_t pid);
+int gtm_del_by_id(char *id);
+int gtm_del_all(void);
+/* Returns len on success, error code on fail.
+ * Allocates memory with malloc(), caller should free it */
+ssize_t gtm_get_targets(char **targets);
+
+/* Handlers data */
+int gtm_set_handler_path(char *path);
+int gtm_set_handler_fixup_off(unsigned long offset);
+int gtm_set_handler_reloc_off(unsigned long offset);
+int gtm_set_proc_features_off(unsigned long offset);
+
+/* Pthread data */
+int gtm_set_pthread_path(char *path);
+int gtm_set_init_off(unsigned long offset);
+
+#endif /* __GT_MODULE_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_kprobe.o
+swap_kprobe-y := \
+ swap_kprobes_deps.o \
+ swap_slots.o \
+ swap_td_raw.o \
+ swap_ktd.o
+
+
+### ARM64
+swap_kprobe-$(CONFIG_ARM64) += \
+ arch/arm64/swap-asm/simulate-insn.o \
+ arch/arm64/swap-asm/condn-helpers.o
+
+
+ifeq ($(CONFIG_SWAP_KERNEL_IMMUTABLE), y)
+
+swap_kprobe-y += swap_no_kprobes.o
+
+else # CONFIG_SWAP_KERNEL_IMMUTABLE
+
+swap_kprobe-y += swap_kprobes.o
+
+### ARM
+swap_kprobe-$(CONFIG_ARM) += \
+ arch/arm/swap-asm/swap_kprobes.o \
+ ../arch/arm/probes/probes_arm.o
+
+ifeq ($(CONFIG_STRICT_MEMORY_RWX), y)
+swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/memory_rwx.o
+endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y)
+
+
+### ARM64
+swap_kprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_kprobes.o \
+ arch/arm64/swap-asm/dbg_interface.o \
+ arch/arm64/swap-asm/kprobes-arm64.o
+
+
+### X86
+swap_kprobe-$(CONFIG_X86) += arch/x86/swap-asm/swap_kprobes.o
+
+endif # CONFIG_SWAP_KERNEL_IMMUTABLE
--- /dev/null
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ */
+
+
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+#include <asm/mmu_writeable.h>
+
+
+#include <ksyms/ksyms.h>
+
+
+static struct mm_struct *swap_init_mm;
+static int (*swap_set_memory_ro)(unsigned long addr, int numpages);
+static int (*swap_set_memory_rw)(unsigned long addr, int numpages);
+
+
+static int get_pte_cb(pte_t *ptep, pgtable_t token,
+ unsigned long addr, void *data)
+{
+ *(pte_t *)data = *ptep;
+
+ return 1;
+}
+
+static pte_t get_pte(unsigned long page_addr)
+{
+ pte_t pte = 0;
+
+ apply_to_page_range(swap_init_mm, page_addr,
+ PAGE_SIZE, get_pte_cb, &pte);
+
+ return pte;
+}
+
+static void write_to_module(unsigned long addr, unsigned long val)
+{
+ unsigned long *maddr = (unsigned long *)addr;
+ unsigned long page_addr = addr & PAGE_MASK;
+ pte_t pte;
+
+ pte = get_pte(page_addr);
+ if (pte_write(pte) == 0) {
+ unsigned long flags;
+ DEFINE_SPINLOCK(mem_lock);
+
+ spin_lock_irqsave(&mem_lock, flags);
+ if (swap_set_memory_rw(page_addr, 1) == 0) {
+ *maddr = val;
+ swap_set_memory_ro(page_addr, 1);
+ } else {
+ printk(KERN_INFO "RWX: failed to write memory %08lx (%08lx)\n",
+ addr, val);
+ }
+ spin_unlock_irqrestore(&mem_lock, flags);
+ } else {
+ *maddr = val;
+ }
+
+ flush_icache_range(addr, addr + sizeof(long));
+}
+
+void mem_rwx_write_u32(unsigned long addr, unsigned long val)
+{
+ if (addr < MODULES_VADDR || addr >= MODULES_END) {
+ /*
+ * if addr doesn't belongs kernel space,
+ * segmentation fault will occur
+ */
+ mem_text_write_kernel_word((long *)addr, val);
+ } else {
+ write_to_module(addr, val);
+ }
+}
+
+int mem_rwx_once(void)
+{
+ const char *sym;
+
+ sym = "set_memory_ro";
+ swap_set_memory_ro = (void *)swap_ksyms(sym);
+ if (swap_set_memory_ro == NULL)
+ goto not_found;
+
+ sym = "set_memory_rw";
+ swap_set_memory_rw = (void *)swap_ksyms(sym);
+ if (swap_set_memory_rw == NULL)
+ goto not_found;
+
+ sym = "init_mm";
+ swap_init_mm = (void *)swap_ksyms(sym);
+ if (swap_init_mm == NULL)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
--- /dev/null
+/**
+ * @file kprobe/arch/asm-arm/memory_rwx.h
+ *
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ */
+
+
+#ifndef _MEMORY_RWX_H
+#define _MEMORY_RWX_H
+
+
+int mem_rwx_once(void);
+void mem_rwx_write_u32(unsigned long addr, unsigned long val);
+
+
+#endif /* _MEMORY_RWX_H */
--- /dev/null
+/**
+ * kprobe/arch/asm-arm/swap_kprobes.c
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM/MIPS
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation; Support x86.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ * @author Alexander Shirshikov <a.shirshikov@samsung.com>: initial implementation for Thumb
+ * @author Stanislav Andreev <s.andreev@samsung.com>: added time debug profiling support; BUG() message fix
+ * @author Stanislav Andreev <s.andreev@samsung.com>: redesign of kprobe functionality -
+ * kprobe_handler() now called via undefined instruction hooks
+ * @author Stanislav Andreev <s.andreev@samsung.com>: hash tables search implemented for uprobes
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2014
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kprobe implementation for ARM architecture.
+ */
+
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
+# error "Kernel is immutable"
+#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
+
+
+#include <linux/module.h>
+#include <asm/cacheflush.h>
+#include <asm/traps.h>
+#include <linux/ptrace.h>
+#include <linux/list.h>
+#include <linux/hash.h>
+#include <ksyms/ksyms.h>
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_kdebug.h>
+#include <kprobe/swap_kprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <arch/arm/probes/probes_arm.h>
+#include "swap_kprobes.h"
+
+
+static void (*__swap_register_undef_hook)(struct undef_hook *hook);
+static void (*__swap_unregister_undef_hook)(struct undef_hook *hook);
+
+
+/**
+ * @brief Creates trampoline for kprobe.
+ *
+ * @param p Pointer to kp_core.
+ * @param sm Pointer to slot manager
+ * @return 0 on success, error code on error.
+ */
+int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm)
+{
+ u32 *tramp;
+ int ret;
+
+ tramp = swap_slot_alloc(sm);
+ if (tramp == NULL)
+ return -ENOMEM;
+
+ p->opcode = *(unsigned long *)p->addr;
+ ret = make_trampoline_arm(p->addr, p->opcode, tramp);
+ if (ret) {
+ swap_slot_free(sm, tramp);
+ return ret;
+ }
+
+ flush_icache_range((unsigned long)tramp,
+ (unsigned long)tramp + KPROBES_TRAMP_LEN);
+
+ p->ainsn.insn = (unsigned long *)tramp;
+
+ return 0;
+}
+
+/**
+ * @brief Prepares singlestep for current CPU.
+ *
+ * @param p Pointer to kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return Void.
+ */
+static void prepare_singlestep(struct kp_core *p, struct pt_regs *regs)
+{
+ regs->ARM_pc = (unsigned long)p->ainsn.insn;
+}
+
+static void save_previous_kp_core(struct kp_core_ctlblk *kcb)
+{
+ kcb->prev_kp_core.p = kp_core_running();
+ kcb->prev_kp_core.status = kcb->kp_core_status;
+}
+
+/**
+ * @brief Restores previous kp_core.
+ *
+ * @param kcb Pointer to kp_core_ctlblk which contains previous kp_core.
+ * @return Void.
+ */
+void restore_previous_kp_core(struct kp_core_ctlblk *kcb)
+{
+ kp_core_running_set(kcb->prev_kp_core.p);
+ kcb->kp_core_status = kcb->prev_kp_core.status;
+}
+
+static int kprobe_handler(struct pt_regs *regs)
+{
+ struct kp_core *p, *cur;
+ struct kp_core_ctlblk *kcb;
+ struct kctx *ctx = current_kctx;
+
+ if (regs->ARM_pc == sched_addr)
+ switch_to_bits_set(ctx, SWITCH_TO_KP);
+
+ kcb = kp_core_ctlblk();
+ cur = kp_core_running();
+
+ rcu_read_lock();
+ p = kp_core_by_addr(regs->ARM_pc);
+ if (p)
+ kp_core_get(p);
+ rcu_read_unlock();
+
+ if (p) {
+ if (cur) {
+ /* Kprobe is pending, so we're recursing. */
+ switch (kcb->kp_core_status) {
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /* A pre- or post-handler probe got us here. */
+ save_previous_kp_core(kcb);
+ kp_core_running_set(p);
+ kcb->kp_core_status = KPROBE_REENTER;
+ prepare_singlestep(p, regs);
+ restore_previous_kp_core(kcb);
+ break;
+ default:
+ /* impossible cases */
+ BUG();
+ }
+ } else {
+ kp_core_running_set(p);
+ kcb->kp_core_status = KPROBE_HIT_ACTIVE;
+
+ if (!(regs->ARM_cpsr & PSR_I_BIT))
+ local_irq_enable();
+
+ if (!p->handlers.pre(p, regs)) {
+ kcb->kp_core_status = KPROBE_HIT_SS;
+ prepare_singlestep(p, regs);
+ kp_core_running_set(NULL);
+ }
+ }
+ kp_core_put(p);
+ } else {
+ goto no_kprobe;
+ }
+
+ switch_to_bits_reset(ctx, SWITCH_TO_KP);
+
+ return 0;
+
+no_kprobe:
+ printk(KERN_INFO "no_kprobe: Not one of ours: let kernel handle it %p\n",
+ (unsigned long *)regs->ARM_pc);
+ return 1;
+}
+
+/**
+ * @brief Trap handler.
+ *
+ * @param regs Pointer to CPU register data.
+ * @param instr Instruction.
+ * @return kprobe_handler result.
+ */
+int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+ int ret;
+
+ if (likely(instr == BREAK_ARM)) {
+ ret = kprobe_handler(regs);
+ } else {
+ struct kp_core *p;
+
+ rcu_read_lock();
+ p = kp_core_by_addr(regs->ARM_pc);
+
+ /* skip false exeption */
+ ret = p && (p->opcode == instr) ? 0 : 1;
+ rcu_read_unlock();
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Probe pre handler.
+ *
+ * @param p Pointer to fired kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return 0.
+ */
+int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ entry_point_t entry = (entry_point_t)jp->entry;
+
+ if (entry) {
+ entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
+ regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
+ } else {
+ swap_jprobe_return();
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Jprobe return stub.
+ *
+ * @return Void.
+ */
+void swap_jprobe_return(void)
+{
+}
+EXPORT_SYMBOL_GPL(swap_jprobe_return);
+
+/**
+ * @brief Break handler stub.
+ *
+ * @param p Pointer to fired kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return 0.
+ */
+int swap_longjmp_break_handler (struct kprobe *p, struct pt_regs *regs)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swap_longjmp_break_handler);
+
+#ifdef CONFIG_STRICT_MEMORY_RWX
+#include "memory_rwx.h"
+
+static void write_u32(unsigned long addr, unsigned long val)
+{
+ mem_rwx_write_u32(addr, val);
+}
+#else /* CONFIG_STRICT_MEMORY_RWX */
+static void write_u32(unsigned long addr, unsigned long val)
+{
+ *(long *)addr = val;
+ flush_icache_range(addr, addr + sizeof(long));
+}
+#endif /* CONFIG_STRICT_MEMORY_RWX */
+
+/**
+ * @brief Arms kprobe.
+ *
+ * @param p Pointer to target kprobe.
+ * @return Void.
+ */
+void arch_kp_core_arm(struct kp_core *core)
+{
+ write_u32(core->addr, BREAK_ARM);
+}
+
+/**
+ * @brief Disarms kprobe.
+ *
+ * @param p Pointer to target kprobe.
+ * @return Void.
+ */
+void arch_kp_core_disarm(struct kp_core *core)
+{
+ write_u32(core->addr, core->opcode);
+}
+
+/**
+ * @brief Kretprobe trampoline. Provides jumping to probe handler.
+ *
+ * @return Void.
+ */
+void __naked swap_kretprobe_trampoline(void)
+{
+ __asm__ __volatile__ (
+ "stmdb sp!, {r0 - r11}\n"
+ "mov r0, sp\n" /* struct pt_regs -> r0 */
+ "bl swap_trampoline_handler\n"
+ "mov lr, r0\n"
+ "ldmia sp!, {r0 - r11}\n"
+ "bx lr\n"
+ : : : "memory");
+}
+
+/**
+ * @brief Prepares kretprobes, saves ret address, makes function return to
+ * trampoline.
+ *
+ * @param ri Pointer to kretprobe_instance.
+ * @param regs Pointer to CPU registers data.
+ * @return Void.
+ */
+void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ unsigned long *ptr_ret_addr;
+
+ /* for __switch_to probe */
+ if ((unsigned long)ri->rp->kp.addr == sched_addr) {
+ struct thread_info *tinfo = (struct thread_info *)regs->ARM_r2;
+
+ ptr_ret_addr = (unsigned long *)&tinfo->cpu_context.pc;
+ ri->sp = NULL;
+ ri->task = tinfo->task;
+ switch_to_bits_set(kctx_by_task(tinfo->task), SWITCH_TO_RP);
+ } else {
+ ptr_ret_addr = (unsigned long *)®s->ARM_lr;
+ ri->sp = (unsigned long *)regs->ARM_sp;
+ }
+
+ /* Save the return address */
+ ri->ret_addr = (unsigned long *)*ptr_ret_addr;
+
+ /* Replace the return addr with trampoline addr */
+ *ptr_ret_addr = (unsigned long)&swap_kretprobe_trampoline;
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * jumper *
+ ******************************************************************************
+ */
+struct cb_data {
+ unsigned long ret_addr;
+ unsigned long r0;
+
+ jumper_cb_t cb;
+ char data[0];
+};
+
+static unsigned long __used get_r0(struct cb_data *data)
+{
+ return data->r0;
+}
+
+static unsigned long __used jump_handler(struct cb_data *data)
+{
+ unsigned long ret_addr = data->ret_addr;
+
+ /* call callback */
+ data->cb(data->data);
+
+ /* FIXME: potential memory leak, when process kill */
+ kfree(data);
+
+ return ret_addr;
+}
+
+/* FIXME: restore condition flags */
+
+/**
+ * @brief Jumper trampoline.
+ *
+ * @return Void.
+ */
+void jump_trampoline(void);
+__asm(
+ "jump_trampoline:\n"
+
+ "push {r0 - r12}\n"
+ "mov r1, r0\n" /* data --> r1 */
+ "bl get_r0\n"
+ "str r0, [sp]\n" /* restore r0 */
+ "mov r0, r1\n" /* data --> r0 */
+ "bl jump_handler\n"
+ "mov lr, r0\n"
+ "pop {r0 - r12}\n"
+ "bx lr\n"
+);
+
+/**
+ * @brief Get jumper address.
+ *
+ * @return Jumper address.
+ */
+unsigned long get_jump_addr(void)
+{
+ return (unsigned long)&jump_trampoline;
+}
+EXPORT_SYMBOL_GPL(get_jump_addr);
+
+/**
+ * @brief Set jumper probe callback.
+ *
+ * @param ret_addr Jumper probe return address.
+ * @param regs Pointer to CPU registers data.
+ * @param cb Jumper callback of jumper_cb_t type.
+ * @param data Data that should be stored in cb_data.
+ * @param size Size of the data.
+ * @return 0.
+ */
+int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
+ jumper_cb_t cb, void *data, size_t size)
+{
+ struct cb_data *cb_data;
+
+ cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
+ if (cb_data == NULL)
+ return -ENOMEM;
+
+ /* save data */
+ if (size)
+ memcpy(cb_data->data, data, size);
+
+ /* save info for restore */
+ cb_data->ret_addr = ret_addr;
+ cb_data->cb = cb;
+ cb_data->r0 = regs->ARM_r0;
+
+ /* save cb_data to r0 */
+ regs->ARM_r0 = (long)cb_data;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(set_jump_cb);
+
+
+
+
+/**
+ * @brief Registers hook on specified instruction.
+ *
+ * @param hook Pointer to struct undef_hook.
+ * @return Void.
+ */
+void swap_register_undef_hook(struct undef_hook *hook)
+{
+ __swap_register_undef_hook(hook);
+}
+EXPORT_SYMBOL_GPL(swap_register_undef_hook);
+
+/**
+ * @brief Unregisters hook.
+ *
+ * @param hook Pointer to struct undef_hook.
+ * @return Void.
+ */
+void swap_unregister_undef_hook(struct undef_hook *hook)
+{
+ __swap_unregister_undef_hook(hook);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_undef_hook);
+
+/* kernel probes hook */
+static struct undef_hook undef_ho_k = {
+ .instr_mask = 0,
+ .instr_val = 0,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = SVC_MODE,
+ .fn = kprobe_trap_handler
+};
+
+/**
+ * @brief Arch-dependend module deps initialization stub.
+ *
+ * @return 0.
+ */
+int arch_init_module_deps(void)
+{
+ const char *sym;
+#ifdef CONFIG_STRICT_MEMORY_RWX
+ int ret;
+
+ ret = mem_rwx_once();
+ if (ret)
+ return ret;
+#endif /* CONFIG_STRICT_MEMORY_RWX */
+
+ sym = "register_undef_hook";
+ __swap_register_undef_hook = (void *)swap_ksyms(sym);
+ if (__swap_register_undef_hook == NULL)
+ goto not_found;
+
+ sym = "unregister_undef_hook";
+ __swap_unregister_undef_hook = (void *)swap_ksyms(sym);
+ if (__swap_unregister_undef_hook == NULL)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
+/**
+ * @brief Initializes kprobes module for ARM arch.
+ *
+ * @return 0 on success, error code on error.
+ */
+int swap_arch_init_kprobes(void)
+{
+ swap_register_undef_hook(&undef_ho_k);
+
+ return 0;
+}
+
+/**
+ * @brief Uninitializes kprobe module.
+ *
+ * @return Void.
+ */
+void swap_arch_exit_kprobes(void)
+{
+ swap_unregister_undef_hook(&undef_ho_k);
+}
+
+
+/* export symbol for probes_arm.h */
+EXPORT_SYMBOL_GPL(noret_arm);
+EXPORT_SYMBOL_GPL(make_trampoline_arm);
+
+#include <arch/arm/probes/tramps_arm.h>
+/* export symbol for tramps_arm.h */
+EXPORT_SYMBOL_GPL(gen_insn_execbuf);
+EXPORT_SYMBOL_GPL(pc_dep_insn_execbuf);
+EXPORT_SYMBOL_GPL(b_r_insn_execbuf);
+EXPORT_SYMBOL_GPL(b_cond_insn_execbuf);
+EXPORT_SYMBOL_GPL(blx_off_insn_execbuf);
--- /dev/null
+/**
+ * @file kprobe/arch/asm-arm/swap_kprobes.h
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>:
+ * initial implementation for ARM/MIPS
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com>:
+ * User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>:
+ * redesign module for separating core and arch parts
+ * @author Alexander Shirshikov <a.shirshikov@samsung.com>:
+ * initial implementation for Thumb
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * ARM arch-dependent kprobes interface declaration.
+ */
+
+
+#ifndef _SWAP_ASM_ARM_KPROBES_H
+#define _SWAP_ASM_ARM_KPROBES_H
+
+#include <linux/sched.h>
+#include <linux/compiler.h>
+#include <arch/arm/probes/probes.h>
+
+typedef unsigned long kprobe_opcode_t;
+
+
+/** Kprobes trampoline length */
+#define KPROBES_TRAMP_LEN PROBES_TRAMP_LEN
+
+/** User register offset */
+#define UREGS_OFFSET 8
+
+/**
+ * @struct prev_kp_core
+ * @brief Stores previous kp_core.
+ * @var prev_kp_core::p
+ * Pointer to kp_core struct.
+ * @var prev_kp_core::status
+ * Kprobe status.
+ */
+struct prev_kp_core {
+ struct kp_core *p;
+ unsigned long status;
+};
+
+/**
+ * @brief Gets task pc.
+ *
+ * @param p Pointer to task_struct
+ * @return Value in pc.
+ */
+static inline unsigned long arch_get_task_pc(struct task_struct *p)
+{
+ return task_thread_info(p)->cpu_context.pc;
+}
+
+/**
+ * @brief Sets task pc.
+ *
+ * @param p Pointer to task_struct.
+ * @param val Value that should be set.
+ * @return Void.
+ */
+static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
+{
+ task_thread_info(p)->cpu_context.pc = val;
+}
+
+/**
+ * @brief Gets syscall registers.
+ *
+ * @param sp Pointer to stack.
+ * @return Pointer to CPU regs data.
+ */
+static inline struct pt_regs *swap_get_syscall_uregs(unsigned long sp)
+{
+ return (struct pt_regs *)(sp + UREGS_OFFSET);
+}
+
+/**
+ * @brief Gets stack pointer.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @return Stack address.
+ */
+static inline unsigned long swap_get_stack_ptr(struct pt_regs *regs)
+{
+ return regs->ARM_sp;
+}
+
+/**
+ * @brief Sets stack pointer.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @param sp New stack pointer value.
+ * @return Void
+ */
+static inline void swap_set_stack_ptr(struct pt_regs *regs, unsigned long sp)
+{
+ regs->ARM_sp = sp;
+}
+
+/**
+ * @brief Gets instruction pointer.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @return Pointer to pc.
+ */
+static inline unsigned long swap_get_kpc(struct pt_regs *regs)
+{
+ return regs->ARM_pc | !!thumb_mode(regs);
+}
+
+/**
+ * @brief Sets instruction pointer.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @param val Address that should be stored in pc.
+ * @return Void.
+ */
+static inline void swap_set_kpc(struct pt_regs *regs, unsigned long val)
+{
+ if (val & 1) {
+ regs->ARM_pc = val & ~1;
+ regs->ARM_cpsr |= PSR_T_BIT;
+ } else {
+ regs->ARM_pc = val;
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+ }
+}
+
+/**
+ * @brief Gets specified argument.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @param num Number of the argument.
+ * @return Argument value.
+ */
+static inline unsigned long swap_get_arg(struct pt_regs *regs, int num)
+{
+ return regs->uregs[num];
+}
+
+/**
+ * @brief Sets specified argument.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @param num Number of the argument.
+ * @param val New argument value.
+ * @return Void.
+ */
+static inline void swap_set_arg(struct pt_regs *regs, int num,
+ unsigned long val)
+{
+ regs->uregs[num] = val;
+}
+
+/**
+ * @struct kp_core_ctlblk
+ * @brief Per-cpu kp_core control block.
+ * @var kp_core_ctlblk::kp_core_status
+ * Kprobe status.
+ * @var kp_core_ctlblk::prev_kp_core
+ * Previous kp_core.
+ */
+struct kp_core_ctlblk {
+ unsigned long kp_core_status;
+ struct prev_kp_core prev_kp_core;
+};
+
+/**
+ * @struct swap_arch_specific_insn
+ * @brief Architecture specific copy of original instruction.
+ * @var swap_arch_specific_insn::insn
+ * Copy of the original instruction.
+ */
+struct swap_arch_specific_insn {
+ kprobe_opcode_t *insn;
+};
+
+typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long,
+ unsigned long, unsigned long,
+ unsigned long, unsigned long);
+
+struct undef_hook;
+
+void swap_register_undef_hook(struct undef_hook *hook);
+void swap_unregister_undef_hook(struct undef_hook *hook);
+
+int arch_init_module_deps(void);
+
+struct slot_manager;
+struct kretprobe;
+struct kretprobe_instance;
+struct kp_core;
+struct kprobe;
+
+int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm);
+void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+
+void arch_kp_core_arm(struct kp_core *p);
+void arch_kp_core_disarm(struct kp_core *p);
+
+int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs);
+int swap_longjmp_break_handler(struct kprobe *p, struct pt_regs *regs);
+
+void restore_previous_kp_core(struct kp_core_ctlblk *kcb);
+
+void __naked swap_kretprobe_trampoline(void);
+
+/**
+ * @brief Gets arguments of kernel functions.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @param n Number of the argument.
+ * @return Argument value.
+ */
+static inline unsigned long swap_get_karg(struct pt_regs *regs, unsigned long n)
+{
+ switch (n) {
+ case 0:
+ return regs->ARM_r0;
+ case 1:
+ return regs->ARM_r1;
+ case 2:
+ return regs->ARM_r2;
+ case 3:
+ return regs->ARM_r3;
+ }
+
+ return *((unsigned long *)regs->ARM_sp + n - 4);
+}
+
+/**
+ * @brief swap_get_karg wrapper.
+ *
+ * @param regs Pointer to CPU registers data.
+ * @param n Number of the argument.
+ * @return Argument value.
+ */
+static inline unsigned long swap_get_sarg(struct pt_regs *regs, unsigned long n)
+{
+ return swap_get_karg(regs, n);
+}
+
+/* jumper */
+typedef unsigned long (*jumper_cb_t)(void *);
+
+unsigned long get_jump_addr(void);
+int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
+ jumper_cb_t cb, void *data, size_t size);
+
+int swap_arch_init_kprobes(void);
+void swap_arch_exit_kprobes(void);
+
+/* void gen_insn_execbuf (void); */
+/* void pc_dep_insn_execbuf (void); */
+/* void gen_insn_execbuf_holder (void); */
+/* void pc_dep_insn_execbuf_holder (void); */
+
+#endif /* _SWAP_ASM_ARM_KPROBES_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_ARM_PROBES_H
+#define _SWAP_ASM_ARM_PROBES_H
+
+
+#define BREAK_ARM 0xffffdeff
+#define BREAK_THUMB (BREAK_ARM & 0xffff)
+#define RET_BREAK_ARM BREAK_ARM
+#define RET_BREAK_THUMB BREAK_THUMB
+
+#define PROBES_TRAMP_LEN (9 * 4)
+#define PROBES_TRAMP_INSN_IDX 2
+#define PROBES_TRAMP_RET_BREAK_IDX 5
+
+
+#endif /* _SWAP_ASM_ARM_PROBES_H */
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/condn-helpers.c
+ *
+ * Copyright (C) 2013 Linaro Limited
+ *
+ * Copied from: arch/arm/kernel/kprobes-common.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Description:
+ *
+ * AArch64 and AArch32 shares same conditional(CNZV) flags encoding.
+ * This file implements conditional check helpers compatible with
+ * both AArch64 and AArch32 modes. Uprobes on v8 can handle both 32-bit
+ * & 64-bit user-space instructions, so we abstract the common functions
+ * in this file. While AArch64 and AArch32 specific instruction handling
+ * are implemented in separate files, this file contains common bits.
+ */
+
+
+#include <linux/module.h>
+#include "condn-helpers.h"
+
+
+static unsigned long __check_eq(unsigned long pstate)
+{
+ return pstate & PSR_Z_BIT;
+}
+
+static unsigned long __check_ne(unsigned long pstate)
+{
+ return (~pstate) & PSR_Z_BIT;
+}
+
+static unsigned long __check_cs(unsigned long pstate)
+{
+ return pstate & PSR_C_BIT;
+}
+
+static unsigned long __check_cc(unsigned long pstate)
+{
+ return (~pstate) & PSR_C_BIT;
+}
+
+static unsigned long __check_mi(unsigned long pstate)
+{
+ return pstate & PSR_N_BIT;
+}
+
+static unsigned long __check_pl(unsigned long pstate)
+{
+ return (~pstate) & PSR_N_BIT;
+}
+
+static unsigned long __check_vs(unsigned long pstate)
+{
+ return pstate & PSR_V_BIT;
+}
+
+static unsigned long __check_vc(unsigned long pstate)
+{
+ return (~pstate) & PSR_V_BIT;
+}
+
+static unsigned long __check_hi(unsigned long pstate)
+{
+ pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return pstate & PSR_C_BIT;
+}
+
+static unsigned long __check_ls(unsigned long pstate)
+{
+ pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return (~pstate) & PSR_C_BIT;
+}
+
+static unsigned long __check_ge(unsigned long pstate)
+{
+ pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return (~pstate) & PSR_N_BIT;
+}
+
+static unsigned long __check_lt(unsigned long pstate)
+{
+ pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return pstate & PSR_N_BIT;
+}
+
+static unsigned long __check_gt(unsigned long pstate)
+{
+ /*PSR_N_BIT ^= PSR_V_BIT */
+ unsigned long temp = pstate ^ (pstate << 3);
+
+ temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
+ return (~temp) & PSR_N_BIT;
+}
+
+static unsigned long __check_le(unsigned long pstate)
+{
+ /*PSR_N_BIT ^= PSR_V_BIT */
+ unsigned long temp = pstate ^ (pstate << 3);
+
+ temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
+ return temp & PSR_N_BIT;
+}
+
+static unsigned long __check_al(unsigned long pstate)
+{
+ return true;
+}
+
+probes_pstate_check_t * const probe_condition_checks[16] = {
+ &__check_eq, &__check_ne, &__check_cs, &__check_cc,
+ &__check_mi, &__check_pl, &__check_vs, &__check_vc,
+ &__check_hi, &__check_ls, &__check_ge, &__check_lt,
+ &__check_gt, &__check_le, &__check_al, &__check_al
+};
+EXPORT_SYMBOL_GPL(probe_condition_checks);
--- /dev/null
+#ifndef _ASM_CONDN_HELPER_H
+#define _ASM_CONDN_HELPER_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+typedef unsigned long (probes_pstate_check_t)(unsigned long);
+
+
+extern probes_pstate_check_t * const probe_condition_checks[16];
+
+
+#endif /* _ASM_CONDN_HELPER_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
+# error "Kernel is immutable"
+#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
+
+
+#include <linux/module.h>
+#include <linux/rwlock.h>
+#include <asm/debug-monitors.h>
+#include <ksyms/ksyms.h>
+#include "dbg_interface.h"
+
+
+
+
+/* ============================================================================
+ * = BRK IMPLEMENT =
+ * ============================================================================
+ */
+static LIST_HEAD(brk_list);
+static DEFINE_RWLOCK(brk_list_lock);
+
+void dbg_brk_hook_reg(struct brk_hook *hook)
+{
+ write_lock(&brk_list_lock);
+ list_add(&hook->list, &brk_list);
+ write_unlock(&brk_list_lock);
+}
+EXPORT_SYMBOL_GPL(dbg_brk_hook_reg);
+
+void dbg_brk_hook_unreg(struct brk_hook *hook)
+{
+ write_lock(&brk_list_lock);
+ list_del(&hook->list);
+ write_unlock(&brk_list_lock);
+}
+EXPORT_SYMBOL_GPL(dbg_brk_hook_unreg);
+
+static enum dbg_code call_brk_hook(struct pt_regs *regs, unsigned int esr)
+{
+ struct brk_hook *hook;
+ enum dbg_code (*fn)(struct pt_regs *regs, unsigned int esr) = NULL;
+
+ read_lock(&brk_list_lock);
+ list_for_each_entry(hook, &brk_list, list)
+ if (((esr & hook->esr_mask) == hook->esr_val) &&
+ ((regs->pstate & hook->spsr_mask) == hook->spsr_val))
+ fn = hook->fn;
+ read_unlock(&brk_list_lock);
+
+ return fn ? fn(regs, esr) : DBG_ERROR;
+}
+
+
+typedef int (*dbg_fn_t)(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs);
+
+static dbg_fn_t *brk_handler_ptr;
+static dbg_fn_t orig_brk_handler;
+
+static int brk_handler(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ /* call the registered breakpoint handler */
+ if (call_brk_hook(regs, esr) == DBG_HANDLED)
+ return 0;
+
+ return orig_brk_handler(addr, esr, regs);
+}
+
+static void init_brk(dbg_fn_t *fn)
+{
+ brk_handler_ptr = fn;
+ orig_brk_handler = *brk_handler_ptr;
+ *brk_handler_ptr = brk_handler;
+}
+
+static void uninit_brk(void)
+{
+ *brk_handler_ptr = orig_brk_handler;
+}
+
+
+
+
+
+
+/* ============================================================================
+ * = INIT / EXIT =
+ * ============================================================================
+ */
+int dbg_iface_init(void)
+{
+ struct fault_info {
+ int (*fn)(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs);
+ int sig;
+ int code;
+ const char *name;
+ };
+
+ struct fault_info *debug_finfo;
+ struct fault_info *finfo_brk;
+
+ debug_finfo = (struct fault_info *)swap_ksyms("debug_fault_info");
+ if (debug_finfo == NULL) {
+ pr_err("cannot found 'debug_fault_info'\n");
+ return -EINVAL;
+ }
+
+ finfo_brk = &debug_finfo[DBG_ESR_EVT_BRK];
+
+ init_brk(&finfo_brk->fn);
+
+ return 0;
+}
+
+void dbg_iface_uninit(void)
+{
+ uninit_brk();
+}
--- /dev/null
+#ifndef _ASM_DBG_INTERFACE_H
+#define _ASM_DBG_INTERFACE_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/list.h>
+#include <linux/types.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
+# include <asm/esr.h>
+#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) */
+# define ESR_ELx_IL (1 << 25)
+# define ESR_ELx_EC_MASK 0xfc000000
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) */
+
+#define ESR_ELx_EC_BRK 0xf0000000
+#define BRK_COMM_MASK 0x0000ffff
+
+#define DBG_BRK_ESR_MASK (ESR_ELx_EC_MASK | ESR_ELx_IL | BRK_COMM_MASK)
+#define DBG_BRK_ESR(x) (ESR_ELx_EC_BRK | ESR_ELx_IL | (BRK_COMM_MASK & (x)))
+
+#define MAKE_BRK(v) ((((v) & 0xffff) << 5) | 0xd4200000)
+
+
+enum dbg_code {
+ DBG_HANDLED,
+ DBG_ERROR,
+};
+
+
+struct brk_hook {
+ struct list_head list;
+ u32 spsr_mask;
+ u32 spsr_val;
+ u32 esr_mask;
+ u32 esr_val;
+ enum dbg_code (*fn)(struct pt_regs *regs, unsigned int esr);
+};
+
+
+void dbg_brk_hook_reg(struct brk_hook *hook);
+void dbg_brk_hook_unreg(struct brk_hook *hook);
+
+int dbg_iface_init(void);
+void dbg_iface_uninit(void);
+
+
+#endif /* _ASM_DBG_INTERFACE_H */
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/kprobes-arm64.c
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/ptrace.h>
+#include <kprobe/swap_kprobes.h>
+
+#include "probes-decode.h"
+#include "kprobes-arm64.h"
+#include "simulate-insn.h"
+#include "condn-helpers.h"
+
+
+/*
+ * condition check functions for kp_cores simulation
+ */
+static unsigned long __check_pstate(struct kp_core *p, struct pt_regs *regs)
+{
+ struct swap_arch_specific_insn *asi = &p->ainsn;
+ unsigned long pstate = regs->pstate & 0xffffffff;
+
+ return asi->pstate_cc(pstate);
+}
+
+static unsigned long __check_cbz(struct kp_core *p, struct pt_regs *regs)
+{
+ return check_cbz((u32)p->opcode, regs);
+}
+
+static unsigned long __check_cbnz(struct kp_core *p, struct pt_regs *regs)
+{
+ return check_cbnz((u32)p->opcode, regs);
+}
+
+static unsigned long __check_tbz(struct kp_core *p, struct pt_regs *regs)
+{
+ return check_tbz((u32)p->opcode, regs);
+}
+
+static unsigned long __check_tbnz(struct kp_core *p, struct pt_regs *regs)
+{
+ return check_tbnz((u32)p->opcode, regs);
+}
+
+/*
+ * prepare functions for instruction simulation
+ */
+static void prepare_none(struct kp_core *p, struct swap_arch_specific_insn *asi)
+{
+}
+
+static void prepare_bcond(struct kp_core *p,
+ struct swap_arch_specific_insn *asi)
+{
+ kprobe_opcode_t insn = p->opcode;
+
+ asi->check_condn = __check_pstate;
+ asi->pstate_cc = probe_condition_checks[insn & 0xf];
+}
+
+static void prepare_cbz_cbnz(struct kp_core *p,
+ struct swap_arch_specific_insn *asi)
+{
+ kprobe_opcode_t insn = p->opcode;
+
+ asi->check_condn = (insn & (1 << 24)) ? __check_cbnz : __check_cbz;
+}
+
+static void prepare_tbz_tbnz(struct kp_core *p,
+ struct swap_arch_specific_insn *asi)
+{
+ kprobe_opcode_t insn = p->opcode;
+
+ asi->check_condn = (insn & (1 << 24)) ? __check_tbnz : __check_tbz;
+}
+
+
+/* Load literal (PC-relative) instructions
+ * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
+ *
+ * opcode[26]: V=0, Load GP registers, simulate them.
+ * Encoding: xx01 1000 xxxx xxxx xxxx xxxx xxxx xxxx
+ * opcode[31:30]: op = 00, 01 - LDR literal
+ * opcode[31:30]: op = 10, - LDRSW literal
+ *
+ * 1. V=1 -Load FP/AdvSIMD registers
+ * Encoding: xx01 1100 xxxx xxxx xxxx xxxx xxxx xxxx
+ * 2. V=0,opc=11 -PRFM(Prefetch literal)
+ * Encoding: 1101 1000 xxxx xxxx xxxx xxxx xxxx xxxx
+ * Reject FP/AdvSIMD literal load & PRFM literal.
+ */
+static const struct aarch64_decode_item load_literal_subtable[] = {
+ DECODE_REJECT(0x1C000000, 0x3F000000),
+ DECODE_REJECT(0xD8000000, 0xFF000000),
+ DECODE_LITERAL(0x18000000, 0xBF000000, prepare_none,
+ simulate_ldr_literal),
+ DECODE_LITERAL(0x98000000, 0xFF000000, prepare_none,
+ simulate_ldrsw_literal),
+ DECODE_END,
+};
+
+/* AArch64 instruction decode table for kp_cores:
+ * The instruction will fall into one of the 3 groups:
+ * 1. Single stepped out-of-the-line slot.
+ * -Most instructions fall in this group, those does not
+ * depend on PC address.
+ *
+ * 2. Should be simulated because of PC-relative/literal access.
+ * -All branching and PC-relative insrtcutions are simulated
+ * in C code, making use of saved pt_regs
+ * Catch: SIMD/NEON register context are not saved while
+ * entering debug exception, so are rejected for now.
+ *
+ * 3. Cannot be probed(not safe) so are rejected.
+ * - Exception generation and exception return instructions
+ * - Exclusive monitor(LDREX/STREX family)
+ *
+ */
+static const struct aarch64_decode_item aarch64_decode_table[] = {
+ /*
+ * Data processing - PC relative(literal) addressing:
+ * Encoding: xxx1 0000 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_LITERAL(0x10000000, 0x1F000000, prepare_none,
+ simulate_adr_adrp),
+
+ /*
+ * Data processing - Add/Substract Immediate:
+ * Encoding: xxx1 0001 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x11000000, 0x1F000000),
+
+ /*
+ * Data processing
+ * Encoding:
+ * xxx1 0010 0xxx xxxx xxxx xxxx xxxx xxxx (Logical)
+ * xxx1 0010 1xxx xxxx xxxx xxxx xxxx xxxx (Move wide)
+ * xxx1 0011 0xxx xxxx xxxx xxxx xxxx xxxx (Bitfield)
+ * xxx1 0011 1xxx xxxx xxxx xxxx xxxx xxxx (Extract)
+ */
+ DECODE_SINGLESTEP(0x12000000, 0x1E000000),
+
+ /*
+ * Data processing - SIMD/FP/AdvSIMD/Crypto-AES/SHA
+ * Encoding: xxx0 111x xxxx xxxx xxxx xxxx xxxx xxxx
+ * Encoding: xxx1 111x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x0E000000, 0x0E000000),
+
+ /*
+ * Data processing - Register
+ * Encoding: xxxx 101x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x0A000000, 0x0E000000),
+
+ /* Branching Instructions
+ *
+ * Encoding:
+ * x001 01xx xxxx xxxx xxxx xxxx xxxx xxxx (uncondtional Branch)
+ * x011 010x xxxx xxxx xxxx xxxx xxxx xxxx (compare & branch)
+ * x011 011x xxxx xxxx xxxx xxxx xxxx xxxx (Test & Branch)
+ * 0101 010x xxxx xxxx xxxx xxxx xxxx xxxx (Conditional, immediate)
+ * 1101 011x xxxx xxxx xxxx xxxx xxxx xxxx (Unconditional,register)
+ */
+ DECODE_BRANCH(0x14000000, 0x7C000000, prepare_none,
+ simulate_b_bl),
+ DECODE_BRANCH(0x34000000, 0x7E000000, prepare_cbz_cbnz,
+ simulate_cbz_cbnz),
+ DECODE_BRANCH(0x36000000, 0x7E000000, prepare_tbz_tbnz,
+ simulate_tbz_tbnz),
+ DECODE_BRANCH(0x54000000, 0xFE000000, prepare_bcond,
+ simulate_b_cond),
+ DECODE_BRANCH(0xD6000000, 0xFE000000, prepare_none,
+ simulate_br_blr_ret),
+
+ /* System insn:
+ * Encoding: 1101 0101 00xx xxxx xxxx xxxx xxxx xxxx
+ *
+ * Note: MSR immediate (update PSTATE daif) is not safe handling
+ * within kp_cores, rejected.
+ *
+ * Don't re-arrange these decode table entries.
+ */
+ DECODE_REJECT(0xD500401F, 0xFFF8F01F),
+ DECODE_SINGLESTEP(0xD5000000, 0xFFC00000),
+
+ /* Exception Generation:
+ * Encoding: 1101 0100 xxxx xxxx xxxx xxxx xxxx xxxx
+ * Instructions: SVC, HVC, SMC, BRK, HLT, DCPS1, DCPS2, DCPS3
+ */
+ DECODE_REJECT(0xD4000000, 0xFF000000),
+
+ /*
+ * Load/Store - Exclusive monitor
+ * Encoding: xx00 1000 xxxx xxxx xxxx xxxx xxxx xxxx
+ *
+ * Reject exlusive monitor'ed instructions
+ */
+ DECODE_REJECT(0x08000000, 0x3F000000),
+
+ /*
+ * Load/Store - PC relative(literal):
+ * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE(0x18000000, 0x3B000000, load_literal_subtable),
+
+ /*
+ * Load/Store - Register Pair
+ * Encoding:
+ * xx10 1x00 0xxx xxxx xxxx xxxx xxxx xxxx
+ * xx10 1x00 1xxx xxxx xxxx xxxx xxxx xxxx
+ * xx10 1x01 0xxx xxxx xxxx xxxx xxxx xxxx
+ * xx10 1x01 1xxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x28000000, 0x3A000000),
+
+ /*
+ * Load/Store - Register
+ * Encoding:
+ * xx11 1x00 xx0x xxxx xxxx 00xx xxxx xxxx (unscaled imm)
+ * xx11 1x00 xx0x xxxx xxxx 01xx xxxx xxxx (imm post-indexed)
+ * xx11 1x00 xx0x xxxx xxxx 10xx xxxx xxxx (unpriviledged)
+ * xx11 1x00 xx0x xxxx xxxx 11xx xxxx xxxx (imm pre-indexed)
+ *
+ * xx11 1x00 xx10 xxxx xxxx xx10 xxxx xxxx (register offset)
+ *
+ * xx11 1x01 xxxx xxxx xxxx xxxx xxxx xxxx (unsigned imm)
+ */
+ DECODE_SINGLESTEP(0x38000000, 0x3B200000),
+ DECODE_SINGLESTEP(0x38200200, 0x38300300),
+ DECODE_SINGLESTEP(0x39000000, 0x3B000000),
+
+ /*
+ * Load/Store - AdvSIMD
+ * Encoding:
+ * 0x00 1100 0x00 0000 xxxx xxxx xxxx xxxx (Multiple-structure)
+ * 0x00 1100 1x0x xxxx xxxx xxxx xxxx xxxx (Multi-struct post-indexed)
+ * 0x00 1101 0xx0 0000 xxxx xxxx xxxx xxxx (Single-structure))
+ * 0x00 1101 1xxx xxxx xxxx xxxx xxxx xxxx (Single-struct post-index)
+ */
+ DECODE_SINGLESTEP(0x0C000000, 0xBFBF0000),
+ DECODE_SINGLESTEP(0x0C800000, 0xBFA00000),
+ DECODE_SINGLESTEP(0x0D000000, 0xBF9F0000),
+ DECODE_SINGLESTEP(0x0D800000, 0xBF800000),
+
+ /* Unallocated: xxx0 0xxx xxxx xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT(0x00000000, 0x18000000),
+ DECODE_END,
+};
+
+static int kp_core_decode_insn(kprobe_opcode_t insn,
+ struct swap_arch_specific_insn *asi,
+ const struct aarch64_decode_item *tbl)
+{
+ unsigned int entry, ret = INSN_REJECTED;
+
+ for (entry = 0; !decode_table_end(tbl[entry]); entry++) {
+ if (decode_table_hit(tbl[entry], insn))
+ break;
+ }
+
+ switch (decode_get_type(tbl[entry])) {
+ case DECODE_TYPE_END:
+ case DECODE_TYPE_REJECT:
+ default:
+ ret = INSN_REJECTED;
+ break;
+
+ case DECODE_TYPE_SINGLESTEP:
+ ret = INSN_GOOD;
+ break;
+
+ case DECODE_TYPE_SIMULATE:
+ asi->prepare = decode_prepare_fn(tbl[entry]);
+ asi->handler = decode_handler_fn(tbl[entry]);
+ ret = INSN_GOOD_NO_SLOT;
+ break;
+
+ case DECODE_TYPE_TABLE:
+ /* recurse with next level decode table */
+ ret = kp_core_decode_insn(insn, asi,
+ decode_sub_table(tbl[entry]));
+ };
+
+ return ret;
+}
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD If instruction is supported and uses instruction slot,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ */
+enum kp_core_insn arm_kp_core_decode_insn(kprobe_opcode_t insn,
+ struct swap_arch_specific_insn *asi)
+{
+ return kp_core_decode_insn(insn, asi, aarch64_decode_table);
+}
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/kprobes-arm64.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _ARM_KERNEL_KPROBES_ARM64_H
+#define _ARM_KERNEL_KPROBES_ARM64_H
+
+enum kp_core_insn {
+ INSN_REJECTED,
+ INSN_GOOD_NO_SLOT,
+ INSN_GOOD,
+};
+
+extern kp_core_pstate_check_t * const kp_core_condition_checks[16];
+
+enum kp_core_insn arm_kp_core_decode_insn(kprobe_opcode_t insn,
+ struct swap_arch_specific_insn *asi);
+
+#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/probes-decode.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _ARM_KERNEL_PROBES_DECODE_H
+#define _ARM_KERNEL_PROBES_DECODE_H
+
+/*
+ * The following definitions and macros are used to build instruction
+ * decoding tables.
+ */
+enum decode_type {
+ DECODE_TYPE_END,
+ DECODE_TYPE_SINGLESTEP,
+ DECODE_TYPE_SIMULATE,
+ DECODE_TYPE_TABLE,
+ DECODE_TYPE_REJECT,
+};
+
+struct aarch64_decode_item;
+
+struct aarch64_decode_header {
+ enum decode_type type;
+ u32 mask;
+ u32 val;
+};
+
+struct aarch64_decode_actions {
+ kp_core_prepare_t *prepare;
+ kp_core_handler_t *handler;
+};
+
+struct aarch64_decode_table {
+ const struct aarch64_decode_item *tbl;
+};
+
+union aarch64_decode_handler {
+ struct aarch64_decode_actions actions;
+ struct aarch64_decode_table table;
+};
+
+struct aarch64_decode_item {
+ struct aarch64_decode_header header;
+ union aarch64_decode_handler decode;
+};
+
+#define decode_get_type(_entry) ((_entry).header.type)
+
+#define decode_table_end(_entry) \
+ ((_entry).header.type == DECODE_TYPE_END)
+
+#define decode_table_hit(_entry, insn) \
+ ((insn & (_entry).header.mask) == (_entry).header.val)
+
+#define decode_prepare_fn(_entry) ((_entry).decode.actions.prepare)
+#define decode_handler_fn(_entry) ((_entry).decode.actions.handler)
+#define decode_sub_table(_entry) ((_entry).decode.table.tbl)
+
+#define DECODE_ADD_HEADER(_type, _val, _mask) \
+ .header = { \
+ .type = _type, \
+ .mask = _mask, \
+ .val = _val, \
+ }
+
+#define DECODE_ADD_ACTION(_prepare, _handler) \
+ .decode = { \
+ .actions = { \
+ .prepare = _prepare, \
+ .handler = _handler, \
+ } \
+ }
+
+#define DECODE_ADD_TABLE(_table) \
+ .decode = { \
+ .table = {.tbl = _table} \
+ }
+
+#define DECODE_REJECT(_v, _m) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_REJECT, _v, _m) }
+
+#define DECODE_SINGLESTEP(_v, _m) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_SINGLESTEP, _v, _m) }
+
+#define DECODE_SIMULATE(_v, _m, _p, _h) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_SIMULATE, _v, _m), \
+ DECODE_ADD_ACTION(_p, _h) }
+
+#define DECODE_TABLE(_v, _m, _table) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_TABLE, _v, _m), \
+ DECODE_ADD_TABLE(_table) }
+
+#define DECODE_LITERAL(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
+#define DECODE_BRANCH(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
+
+/* should be the last element in decode structure */
+#define DECODE_END { .header = {.type = DECODE_TYPE_END, } }
+
+#endif /* _ARM_KERNEL_PROBES_DECODE_H */
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/simulate-insn.c
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/ptrace.h>
+
+#include "simulate-insn.h"
+
+#define sign_extend(x, signbit) \
+ ((x) | (0 - ((x) & ((long)1 << (signbit)))))
+
+#define bbl_displacement(insn) \
+ sign_extend(((insn) & 0x3ffffff) << 2, 27)
+
+#define bcond_displacement(insn) \
+ sign_extend(((insn >> 5) & 0xfffff) << 2, 21)
+
+#define cbz_displacement(insn) \
+ sign_extend(((insn >> 5) & 0xfffff) << 2, 21)
+
+#define tbz_displacement(insn) \
+ sign_extend(((insn >> 5) & 0x3fff) << 2, 15)
+
+#define ldr_displacement(insn) \
+ sign_extend(((insn >> 5) & 0xfffff) << 2, 21)
+
+
+unsigned long check_cbz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+
+ return (opcode & (1 << 31)) ?
+ !(regs->regs[xn]) : !(regs->regs[xn] & 0xffffffff);
+}
+EXPORT_SYMBOL_GPL(check_cbz);
+
+unsigned long check_cbnz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+
+ return (opcode & (1 << 31)) ?
+ (regs->regs[xn]) : (regs->regs[xn] & 0xffffffff);
+}
+EXPORT_SYMBOL_GPL(check_cbnz);
+
+unsigned long check_tbz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+ int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
+
+ return ~((regs->regs[xn] >> bit_pos) & 0x1);
+}
+EXPORT_SYMBOL_GPL(check_tbz);
+
+unsigned long check_tbnz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+ int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
+
+ return (regs->regs[xn] >> bit_pos) & 0x1;
+}
+EXPORT_SYMBOL_GPL(check_tbnz);
+
+/*
+ * instruction simulate functions
+ */
+void simulate_none(u32 opcode, long addr, struct pt_regs *regs)
+{
+}
+
+void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
+{
+ long res, imm, xn, shift;
+
+ xn = opcode & 0x1f;
+ shift = (opcode >> 31) ? 12 : 0; /* check insn ADRP/ADR */
+ imm = ((opcode >> 3) & 0xffffc) | ((opcode >> 29) & 0x3);
+ res = addr + 8 + (sign_extend(imm, 20) << shift);
+
+ regs->regs[xn] = opcode & 0x80000000 ? res & 0xfffffffffffff000 : res;
+ regs->pc += 4;
+}
+EXPORT_SYMBOL_GPL(simulate_adr_adrp);
+
+void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = bbl_displacement(opcode);
+
+ /* Link register is x30 */
+ if (opcode & (1 << 31))
+ regs->regs[30] = addr + 4;
+
+ regs->pc = addr + disp;
+}
+EXPORT_SYMBOL_GPL(simulate_b_bl);
+
+void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = bcond_displacement(opcode);
+
+ regs->pc = addr + disp;
+}
+EXPORT_SYMBOL_GPL(simulate_b_cond);
+
+void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int xn = (opcode >> 5) & 0x1f;
+
+ /* Link register is x30 */
+ if (((opcode >> 21) & 0x3) == 1)
+ regs->regs[30] = addr + 4;
+
+ regs->pc = regs->regs[xn];
+}
+EXPORT_SYMBOL_GPL(simulate_br_blr_ret);
+
+void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = cbz_displacement(opcode);
+
+ regs->pc = addr + disp;
+}
+EXPORT_SYMBOL_GPL(simulate_cbz_cbnz);
+
+void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = tbz_displacement(opcode);
+
+ regs->pc = addr + disp;
+}
+EXPORT_SYMBOL_GPL(simulate_tbz_tbnz);
+
+void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
+{
+ u64 *load_addr;
+ int xn = opcode & 0x1f;
+ int disp = ldr_displacement(opcode);
+
+ load_addr = (u64 *) (addr + disp);
+
+ if (opcode & (1 << 30)) /* x0-x31 */
+ regs->regs[xn] = *load_addr;
+ else /* w0-w31 */
+ *(u32 *) (®s->regs[xn]) = (*(u32 *) (load_addr));
+}
+EXPORT_SYMBOL_GPL(simulate_ldr_literal);
+
+void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
+{
+ u64 *load_addr;
+ long data;
+ int xn = opcode & 0x1f;
+ int disp = ldr_displacement(opcode);
+
+ load_addr = (u64 *) (addr + disp);
+ data = *load_addr;
+
+ regs->regs[xn] = sign_extend(data, 63);
+}
+EXPORT_SYMBOL_GPL(simulate_ldrsw_literal);
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/simulate-insn.h
+ *
+ * Copyright (C) 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _ARM_KERNEL_SIMULATE_INSN_H
+#define _ARM_KERNEL_SIMULATE_INSN_H
+
+unsigned long check_cbz(u32 opcode, struct pt_regs *regs);
+unsigned long check_cbnz(u32 opcode, struct pt_regs *regs);
+unsigned long check_tbz(u32 opcode, struct pt_regs *regs);
+unsigned long check_tbnz(u32 opcode, struct pt_regs *regs);
+void simulate_none(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs);
+
+#endif /* _ARM_KERNEL_SIMULATE_INSN_H */
--- /dev/null
+/*
+ * Copied from: arch/arm64/kernel/kprobes.c
+ *
+ * Kprobes support for ARM64
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ * Author: Sandeepa Prabhu <sandeepa.prabhu@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
+# error "Kernel is immutable"
+#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
+
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <asm/cacheflush.h>
+#include <asm/debug-monitors.h>
+#include <ksyms/ksyms.h>
+#include <kprobe/swap_ktd.h>
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_kprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include "swap_kprobes.h"
+#include "kprobes-arm64.h"
+#include "dbg_interface.h"
+
+
+#define BRK_BP 0x63
+#define BRK_PSEUDO_SS 0x64
+#define BRK64_OPCODE_BP MAKE_BRK(BRK_BP)
+#define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS)
+
+
+#ifdef CONFIG_CPU_BIG_ENDIAN
+static u32 conv_inst(u32 inst)
+{
+ return swab32(inst);
+}
+#else /* CONFIG_CPU_BIG_ENDIAN */
+static u32 conv_inst(u32 inst)
+{
+ return inst;
+}
+#endif /* CONFIG_CPU_BIG_ENDIAN */
+
+
+static void flush_icache(unsigned long addr, size_t size)
+{
+ flush_icache_range(addr, addr + size);
+}
+
+static void write_u32(u32 *addr, u32 val)
+{
+ *addr = val;
+ flush_icache((unsigned long)addr, sizeof(val));
+}
+
+void arch_kp_core_arm(struct kp_core *p)
+{
+ write_u32((u32 *)p->addr, conv_inst(BRK64_OPCODE_BP));
+}
+
+void arch_kp_core_disarm(struct kp_core *p)
+{
+ write_u32((u32 *)p->addr, p->opcode);
+}
+
+
+struct restore_data {
+ unsigned long restore_addr;
+};
+
+static void ktd_restore_init(struct task_struct *task, void *data)
+{
+ struct restore_data *rdata = (struct restore_data *)data;
+
+ rdata->restore_addr = 0;
+}
+
+static void ktd_restore_exit(struct task_struct *task, void *data)
+{
+ struct restore_data *rdata = (struct restore_data *)data;
+
+ WARN(rdata->restore_addr, "restore_addr=%lx", rdata->restore_addr);
+}
+
+struct ktask_data ktd_restore = {
+ .init = ktd_restore_init,
+ .exit = ktd_restore_exit,
+ .size = sizeof(struct restore_data),
+};
+
+static DEFINE_PER_CPU(struct restore_data, per_cpu_restore_i);
+static DEFINE_PER_CPU(struct restore_data, per_cpu_restore_st);
+
+static struct restore_data *current_restore_td(void)
+{
+ if (swap_in_interrupt())
+ return &__get_cpu_var(per_cpu_restore_i);
+ else if (switch_to_bits_get(current_kctx, SWITCH_TO_ALL))
+ return &__get_cpu_var(per_cpu_restore_st);
+
+ return (struct restore_data *)swap_ktd(&ktd_restore, current);
+}
+
+
+static void arch_prepare_ss_slot(struct kp_core *p)
+{
+ /* prepare insn slot */
+ p->ainsn.insn[0] = p->opcode;
+ p->ainsn.insn[1] = conv_inst(BRK64_OPCODE_PSEUDO_SS);
+
+ flush_icache((unsigned long)p->ainsn.insn, KPROBES_TRAMP_LEN);
+}
+
+static void arch_prepare_simulate(struct kp_core *p)
+{
+ if (p->ainsn.prepare)
+ p->ainsn.prepare(p, &p->ainsn);
+}
+
+int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm)
+{
+ kprobe_opcode_t insn;
+
+ /* copy instruction */
+ insn = *(kprobe_opcode_t *)p->addr;
+ p->opcode = insn;
+
+ /* decode instruction */
+ switch (arm_kp_core_decode_insn(insn, &p->ainsn)) {
+ case INSN_REJECTED: /* insn not supported */
+ return -EINVAL;
+
+ case INSN_GOOD_NO_SLOT: /* insn need simulation */
+ p->ainsn.insn = NULL;
+ arch_prepare_simulate(p);
+ break;
+
+ case INSN_GOOD: /* instruction uses slot */
+ p->ainsn.insn = swap_slot_alloc(sm);
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+
+ arch_prepare_ss_slot(p);
+ break;
+ };
+
+ return 0;
+}
+
+static void save_previous_kp_core(struct kp_core_ctlblk *kcb,
+ unsigned long restore_addr)
+{
+ kcb->prev_kp_core.p = kp_core_running();
+ kcb->prev_kp_core.status = kcb->kp_core_status;
+ kcb->prev_kp_core.restore_addr = restore_addr;
+}
+
+void restore_previous_kp_core(struct kp_core_ctlblk *kcb)
+{
+ kp_core_running_set(kcb->prev_kp_core.p);
+ kcb->kp_core_status = kcb->prev_kp_core.status;
+ current_restore_td()->restore_addr = kcb->prev_kp_core.restore_addr;
+}
+
+static void set_ss_context(struct kp_core_ctlblk *kcb, unsigned long addr)
+{
+ kcb->ss_ctx.ss_status = KP_CORE_STEP_PENDING;
+ kcb->ss_ctx.match_addr = addr + sizeof(kprobe_opcode_t);
+}
+
+static void clear_ss_context(struct kp_core_ctlblk *kcb)
+{
+ kcb->ss_ctx.ss_status = KP_CORE_STEP_NONE;
+ kcb->ss_ctx.match_addr = 0;
+}
+
+static void nop_singlestep_skip(struct pt_regs *regs)
+{
+ /* set return addr to next pc to continue */
+ regs->pc += sizeof(kprobe_opcode_t);
+}
+
+static int post_kp_core_handler(struct kp_core_ctlblk *kcb,
+ struct pt_regs *regs);
+
+static void arch_simulate_insn(struct kp_core *p, struct pt_regs *regs)
+{
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ if (p->ainsn.handler)
+ p->ainsn.handler(p->opcode, (long)p->addr, regs);
+
+ /* single step simulated, now go for post processing */
+ post_kp_core_handler(kcb, regs);
+}
+
+static bool is_ss_setup(struct restore_data *restore)
+{
+ return !!restore->restore_addr;
+}
+
+static void setup_singlestep(struct kp_core *p, struct pt_regs *regs,
+ struct kp_core_ctlblk *kcb, int reenter)
+{
+ struct restore_data *restore = current_restore_td();
+
+ if (reenter) {
+ save_previous_kp_core(kcb, restore->restore_addr);
+ kp_core_running_set(p);
+ kcb->kp_core_status = KPROBE_REENTER;
+ } else {
+ kcb->kp_core_status = KPROBE_HIT_SS;
+ }
+
+ if (p->ainsn.insn) {
+ unsigned long slot = (unsigned long)p->ainsn.insn;
+
+ /* prepare for single stepping */
+ restore->restore_addr = regs->pc + 4;
+ regs->pc = slot;
+
+ set_ss_context(kcb, slot); /* mark pending ss */
+ } else {
+ restore->restore_addr = 0; /* reset */
+
+ /* insn simulation */
+ arch_simulate_insn(p, regs);
+ }
+}
+
+static int reenter_kp_core(struct kp_core *p, struct pt_regs *regs,
+ struct kp_core_ctlblk *kcb)
+{
+ switch (kcb->kp_core_status) {
+ case KPROBE_HIT_SSDONE:
+ case KPROBE_HIT_ACTIVE:
+ if (!p->ainsn.check_condn || p->ainsn.check_condn(p, regs)) {
+ setup_singlestep(p, regs, kcb, 1);
+ } else {
+ /* condition failed, it's NOP so skip stepping */
+ nop_singlestep_skip(regs);
+ }
+ break;
+ case KPROBE_HIT_SS:
+ pr_warn("Unrecoverable kp_core detected at %lx\n", p->addr);
+ BUG();
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int post_kp_core_handler(struct kp_core_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ struct kp_core *cur = kp_core_running();
+ struct restore_data *restore = current_restore_td();
+
+ if (cur == NULL) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ /* return addr restore if non-branching insn */
+ if (is_ss_setup(restore)) {
+ regs->pc = restore->restore_addr;
+ restore->restore_addr = 0;
+ kp_core_put(cur);
+ } else {
+ WARN_ON(1);
+ }
+
+ /* restore back original saved kp_core variables and continue */
+ if (kcb->kp_core_status == KPROBE_REENTER) {
+ restore_previous_kp_core(kcb);
+ } else { /* call post handler */
+ kcb->kp_core_status = KPROBE_HIT_SSDONE;
+ kp_core_running_set(NULL);
+ }
+
+ return 1;
+}
+
+static enum dbg_code kprobe_handler(struct pt_regs *regs, unsigned int esr)
+{
+ struct kp_core *p, *cur;
+ struct kp_core_ctlblk *kcb;
+ unsigned long addr = regs->pc;
+ struct restore_data *restore = current_restore_td();
+
+ kcb = kp_core_ctlblk();
+ cur = kp_core_running();
+
+ rcu_read_lock();
+ p = kp_core_by_addr(addr);
+ if (p)
+ kp_core_get(p);
+ rcu_read_unlock();
+
+ if (p) {
+ if (cur && reenter_kp_core(p, regs, kcb)) {
+ if (!is_ss_setup(restore))
+ kp_core_put(p);
+
+ return DBG_HANDLED;
+ } else if (!p->ainsn.check_condn ||
+ p->ainsn.check_condn(p, regs)) {
+ /* Probe hit and conditional execution check ok. */
+ kp_core_running_set(p);
+ kcb->kp_core_status = KPROBE_HIT_ACTIVE;
+
+ if (!(regs->pstate & PSR_I_BIT))
+ local_irq_enable();
+
+ if (!p->handlers.pre(p, regs)) {
+ kcb->kp_core_status = KPROBE_HIT_SS;
+ setup_singlestep(p, regs, kcb, 0);
+ }
+
+ local_irq_disable();
+ } else {
+ /*
+ * Breakpoint hit but conditional check failed,
+ * so just skip handling since it is NOP.
+ */
+ nop_singlestep_skip(regs);
+ }
+
+ if (!is_ss_setup(restore))
+ kp_core_put(p);
+ } else if (*(kprobe_opcode_t *)addr != BRK64_OPCODE_BP) {
+ /*
+ * The breakpoint instruction was removed right
+ * after we hit it. Another cpu has removed
+ * either a probepoint or a debugger breakpoint
+ * at this address. In either case, no further
+ * handling of this interrupt is appropriate.
+ * Return back to original instruction, and continue.
+ */
+ } else {
+ pr_info("no_kprobe: pc=%llx\n", regs->pc);
+ }
+
+ return DBG_HANDLED;
+}
+
+static enum dbg_code kp_core_ss_hit(struct kp_core_ctlblk *kcb,
+ unsigned long addr)
+{
+ if ((kcb->ss_ctx.ss_status == KP_CORE_STEP_PENDING)
+ && (kcb->ss_ctx.match_addr == addr)) {
+ /* clear pending ss */
+ clear_ss_context(kcb);
+ return DBG_HANDLED;
+ }
+
+ /* not ours, kp_cores should ignore it */
+ return DBG_ERROR;
+}
+
+static enum dbg_code kprobe_ss_handler(struct pt_regs *regs, unsigned int esr)
+{
+ enum dbg_code ret;
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ /* check, and return error if this is not our step */
+ ret = kp_core_ss_hit(kcb, regs->pc);
+ if (ret == DBG_HANDLED) {
+ /* single step complete, call post handlers */
+ post_kp_core_handler(kcb, regs);
+ }
+
+ return ret;
+}
+
+static struct brk_hook dbg_bp = {
+ .spsr_mask = PSR_MODE_MASK,
+ .spsr_val = PSR_MODE_EL1h,
+ .esr_mask = DBG_BRK_ESR_MASK,
+ .esr_val = DBG_BRK_ESR(BRK_BP),
+ .fn = kprobe_handler,
+};
+
+static struct brk_hook dbg_ss = {
+ .spsr_mask = PSR_MODE_MASK,
+ .spsr_val = PSR_MODE_EL1h,
+ .esr_mask = DBG_BRK_ESR_MASK,
+ .esr_val = DBG_BRK_ESR(BRK_PSEUDO_SS),
+ .fn = kprobe_ss_handler,
+};
+
+
+
+
+
+/* ============================================================================
+ * = KRETPROBE =
+ * ============================================================================
+ */
+void swap_kretprobe_trampoline(void);
+__asm(
+ ".text\n"
+ ".global swap_kretprobe_trampoline\n"
+ "swap_kretprobe_trampoline:\n"
+ "stp x6, x7, [sp,#-16]!\n"
+ "stp x4, x5, [sp,#-16]!\n"
+ "stp x2, x3, [sp,#-16]!\n"
+ "stp x0, x1, [sp,#-16]!\n"
+ "mov x0, sp\n" /* struct pt_regs (x0..x7) */
+ "bl swap_trampoline_handler\n"
+ "mov x30, x0\n" /* set real lr */
+ "ldp x0, x1, [sp],#16\n"
+ "ldp x2, x3, [sp],#16\n"
+ "ldp x4, x5, [sp],#16\n"
+ "ldp x6, x7, [sp],#16\n"
+ "ret\n"
+);
+
+void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ ri->ret_addr = (unsigned long *)regs->regs[30]; /* lr */
+ regs->regs[30] = (unsigned long)&swap_kretprobe_trampoline;
+ ri->sp = (unsigned long *)regs->sp;
+}
+
+
+
+
+
+/* ============================================================================
+ * = JUMPER =
+ * ============================================================================
+ */
+struct cb_data {
+ unsigned long ret_addr;
+ unsigned long x0;
+
+ jumper_cb_t cb;
+ char data[0];
+};
+
+static unsigned long __used get_x0(struct cb_data *data)
+{
+ return data->x0;
+}
+
+static unsigned long __used jump_handler(struct cb_data *data)
+{
+ unsigned long ret_addr = data->ret_addr;
+
+ /* call callback */
+ data->cb(data->data);
+
+ /* FIXME: potential memory leak, when process kill */
+ kfree(data);
+
+ return ret_addr;
+}
+
+void jump_trampoline(void);
+__asm(
+ ".text\n"
+ "jump_trampoline:\n"
+
+ "stp x6, x7, [sp,#-16]!\n"
+ "stp x4, x5, [sp,#-16]!\n"
+ "stp x2, x3, [sp,#-16]!\n"
+ "stp x0, x1, [sp,#-16]!\n"
+ "mov x1, x0\n" /* data --> x1 */
+ "bl get_x0\n"
+ "str x0, [sp]\n" /* restore x0 */
+ "mov x0, x1\n" /* data --> x0 */
+ "bl jump_handler\n"
+ "mov x30, x0\n" /* set lr */
+ "ldp x0, x1, [sp],#16\n"
+ "ldp x2, x3, [sp],#16\n"
+ "ldp x4, x5, [sp],#16\n"
+ "ldp x6, x7, [sp],#16\n"
+ "ret\n"
+);
+
+unsigned long get_jump_addr(void)
+{
+ return (unsigned long)&jump_trampoline;
+}
+EXPORT_SYMBOL_GPL(get_jump_addr);
+
+int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
+ jumper_cb_t cb, void *data, size_t size)
+{
+ struct cb_data *cb_data;
+
+ cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
+ if (cb_data == NULL)
+ return -ENOMEM;
+
+ /* save data */
+ if (size)
+ memcpy(cb_data->data, data, size);
+
+ /* save info for restore */
+ cb_data->ret_addr = ret_addr;
+ cb_data->cb = cb;
+ cb_data->x0 = regs->regs[0];
+
+ /* save cb_data to x0 */
+ regs->regs[0] = (long)cb_data;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(set_jump_cb);
+
+
+
+
+
+/* ============================================================================
+ * = ARCH INIT/EXIT KPROBES =
+ * ============================================================================
+ */
+int arch_init_module_deps(void)
+{
+ return 0;
+}
+
+int swap_arch_init_kprobes(void)
+{
+ int ret;
+
+ ret = swap_ktd_reg(&ktd_restore);
+ if (ret)
+ return ret;
+
+ ret = dbg_iface_init();
+ if (ret)
+ swap_ktd_unreg(&ktd_restore);
+
+ dbg_brk_hook_reg(&dbg_ss);
+ dbg_brk_hook_reg(&dbg_bp);
+
+ return 0;
+}
+
+void swap_arch_exit_kprobes(void)
+{
+ dbg_brk_hook_unreg(&dbg_ss);
+ dbg_brk_hook_unreg(&dbg_bp);
+ dbg_iface_uninit();
+ swap_ktd_unreg(&ktd_restore);
+}
--- /dev/null
+#ifndef _ASM_ARM64_KPROBES_H
+#define _ASM_ARM64_KPROBES_H
+
+/*
+ * Copied from: arch/arm64/kernel/kprobes.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/types.h>
+
+
+#define MAX_INSN_SIZE 2
+#define KPROBES_TRAMP_LEN (MAX_INSN_SIZE * 4) /* 4 - instruction size */
+
+
+struct kprobe;
+struct kp_core;
+struct slot_manager;
+struct swap_arch_specific_insn;
+struct kretprobe_instance;
+
+
+typedef u32 kprobe_opcode_t;
+typedef unsigned long (kp_core_pstate_check_t)(unsigned long);
+typedef unsigned long (kp_core_condition_check_t)(struct kp_core *,
+ struct pt_regs *);
+typedef void (kp_core_prepare_t)(struct kp_core *,
+ struct swap_arch_specific_insn *);
+typedef void (kp_core_handler_t)(u32 opcode, long addr, struct pt_regs *);
+
+
+/* architecture specific copy of original instruction */
+struct swap_arch_specific_insn {
+ kprobe_opcode_t *insn;
+ kp_core_pstate_check_t *pstate_cc;
+ kp_core_condition_check_t *check_condn;
+ kp_core_prepare_t *prepare;
+ kp_core_handler_t *handler;
+};
+
+struct prev_kp_core {
+ struct kp_core *p;
+ unsigned int status;
+ unsigned long restore_addr; /* restore address after single step */
+};
+
+enum ss_status {
+ KP_CORE_STEP_NONE,
+ KP_CORE_STEP_PENDING,
+};
+
+/* Single step context for kp_core */
+struct kp_core_step_ctx {
+ enum ss_status ss_status;
+ unsigned long match_addr;
+};
+
+/* kp_core control block */
+struct kp_core_ctlblk {
+ unsigned int kp_core_status;
+ struct prev_kp_core prev_kp_core;
+ struct kp_core_step_ctx ss_ctx;
+};
+
+
+static inline unsigned long swap_get_karg(struct pt_regs *regs,
+ unsigned long n)
+{
+ return n < 8 ? regs->regs[n] : *(((long *)regs->sp) + (n - 8));
+}
+
+static inline unsigned long swap_get_sarg(struct pt_regs *regs,
+ unsigned long n)
+{
+ return swap_get_karg(regs, n);
+}
+
+static inline unsigned long swap_get_kpc(struct pt_regs *regs)
+{
+ return regs->pc;
+}
+
+static inline void swap_set_kpc(struct pt_regs *regs, unsigned long val)
+{
+ regs->pc = val;
+}
+
+
+void arch_kp_core_arm(struct kp_core *p);
+void arch_kp_core_disarm(struct kp_core *p);
+
+int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm);
+void restore_previous_kp_core(struct kp_core_ctlblk *kcb);
+
+void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+void swap_kretprobe_trampoline(void);
+
+
+static inline unsigned long arch_get_task_pc(struct task_struct *p)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0xdeadc0de;
+}
+
+static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+}
+
+static inline int swap_setjmp_pre_handler(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0;
+}
+
+/* jumper */
+typedef unsigned long (*jumper_cb_t)(void *);
+
+unsigned long get_jump_addr(void);
+int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
+ jumper_cb_t cb, void *data, size_t size);
+
+
+int arch_init_module_deps(void);
+
+int swap_arch_init_kprobes(void);
+void swap_arch_exit_kprobes(void);
+
+
+#endif /* _ASM_ARM64_KPROBES_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_ARM_PROBES_H
+#define _SWAP_ASM_ARM_PROBES_H
+
+
+#define BREAK_ARM 0xffffdeff
+#define BREAK_THUMB (BREAK_ARM & 0xffff)
+#define RET_BREAK_ARM BREAK_ARM
+#define RET_BREAK_THUMB BREAK_THUMB
+
+#define PROBES_TRAMP_LEN (9 * 4)
+#define PROBES_TRAMP_INSN_IDX 2
+#define PROBES_TRAMP_RET_BREAK_IDX 5
+
+
+#endif /* _SWAP_ASM_ARM_PROBES_H */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/kprobe/arch/asm-mips/dbi_kprobes.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * 2006-2007 Ekaterina Gorelkina <e.gorelkina@samsung.com>:
+ * initial implementation for ARM/MIPS
+ * 2008-2009 Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
+ * Probes initial implementation; Support x86/ARM/MIPS for both
+ * user-space and kernel space.
+ * 2010 Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module
+ * for separating core and arch parts
+ * 2012 Stanislav Andreev <s.andreev@samsung.com>: added time debug
+ * profiling support; BUG() message fix
+ */
+
+#include "dbi_kprobes.h"
+#include <kprobe/dbi_kprobes.h>
+
+#include <kprobe/dbi_kdebug.h>
+#include <kprobe/dbi_insn_slots.h>
+#include <kprobe/dbi_kprobes_deps.h>
+#include <kprobe/dbi_uprobes.h>
+#include <ksyms/ksyms.h>
+
+unsigned int *arr_traps_original;
+
+
+unsigned int arr_traps_template[] = { 0x3c010000, /* lui a1 [0] */
+ 0x24210000, /* addiu a1, a1 [1] */
+ 0x00200008, /* jr a1 [2] */
+ 0x00000000, /* nop */
+ 0xffffffff /* end */
+};
+
+struct kprobe trampoline_p = {
+ .addr = (kprobe_opcode_t *)&kretprobe_trampoline,
+ .pre_handler = trampoline_probe_handler
+};
+
+void gen_insn_execbuf(void);
+
+void gen_insn_execbuf_holder(void)
+{
+ asm volatile(".global gen_insn_execbuf\n"
+ "gen_insn_execbuf:\n"
+ "nop\n" /* original instruction */
+ "nop\n" /* ssbreak */
+ "nop\n"); /* retbreak */
+}
+
+
+int arch_check_insn(struct arch_specific_insn *ainsn)
+{
+ int ret = 0;
+
+ switch (MIPS_INSN_OPCODE(ainsn->insn[0])) {
+ case MIPS_BEQ_OPCODE: /* B, BEQ */
+ case MIPS_BEQL_OPCODE: /* BEQL */
+ case MIPS_BNE_OPCODE: /* BNE */
+ case MIPS_BNEL_OPCODE: /* BNEL */
+ case MIPS_BGTZ_OPCODE: /* BGTZ */
+ case MIPS_BGTZL_OPCODE: /* BGTZL */
+ case MIPS_BLEZ_OPCODE: /* BLEZ */
+ case MIPS_BLEZL_OPCODE: /* BLEZL */
+ case MIPS_J_OPCODE: /* J */
+ case MIPS_JAL_OPCODE: /* JAL */
+ DBPRINTF("arch_check_insn: opcode");
+ ret = -EFAULT;
+ break;
+ case MIPS_REGIMM_OPCODE:
+ /* BAL, BGEZ, BGEZAL, BGEZALL, BGEZL,
+ * BLTZ, BLTZAL, BLTZALL, BLTZL */
+ switch (MIPS_INSN_RT(ainsn->insn[0])) {
+ case MIPS_BLTZ_RT:
+ case MIPS_BGEZ_RT:
+ case MIPS_BLTZL_RT:
+ case MIPS_BGEZL_RT:
+ case MIPS_BLTZAL_RT:
+ case MIPS_BGEZAL_RT:
+ case MIPS_BLTZALL_RT:
+ case MIPS_BGEZALL_RT:
+ DBPRINTF("arch_check_insn: REGIMM opcode\n");
+ ret = -EFAULT;
+ break;
+ }
+ break;
+ /* BC1F, BC1FL, BC1T, BC1TL */
+ case MIPS_COP1_OPCODE:
+ /* BC2F, BC2FL, BC2T, BC2TL */
+ case MIPS_COP2_OPCODE:
+ if (MIPS_INSN_RS(ainsn->insn[0]) == MIPS_BC_RS) {
+ DBPRINTF("arch_check_insn: COP1 opcode\n");
+ ret = -EFAULT;
+ }
+ break;
+ case MIPS_SPECIAL_OPCODE:
+ /* BREAK, JALR, JALR.HB, JR, JR.HB */
+ switch (MIPS_INSN_FUNC(ainsn->insn[0])) {
+ case MIPS_JR_FUNC:
+ case MIPS_JALR_FUNC:
+ case MIPS_BREAK_FUNC:
+ case MIPS_SYSCALL_FUNC:
+ DBPRINTF("arch_check_insn: SPECIAL opcode\n");
+ ret = -EFAULT;
+ break;
+ }
+ break;
+ }
+ return ret;
+}
+
+int arch_prepare_kprobe(struct kprobe *p)
+{
+ kprobe_opcode_t insns[KPROBES_TRAMP_LEN];
+
+ int ret = 0;
+ if (!ret) {
+ kprobe_opcode_t insn[MAX_INSN_SIZE];
+ struct arch_specific_insn ainsn;
+ /* insn: must be on special executable page on i386. */
+ p->ainsn.insn = get_insn_slot(NULL, 0);
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+ memcpy(insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+ ainsn.insn = insn;
+ ret = arch_check_insn(&ainsn);
+ if (!ret) {
+ p->opcode = *p->addr;
+ p->ainsn.boostable = 0;
+ memcpy(insns, gen_insn_execbuf, sizeof(insns));
+ insns[KPROBES_TRAMP_INSN_IDX] = insn[0];
+ insns[KPROBES_TRAMP_SS_BREAK_IDX] =
+ BREAKPOINT_INSTRUCTION;
+ insns[KPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
+ DBPRINTF("arch_prepare_kprobe: insn %lx", insn[0]);
+ DBPRINTF("arch_prepare_kprobe: to %p - %lx %lx %lx",
+ p->ainsn.insn, insns[0],
+ insns[1], insns[2]);
+ memcpy(p->ainsn.insn, insns, sizeof(insns));
+ } else {
+ free_insn_slot(&kprobe_insn_pages, NULL, p->ainsn.insn);
+ }
+ }
+
+ return ret;
+}
+
+int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
+{
+ int ret = 0;
+ kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
+
+ if ((unsigned long) p->addr & 0x01) {
+ DBPRINTF("Attempt to register kprobe at an unaligned address");
+ ret = -EINVAL;
+ }
+
+ if (!ret) {
+ kprobe_opcode_t insn[MAX_INSN_SIZE];
+ struct arch_specific_insn ainsn;
+
+ if (!read_proc_vm_atomic(task, (unsigned long) p->addr,
+ &insn,
+ MAX_INSN_SIZE *
+ sizeof(kprobe_opcode_t)))
+ panic("failed to read memory %p!\n", p->addr);
+ ainsn.insn = insn;
+ ret = arch_check_insn(&ainsn);
+ if (!ret) {
+ p->opcode = insn[0];
+ p->ainsn.insn = get_insn_slot(task, atomic);
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+ p->ainsn.boostable = 0;
+ memcpy(insns, gen_insn_execbuf, sizeof(insns));
+ insns[UPROBES_TRAMP_INSN_IDX] = insn[0];
+ insns[UPROBES_TRAMP_SS_BREAK_IDX] =
+ BREAKPOINT_INSTRUCTION;
+ insns[UPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
+ DBPRINTF("arch_prepare_uprobe: insn %lx", insn[0]);
+ DBPRINTF("arch_prepare_uprobe: to %p - %lx %lx %lx",
+ p->ainsn.insn, insns[0], insns[1], insns[2]);
+
+ if (!write_proc_vm_atomic(task,
+ (unsigned long) p->ainsn.insn,
+ insns, sizeof(insns))) {
+ panic("failed to write memory %p!\n",
+ p->ainsn.insn);
+ DBPRINTF("failed to write insn slot to "
+ "process memory: insn %p, addr %p, "
+ "probe %p!",
+ insn, p->ainsn.insn, p->addr);
+ /* printk("failed to write insn slot to process
+ * memory: %p/%d insn %lx, addr %p,
+ * probe %p!\n",task, task->pid, insn,
+ * p->ainsn.insn, p->addr);*/
+ free_insn_slot(&uprobe_insn_pages, task,
+ p->ainsn.insn);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ if (p->ss_addr) {
+ regs->cp0_epc = (unsigned long)p->ss_addr;
+ p->ss_addr = NULL;
+ } else
+ regs->cp0_epc = (unsigned long)p->ainsn.insn;
+}
+
+
+void save_previous_kprobe(struct kprobe_ctlblk *kcb, struct kprobe *cur_p)
+{
+ if (kcb->prev_kprobe.kp != NULL) {
+ panic("no space to save new probe[]: task = %d/%s, prev %d/%p,"
+ " current %d/%p, new %d/%p,",
+ current->pid, current->comm, kcb->prev_kprobe.kp->tgid,
+ kcb->prev_kprobe.kp->addr, kprobe_running()->tgid,
+ kprobe_running()->addr, cur_p->tgid, cur_p->addr);
+ }
+
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+ kcb->prev_kprobe.kp = NULL;
+ kcb->prev_kprobe.status = 0;
+}
+
+void set_current_kprobe(struct kprobe *p,
+ struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb)
+{
+ __get_cpu_var(current_kprobe) = p;
+ DBPRINTF("set_current_kprobe[]: p=%p addr=%p\n", p, p->addr);
+}
+
+int kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p = 0;
+ int ret = 0, pid = 0, retprobe = 0, reenter = 0;
+ kprobe_opcode_t *addr = NULL, *ssaddr = 0;
+ struct kprobe_ctlblk *kcb;
+
+ /* We're in an interrupt, but this is clear and BUG()-safe. */
+
+ addr = (kprobe_opcode_t *) regs->cp0_epc;
+ DBPRINTF("regs->regs[ 31 ] = 0x%lx\n", regs->regs[31]);
+
+ preempt_disable();
+
+ kcb = get_kprobe_ctlblk();
+
+ if (user_mode(regs)) {
+ /* DBPRINTF("exception[%lu] from user mode %s/%u addr %p(%lx).",
+ * nCount, current->comm,
+ * current->pid, addr, regs->uregs[14]); */
+ pid = current->tgid;
+ }
+
+ /* Check we're not actually recursing */
+ if (kprobe_running()) {
+ DBPRINTF("lock???");
+ p = get_kprobe(addr, pid);
+ if (p) {
+ if (!pid && (addr ==
+ (kprobe_opcode_t *)kretprobe_trampoline)) {
+ save_previous_kprobe(kcb, p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ reenter = 1;
+ } else {
+ /* We have reentered the kprobe_handler(), since
+ * another probe was hit while within the
+ * handler. We here save the original kprobes
+ * variables and just single step on the
+ * instruction of the new probe without calling
+ * any user handlers.
+ */
+ if (!p->ainsn.boostable) {
+ save_previous_kprobe(kcb, p);
+ set_current_kprobe(p, regs, kcb);
+ }
+ kprobes_inc_nmissed_count(p);
+ prepare_singlestep(p, regs);
+ if (!p->ainsn.boostable)
+ kcb->kprobe_status = KPROBE_REENTER;
+ preempt_enable_no_resched();
+ return 1;
+ }
+ } else {
+ if (pid) {
+ /* we can reenter probe upon
+ * uretprobe exception */
+ DBPRINTF("check for UNDEF_INSTRUCTION %p\n",
+ addr);
+ /* UNDEF_INSTRUCTION from user space */
+ p = get_kprobe_by_insn_slot(
+ addr-UPROBES_TRAMP_RET_BREAK_IDX,
+ pid, current);
+ if (p) {
+ save_previous_kprobe(kcb, p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ reenter = 1;
+ retprobe = 1;
+ DBPRINTF("uretprobe %p\n", addr);
+ }
+ }
+ if (!p) {
+ p = __get_cpu_var(current_kprobe);
+ DBPRINTF("kprobe_running !!! p = 0x%p "
+ "p->break_handler = 0x%p", p,
+ p->break_handler);
+ /* if (p->break_handler &&
+ * p->break_handler(p, regs)) {
+ * DBPRINTF("kprobe_running !!! goto ss");
+ * goto ss_probe;
+ * } */
+ DBPRINTF("unknown uprobe at %p cur at %p/%p\n",
+ addr, p->addr, p->ainsn.insn);
+ if (pid)
+ ssaddr = p->ainsn.insn +
+ UPROBES_TRAMP_SS_BREAK_IDX;
+ else
+ ssaddr = p->ainsn.insn +
+ KPROBES_TRAMP_SS_BREAK_IDX;
+ if (addr == ssaddr) {
+ regs->cp0_epc =
+ (unsigned long)(p->addr + 1);
+ DBPRINTF("finish step at %p cur at "
+ "%p/%p, redirect to %lx\n",
+ addr, p->addr,
+ p->ainsn.insn, regs->cp0_epc);
+
+ if (kcb->kprobe_status ==
+ KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ } else {
+ reset_current_kprobe();
+ }
+ }
+ DBPRINTF("kprobe_running !!! goto no");
+ ret = 1;
+ /* If it's not ours, can't be delete race,
+ * (we hold lock). */
+ DBPRINTF("no_kprobe");
+ goto no_kprobe;
+ }
+ }
+ }
+
+ /* if(einsn != UNDEF_INSTRUCTION) { */
+ DBPRINTF("get_kprobe %p-%d", addr, pid);
+ if (!p)
+ p = get_kprobe(addr, pid);
+ if (!p) {
+ if (pid) {
+ DBPRINTF("search UNDEF_INSTRUCTION %p\n", addr);
+ /* UNDEF_INSTRUCTION from user space */
+ p = get_kprobe_by_insn_slot(
+ addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
+ if (!p) {
+ /* Not one of ours: let kernel handle it */
+ DBPRINTF("no_kprobe");
+ /* printk("no_kprobe2 ret = %d\n", ret); */
+ goto no_kprobe;
+ }
+ retprobe = 1;
+ DBPRINTF("uretprobe %p\n", addr);
+ } else {
+ /* Not one of ours: let kernel handle it */
+ DBPRINTF("no_kprobe");
+ /* printk(KERN_INFO "no_kprobe2 ret = %d\n", ret); */
+ goto no_kprobe;
+ }
+ }
+
+ set_current_kprobe(p, regs, kcb);
+ if (!reenter)
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ if (retprobe) /* (einsn == UNDEF_INSTRUCTION) */
+ ret = trampoline_probe_handler(p, regs);
+ else if (p->pre_handler) {
+ ret = p->pre_handler(p, regs);
+ if (!p->ainsn.boostable)
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ else if (p->pre_handler != trampoline_probe_handler) {
+ reset_current_kprobe();
+ }
+ }
+
+ if (ret) {
+ DBPRINTF("p->pre_handler[] 1");
+ /* handler has already set things up, so skip ss setup */
+ return 1;
+ }
+ DBPRINTF("p->pre_handler 0");
+
+no_kprobe:
+ preempt_enable_no_resched();
+ return ret;
+}
+
+void patch_suspended_task_ret_addr(struct task_struct *p, struct kretprobe *rp)
+{
+ DBPRINTF("patch_suspended_task_ret_addr is not implemented");
+}
+
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ kprobe_pre_entry_handler_t pre_entry;
+ entry_point_t entry;
+
+ DBPRINTF("pjp = 0x%p jp->entry = 0x%p", jp, jp->entry);
+ entry = (entry_point_t) jp->entry;
+ pre_entry = (kprobe_pre_entry_handler_t) jp->pre_entry;
+ /* if(!entry) */
+ /* DIE("entry NULL", regs) */
+ DBPRINTF("entry = 0x%p jp->entry = 0x%p", entry, jp->entry);
+
+ /* call handler for all kernel probes and user space
+ * ones which belong to current tgid */
+ if (!p->tgid || (p->tgid == current->tgid)) {
+ if (!p->tgid && (p->addr == sched_addr) && sched_rp) {
+ struct task_struct *p, *g;
+ rcu_read_lock();
+ /* swapper task */
+ if (current != &init_task)
+ patch_suspended_task_ret_addr(&init_task,
+ sched_rp);
+ /* other tasks */
+ do_each_thread(g, p) {
+ if (p == current)
+ continue;
+ patch_suspended_task_ret_addr(p, sched_rp);
+ } while_each_thread(g, p);
+ rcu_read_unlock();
+ }
+ if (pre_entry)
+ p->ss_addr = (void *)pre_entry(jp->priv_arg, regs);
+ if (entry) {
+ entry(regs->regs[4], regs->regs[5], regs->regs[6],
+ regs->regs[7], regs->regs[8], regs->regs[9]);
+ } else {
+ if (p->tgid)
+ arch_ujprobe_return();
+ else
+ dbi_jprobe_return();
+ }
+ } else if (p->tgid)
+ arch_ujprobe_return();
+
+ prepare_singlestep(p, regs);
+
+ return 1;
+}
+
+
+void dbi_jprobe_return(void)
+{
+ preempt_enable_no_resched();
+}
+
+void arch_ujprobe_return(void)
+{
+ preempt_enable_no_resched();
+}
+
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ return 0;
+}
+
+void arch_arm_kprobe(struct kprobe *p)
+{
+ *p->addr = BREAKPOINT_INSTRUCTION;
+ flush_icache_range((unsigned long) p->addr,
+ (unsigned long) p->addr + sizeof(kprobe_opcode_t));
+}
+
+void arch_disarm_kprobe(struct kprobe *p)
+{
+ *p->addr = p->opcode;
+ flush_icache_range((unsigned long) p->addr,
+ (unsigned long) p->addr + sizeof(kprobe_opcode_t));
+}
+
+int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri = NULL;
+ struct hlist_head *head, empty_rp;
+ struct hlist_node *node, *tmp;
+ unsigned long flags, orig_ret_address = 0;
+ unsigned long trampoline_address =
+ (unsigned long) &kretprobe_trampoline;
+ struct kretprobe *crp = NULL;
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ DBPRINTF("start");
+
+ if (p && p->tgid) {
+ /* in case of user space retprobe trampoline
+ * is at the Nth instruction of US tramp */
+ trampoline_address =
+ (unsigned long)(p->ainsn.insn +
+ UPROBES_TRAMP_RET_BREAK_IDX);
+ }
+
+ INIT_HLIST_HEAD(&empty_rp);
+ spin_lock_irqsave(&kretprobe_lock, flags);
+ head = kretprobe_inst_table_head(current);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because an multiple functions in the call path
+ * have a return probe installed on them, and/or more then one
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * kretprobe_trampoline
+ */
+ hlist_for_each_entry_safe(ri, node, tmp, head, hlist)
+ {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+ if (ri->rp && ri->rp->handler)
+ ri->rp->handler(ri, regs, ri->rp->priv_arg);
+
+ orig_ret_address = (unsigned long) ri->ret_addr;
+ recycle_rp_inst(ri);
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
+ /* BUG_ON(!orig_ret_address ||
+ * (orig_ret_address == trampoline_address)); */
+ if (trampoline_address != (unsigned long) &kretprobe_trampoline)
+ if (ri->rp)
+ BUG_ON(ri->rp->kp.tgid == 0);
+
+ if (ri->rp && ri->rp->kp.tgid)
+ BUG_ON(trampoline_address ==
+ (unsigned long) &kretprobe_trampoline);
+
+ regs->regs[31] = orig_ret_address;
+ DBPRINTF("regs->cp0_epc = 0x%lx", regs->cp0_epc);
+ if (trampoline_address != (unsigned long) &kretprobe_trampoline)
+ regs->cp0_epc = orig_ret_address;
+ else
+ regs->cp0_epc = regs->cp0_epc + 4;
+ DBPRINTF("regs->cp0_epc = 0x%lx", regs->cp0_epc);
+ DBPRINTF("regs->cp0_status = 0x%lx", regs->cp0_status);
+
+ if (p) { /* ARM, MIPS, X86 user space */
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ restore_previous_kprobe(kcb);
+ else
+ reset_current_kprobe();
+ }
+
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+ hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist)
+ {
+ hlist_del(&ri->hlist);
+ kfree(ri);
+ }
+ preempt_enable_no_resched();
+ /*
+ * By returning a non-zero value, we are telling
+ * kprobe_handler() that we don't want the post_handler
+ * to run (and have re-enabled preemption)
+ */
+ return 1;
+}
+
+void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
+{
+
+ struct kretprobe_instance *ri;
+
+ DBPRINTF("start\n");
+ /* TODO: test - remove retprobe after func entry but before its exit */
+ ri = get_free_rp_inst(rp);
+ if (ri != NULL) {
+ ri->rp = rp;
+ ri->task = current;
+ ri->ret_addr = (kprobe_opcode_t *) regs->regs[31];
+ if (rp->kp.tgid)
+ regs->regs[31] =
+ (unsigned long)(rp->kp.ainsn.insn +
+ UPROBES_TRAMP_RET_BREAK_IDX);
+ else /* Replace the return addr with trampoline addr */
+ regs->regs[31] = (unsigned long) &kretprobe_trampoline;
+ add_rp_inst(ri);
+ } else {
+ DBPRINTF("WARNING: missed retprobe %p\n", rp->kp.addr);
+ rp->nmissed++;
+ }
+}
+
+DECLARE_MOD_CB_DEP(flush_icache_range, \
+ void, unsigned long __user start, unsigned long __user end);
+DECLARE_MOD_CB_DEP(flush_icache_page, \
+ void, struct vm_area_struct *vma, struct page *page);
+DECLARE_MOD_CB_DEP(flush_cache_page, \
+ void, struct vm_area_struct *vma, unsigned long page);
+
+int arch_init_module_deps()
+{
+ INIT_MOD_DEP_CB(flush_icache_range, r4k_flush_icache_range);
+ INIT_MOD_DEP_CB(flush_icache_page, r4k_flush_icache_page);
+ INIT_MOD_DEP_CB(flush_cache_page, r4k_flush_cache_page);
+
+ return 0;
+}
+
+
+int __init arch_init_kprobes(void)
+{
+ unsigned int do_bp_handler;
+ unsigned int kprobe_handler_addr;
+
+ unsigned int insns_num = 0;
+ unsigned int code_size = 0;
+
+ unsigned int reg_hi;
+ unsigned int reg_lo;
+
+ int ret;
+
+ if (arch_init_module_dependencies()) {
+ DBPRINTF("Unable to init module dependencies\n");
+ return -1;
+ }
+
+ do_bp_handler = (unsigned int)swap_ksyms("do_bp");
+
+ kprobe_handler_addr = (unsigned int) &kprobe_handler;
+ insns_num = sizeof(arr_traps_template) / sizeof(arr_traps_template[0]);
+ code_size = insns_num * sizeof(unsigned int);
+ DBPRINTF("insns_num = %d\n", insns_num);
+ /* Save original code */
+ arr_traps_original = kmalloc(code_size, GFP_KERNEL);
+ if (!arr_traps_original) {
+ DBPRINTF("Unable to allocate space for "
+ "original code of <do_bp>!\n");
+ return -1;
+ }
+ memcpy(arr_traps_original, (void *) do_bp_handler, code_size);
+
+ reg_hi = HIWORD(kprobe_handler_addr);
+ reg_lo = LOWORD(kprobe_handler_addr);
+ if (reg_lo >= 0x8000)
+ reg_hi += 0x0001;
+ arr_traps_template[REG_HI_INDEX] |= reg_hi;
+ arr_traps_template[REG_LO_INDEX] |= reg_lo;
+
+ /* Insert new code */
+ memcpy((void *) do_bp_handler, arr_traps_template, code_size);
+ flush_icache_range(do_bp_handler, do_bp_handler + code_size);
+ ret = dbi_register_kprobe(&trampoline_p);
+ if (ret != 0) {
+ /* dbi_unregister_jprobe(&do_exit_p, 0); */
+ return ret;
+ }
+}
+
+void __exit dbi_arch_exit_kprobes(void)
+{
+ unsigned int do_bp_handler;
+
+ unsigned int insns_num = 0;
+ unsigned int code_size = 0;
+
+ /* Get instruction address */
+ do_bp_handler = (unsigned int)swap_ksyms("do_undefinstr");
+
+ /* dbi_unregister_jprobe(&do_exit_p, 0); */
+
+ /* Replace back the original code */
+
+ insns_num = sizeof(arr_traps_template) / sizeof(arr_traps_template[0]);
+ code_size = insns_num * sizeof(unsigned int);
+ memcpy((void *) do_bp_handler, arr_traps_original, code_size);
+ flush_icache_range(do_bp_handler, do_bp_handler + code_size);
+ kfree(arr_traps_original);
+ arr_traps_original = NULL;
+}
+
+
--- /dev/null
+#ifndef _SRC_ASM_MIPS_KPROBES_H
+#define _SRC_ASM_MIPS_KPROBES_H
+
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/kprobe/arch/asm-mips/dbi_kprobes.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * 2006-2007 Ekaterina Gorelkina <e.gorelkina@samsung.com>:
+ * initial implementation for ARM/MIPS
+ * 2008-2009 Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
+ * Probes initial implementation; Support x86/ARM/MIPS for both
+ * user-space and kernel space.
+ * 2010 Ekaterina Gorelkina <e.gorelkina@samsung.com>:
+ * redesign module for separating core and arch parts
+ *
+ */
+
+#include <kprobe/dbi_kprobes_deps.h>
+#include <kprobe/dbi_kprobes.h>
+
+typedef unsigned long kprobe_opcode_t;
+
+#define BREAKPOINT_INSTRUCTION 0x0000000d
+
+#ifndef KPROBES_RET_PROBE_TRAMP
+#define UNDEF_INSTRUCTION 0x0000004d
+#endif
+
+#define MAX_INSN_SIZE 1
+
+# define UPROBES_TRAMP_LEN 3
+# define UPROBES_TRAMP_INSN_IDX 0
+# define UPROBES_TRAMP_SS_BREAK_IDX 1
+# define UPROBES_TRAMP_RET_BREAK_IDX 2
+# define KPROBES_TRAMP_LEN UPROBES_TRAMP_LEN
+# define KPROBES_TRAMP_INSN_IDX UPROBES_TRAMP_INSN_IDX
+# define KPROBES_TRAMP_SS_BREAK_IDX UPROBES_TRAMP_SS_BREAK_IDX
+# define KPROBES_TRAMP_RET_BREAK_IDX UPROBES_TRAMP_RET_BREAK_IDX
+
+#define REG_HI_INDEX 0
+#define REG_LO_INDEX 1
+#define NOTIFIER_CALL_CHAIN_INDEX 0
+
+
+#define MIPS_INSN_OPCODE_MASK 0xFC000000
+#define MIPS_INSN_RS_MASK 0x03E00000
+#define MIPS_INSN_RT_MASK 0x001F0000
+/* #define MIPS_INSN_UN_MASK 0x0000FFC0 */
+#define MIPS_INSN_FUNC_MASK 0x0000003F
+#define MIPS_INSN_OPCODE(insn) (insn & MIPS_INSN_OPCODE_MASK)
+#define MIPS_INSN_RS(insn) (insn & MIPS_INSN_RS_MASK)
+#define MIPS_INSN_RT(insn) (insn & MIPS_INSN_RT_MASK)
+#define MIPS_INSN_FUNC(insn) (insn & MIPS_INSN_FUNC_MASK)
+/* opcodes 31..26 */
+#define MIPS_BEQ_OPCODE 0x10000000
+#define MIPS_BNE_OPCODE 0x14000000
+#define MIPS_BLEZ_OPCODE 0x18000000
+#define MIPS_BGTZ_OPCODE 0x1C000000
+#define MIPS_BEQL_OPCODE 0x50000000
+#define MIPS_BNEL_OPCODE 0x54000000
+#define MIPS_BLEZL_OPCODE 0x58000000
+#define MIPS_BGTZL_OPCODE 0x5C000000
+#define MIPS_REGIMM_OPCODE 0x04000000
+#define MIPS_SPECIAL_OPCODE 0x00000000
+#define MIPS_COP1_OPCODE 0x44000000
+#define MIPS_COP2_OPCODE 0x48000000
+#define MIPS_J_OPCODE 0x08000000
+#define MIPS_JAL_OPCODE 0x0C000000
+#define MIPS_JALX_OPCODE 0x74000000
+/* rs 25..21 */
+#define MIPS_BC_RS 0x01000000
+/* rt 20..16 */
+#define MIPS_BLTZ_RT 0x00000000
+#define MIPS_BGEZ_RT 0x00010000
+#define MIPS_BLTZL_RT 0x00020000
+#define MIPS_BGEZL_RT 0x00030000
+#define MIPS_BLTZAL_RT 0x00100000
+#define MIPS_BGEZAL_RT 0x00110000
+#define MIPS_BLTZALL_RT 0x00120000
+#define MIPS_BGEZALL_RT 0x00130000
+/* unnamed 15..6 */
+/* function 5..0 */
+#define MIPS_JR_FUNC 0x00000008
+#define MIPS_JALR_FUNC 0x00000009
+#define MIPS_BREAK_FUNC 0x0000000D
+#define MIPS_SYSCALL_FUNC 0x0000000C
+
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+ unsigned long kprobe_status;
+ struct prev_kprobe prev_kprobe;
+};
+
+/* Architecture specific copy of original instruction */
+struct arch_specific_insn {
+ /* copy of the original instruction */
+ kprobe_opcode_t *insn;
+ /*
+ * If this flag is not 0, this kprobe can be boost when its
+ * post_handler and break_handler is not set.
+ */
+ int boostable;
+};
+
+typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long,
+ unsigned long, unsigned long,
+ unsigned long, unsigned long);
+
+
+void gen_insn_execbuf_holder(void);
+
+void patch_suspended_task_ret_addr(struct task_struct *p, struct kretprobe *rp);
+int arch_init_module_deps(void);
+
+#endif /* _SRC_ASM_MIPS_KPROBES_H */
--- /dev/null
+/**
+ * arch/asm-x86/swap_kprobes.c
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ * @author Stanislav Andreev <s.andreev@samsung.com>: added time debug profiling support; BUG() message fix
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP krpobes arch-dependend part for x86.
+ */
+
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
+# error "Kernel is immutable"
+#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
+
+
+#include<linux/module.h>
+#include <linux/kdebug.h>
+
+#include "swap_kprobes.h"
+#include <kprobe/swap_kprobes.h>
+#include <kprobe/swap_td_raw.h>
+#include <kprobe/swap_kdebug.h>
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_kprobes_deps.h>
+
+
+static int (*swap_fixup_exception)(struct pt_regs *regs);
+static void *(*swap_text_poke)(void *addr, const void *opcode, size_t len);
+static void (*swap_show_registers)(struct pt_regs *regs);
+
+
+#define SWAP_SAVE_REGS_STRING \
+ /* Skip cs, ip, orig_ax and gs. */ \
+ "subl $16, %esp\n" \
+ "pushl %fs\n" \
+ "pushl %es\n" \
+ "pushl %ds\n" \
+ "pushl %eax\n" \
+ "pushl %ebp\n" \
+ "pushl %edi\n" \
+ "pushl %esi\n" \
+ "pushl %edx\n" \
+ "pushl %ecx\n" \
+ "pushl %ebx\n"
+#define SWAP_RESTORE_REGS_STRING \
+ "popl %ebx\n" \
+ "popl %ecx\n" \
+ "popl %edx\n" \
+ "popl %esi\n" \
+ "popl %edi\n" \
+ "popl %ebp\n" \
+ "popl %eax\n" \
+ /* Skip ds, es, fs, gs, orig_ax, and ip. Note: don't pop cs here*/\
+ "addl $24, %esp\n"
+
+
+/*
+ * Function return probe trampoline:
+ * - init_kprobes() establishes a probepoint here
+ * - When the probed function returns, this probe
+ * causes the handlers to fire
+ */
+__asm(
+ ".global swap_kretprobe_trampoline\n"
+ "swap_kretprobe_trampoline:\n"
+ "pushf\n"
+ SWAP_SAVE_REGS_STRING
+ "movl %esp, %eax\n"
+ "call swap_trampoline_handler\n"
+ /* move eflags to cs */
+ "movl 56(%esp), %edx\n"
+ "movl %edx, 52(%esp)\n"
+ /* replace saved flags with true return address. */
+ "movl %eax, 56(%esp)\n"
+ SWAP_RESTORE_REGS_STRING
+ "popf\n"
+ "ret\n"
+);
+
+/* insert a jmp code */
+static __always_inline void set_jmp_op(void *from, void *to)
+{
+ struct __arch_jmp_op {
+ char op;
+ long raddr;
+ } __packed * jop;
+ jop = (struct __arch_jmp_op *) from;
+ jop->raddr = (long) (to) - ((long) (from) + 5);
+ jop->op = RELATIVEJUMP_INSTRUCTION;
+}
+
+/**
+ * @brief Check if opcode can be boosted.
+ *
+ * @param opcodes Opcode to check.
+ * @return Non-zero if opcode can be boosted.
+ */
+int swap_can_boost(kprobe_opcode_t *opcodes)
+{
+#define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf) \
+ (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \
+ (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \
+ (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \
+ (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \
+ << (row % 32))
+ /*
+ * Undefined/reserved opcodes, conditional jump, Opcode Extension
+ * Groups, and some special opcodes can not be boost.
+ */
+ static const unsigned long twobyte_is_boostable[256 / 32] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ W(0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) |
+ W(0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |
+ W(0x30, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) |
+ W(0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1) |
+ W(0x70, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1),
+ W(0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |
+ W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+ W(0xa0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1) |
+ W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1),
+ W(0xc0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) |
+ W(0xd0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1),
+ W(0xe0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1) |
+ W(0xf0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0)
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+
+ };
+#undef W
+ kprobe_opcode_t opcode;
+ kprobe_opcode_t *orig_opcodes = opcodes;
+retry:
+ if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1)
+ return 0;
+ opcode = *(opcodes++);
+
+ /* 2nd-byte opcode */
+ if (opcode == 0x0f) {
+ if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1)
+ return 0;
+ return test_bit(*opcodes, twobyte_is_boostable);
+ }
+
+ switch (opcode & 0xf0) {
+ case 0x60:
+ if (0x63 < opcode && opcode < 0x67)
+ goto retry; /* prefixes */
+ /* can't boost Address-size override and bound */
+ return (opcode != 0x62 && opcode != 0x67);
+ case 0x70:
+ return 0; /* can't boost conditional jump */
+ case 0xc0:
+ /* can't boost software-interruptions */
+ return (0xc1 < opcode && opcode < 0xcc) || opcode == 0xcf;
+ case 0xd0:
+ /* can boost AA* and XLAT */
+ return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7);
+ case 0xe0:
+ /* can boost in/out and absolute jmps */
+ return ((opcode & 0x04) || opcode == 0xea);
+ case 0xf0:
+ if ((opcode & 0x0c) == 0 && opcode != 0xf1)
+ goto retry; /* lock/rep(ne) prefix */
+ /* clear and set flags can be boost */
+ return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe));
+ default:
+ if (opcode == 0x26 || opcode == 0x36 || opcode == 0x3e)
+ goto retry; /* prefixes */
+ /* can't boost CS override and call */
+ return (opcode != 0x2e && opcode != 0x9a);
+ }
+}
+EXPORT_SYMBOL_GPL(swap_can_boost);
+
+/*
+ * returns non-zero if opcode modifies the interrupt flag.
+ */
+static int is_IF_modifier(kprobe_opcode_t opcode)
+{
+ switch (opcode) {
+ case 0xfa: /* cli */
+ case 0xfb: /* sti */
+ case 0xcf: /* iret/iretd */
+ case 0x9d: /* popf/popfd */
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Creates trampoline for kprobe.
+ *
+ * @param p Pointer to kprobe.
+ * @param sm Pointer to slot manager
+ * @return 0 on success, error code on error.
+ */
+int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm)
+{
+ /* insn: must be on special executable page on i386. */
+ p->ainsn.insn = swap_slot_alloc(sm);
+ if (p->ainsn.insn == NULL)
+ return -ENOMEM;
+
+ memcpy(p->ainsn.insn, (void *)p->addr, MAX_INSN_SIZE);
+
+ p->opcode = *(char *)p->addr;
+ p->ainsn.boostable = swap_can_boost((void *)p->addr) ? 0 : -1;
+
+ return 0;
+}
+
+/**
+ * @brief Prepares singlestep for current CPU.
+ *
+ * @param p Pointer to kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return Void.
+ */
+static void prepare_singlestep(struct kp_core *p, struct pt_regs *regs)
+{
+ regs->flags |= TF_MASK;
+ regs->flags &= ~IF_MASK;
+
+ /* single step inline if the instruction is an int3 */
+ if (p->opcode == BREAKPOINT_INSTRUCTION)
+ regs->ip = (unsigned long)p->addr;
+ else
+ regs->ip = (unsigned long)p->ainsn.insn;
+}
+
+/**
+ * @brief Saves previous kprobe.
+ *
+ * @param kcb Pointer to kp_core_ctlblk struct whereto save current kprobe.
+ * @param p_run Pointer to kprobe.
+ * @return Void.
+ */
+static void save_previous_kp_core(struct kp_core_ctlblk *kcb, struct kp_core *cur)
+{
+ if (kcb->prev_kp_core.p != NULL) {
+ panic("no space to save new probe[]: "
+ "task = %d/%s, prev %08lx, current %08lx, new %08lx,",
+ current->pid, current->comm, kcb->prev_kp_core.p->addr,
+ kp_core_running()->addr, cur->addr);
+ }
+
+
+ kcb->prev_kp_core.p = kp_core_running();
+ kcb->prev_kp_core.status = kcb->kp_core_status;
+}
+
+/**
+ * @brief Restores previous kp_core.
+ *
+ * @param kcb Pointer to kp_core_ctlblk which contains previous kp_core.
+ * @return Void.
+ */
+void restore_previous_kp_core(struct kp_core_ctlblk *kcb)
+{
+ kp_core_running_set(kcb->prev_kp_core.p);
+ kcb->kp_core_status = kcb->prev_kp_core.status;
+ kcb->prev_kp_core.p = NULL;
+ kcb->prev_kp_core.status = 0;
+}
+
+static void set_current_kp_core(struct kp_core *p, struct pt_regs *regs,
+ struct kp_core_ctlblk *kcb)
+{
+ kp_core_running_set(p);
+ kcb->kp_core_saved_eflags = kcb->kp_core_old_eflags =
+ (regs->EREG(flags) & (TF_MASK | IF_MASK));
+ if (is_IF_modifier(p->opcode))
+ kcb->kp_core_saved_eflags &= ~IF_MASK;
+}
+
+static int setup_singlestep(struct kp_core *p, struct pt_regs *regs,
+ struct kp_core_ctlblk *kcb)
+{
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+ if (p->ainsn.boostable == 1) {
+ /* Boost up -- we can execute copied instructions directly */
+ kp_core_running_set(NULL);
+ regs->ip = (unsigned long)p->ainsn.insn;
+
+ return 1;
+ }
+#endif /* !CONFIG_PREEMPT */
+
+ prepare_singlestep(p, regs);
+ kcb->kp_core_status = KPROBE_HIT_SS;
+
+ return 1;
+}
+
+
+struct regs_td {
+ struct pt_regs *sp_regs;
+ struct pt_regs regs;
+};
+
+static struct td_raw kp_tdraw;
+static DEFINE_PER_CPU(struct regs_td, per_cpu_regs_td_i);
+static DEFINE_PER_CPU(struct regs_td, per_cpu_regs_td_st);
+
+static struct regs_td *current_regs_td(void)
+{
+ if (swap_in_interrupt())
+ return &__get_cpu_var(per_cpu_regs_td_i);
+ else if (switch_to_bits_get(current_kctx, SWITCH_TO_ALL))
+ return &__get_cpu_var(per_cpu_regs_td_st);
+
+ return (struct regs_td *)swap_td_raw(&kp_tdraw, current);
+}
+
+/** Stack address. */
+unsigned long swap_kernel_sp(struct pt_regs *regs)
+{
+ struct pt_regs *sp_regs = current_regs_td()->sp_regs;
+
+ if (sp_regs == NULL)
+ sp_regs = regs;
+
+ return kernel_stack_pointer(sp_regs);
+}
+EXPORT_SYMBOL_GPL(swap_kernel_sp);
+
+void exec_trampoline(void);
+void exec_trampoline_int3(void);
+__asm(
+ ".text\n"
+ "exec_trampoline:\n"
+ "call exec_handler\n"
+ "exec_trampoline_int3:\n"
+ "int3\n"
+);
+
+static int __used exec_handler(void)
+{
+ struct kp_core *p = kp_core_running();
+ struct pt_regs *regs = ¤t_regs_td()->regs;
+
+ return p->handlers.pre(p, regs);
+}
+
+static int after_exec_trampoline(struct pt_regs *regs)
+{
+ int ret = (int)regs->ax;
+ struct kp_core *p = kp_core_running();
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ /*
+ * Restore regs from stack.
+ * Don't restore SP and SS registers because they are invalid (- 8)
+ */
+ memcpy(regs, ¤t_regs_td()->regs, sizeof(*regs) - 8);
+
+ if (ret) {
+ kp_core_put(p);
+ return 1;
+ }
+
+ setup_singlestep(p, regs, kcb);
+ if (!(regs->flags & TF_MASK))
+ kp_core_put(p);
+
+ return 1;
+}
+
+#define KSTAT_NOT_FOUND 0x00
+#define KSTAT_FOUND 0x01
+#define KSTAT_PREPARE_KCB 0x02
+
+static unsigned long kprobe_pre_handler(struct kp_core *p,
+ struct pt_regs *regs,
+ struct kp_core_ctlblk *kcb)
+{
+ int ret = KSTAT_NOT_FOUND;
+ unsigned long addr = regs->ip - 1;
+
+ /* Check we're not actually recursing */
+ if (kp_core_running()) {
+ if (p) {
+ if (kcb->kp_core_status == KPROBE_HIT_SS &&
+ *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+ regs->flags &= ~TF_MASK;
+ regs->flags |= kcb->kp_core_saved_eflags;
+ goto out;
+ }
+
+ /* We have reentered the kprobe_handler(), since
+ * another probe was hit while within the handler.
+ * We here save the original kprobes variables and
+ * just single step on the instruction of the new probe
+ * without calling any user handlers.
+ */
+ save_previous_kp_core(kcb, p);
+ set_current_kp_core(p, regs, kcb);
+ prepare_singlestep(p, regs);
+ kcb->kp_core_status = KPROBE_REENTER;
+
+ ret = KSTAT_FOUND;
+ goto out;
+ } else {
+ if (*(char *)addr != BREAKPOINT_INSTRUCTION) {
+ /* The breakpoint instruction was removed by
+ * another cpu right after we hit, no further
+ * handling of this interrupt is appropriate
+ */
+ regs->EREG(ip) -= sizeof(kprobe_opcode_t);
+
+ ret = KSTAT_FOUND;
+ goto out;
+ }
+
+ goto out;
+ }
+ }
+
+ if (!p) {
+ if (*(char *)addr != BREAKPOINT_INSTRUCTION) {
+ /*
+ * The breakpoint instruction was removed right
+ * after we hit it. Another cpu has removed
+ * either a probepoint or a debugger breakpoint
+ * at this address. In either case, no further
+ * handling of this interrupt is appropriate.
+ * Back up over the (now missing) int3 and run
+ * the original instruction.
+ */
+ regs->EREG(ip) -= sizeof(kprobe_opcode_t);
+
+ ret = KSTAT_FOUND;
+ }
+
+ goto out;
+ }
+
+ set_current_kp_core(p, regs, kcb);
+ kcb->kp_core_status = KPROBE_HIT_ACTIVE;
+
+ ret = KSTAT_PREPARE_KCB;
+out:
+ return ret;
+}
+
+static int __kprobe_handler(struct pt_regs *regs)
+{
+ int ret;
+ struct kp_core *p;
+ struct kp_core_ctlblk *kcb;
+ unsigned long addr = regs->ip - 1;
+ struct kctx *ctx = current_kctx;
+
+ if (addr == sched_addr)
+ switch_to_bits_set(ctx, SWITCH_TO_KP);
+
+ kcb = kp_core_ctlblk();
+
+ rcu_read_lock();
+ p = kp_core_by_addr(addr);
+ kp_core_get(p);
+ rcu_read_unlock();
+
+ if (able2resched(ctx)) {
+ ret = kprobe_pre_handler(p, regs, kcb);
+ if (ret == KSTAT_PREPARE_KCB) {
+ struct regs_td *rtd = current_regs_td();
+
+ /* save regs to stack */
+ rtd->regs = *regs;
+ rtd->sp_regs = regs;
+
+ regs->ip = (unsigned long)exec_trampoline;
+ return 1;
+ }
+
+ if (!(regs->flags & TF_MASK))
+ kp_core_put(p);
+ } else {
+ ret = kprobe_pre_handler(p, regs, kcb);
+ if (ret == KSTAT_PREPARE_KCB) {
+ int rr;
+
+ current_regs_td()->sp_regs = NULL;
+ rr = p->handlers.pre(p, regs);
+ if (rr) {
+ switch_to_bits_reset(ctx, SWITCH_TO_KP);
+ kp_core_put(p);
+ return 1;
+ }
+
+ setup_singlestep(p, regs, kcb);
+ }
+
+ /*
+ * If TF is enabled then processing instruction
+ * takes place in two stages.
+ */
+ if (regs->flags & TF_MASK) {
+ preempt_disable();
+ } else {
+ switch_to_bits_reset(ctx, SWITCH_TO_KP);
+ kp_core_put(p);
+ }
+ }
+
+ return !!ret;
+}
+
+static int kprobe_handler(struct pt_regs *regs)
+{
+ int ret;
+
+ if (regs->ip == (unsigned long)exec_trampoline_int3 + 1)
+ ret = after_exec_trampoline(regs);
+ else
+ ret = __kprobe_handler(regs);
+
+ return ret;
+}
+
+/**
+ * @brief Probe pre handler.
+ *
+ * @param p Pointer to fired kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return 0.
+ */
+int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ kprobe_pre_entry_handler_t pre_entry;
+
+ unsigned long addr;
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ pre_entry = (kprobe_pre_entry_handler_t) jp->pre_entry;
+
+ kcb->jprobe_saved_regs = *regs;
+ kcb->jprobe_saved_esp = (unsigned long *)swap_kernel_sp(regs);
+ addr = (unsigned long)(kcb->jprobe_saved_esp);
+
+ /* TBD: As Linus pointed out, gcc assumes that the callee
+ * owns the argument space and could overwrite it, e.g.
+ * tailcall optimization. So, to be absolutely safe
+ * we also save and restore enough stack bytes to cover
+ * the argument area. */
+ memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,
+ MIN_STACK_SIZE(addr));
+ regs->EREG(flags) &= ~IF_MASK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ trace_hardirqs_off();
+#endif
+
+ regs->EREG(ip) = (unsigned long)(jp->entry);
+
+ return 1;
+}
+
+/**
+ * @brief Jprobe return end.
+ *
+ * @return Void.
+ */
+void swap_jprobe_return_end(void);
+
+/**
+ * @brief Jprobe return code.
+ *
+ * @return Void.
+ */
+void swap_jprobe_return(void)
+{
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ asm volatile(" xchgl %%ebx,%%esp\n"
+ " int3\n"
+ " .globl swap_jprobe_return_end\n"
+ " swap_jprobe_return_end:\n"
+ " nop\n"
+ : : "b" (kcb->jprobe_saved_esp) : "memory");
+}
+EXPORT_SYMBOL_GPL(swap_jprobe_return);
+
+void arch_ujprobe_return(void)
+{
+}
+
+/*
+ * Called after single-stepping. p->addr is the address of the
+ * instruction whose first byte has been replaced by the "int 3"
+ * instruction. To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction. The address of this
+ * copy is p->ainsn.insn.
+ *
+ * This function prepares to return from the post-single-step
+ * interrupt. We have to fix up the stack as follows:
+ *
+ * 0) Except in the case of absolute or indirect jump or call instructions,
+ * the new eip is relative to the copied instruction. We need to make
+ * it relative to the original instruction.
+ *
+ * 1) If the single-stepped instruction was pushfl, then the TF and IF
+ * flags are set in the just-pushed eflags, and may need to be cleared.
+ *
+ * 2) If the single-stepped instruction was a call, the return address
+ * that is atop the stack is the address following the copied instruction.
+ * We need to make it the address following the original instruction.
+ *
+ * This function also checks instruction size for preparing direct execution.
+ */
+static void resume_execution(struct kp_core *p,
+ struct pt_regs *regs,
+ struct kp_core_ctlblk *kcb)
+{
+ unsigned long *tos;
+ unsigned long copy_eip = (unsigned long) p->ainsn.insn;
+ unsigned long orig_eip = (unsigned long) p->addr;
+ kprobe_opcode_t insns[2];
+
+ regs->EREG(flags) &= ~TF_MASK;
+
+ tos = (unsigned long *)swap_kernel_sp(regs);
+ insns[0] = p->ainsn.insn[0];
+ insns[1] = p->ainsn.insn[1];
+
+ switch (insns[0]) {
+ case 0x9c: /* pushfl */
+ *tos &= ~(TF_MASK | IF_MASK);
+ *tos |= kcb->kp_core_old_eflags;
+ break;
+ case 0xc2: /* iret/ret/lret */
+ case 0xc3:
+ case 0xca:
+ case 0xcb:
+ case 0xcf:
+ case 0xea: /* jmp absolute -- eip is correct */
+ /* eip is already adjusted, no more changes required */
+ p->ainsn.boostable = 1;
+ goto no_change;
+ case 0xe8: /* call relative - Fix return addr */
+ *tos = orig_eip + (*tos - copy_eip);
+ break;
+ case 0x9a: /* call absolute -- same as call absolute, indirect */
+ *tos = orig_eip + (*tos - copy_eip);
+ goto no_change;
+ case 0xff:
+ if ((insns[1] & 0x30) == 0x10) {
+ /*
+ * call absolute, indirect
+ * Fix return addr; eip is correct.
+ * But this is not boostable
+ */
+ *tos = orig_eip + (*tos - copy_eip);
+ goto no_change;
+ } else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute
+ * indirect */
+ ((insns[1] & 0x31) == 0x21)) {
+ /* jmp far, absolute indirect */
+ /* eip is correct. And this is boostable */
+ p->ainsn.boostable = 1;
+ goto no_change;
+ }
+ default:
+ break;
+ }
+
+ if (p->ainsn.boostable == 0) {
+ if ((regs->EREG(ip) > copy_eip) &&
+ (regs->EREG(ip) - copy_eip) + 5 < MAX_INSN_SIZE) {
+ /*
+ * These instructions can be executed directly if it
+ * jumps back to correct address.
+ */
+ set_jmp_op((void *)regs->EREG(ip),
+ (void *)orig_eip +
+ (regs->EREG(ip) - copy_eip));
+ p->ainsn.boostable = 1;
+ } else {
+ p->ainsn.boostable = -1;
+ }
+ }
+
+ regs->EREG(ip) = orig_eip + (regs->EREG(ip) - copy_eip);
+
+no_change:
+ return;
+}
+
+/*
+ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
+ * remain disabled thoroughout this function.
+ */
+static int post_kprobe_handler(struct pt_regs *regs)
+{
+ struct kctx *ctx = current_kctx;
+ struct kp_core *cur = kp_core_running();
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ if (!cur)
+ return 0;
+
+ resume_execution(cur, regs, kcb);
+ regs->flags |= kcb->kp_core_saved_eflags;
+#ifndef CONFIG_X86
+ trace_hardirqs_fixup_flags(regs->EREG(flags));
+#endif /* CONFIG_X86 */
+ /* Restore back the original saved kprobes variables and continue. */
+ if (kcb->kp_core_status == KPROBE_REENTER) {
+ restore_previous_kp_core(kcb);
+ goto out;
+ }
+ kp_core_running_set(NULL);
+
+out:
+ if (!able2resched(ctx))
+ swap_preempt_enable_no_resched();
+
+ switch_to_bits_reset(ctx, SWITCH_TO_KP);
+ kp_core_put(cur);
+
+ /*
+ * if somebody else is singlestepping across a probe point, eflags
+ * will have TF set, in which case, continue the remaining processing
+ * of do_debug, as if this is not a probe hit.
+ */
+ if (regs->EREG(flags) & TF_MASK)
+ return 0;
+
+ return 1;
+}
+
+static int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ struct kp_core *cur = kp_core_running();
+ struct kp_core_ctlblk *kcb = kp_core_ctlblk();
+
+ switch (kcb->kp_core_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the eip points back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ regs->ip = cur->addr;
+ regs->flags |= kcb->kp_core_old_eflags;
+ if (kcb->kp_core_status == KPROBE_REENTER)
+ restore_previous_kp_core(kcb);
+ else
+ kp_core_running_set(NULL);
+ break;
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * In case the user-specified fault handler returned
+ * zero, try to fix up.
+ */
+ if (swap_fixup_exception(regs))
+ return 1;
+
+ /*
+ * fixup_exception() could not handle it,
+ * Let do_page_fault() fix it.
+ */
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = (struct die_args *) data;
+ int ret = NOTIFY_DONE;
+
+ DBPRINTF("val = %ld, data = 0x%X", val, (unsigned int) data);
+
+ if (args->regs == NULL || swap_user_mode(args->regs))
+ return ret;
+
+ DBPRINTF("switch (val) %lu %d %d", val, DIE_INT3, DIE_TRAP);
+ switch (val) {
+#ifdef CONFIG_KPROBES
+ case DIE_INT3:
+#else
+ case DIE_TRAP:
+#endif
+ DBPRINTF("before kprobe_handler ret=%d %p",
+ ret, args->regs);
+ if (kprobe_handler (args->regs))
+ ret = NOTIFY_STOP;
+ DBPRINTF("after kprobe_handler ret=%d %p",
+ ret, args->regs);
+ break;
+ case DIE_DEBUG:
+ if (post_kprobe_handler(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_GPF:
+ if (kp_core_running() &&
+ kprobe_fault_handler(args->regs, args->trapnr))
+ ret = NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+ DBPRINTF("ret=%d", ret);
+ /* if(ret == NOTIFY_STOP) */
+ /* handled_exceptions++; */
+
+ return ret;
+}
+
+static struct notifier_block kprobe_exceptions_nb = {
+ .notifier_call = kprobe_exceptions_notify,
+ .priority = INT_MAX
+};
+
+/**
+ * @brief Arms kp_core.
+ *
+ * @param core Pointer to target kp_core.
+ * @return Void.
+ */
+void arch_kp_core_arm(struct kp_core *p)
+{
+ swap_text_poke((void *)p->addr,
+ ((unsigned char[]){BREAKPOINT_INSTRUCTION}), 1);
+}
+
+/**
+ * @brief Disarms kp_core.
+ *
+ * @param core Pointer to target kp_core.
+ * @return Void.
+ */
+void arch_kp_core_disarm(struct kp_core *p)
+{
+ swap_text_poke((void *)p->addr, &p->opcode, 1);
+}
+
+/**
+ * @brief Prepares kretprobes, saves ret address, makes function return to
+ * trampoline.
+ *
+ * @param ri Pointer to kretprobe_instance.
+ * @param regs Pointer to CPU registers data.
+ * @return Void.
+ */
+void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ unsigned long *ptr_ret_addr = (unsigned long *)swap_kernel_sp(regs);
+
+ /* for __switch_to probe */
+ if ((unsigned long)ri->rp->kp.addr == sched_addr) {
+ struct task_struct *next = (struct task_struct *)swap_get_karg(regs, 1);
+ ri->sp = NULL;
+ ri->task = next;
+ switch_to_bits_set(kctx_by_task(next), SWITCH_TO_RP);
+ } else {
+ ri->sp = ptr_ret_addr;
+ }
+
+ /* Save the return address */
+ ri->ret_addr = (unsigned long *)*ptr_ret_addr;
+
+ /* Replace the return addr with trampoline addr */
+ *ptr_ret_addr = (unsigned long)&swap_kretprobe_trampoline;
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * jumper *
+ ******************************************************************************
+ */
+struct cb_data {
+ unsigned long ret_addr;
+ unsigned long bx;
+
+ jumper_cb_t cb;
+ char data[0];
+};
+
+static unsigned long __used get_bx(struct cb_data *data)
+{
+ return data->bx;
+}
+
+static unsigned long __used jump_handler(struct cb_data *data)
+{
+ unsigned long ret_addr = data->ret_addr;
+
+ /* call callback */
+ data->cb(data->data);
+
+ /* FIXME: potential memory leak, when process kill */
+ kfree(data);
+
+ return ret_addr;
+}
+
+void jump_trampoline(void);
+__asm(
+ "jump_trampoline:\n"
+ "pushf\n"
+ SWAP_SAVE_REGS_STRING
+ "movl %ebx, %eax\n" /* data --> ax */
+ "call get_bx\n"
+ "movl %eax, (%esp)\n" /* restore bx */
+ "movl %ebx, %eax\n" /* data --> ax */
+ "call jump_handler\n"
+ /* move flags to cs */
+ "movl 56(%esp), %edx\n"
+ "movl %edx, 52(%esp)\n"
+ /* replace saved flags with true return address. */
+ "movl %eax, 56(%esp)\n"
+ SWAP_RESTORE_REGS_STRING
+ "popf\n"
+ "ret\n"
+);
+
+unsigned long get_jump_addr(void)
+{
+ return (unsigned long)&jump_trampoline;
+}
+EXPORT_SYMBOL_GPL(get_jump_addr);
+
+int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
+ jumper_cb_t cb, void *data, size_t size)
+{
+ struct cb_data *cb_data;
+
+ cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
+ if (cb_data == NULL)
+ return -ENOMEM;
+
+ /* save data */
+ if (size)
+ memcpy(cb_data->data, data, size);
+
+ /* save info for restore */
+ cb_data->ret_addr = ret_addr;
+ cb_data->cb = cb;
+ cb_data->bx = regs->bx;
+
+ /* save cb_data to bx */
+ regs->bx = (long)cb_data;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(set_jump_cb);
+
+
+
+
+
+/**
+ * @brief Initializes x86 module deps.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int arch_init_module_deps()
+{
+ const char *sym;
+
+ sym = "fixup_exception";
+ swap_fixup_exception = (void *)swap_ksyms(sym);
+ if (swap_fixup_exception == NULL)
+ goto not_found;
+
+ sym = "text_poke";
+ swap_text_poke = (void *)swap_ksyms(sym);
+ if (swap_text_poke == NULL)
+ goto not_found;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+ sym = "show_registers";
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */
+ sym = "show_regs";
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */
+ swap_show_registers = (void *)swap_ksyms(sym);
+ if (swap_show_registers == NULL)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol %s(...) not found\n", sym);
+ return -ESRCH;
+}
+
+/**
+ * @brief Initializes kprobes module for ARM arch.
+ *
+ * @return 0 on success, error code on error.
+ */
+int swap_arch_init_kprobes(void)
+{
+ int ret;
+
+ ret = swap_td_raw_reg(&kp_tdraw, sizeof(struct regs_td));
+ if (ret)
+ return ret;
+
+ ret = register_die_notifier(&kprobe_exceptions_nb);
+ if (ret)
+ swap_td_raw_unreg(&kp_tdraw);
+
+ return ret;
+}
+
+/**
+ * @brief Uninitializes kprobe module.
+ *
+ * @return Void.
+ */
+void swap_arch_exit_kprobes(void)
+{
+ unregister_die_notifier(&kprobe_exceptions_nb);
+ swap_td_raw_unreg(&kp_tdraw);
+}
--- /dev/null
+/**
+ * @file kprobe/arch/asm-x86/swap_kprobes.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Arch-dependent kprobes interface for x86 arch.
+ */
+
+#ifndef _SWAP_ASM_X86_KPROBES_H
+#define _SWAP_ASM_X86_KPROBES_H
+
+
+#include <linux/version.h>
+#include <kprobe/swap_kprobes_deps.h>
+
+/**
+ * @brief Opcode type.
+ */
+typedef u8 kprobe_opcode_t;
+
+#define BREAKPOINT_INSTRUCTION 0xcc
+#define RELATIVEJUMP_INSTRUCTION 0xe9
+
+#define BP_INSN_SIZE 1
+#define MAX_INSN_SIZE 16
+#define MAX_STACK_SIZE 64
+
+#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
+ (((unsigned long)current_thread_info()) \
+ + THREAD_SIZE - (ADDR))) \
+ ? (MAX_STACK_SIZE) \
+ : (((unsigned long)current_thread_info()) \
+ + THREAD_SIZE - (ADDR)))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+
+#define EREG(rg) e##rg
+#define XREG(rg) x##rg
+#define ORIG_EAX_REG orig_eax
+
+#else
+
+#define EREG(rg) rg
+#define XREG(rg) rg
+#define ORIG_EAX_REG orig_ax
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#define TF_MASK X86_EFLAGS_TF
+#define IF_MASK X86_EFLAGS_IF
+#endif
+#define UPROBES_TRAMP_LEN (MAX_INSN_SIZE+sizeof(kprobe_opcode_t))
+#define UPROBES_TRAMP_INSN_IDX 0
+#define UPROBES_TRAMP_RET_BREAK_IDX MAX_INSN_SIZE
+#define KPROBES_TRAMP_LEN MAX_INSN_SIZE
+#define KPROBES_TRAMP_INSN_IDX 0
+
+static inline int swap_user_mode(struct pt_regs *regs)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ return user_mode(regs);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
+ return user_mode_vm(regs);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+#define swap_in_interrupt() (in_interrupt() & ~HARDIRQ_OFFSET)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
+
+static inline unsigned long arch_get_task_pc(struct task_struct *p)
+{
+ /* FIXME: Not implemented yet */
+ return 0;
+}
+
+static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
+{
+ /* FIXME: Not implemented yet */
+}
+
+static inline struct pt_regs *swap_get_syscall_uregs(unsigned long sp)
+{
+ return NULL; /* FIXME currently not implemented for x86 */
+}
+
+static inline unsigned long swap_get_stack_ptr(struct pt_regs *regs)
+{
+ return regs->EREG(sp);
+}
+
+static inline void swap_set_stack_ptr(struct pt_regs *regs, unsigned long sp)
+{
+ regs->EREG(sp) = sp;
+}
+
+static inline unsigned long swap_get_kpc(struct pt_regs *regs)
+{
+ return regs->ip;
+}
+
+static inline void swap_set_kpc(struct pt_regs *regs, unsigned long val)
+{
+ regs->ip = val;
+}
+
+static inline unsigned long swap_get_arg(struct pt_regs *regs, int num)
+{
+ unsigned long arg = 0;
+ read_proc_vm_atomic(current, regs->EREG(sp) + (1 + num) * 4,
+ &arg, sizeof(arg));
+ return arg;
+}
+
+static inline void swap_set_arg(struct pt_regs *regs, int num,
+ unsigned long val)
+{
+ write_proc_vm_atomic(current, regs->EREG(sp) + (1 + num) * 4,
+ &val, sizeof(val));
+}
+
+/**
+ * @struct prev_kp_core
+ * @brief Stores previous kp_core.
+ * @var prev_kp_core::kp
+ * Pointer to kp_core struct.
+ * @var prev_kp_core::status
+ * kp_core status.
+ */
+struct prev_kp_core {
+ struct kp_core *p;
+ unsigned long status;
+};
+
+/**
+ * @struct kp_core_ctlblk
+ * @brief Per-cpu kp_core control block.
+ * @var kp_core_ctlblk::kp_core_status
+ * kp_core status.
+ * @var kp_core_ctlblk::prev_kp_core
+ * Previous kp_core.
+ */
+struct kp_core_ctlblk {
+ unsigned long kp_core_status;
+ struct prev_kp_core prev_kp_core;
+ struct pt_regs jprobe_saved_regs;
+ unsigned long kp_core_old_eflags;
+ unsigned long kp_core_saved_eflags;
+ unsigned long *jprobe_saved_esp;
+ kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
+};
+
+
+/**
+ * @struct swap_arch_specific_insn
+ * @brief Architecture specific copy of original instruction.
+ * @var swap_arch_specific_insn::insn
+ * Copy of the original instruction.
+ * @var swap_arch_specific_insn::boostable
+ * If this flag is not 0, this kp_core can be boost when its
+ * post_handler and break_handler is not set.
+ */
+struct swap_arch_specific_insn {
+ kprobe_opcode_t *insn;
+ int boostable;
+};
+
+/**
+ * @brief Entry point.
+ */
+typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long,
+ unsigned long, unsigned long,
+ unsigned long, unsigned long);
+
+int arch_init_module_deps(void);
+
+struct kprobe;
+struct kp_core;
+struct slot_manager;
+struct kretprobe_instance;
+
+int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm);
+void arch_kp_core_arm(struct kp_core *core);
+void arch_kp_core_disarm(struct kp_core *core);
+int swap_setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs);
+void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs);
+void swap_kretprobe_trampoline(void);
+
+void restore_previous_kp_core(struct kp_core_ctlblk *kcb);
+int swap_can_boost(kprobe_opcode_t *opcodes);
+static inline int arch_check_insn(struct swap_arch_specific_insn *ainsn)
+{
+ return 0;
+}
+
+unsigned long swap_kernel_sp(struct pt_regs *regs);
+
+static inline unsigned long swap_get_karg(struct pt_regs *regs, unsigned long n)
+{
+ switch (n) {
+ case 0:
+ return regs->ax;
+ case 1:
+ return regs->dx;
+ case 2:
+ return regs->cx;
+ }
+
+ /*
+ * 2 = 3 - 1
+ * 3 - arguments from registers
+ * 1 - return address saved on top of the stack
+ */
+ return *((unsigned long *)swap_kernel_sp(regs) + n - 2);
+}
+
+static inline unsigned long swap_get_sarg(struct pt_regs *regs, unsigned long n)
+{
+ /* 1 - return address saved on top of the stack */
+ return *((unsigned long *)kernel_stack_pointer(regs) + n + 1);
+}
+
+/* jumper */
+typedef unsigned long (*jumper_cb_t)(void *);
+
+unsigned long get_jump_addr(void);
+int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
+ jumper_cb_t cb, void *data, size_t size);
+
+int swap_arch_init_kprobes(void);
+void swap_arch_exit_kprobes(void);
+
+#endif /* _SWAP_ASM_X86_KPROBES_H */
--- /dev/null
+/**
+ * @file kprobe/swap_kdebug.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Header for debug purposes.
+ */
+
+
+#ifndef _SWAP_KPROBE_DEBUG_H
+#define _SWAP_KPROBE_DEBUG_H
+
+/* #define _DEBUG */
+
+#ifdef _DEBUG
+#define DBPRINTF(format, args...) do { \
+ if (1) { \
+ char *f = __FILE__; \
+ char *n = strrchr(f, '/'); \
+ printk(KERN_INFO "%s : %u : %s : " format "\n" , \
+ (n) ? n+1 : f, __LINE__, __func__, ##args); \
+ } \
+ } while (0)
+#else
+#define DBPRINTF(format, args...)
+#endif
+
+
+#endif /* _SWAP_KPROBE_DEBUG_H */
--- /dev/null
+/**
+ * kprobe/swap_kprobes.c
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM and MIPS
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kprobe implementation. Dynamic kernel functions instrumentation.
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#include <linux/config.h>
+#endif
+
+#include <linux/hash.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/stop_machine.h>
+#include <linux/delay.h>
+#include <ksyms/ksyms.h>
+#include <master/swap_initializer.h>
+#include <swap-asm/swap_kprobes.h>
+#include "swap_ktd.h"
+#include "swap_slots.h"
+#include "swap_ktd.h"
+#include "swap_td_raw.h"
+#include "swap_kdebug.h"
+#include "swap_kprobes.h"
+#include "swap_kprobes_deps.h"
+
+
+#define KRETPROBE_STACK_DEPTH 64
+
+
+/**
+ * @var sched_addr
+ * @brief Scheduler address.
+ */
+unsigned long sched_addr;
+static unsigned long exit_addr;
+static unsigned long do_group_exit_addr;
+static unsigned long sys_exit_group_addr;
+static unsigned long sys_exit_addr;
+
+/**
+ * @var sm
+ * @brief Current slot manager. Slots are the places where trampolines are
+ * located.
+ */
+struct slot_manager sm;
+
+static DEFINE_SPINLOCK(kretprobe_lock); /* Protects kretprobe_inst_table */
+
+static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
+static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
+
+/**
+ * @var kprobe_count
+ * @brief Count of kprobes.
+ */
+atomic_t kprobe_count;
+EXPORT_SYMBOL_GPL(kprobe_count);
+
+
+static void *(*__module_alloc)(unsigned long size);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+static void (*__module_free)(void *module_region);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
+static void (*__module_free)(struct module *mod, void *module_region);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
+
+static void *wrapper_module_alloc(unsigned long size)
+{
+ return __module_alloc(size);
+}
+
+static void wrapper_module_free(void *module_region)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ __module_free(module_region);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
+ __module_free(NULL, module_region);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
+}
+
+static void *sm_alloc(struct slot_manager *sm)
+{
+ return wrapper_module_alloc(PAGE_SIZE);
+}
+
+static void sm_free(struct slot_manager *sm, void *ptr)
+{
+ wrapper_module_free(ptr);
+}
+
+static void init_sm(void)
+{
+ sm.slot_size = KPROBES_TRAMP_LEN;
+ sm.alloc = sm_alloc;
+ sm.free = sm_free;
+ INIT_HLIST_HEAD(&sm.page_list);
+}
+
+static void exit_sm(void)
+{
+ /* FIXME: free */
+}
+
+static struct hlist_head *kpt_head_by_addr(unsigned long addr)
+{
+ return &kprobe_table[hash_ptr((void *)addr, KPROBE_HASH_BITS)];
+}
+
+static void kretprobe_assert(struct kretprobe_instance *ri,
+ unsigned long orig_ret_address,
+ unsigned long trampoline_address)
+{
+ if (!orig_ret_address || (orig_ret_address == trampoline_address)) {
+ struct task_struct *task;
+ if (ri == NULL)
+ panic("kretprobe BUG!: ri = NULL\n");
+
+ task = ri->task;
+
+ if (task == NULL)
+ panic("kretprobe BUG!: task = NULL\n");
+
+ if (ri->rp == NULL)
+ panic("kretprobe BUG!: ri->rp = NULL\n");
+
+ panic("kretprobe BUG!: "
+ "Processing kretprobe %p @ %08lx (%d/%d - %s)\n",
+ ri->rp, ri->rp->kp.addr, ri->task->tgid,
+ ri->task->pid, ri->task->comm);
+ }
+}
+
+struct kpc_data {
+ struct kp_core *running;
+ struct kp_core_ctlblk ctlblk;
+};
+
+struct kctx {
+ struct kpc_data kpc;
+ unsigned long st_flags;
+};
+
+static void ktd_cur_init(struct task_struct *task, void *data)
+{
+ struct kctx *ctx = (struct kctx *)data;
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static void ktd_cur_exit(struct task_struct *task, void *data)
+{
+ struct kctx *ctx = (struct kctx *)data;
+
+ WARN(ctx->kpc.running, "running=%p\n", ctx->kpc.running);
+}
+
+struct ktask_data ktd_cur = {
+ .init = ktd_cur_init,
+ .exit = ktd_cur_exit,
+ .size = sizeof(struct kctx),
+};
+
+struct kctx *kctx_by_task(struct task_struct *task)
+{
+ return (struct kctx *)swap_ktd(&ktd_cur, task);
+}
+
+void switch_to_bits_set(struct kctx *ctx, unsigned long mask)
+{
+ ctx->st_flags |= mask;
+}
+
+void switch_to_bits_reset(struct kctx *ctx, unsigned long mask)
+{
+ ctx->st_flags &= ~mask;
+}
+
+unsigned long switch_to_bits_get(struct kctx *ctx, unsigned long mask)
+{
+ return ctx->st_flags & mask;
+}
+
+static DEFINE_PER_CPU(struct kpc_data, per_cpu_kpc_data_i);
+static DEFINE_PER_CPU(struct kpc_data, per_cpu_kpc_data_st);
+
+static struct kpc_data *kp_core_data(void)
+{
+ struct kctx *ctx = current_kctx;
+
+ if (swap_in_interrupt())
+ return &__get_cpu_var(per_cpu_kpc_data_i);
+ else if (switch_to_bits_get(ctx, SWITCH_TO_ALL))
+ return &__get_cpu_var(per_cpu_kpc_data_st);
+
+ return &ctx->kpc;
+}
+
+static int kprobe_cur_reg(void)
+{
+ return swap_ktd_reg(&ktd_cur);
+}
+
+static void kprobe_cur_unreg(void)
+{
+ swap_ktd_unreg(&ktd_cur);
+}
+
+struct kp_core *kp_core_running(void)
+{
+ return kp_core_data()->running;
+}
+
+void kp_core_running_set(struct kp_core *p)
+{
+ kp_core_data()->running = p;
+}
+
+/**
+ * @brief Gets kp_core_ctlblk for the current CPU.
+ *
+ * @return Current CPU struct kp_core_ctlblk.
+ */
+struct kp_core_ctlblk *kp_core_ctlblk(void)
+{
+ return &kp_core_data()->ctlblk;
+}
+
+/*
+ * This routine is called either:
+ * - under the kprobe_mutex - during kprobe_[un]register()
+ * OR
+ * - with preemption disabled - from arch/xxx/kernel/kprobes.c
+ */
+
+/**
+ * @brief Gets kp_core.
+ *
+ * @param addr Probe address.
+ * @return kprobe_core for addr.
+ */
+struct kp_core *kp_core_by_addr(unsigned long addr)
+{
+ struct hlist_head *head;
+ struct kp_core *core;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ head = kpt_head_by_addr(addr);
+ swap_hlist_for_each_entry_rcu(core, node, head, hlist) {
+ if (core->addr == addr)
+ return core;
+ }
+
+ return NULL;
+}
+
+
+static int alloc_nodes_kretprobe(struct kretprobe *rp);
+
+/* Called with kretprobe_lock held */
+static struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp)
+{
+ struct kretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
+ return ri;
+ }
+
+ if (!alloc_nodes_kretprobe(rp)) {
+ swap_hlist_for_each_entry(ri, node, &rp->free_instances,
+ uflist) {
+ return ri;
+ }
+ }
+
+ return NULL;
+}
+
+/* Called with kretprobe_lock held */
+static struct kretprobe_instance *
+get_free_rp_inst_no_alloc(struct kretprobe *rp)
+{
+ struct kretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
+ return ri;
+ }
+
+ return NULL;
+}
+
+/* Called with kretprobe_lock held */
+static struct kretprobe_instance *get_used_rp_inst(struct kretprobe *rp)
+{
+ struct kretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->used_instances, uflist) {
+ return ri;
+ }
+
+ return NULL;
+}
+
+/* Called with kretprobe_lock held */
+static void add_rp_inst(struct kretprobe_instance *ri)
+{
+ /*
+ * Remove rp inst off the free list -
+ * Add it back when probed function returns
+ */
+ hlist_del(&ri->uflist);
+
+ /* Add rp inst onto table */
+ INIT_HLIST_NODE(&ri->hlist);
+
+ hlist_add_head(&ri->hlist,
+ &kretprobe_inst_table[hash_ptr(ri->task,
+ KPROBE_HASH_BITS)]);
+
+ /* Also add this rp inst to the used list. */
+ INIT_HLIST_NODE(&ri->uflist);
+ hlist_add_head(&ri->uflist, &ri->rp->used_instances);
+}
+
+/* Called with kretprobe_lock held */
+static void recycle_rp_inst(struct kretprobe_instance *ri)
+{
+ if (ri->rp) {
+ hlist_del(&ri->hlist);
+ /* remove rp inst off the used list */
+ hlist_del(&ri->uflist);
+ /* put rp inst back onto the free list */
+ INIT_HLIST_NODE(&ri->uflist);
+ hlist_add_head(&ri->uflist, &ri->rp->free_instances);
+ }
+}
+
+static struct hlist_head *kretprobe_inst_table_head(void *hash_key)
+{
+ return &kretprobe_inst_table[hash_ptr(hash_key, KPROBE_HASH_BITS)];
+}
+
+static void free_rp_inst(struct kretprobe *rp)
+{
+ struct kretprobe_instance *ri;
+ while ((ri = get_free_rp_inst_no_alloc(rp)) != NULL) {
+ hlist_del(&ri->uflist);
+ kfree(ri);
+ }
+}
+
+static void kp_core_remove(struct kp_core *core)
+{
+ /* TODO: check boostable for x86 and MIPS */
+ swap_slot_free(&sm, core->ainsn.insn);
+}
+
+static void kp_core_wait(struct kp_core *p)
+{
+ int ms = 1;
+
+ while (atomic_read(&p->usage)) {
+ msleep(ms);
+ ms += ms < 7 ? 1 : 0;
+ }
+}
+
+static struct kp_core *kp_core_create(unsigned long addr)
+{
+ struct kp_core *core;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (core) {
+ INIT_HLIST_NODE(&core->hlist);
+ core->addr = addr;
+ atomic_set(&core->usage, 0);
+ rwlock_init(&core->handlers.lock);
+ }
+
+ return core;
+}
+
+static void kp_core_free(struct kp_core *core)
+{
+ WARN_ON(atomic_read(&core->usage));
+ kfree(core);
+}
+
+static int pre_handler_one(struct kp_core *core, struct pt_regs *regs)
+{
+ int ret = 0;
+ struct kprobe *p = core->handlers.kps[0];
+
+ if (p && p->pre_handler)
+ ret = p->pre_handler(p, regs);
+
+ return ret;
+}
+
+static int pre_handler_multi(struct kp_core *core, struct pt_regs *regs)
+{
+ int i, ret = 0;
+
+ /* TODO: add sync use kprobe */
+ for (i = 0; i < ARRAY_SIZE(core->handlers.kps); ++i) {
+ struct kprobe *p = core->handlers.kps[i];
+
+ if (p && p->pre_handler) {
+ ret = p->pre_handler(p, regs);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int kp_core_add_kprobe(struct kp_core *core, struct kprobe *p)
+{
+ int i, ret = 0;
+ unsigned long flags;
+ struct kp_handlers *h = &core->handlers;
+
+ write_lock_irqsave(&h->lock, flags);
+ if (h->pre == NULL) {
+ h->pre = pre_handler_one;
+ } else if (h->pre == pre_handler_one) {
+ h->pre = pre_handler_multi;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(core->handlers.kps); ++i) {
+ if (core->handlers.kps[i])
+ continue;
+
+ core->handlers.kps[i] = p;
+ goto unlock;
+ }
+
+ pr_err("all kps slots is busy\n");
+ ret = -EBUSY;
+unlock:
+ write_unlock_irqrestore(&h->lock, flags);
+ return ret;
+}
+
+static void kp_core_del_kprobe(struct kp_core *core, struct kprobe *p)
+{
+ int i, cnt = 0;
+ unsigned long flags;
+ struct kp_handlers *h = &core->handlers;
+
+ write_lock_irqsave(&h->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(h->kps); ++i) {
+ if (h->kps[i] == p)
+ h->kps[i] = NULL;
+
+ if (h->kps[i] == NULL)
+ ++cnt;
+ }
+ write_unlock_irqrestore(&h->lock, flags);
+
+ if (cnt == ARRAY_SIZE(h->kps)) {
+ arch_kp_core_disarm(core);
+ synchronize_sched();
+
+ hlist_del_rcu(&core->hlist);
+ synchronize_rcu();
+
+ kp_core_wait(core);
+ kp_core_remove(core);
+ kp_core_free(core);
+ }
+}
+
+static DEFINE_MUTEX(kp_mtx);
+/**
+ * @brief Registers kprobe.
+ *
+ * @param p Pointer to the target kprobe.
+ * @return 0 on success, error code on error.
+ */
+int swap_register_kprobe(struct kprobe *p)
+{
+ struct kp_core *core;
+ unsigned long addr;
+ int ret = 0;
+ /*
+ * If we have a symbol_name argument look it up,
+ * and add it to the address. That way the addr
+ * field can either be global or relative to a symbol.
+ */
+ if (p->symbol_name) {
+ if (p->addr)
+ return -EINVAL;
+ p->addr = swap_ksyms(p->symbol_name);
+ }
+
+ if (!p->addr)
+ return -EINVAL;
+
+ addr = p->addr + p->offset;
+
+ mutex_lock(&kp_mtx);
+ core = kp_core_by_addr(addr);
+ if (core == NULL) {
+ core = kp_core_create(addr);
+ if (core == NULL) {
+ pr_err("Out of memory\n");
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = arch_kp_core_prepare(core, &sm);
+ if (ret) {
+ kp_core_free(core);
+ goto unlock;
+ }
+
+ ret = kp_core_add_kprobe(core, p);
+ if (ret) {
+ kp_core_free(core);
+ goto unlock;
+ }
+
+ hlist_add_head_rcu(&core->hlist, kpt_head_by_addr(core->addr));
+ arch_kp_core_arm(core);
+ } else {
+ ret = kp_core_add_kprobe(core, p);
+ }
+
+unlock:
+ mutex_unlock(&kp_mtx);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_register_kprobe);
+
+/**
+ * @brief Unregistes kprobe.
+ *
+ * @param kp Pointer to the target kprobe.
+ * @return Void.
+ */
+void swap_unregister_kprobe(struct kprobe *p)
+{
+ unsigned long addr = p->addr + p->offset;
+ struct kp_core *core;
+
+ mutex_lock(&kp_mtx);
+ core = kp_core_by_addr(addr);
+ BUG_ON(core == NULL);
+
+ kp_core_del_kprobe(core, p);
+ mutex_unlock(&kp_mtx);
+
+ /* Set 0 addr for reusability if symbol_name is used */
+ if (p->symbol_name)
+ p->addr = 0;
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kprobe);
+
+/**
+ * @brief Registers jprobe.
+ *
+ * @param jp Pointer to the target jprobe.
+ * @return swap_register_kprobe result.
+ */
+int swap_register_jprobe(struct jprobe *jp)
+{
+ /* Todo: Verify probepoint is a function entry point */
+ jp->kp.pre_handler = swap_setjmp_pre_handler;
+
+ return swap_register_kprobe(&jp->kp);
+}
+EXPORT_SYMBOL_GPL(swap_register_jprobe);
+
+/**
+ * @brief Unregisters jprobe.
+ *
+ * @param jp Pointer to the target jprobe.
+ * @return Void.
+ */
+void swap_unregister_jprobe(struct jprobe *jp)
+{
+ swap_unregister_kprobe(&jp->kp);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_jprobe);
+
+/*
+ * This kprobe pre_handler is registered with every kretprobe. When probe
+ * hits it will set up the return probe.
+ */
+static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kretprobe *rp = container_of(p, struct kretprobe, kp);
+ struct kretprobe_instance *ri;
+ unsigned long flags = 0;
+
+ /* TODO: consider to only swap the RA
+ * after the last pre_handler fired */
+ spin_lock_irqsave(&kretprobe_lock, flags);
+
+ /* TODO: test - remove retprobe after func entry but before its exit */
+ ri = get_free_rp_inst(rp);
+ if (ri != NULL) {
+ int skip = 0;
+
+ ri->rp = rp;
+ ri->task = current;
+
+ if (rp->entry_handler)
+ skip = rp->entry_handler(ri, regs);
+
+ if (skip) {
+ add_rp_inst(ri);
+ recycle_rp_inst(ri);
+ } else {
+ swap_arch_prepare_kretprobe(ri, regs);
+ add_rp_inst(ri);
+ }
+ } else {
+ ++rp->nmissed;
+ }
+
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+
+ return 0;
+}
+
+/**
+ * @brief Trampoline probe handler.
+ *
+ * @param p Pointer to the fired kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return orig_ret_address
+ */
+unsigned long swap_trampoline_handler(struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri = NULL;
+ struct hlist_head *head;
+ unsigned long flags, orig_ret_address = 0;
+ unsigned long trampoline_address;
+
+ struct kp_core_ctlblk *kcb;
+
+ struct hlist_node *tmp;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ trampoline_address = (unsigned long)&swap_kretprobe_trampoline;
+
+ kcb = kp_core_ctlblk();
+
+ spin_lock_irqsave(&kretprobe_lock, flags);
+
+ /*
+ * We are using different hash keys (current and mm) for finding kernel
+ * space and user space probes. Kernel space probes can change mm field
+ * in task_struct. User space probes can be shared between threads of
+ * one process so they have different current but same mm.
+ */
+ head = kretprobe_inst_table_head(current);
+
+#ifdef CONFIG_X86
+ regs->XREG(cs) = __KERNEL_CS | get_kernel_rpl();
+ regs->EREG(ip) = trampoline_address;
+ regs->ORIG_EAX_REG = 0xffffffff;
+#endif
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because an multiple functions in the call path
+ * have a return probe installed on them, and/or more then one
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * kretprobe_trampoline
+ */
+ swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+ if (ri->rp && ri->rp->handler) {
+ /*
+ * Set fake current probe, we don't
+ * want to go into recursion
+ */
+ kp_core_running_set((struct kp_core *)0xfffff);
+ kcb->kp_core_status = KPROBE_HIT_ACTIVE;
+ ri->rp->handler(ri, regs);
+ kp_core_running_set(NULL);
+ }
+
+ orig_ret_address = (unsigned long)ri->ret_addr;
+ recycle_rp_inst(ri);
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
+
+ if (kcb->kp_core_status == KPROBE_REENTER)
+ restore_previous_kp_core(kcb);
+ else
+ kp_core_running_set(NULL);
+
+ switch_to_bits_reset(current_kctx, SWITCH_TO_RP);
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+
+ /*
+ * By returning a non-zero value, we are telling
+ * kprobe_handler() that we don't want the post_handler
+ * to run (and have re-enabled preemption)
+ */
+
+ return orig_ret_address;
+}
+
+#define SCHED_RP_NR 200
+#define COMMON_RP_NR 10
+
+static int alloc_nodes_kretprobe(struct kretprobe *rp)
+{
+ int alloc_nodes;
+ struct kretprobe_instance *inst;
+ int i;
+
+ DBPRINTF("Alloc aditional mem for retprobes");
+
+ if (rp->kp.addr == sched_addr) {
+ rp->maxactive += SCHED_RP_NR; /* max (100, 2 * NR_CPUS); */
+ alloc_nodes = SCHED_RP_NR;
+ } else {
+#if 1/* def CONFIG_PREEMPT */
+ rp->maxactive += max(COMMON_RP_NR, 2 * NR_CPUS);
+#else
+ rp->maxacpptive += NR_CPUS;
+#endif
+ alloc_nodes = COMMON_RP_NR;
+ }
+
+ for (i = 0; i < alloc_nodes; i++) {
+ inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_ATOMIC);
+ if (inst == NULL) {
+ free_rp_inst(rp);
+ return -ENOMEM;
+ }
+ INIT_HLIST_NODE(&inst->uflist);
+ hlist_add_head(&inst->uflist, &rp->free_instances);
+ }
+
+ DBPRINTF("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr,
+ (unsigned long) (*(rp->kp.addr)),
+ (unsigned long) (*(rp->kp.addr + 1)),
+ (unsigned long) (*(rp->kp.addr + 2)));
+ return 0;
+}
+
+/**
+ * @brief Registers kretprobes.
+ *
+ * @param rp Pointer to the target kretprobe.
+ * @return 0 on success, error code on error.
+ */
+int swap_register_kretprobe(struct kretprobe *rp)
+{
+ int ret = 0;
+ struct kretprobe_instance *inst;
+ int i;
+ DBPRINTF("START");
+
+ rp->kp.pre_handler = pre_handler_kretprobe;
+
+ /* Pre-allocate memory for max kretprobe instances */
+ if (rp->kp.addr == exit_addr) {
+ rp->kp.pre_handler = NULL; /* not needed for do_exit */
+ rp->maxactive = 0;
+ } else if (rp->kp.addr == do_group_exit_addr) {
+ rp->kp.pre_handler = NULL;
+ rp->maxactive = 0;
+ } else if (rp->kp.addr == sys_exit_group_addr) {
+ rp->kp.pre_handler = NULL;
+ rp->maxactive = 0;
+ } else if (rp->kp.addr == sys_exit_addr) {
+ rp->kp.pre_handler = NULL;
+ rp->maxactive = 0;
+ } else if (rp->maxactive <= 0) {
+#if 1/* def CONFIG_PREEMPT */
+ rp->maxactive = max(COMMON_RP_NR, 2 * NR_CPUS);
+#else
+ rp->maxactive = NR_CPUS;
+#endif
+ }
+ INIT_HLIST_HEAD(&rp->used_instances);
+ INIT_HLIST_HEAD(&rp->free_instances);
+ for (i = 0; i < rp->maxactive; i++) {
+ inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_KERNEL);
+ if (inst == NULL) {
+ free_rp_inst(rp);
+ return -ENOMEM;
+ }
+ INIT_HLIST_NODE(&inst->uflist);
+ hlist_add_head(&inst->uflist, &rp->free_instances);
+ }
+
+ DBPRINTF("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr,
+ (unsigned long) (*(rp->kp.addr)),
+ (unsigned long) (*(rp->kp.addr + 1)),
+ (unsigned long) (*(rp->kp.addr + 2)));
+ rp->nmissed = 0;
+ /* Establish function entry probe point */
+ ret = swap_register_kprobe(&rp->kp);
+ if (ret != 0)
+ free_rp_inst(rp);
+
+ DBPRINTF("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr,
+ (unsigned long) (*(rp->kp.addr)),
+ (unsigned long) (*(rp->kp.addr + 1)),
+ (unsigned long) (*(rp->kp.addr + 2)));
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_register_kretprobe);
+
+static int swap_disarm_krp_inst(struct kretprobe_instance *ri);
+
+static void swap_disarm_krp(struct kretprobe *rp)
+{
+ struct kretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->used_instances, uflist) {
+ if (swap_disarm_krp_inst(ri) != 0) {
+ printk(KERN_INFO "%s (%d/%d): cannot disarm "
+ "krp instance (%08lx)\n",
+ ri->task->comm, ri->task->tgid, ri->task->pid,
+ rp->kp.addr);
+ }
+ }
+}
+
+
+struct unreg_krp_args {
+ struct kretprobe **rps;
+ size_t size;
+ int rp_disarm;
+};
+
+static int __swap_unregister_kretprobes_top(void *data)
+{
+ struct unreg_krp_args *args = data;
+ struct kretprobe **rps = args->rps;
+ size_t size = args->size;
+ int rp_disarm = args->rp_disarm;
+ unsigned long flags;
+ const size_t end = ((size_t) 0) - 1;
+
+ for (--size; size != end; --size) {
+ if (rp_disarm) {
+ spin_lock_irqsave(&kretprobe_lock, flags);
+ swap_disarm_krp(rps[size]);
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Kretprobes unregister top. Unregisters kprobes.
+ *
+ * @param rps Pointer to the array of pointers to the target kretprobes.
+ * @param size Size of rps array.
+ * @param rp_disarm Disarm flag. If set kretprobe is disarmed.
+ * @return Void.
+ */
+void swap_unregister_kretprobes_top(struct kretprobe **rps, size_t size,
+ int rp_disarm)
+{
+ struct unreg_krp_args args = {
+ .rps = rps,
+ .size = size,
+ .rp_disarm = rp_disarm,
+ };
+ const size_t end = ((size_t)0) - 1;
+
+ for (--size; size != end; --size)
+ swap_unregister_kprobe(&rps[size]->kp);
+
+ if (rp_disarm) {
+ int ret;
+
+ ret = stop_machine(__swap_unregister_kretprobes_top,
+ &args, NULL);
+ if (ret)
+ pr_err("%s failed (%d)\n", __func__, ret);
+ } else {
+ __swap_unregister_kretprobes_top(&args);
+ }
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kretprobes_top);
+
+/**
+ * @brief swap_unregister_kretprobes_top wrapper for a single kretprobe.
+ *
+ * @param rp Pointer to the target kretprobe.
+ * @param rp_disarm Disarm flag.
+ * @return Void.
+ */
+void swap_unregister_kretprobe_top(struct kretprobe *rp, int rp_disarm)
+{
+ swap_unregister_kretprobes_top(&rp, 1, rp_disarm);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kretprobe_top);
+
+/**
+ * @brief Kretprobe unregister bottom. Here is kretprobe memory is released.
+ *
+ * @param rp Pointer to the target kretprobe.
+ * @return Void.
+ */
+void swap_unregister_kretprobe_bottom(struct kretprobe *rp)
+{
+ unsigned long flags;
+ struct kretprobe_instance *ri;
+
+ spin_lock_irqsave(&kretprobe_lock, flags);
+
+ while ((ri = get_used_rp_inst(rp)) != NULL)
+ recycle_rp_inst(ri);
+ free_rp_inst(rp);
+
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kretprobe_bottom);
+
+/**
+ * @brief swap_unregister_kretprobe_bottom wrapper for several kretprobes.
+ *
+ * @param rps Pointer to the array of the target kretprobes pointers.
+ * @param size Size of rps array.
+ * @return Void.
+ */
+void swap_unregister_kretprobes_bottom(struct kretprobe **rps, size_t size)
+{
+ const size_t end = ((size_t) 0) - 1;
+
+ for (--size; size != end; --size)
+ swap_unregister_kretprobe_bottom(rps[size]);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kretprobes_bottom);
+
+/**
+ * @brief Unregisters kretprobes.
+ *
+ * @param rpp Pointer to the array of the target kretprobes pointers.
+ * @param size Size of rpp array.
+ * @return Void.
+ */
+void swap_unregister_kretprobes(struct kretprobe **rpp, size_t size)
+{
+ swap_unregister_kretprobes_top(rpp, size, 1);
+
+ if (!in_atomic())
+ synchronize_sched();
+
+ swap_unregister_kretprobes_bottom(rpp, size);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kretprobes);
+
+/**
+ * @brief swap_unregister_kretprobes wrapper for a single kretprobe.
+ *
+ * @param rp Pointer to the target kretprobe.
+ * @return Void.
+ */
+void swap_unregister_kretprobe(struct kretprobe *rp)
+{
+ swap_unregister_kretprobes(&rp, 1);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_kretprobe);
+
+static inline void rm_task_trampoline(struct task_struct *p,
+ struct kretprobe_instance *ri)
+{
+ arch_set_task_pc(p, (unsigned long)ri->ret_addr);
+}
+
+static int swap_disarm_krp_inst(struct kretprobe_instance *ri)
+{
+ unsigned long *tramp = (unsigned long *)&swap_kretprobe_trampoline;
+ unsigned long *sp = ri->sp;
+ unsigned long *found = NULL;
+ int retval = -ENOENT;
+
+ if (!sp) {
+ unsigned long pc = arch_get_task_pc(ri->task);
+
+ printk(KERN_INFO "---> [%d] %s (%d/%d): pc = %08lx, ra = %08lx, tramp= %08lx (%08lx)\n",
+ task_cpu(ri->task),
+ ri->task->comm, ri->task->tgid, ri->task->pid,
+ pc, (long unsigned int)ri->ret_addr,
+ (long unsigned int)tramp,
+ (ri->rp ? ri->rp->kp.addr : 0));
+
+ /* __switch_to retprobe handling */
+ if (pc == (unsigned long)tramp) {
+ rm_task_trampoline(ri->task, ri);
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ while (sp > ri->sp - KRETPROBE_STACK_DEPTH) {
+ if (*sp == (unsigned long)tramp) {
+ found = sp;
+ break;
+ }
+ sp--;
+ }
+
+ if (found) {
+ printk(KERN_INFO "---> [%d] %s (%d/%d): tramp (%08lx) "
+ "found at %08lx (%08lx /%+ld) - %08lx\n",
+ task_cpu(ri->task),
+ ri->task->comm, ri->task->tgid, ri->task->pid,
+ (long unsigned int)tramp,
+ (long unsigned int)found, (long unsigned int)ri->sp,
+ (unsigned long)(found - ri->sp), ri->rp ? ri->rp->kp.addr : 0);
+ *found = (unsigned long)ri->ret_addr;
+ retval = 0;
+ } else {
+ printk(KERN_INFO "---> [%d] %s (%d/%d): tramp (%08lx) "
+ "NOT found at sp = %08lx - %08lx\n",
+ task_cpu(ri->task),
+ ri->task->comm, ri->task->tgid, ri->task->pid,
+ (long unsigned int)tramp,
+ (long unsigned int)ri->sp,
+ ri->rp ? ri->rp->kp.addr : 0);
+ }
+
+ return retval;
+}
+
+static void krp_inst_flush(struct task_struct *task)
+{
+ unsigned long flags;
+ struct kretprobe_instance *ri;
+ struct hlist_node *tmp;
+ struct hlist_head *head;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ spin_lock_irqsave(&kretprobe_lock, flags);
+ head = kretprobe_inst_table_head(task);
+ swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task == task) {
+ printk("task[%u %u %s]: flush krp_inst, ret_addr=%p\n",
+ task->tgid, task->pid, task->comm,
+ ri->ret_addr);
+ recycle_rp_inst(ri);
+ }
+ }
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+}
+
+static void do_put_task_handler(struct task_struct *task)
+{
+ /* task has died */
+ krp_inst_flush(task);
+ swap_ktd_put_task(task);
+}
+
+#ifdef CONFIG_SWAP_HOOK_TASKDATA
+
+#include <swap/hook_taskdata.h>
+
+static struct hook_taskdata put_hook = {
+ .owner = THIS_MODULE,
+ .put_task = do_put_task_handler,
+};
+
+static int put_task_once(void)
+{
+ return 0;
+}
+
+static int put_task_init(void)
+{
+ return hook_taskdata_reg(&put_hook);
+}
+
+static void put_task_uninit(void)
+{
+ hook_taskdata_unreg(&put_hook);
+}
+
+#else /* CONFIG_SWAP_HOOK_TASKDATA */
+
+/* Handler is called the last because it is registered the first */
+static int put_task_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct task_struct *t = (struct task_struct *)swap_get_karg(regs, 0);
+
+ do_put_task_handler(t);
+
+ return 0;
+}
+
+static struct kprobe put_task_kp = {
+ .pre_handler = put_task_handler,
+};
+
+static int put_task_once(void)
+{
+ const char *sym = "__put_task_struct";
+
+ put_task_kp.addr = swap_ksyms(sym);
+ if (put_task_kp.addr == 0) {
+ pr_err("ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+ }
+
+ return 0;
+}
+
+static int put_task_init(void)
+{
+ return swap_register_kprobe(&put_task_kp);
+}
+
+static void put_task_uninit(void)
+{
+ swap_unregister_kprobe(&put_task_kp);
+}
+
+#endif /* CONFIG_SWAP_HOOK_TASKDATA */
+
+
+static int init_module_deps(void)
+{
+ int ret;
+
+ sched_addr = swap_ksyms("__switch_to");
+ exit_addr = swap_ksyms("do_exit");
+ sys_exit_group_addr = swap_ksyms("sys_exit_group");
+ do_group_exit_addr = swap_ksyms("do_group_exit");
+ sys_exit_addr = swap_ksyms("sys_exit");
+
+ if (sched_addr == 0 ||
+ exit_addr == 0 ||
+ sys_exit_group_addr == 0 ||
+ do_group_exit_addr == 0 ||
+ sys_exit_addr == 0) {
+ return -ESRCH;
+ }
+
+ ret = init_module_dependencies();
+ if (ret)
+ return ret;
+
+ return arch_init_module_deps();
+}
+
+static int once(void)
+{
+ int i, ret;
+ const char *sym;
+
+ ret = put_task_once();
+ if (ret)
+ return ret;
+
+ sym = "module_alloc";
+ __module_alloc = (void *)swap_ksyms(sym);
+ if (__module_alloc == NULL)
+ goto not_found;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ sym = "module_memfree";
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
+ sym = "module_free";
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
+ __module_free = (void *)swap_ksyms(sym);
+ if (__module_free == NULL)
+ goto not_found;
+
+ ret = init_module_deps();
+ if (ret)
+ return ret;
+
+ /*
+ * FIXME allocate the probe table, currently defined statically
+ * initialize all list heads
+ */
+ for (i = 0; i < KPROBE_TABLE_SIZE; ++i) {
+ INIT_HLIST_HEAD(&kprobe_table[i]);
+ INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
+ }
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
+static int init_kprobes(void)
+{
+ int ret;
+
+ init_sm();
+ atomic_set(&kprobe_count, 0);
+
+ ret = swap_td_raw_init();
+ if (ret)
+ return ret;
+
+ ret = swap_arch_init_kprobes();
+ if (ret)
+ goto td_raw_uninit;
+
+ ret = swap_ktd_init();
+ if (ret)
+ goto arch_kp_exit;
+
+ ret = kprobe_cur_reg();
+ if (ret)
+ goto ktd_uninit;
+
+ ret = put_task_init();
+ if (ret)
+ goto cur_uninit;
+
+ return 0;
+
+cur_uninit:
+ kprobe_cur_unreg();
+ktd_uninit:
+ swap_ktd_uninit_top();
+ swap_ktd_uninit_bottom();
+arch_kp_exit:
+ swap_arch_exit_kprobes();
+td_raw_uninit:
+ swap_td_raw_uninit();
+ return ret;
+}
+
+static void exit_kprobes(void)
+{
+ swap_ktd_uninit_top();
+ put_task_uninit();
+ kprobe_cur_unreg();
+ swap_ktd_uninit_bottom();
+ swap_arch_exit_kprobes();
+ swap_td_raw_uninit();
+ exit_sm();
+}
+
+SWAP_LIGHT_INIT_MODULE(once, init_kprobes, exit_kprobes, NULL, NULL);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/**
+ * @file kprobe/swap_kprobes.h
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM and MIPS
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kprobe interface definition.
+ */
+
+
+#ifndef _SWAP_KPROBES_H
+#define _SWAP_KPROBES_H
+
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_SWAP_KERNEL_IMMUTABLE
+# error "Kernel is immutable"
+#endif /* CONFIG_SWAP_KERNEL_IMMUTABLE */
+
+
+#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */
+#include <linux/notifier.h>
+#include <linux/percpu.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+
+#include <swap-asm/swap_kprobes.h>
+
+
+/* kp_core_status settings */
+/** Kprobe hit active */
+#define KPROBE_HIT_ACTIVE 0x00000001
+/** Kprobe hit ss */
+#define KPROBE_HIT_SS 0x00000002
+/** Kprobe reenter */
+#define KPROBE_REENTER 0x00000004
+/** Kprobe hit ss done */
+#define KPROBE_HIT_SSDONE 0x00000008
+
+/** High word */
+#define HIWORD(x) (((x) & 0xFFFF0000) >> 16)
+/** Low word */
+#define LOWORD(x) ((x) & 0x0000FFFF)
+
+/** Invalid value */
+#define INVALID_VALUE 0xFFFFFFFF
+/** Invalid pointer */
+#define INVALID_POINTER (void *)INVALID_VALUE
+
+/** Jprobe entry */
+#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry
+
+
+struct kprobe;
+struct pt_regs;
+struct kretprobe;
+struct kretprobe_instance;
+
+/**
+ * @brief Kprobe pre-handler pointer.
+ */
+typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *);
+
+/**
+ * @brief Kprobe break handler pointer.
+ */
+typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *);
+
+/**
+ * @brief Kprobe post handler pointer.
+ */
+typedef void (*kprobe_post_handler_t) (struct kprobe *,
+ struct pt_regs *,
+ unsigned long flags);
+
+/**
+ * @brief Kprobe fault handler pointer.
+ */
+typedef int (*kprobe_fault_handler_t) (struct kprobe *,
+ struct pt_regs *,
+ int trapnr);
+
+/**
+ * @brief Kretprobe handler pointer.
+ */
+typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
+ struct pt_regs *);
+
+struct kprobe;
+struct kp_core;
+
+struct kp_handlers {
+ int (*pre)(struct kp_core *, struct pt_regs *);
+
+ rwlock_t lock;
+ struct kprobe *kps[4];
+};
+
+struct kp_core {
+ struct hlist_node hlist;
+ atomic_t usage;
+
+ struct kp_handlers handlers;
+
+ unsigned long addr;
+ kprobe_opcode_t opcode;
+
+ struct swap_arch_specific_insn ainsn;
+};
+
+/**
+ * @struct kprobe
+ * @brief Main kprobe struct.
+ */
+struct kprobe {
+ unsigned long addr; /**< Location of the probe point. */
+ char *symbol_name; /**< Symbol name of the probe point. */
+ unsigned long offset; /**< Offset into the symbol.*/
+ /**< Called before addr is executed. */
+ kprobe_pre_handler_t pre_handler;
+};
+
+/**
+ * @brief Kprobe pre-entry handler pointer.
+ */
+typedef unsigned long (*kprobe_pre_entry_handler_t) (void *priv_arg,
+ struct pt_regs *regs);
+
+
+/**
+ * @struct jprobe
+ * @brief Special probe type that uses setjmp-longjmp type tricks to resume
+ * execution at a specified entry with a matching prototype corresponding
+ * to the probed function - a trick to enable arguments to become
+ * accessible seamlessly by probe handling logic.
+ * Note:
+ * Because of the way compilers allocate stack space for local variables
+ * etc upfront, regardless of sub-scopes within a function, this mirroring
+ * principle currently works only for probes placed on function entry points.
+ */
+struct jprobe {
+ struct kprobe kp; /**< This probes kprobe.*/
+ kprobe_opcode_t *entry; /**< Probe handling code to jump to.*/
+ /** Handler which will be called before 'entry'. */
+ kprobe_pre_entry_handler_t pre_entry;
+ void *priv_arg; /**< Private args.*/
+};
+
+
+/**
+ * @struct jprobe_instance
+ * @brief Jprobe instance struct.
+ */
+struct jprobe_instance {
+ /* either on free list or used list */
+ struct hlist_node uflist; /**< Jprobes hash list. */
+ struct hlist_node hlist; /**< Jprobes hash list. */
+ struct jprobe *jp; /**< Pointer to the target jprobe. */
+ /** Pointer to the target task_struct. */
+ struct task_struct *task;
+};
+
+
+
+
+
+/**
+ * @struct kretprobe
+ * @brief Function-return probe
+ * Note: User needs to provide a handler function, and initialize maxactive.
+ */
+struct kretprobe {
+ struct kprobe kp; /**< Kprobe of this kretprobe.*/
+ kretprobe_handler_t handler; /**< Handler of this kretprobe.*/
+ kretprobe_handler_t entry_handler; /**< Entry handler of this kretprobe.*/
+ /** The maximum number of instances of the probed function that can be
+ * active concurrently. */
+ int maxactive;
+ /** Tracks the number of times the probed function's return was ignored,
+ * due to maxactive being too low. */
+ int nmissed;
+ size_t data_size; /**< Size of the data. */
+ /** List of this probe's free_instances. */
+ struct hlist_head free_instances;
+ /** List of this probe's used_instances. */
+ struct hlist_head used_instances;
+
+#ifdef CONFIG_ARM
+ unsigned arm_noret:1; /**< No-return flag for ARM.*/
+ unsigned thumb_noret:1; /**< No-return flag for Thumb.*/
+#endif
+
+};
+
+/**
+ * @struct kretprobe_instance
+ * @brief Instance of kretprobe.
+ */
+struct kretprobe_instance {
+ /* either on free list or used list */
+ struct hlist_node uflist; /**< Kretprobe hash list.*/
+ struct hlist_node hlist; /**< Kretprobe hash list.*/
+ struct kretprobe *rp; /**< Pointer to this instance's kretprobe.*/
+ unsigned long *ret_addr; /**< Return address.*/
+ unsigned long *sp; /**< Stack pointer.*/
+ struct task_struct *task; /**< Pointer to the target task_struct.*/
+ char data[0]; /**< Pointer to data.*/
+};
+
+
+/*
+ * Large value for fast but memory consuming implementation
+ * it is good when a lot of probes are instrumented
+ */
+/* #define KPROBE_HASH_BITS 6 */
+#define KPROBE_HASH_BITS 16
+#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
+
+
+static void inline kp_core_get(struct kp_core *p)
+{
+ atomic_inc(&p->usage);
+}
+
+static void inline kp_core_put(struct kp_core *p)
+{
+ atomic_dec(&p->usage);
+}
+
+
+/* Get the kp_core at this addr (if any) - called with rcu_read_lock() */
+struct kp_core *kp_core_by_addr(unsigned long addr);
+
+int swap_register_kprobe(struct kprobe *p);
+void swap_unregister_kprobe(struct kprobe *p);
+
+int swap_register_jprobe(struct jprobe *p);
+void swap_unregister_jprobe(struct jprobe *p);
+void swap_jprobe_return(void);
+
+
+int swap_register_kretprobe(struct kretprobe *rp);
+void swap_unregister_kretprobe(struct kretprobe *rp);
+void swap_unregister_kretprobes(struct kretprobe **rpp, size_t size);
+
+/*
+ * use:
+ * swap_unregister_kretprobe[s]_top();
+ * synchronize_sched();
+ * swap_unregister_kretprobe[s]_bottom();
+ *
+ * rp_disarm - indicates the need for restoration of the return address
+ */
+void swap_unregister_kretprobe_top(struct kretprobe *rp, int rp_disarm);
+void swap_unregister_kretprobes_top(struct kretprobe **rps, size_t size,
+ int rp_disarm);
+void swap_unregister_kretprobe_bottom(struct kretprobe *rp);
+void swap_unregister_kretprobes_bottom(struct kretprobe **rps, size_t size);
+
+
+unsigned long swap_trampoline_handler(struct pt_regs *regs);
+
+
+extern atomic_t kprobe_count;
+extern unsigned long sched_addr;
+
+struct kp_core *kp_core_running(void);
+void kp_core_running_set(struct kp_core *p);
+struct kp_core_ctlblk *kp_core_ctlblk(void);
+
+
+struct kctx;
+
+/* for __switch_to support */
+#define SWITCH_TO_KP 0b0001
+#define SWITCH_TO_RP 0b0010
+#define SWITCH_TO_ALL (SWITCH_TO_KP | SWITCH_TO_RP)
+
+#define current_kctx kctx_by_task(current)
+struct kctx *kctx_by_task(struct task_struct *task);
+
+void switch_to_bits_set(struct kctx *ctx, unsigned long mask);
+void switch_to_bits_reset(struct kctx *ctx, unsigned long mask);
+unsigned long switch_to_bits_get(struct kctx *ctx, unsigned long mask);
+
+
+#ifndef swap_in_interrupt
+#define swap_in_interrupt() in_interrupt()
+#endif /* swap_in_interrupt */
+
+static inline int able2resched(struct kctx *ctx)
+{
+ if (swap_in_interrupt() || switch_to_bits_get(ctx, SWITCH_TO_ALL))
+ return 0;
+
+ return 1;
+}
+
+
+#endif /* _SWAP_KPROBES_H */
+
--- /dev/null
+/**
+ * kprobe/swap_kprobes_deps.c
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kprobe kernel-dependent dependencies.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+
+#include <asm/pgtable.h>
+
+#include "swap_kprobes_deps.h"
+#include "swap_kdebug.h"
+
+
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+/* kernel define 'pgd_offset_k' redefinition */
+#undef pgd_offset_k
+#define pgd_offset_k(addr) pgd_offset(init_task.active_mm, addr)
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
+#ifndef is_zero_pfn
+
+static unsigned long swap_zero_pfn ;
+
+#endif /* is_zero_pfn */
+#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)
+static inline void *swap_kmap_atomic(struct page *page)
+{
+ return kmap_atomic(page);
+}
+static inline void swap_kunmap_atomic(void *kvaddr)
+{
+ kunmap_atomic(kvaddr);
+}
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) */
+static inline void *swap_kmap_atomic(struct page *page)
+{
+ return kmap_atomic(page, KM_USER0);
+}
+
+static inline void swap_kunmap_atomic(void *kvaddr)
+{
+ kunmap_atomic(kvaddr, KM_USER0);
+}
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+DECLARE_MOD_FUNC_DEP(do_mmap, unsigned long, struct file *file,
+ unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, vm_flags_t vm_flags,
+ unsigned long pgoff, unsigned long *populate);
+DECLARE_MOD_DEP_WRAPPER(swap_do_mmap,
+ unsigned long,
+ struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+ unsigned long flags, vm_flags_t vm_flags,
+ unsigned long pgoff, unsigned long *populate)
+IMP_MOD_DEP_WRAPPER(do_mmap, file, addr, len,
+ prot, flags, vm_flags, pgoff, populate)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+DECLARE_MOD_FUNC_DEP(do_mmap_pgoff, unsigned long, struct file *file,
+ unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff,
+ unsigned long *populate);
+DECLARE_MOD_DEP_WRAPPER(swap_do_mmap_pgoff,
+ unsigned long,
+ struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff,
+ unsigned long *populate)
+IMP_MOD_DEP_WRAPPER(do_mmap_pgoff, file, addr, len,
+ prot, flags, pgoff, populate)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+DECLARE_MOD_FUNC_DEP(do_mmap_pgoff, unsigned long, struct file *file,
+ unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff);
+DECLARE_MOD_DEP_WRAPPER(swap_do_mmap_pgoff,
+ unsigned long,
+ struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff)
+IMP_MOD_DEP_WRAPPER(do_mmap_pgoff, file, addr, len, prot, flags, pgoff)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+EXPORT_SYMBOL_GPL(swap_do_mmap);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+EXPORT_SYMBOL_GPL(swap_do_mmap_pgoff);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
+
+/* copy_to_user_page */
+#ifndef copy_to_user_page
+static DECLARE_MOD_FUNC_DEP(copy_to_user_page, void, struct vm_area_struct *vma,
+ struct page *page, unsigned long uaddr, void *dst,
+ const void *src, unsigned long len);
+DECLARE_MOD_DEP_WRAPPER(swap_copy_to_user_page,
+ void,
+ struct vm_area_struct *vma, struct page *page,
+ unsigned long uaddr, void *dst, const void *src,
+ unsigned long len)
+IMP_MOD_DEP_WRAPPER(copy_to_user_page, vma, page, uaddr, dst, src, len)
+#else /* copy_to_user_page */
+#define swap_copy_to_user_page copy_to_user_page
+#endif /* copy_to_user_page */
+
+
+static DECLARE_MOD_FUNC_DEP(find_extend_vma, struct vm_area_struct *,
+ struct mm_struct *mm, unsigned long addr);
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+static DECLARE_MOD_FUNC_DEP(handle_mm_fault, int, struct mm_struct *mm,
+ struct vm_area_struct *vma, unsigned long address,
+ int write_access);
+#endif
+#else
+static DECLARE_MOD_FUNC_DEP(handle_mm_fault, int, struct mm_struct *mm,
+ struct vm_area_struct *vma, unsigned long address,
+ unsigned int flags);
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) */
+
+#ifdef __HAVE_ARCH_GATE_AREA
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+static DECLARE_MOD_FUNC_DEP(get_gate_vma, struct vm_area_struct *,
+ struct mm_struct *mm);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+static DECLARE_MOD_FUNC_DEP(get_gate_vma, struct vm_area_struct *,
+ struct task_struct *tsk);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+DECLARE_MOD_FUNC_DEP(in_gate_area, int, struct mm_struct *mm,
+ unsigned long addr);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+DECLARE_MOD_FUNC_DEP(in_gate_area, int, struct task_struct *task,
+ unsigned long addr);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+static DECLARE_MOD_FUNC_DEP(in_gate_area_no_mm, int, unsigned long addr);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+static DECLARE_MOD_FUNC_DEP(in_gate_area_no_task, int, unsigned long addr);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#endif /* __HAVE_ARCH_GATE_AREA */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+static DECLARE_MOD_FUNC_DEP(follow_page_mask, \
+ struct page *, struct vm_area_struct *vma, \
+ unsigned long address, unsigned int foll_flags, \
+ unsigned int *page_mask);
+DECLARE_MOD_DEP_WRAPPER(swap_follow_page_mask,
+ struct page *,
+ struct vm_area_struct *vma, unsigned long address,
+ unsigned int foll_flags, unsigned int *page_mask)
+IMP_MOD_DEP_WRAPPER(follow_page_mask, vma, address, foll_flags, page_mask)
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+static DECLARE_MOD_FUNC_DEP(follow_page, \
+ struct page *, struct vm_area_struct *vma, \
+ unsigned long address, unsigned int foll_flags);
+DECLARE_MOD_DEP_WRAPPER(swap_follow_page,
+ struct page *,
+ struct vm_area_struct *vma, unsigned long address,
+ unsigned int foll_flags)
+IMP_MOD_DEP_WRAPPER(follow_page, vma, address, foll_flags)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+
+static DECLARE_MOD_FUNC_DEP(__flush_anon_page, \
+ void, struct vm_area_struct *vma, struct page *page, \
+ unsigned long vmaddr);
+static DECLARE_MOD_FUNC_DEP(vm_normal_page, \
+ struct page *, struct vm_area_struct *vma, \
+ unsigned long addr, pte_t pte);
+
+
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
+static DECLARE_MOD_FUNC_DEP(put_task_struct, \
+ void, struct task_struct *tsk);
+#else
+static DECLARE_MOD_FUNC_DEP(put_task_struct, \
+ void, struct rcu_head *rhp);
+#endif
+
+DECLARE_MOD_DEP_WRAPPER(swap_find_extend_vma,
+ struct vm_area_struct *,
+ struct mm_struct *mm, unsigned long addr)
+IMP_MOD_DEP_WRAPPER(find_extend_vma, mm, addr)
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+DECLARE_MOD_DEP_WRAPPER(swap_handle_mm_fault,
+ int,
+ struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long address, int write_access)
+{
+ if (in_atomic())
+ return VM_FAULT_ERROR | VM_FAULT_OOM;
+
+ IMP_MOD_DEP_WRAPPER(handle_mm_fault, mm, vma, address, write_access)
+}
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18) */
+#else /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) */
+DECLARE_MOD_DEP_WRAPPER(swap_handle_mm_fault,
+ int,
+ struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long address, unsigned int flags)
+{
+ if (in_atomic())
+ return VM_FAULT_ERROR | VM_FAULT_OOM;
+
+ IMP_MOD_DEP_WRAPPER(handle_mm_fault, mm, vma, address, flags)
+}
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) */
+
+struct vm_area_struct *swap_get_gate_vma(struct mm_struct *mm)
+{
+#ifdef __HAVE_ARCH_GATE_AREA
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+IMP_MOD_DEP_WRAPPER(get_gate_vma, mm)
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+IMP_MOD_DEP_WRAPPER(get_gate_vma, tsk)
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#else /* __HAVE_ARCH_GATE_AREA */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ return get_gate_vma(mm);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+ return get_gate_vma(tsk);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#endif /* __HAVE_ARCH_GATE_AREA */
+}
+
+#ifdef CONFIG_HUGETLB_PAGE
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+DECLARE_MOD_FUNC_DEP(follow_hugetlb_page, \
+ int, \
+ struct mm_struct *mm, struct vm_area_struct *vma, \
+ struct page **pages, struct vm_area_struct **vmas, \
+ unsigned long *position, int *length, int i, \
+ unsigned int flags);
+DECLARE_MOD_DEP_WRAPPER(swap_follow_hugetlb_page,
+ int,
+ struct mm_struct *mm, struct vm_area_struct *vma,
+ struct page **pages, struct vm_area_struct **vmas,
+ unsigned long *position, int *length, int i,
+ unsigned int flags)
+IMP_MOD_DEP_WRAPPER(follow_hugetlb_page, \
+ mm, vma, pages, vmas, position, length, i, flags)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) */
+DECLARE_MOD_FUNC_DEP(follow_hugetlb_page, \
+ long, \
+ struct mm_struct *mm, struct vm_area_struct *vma, \
+ struct page **pages, struct vm_area_struct **vmas, \
+ unsigned long *position, unsigned long *nr_pages, \
+ long i, unsigned int flags);
+DECLARE_MOD_DEP_WRAPPER(swap_follow_hugetlb_page,
+ long,
+ struct mm_struct *mm, struct vm_area_struct *vma,
+ struct page **pages, struct vm_area_struct **vmas,
+ unsigned long *position, unsigned long *nr_pages,
+ long i, unsigned int flags)
+IMP_MOD_DEP_WRAPPER(follow_hugetlb_page, \
+ mm, vma, pages, vmas, position, nr_pages, i, flags)
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) */
+
+#else /* CONFIG_HUGETLB_PAGE */
+#define swap_follow_hugetlb_page follow_hugetlb_page
+#endif /* CONFIG_HUGETLB_PAGE */
+
+static inline int swap_in_gate_area(struct task_struct *task,
+ unsigned long addr)
+{
+#ifdef __HAVE_ARCH_GATE_AREA
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ struct mm_struct *mm;
+
+ if (task == NULL)
+ return 0;
+
+ mm = task->mm;
+ IMP_MOD_DEP_WRAPPER(in_gate_area, mm, addr)
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+ IMP_MOD_DEP_WRAPPER(in_gate_area, task, addr)
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#else /*__HAVE_ARCH_GATE_AREA */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ struct mm_struct *mm;
+
+ if (task == NULL)
+ return 0;
+
+ mm = task->mm;
+ return in_gate_area(mm, addr);
+#else
+ return in_gate_area(task, addr);
+#endif
+#endif/*__HAVE_ARCH_GATE_AREA */
+}
+
+
+#ifdef __HAVE_ARCH_GATE_AREA
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+DECLARE_MOD_DEP_WRAPPER(swap_in_gate_area_no_mm, int, unsigned long addr)
+IMP_MOD_DEP_WRAPPER(in_gate_area_no_mm, addr)
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+DECLARE_MOD_DEP_WRAPPER(swap_in_gate_area_no_task, int, unsigned long addr)
+IMP_MOD_DEP_WRAPPER(in_gate_area_no_task, addr)
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#endif /* __HAVE_ARCH_GATE_AREA */
+
+static inline int swap_in_gate_area_no_xxx(unsigned long addr)
+{
+#ifdef __HAVE_ARCH_GATE_AREA
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ return swap_in_gate_area_no_mm(addr);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+ return swap_in_gate_area_no_task(addr);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#else /* __HAVE_ARCH_GATE_AREA */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ return in_gate_area_no_mm(addr);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+ return in_gate_area_no_task(addr);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#endif /* __HAVE_ARCH_GATE_AREA */
+}
+
+DECLARE_MOD_DEP_WRAPPER(swap__flush_anon_page,
+ void,
+ struct vm_area_struct *vma, struct page *page,
+ unsigned long vmaddr)
+IMP_MOD_DEP_WRAPPER(__flush_anon_page, vma, page, vmaddr)
+
+static inline void swap_flush_anon_page(struct vm_area_struct *vma,
+ struct page *page,
+ unsigned long vmaddr)
+{
+#if defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM)
+ if (PageAnon(page))
+ swap__flush_anon_page(vma, page, vmaddr);
+#else /* defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM) */
+ flush_anon_page(vma, page, vmaddr);
+#endif /* defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM) */
+}
+
+DECLARE_MOD_DEP_WRAPPER(swap_vm_normal_page,
+ struct page *,
+ struct vm_area_struct *vma, unsigned long addr,
+ pte_t pte)
+IMP_MOD_DEP_WRAPPER(vm_normal_page, vma, addr, pte)
+
+
+
+/**
+ * @brief Initializes module dependencies.
+ *
+ * @return 0.
+ */
+int init_module_dependencies(void)
+{
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+ INIT_MOD_DEP_VAR(handle_mm_fault, handle_mm_fault);
+#endif
+
+#ifndef copy_to_user_page
+ INIT_MOD_DEP_VAR(copy_to_user_page, copy_to_user_page);
+#endif /* copy_to_user_page */
+
+ INIT_MOD_DEP_VAR(find_extend_vma, find_extend_vma);
+
+#ifdef CONFIG_HUGETLB_PAGE
+ INIT_MOD_DEP_VAR(follow_hugetlb_page, follow_hugetlb_page);
+#endif
+
+#ifdef __HAVE_ARCH_GATE_AREA
+ INIT_MOD_DEP_VAR(in_gate_area, in_gate_area);
+ INIT_MOD_DEP_VAR(get_gate_vma, get_gate_vma);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ INIT_MOD_DEP_VAR(in_gate_area_no_mm, in_gate_area_no_mm);
+#else /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
+ INIT_MOD_DEP_VAR(in_gate_area_no_task, in_gate_area_no_task);
+#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ INIT_MOD_DEP_VAR(follow_page_mask, follow_page_mask);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+ INIT_MOD_DEP_VAR(follow_page, follow_page);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+
+#ifndef is_zero_pfn
+ swap_zero_pfn = page_to_pfn(ZERO_PAGE(0));
+#endif /* is_zero_pfn */
+#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) */
+
+#if defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM)
+ INIT_MOD_DEP_VAR(__flush_anon_page, __flush_anon_page);
+#endif /* defined(ARCH_HAS_FLUSH_ANON_PAGE) && defined(CONFIG_ARM) */
+
+ INIT_MOD_DEP_VAR(vm_normal_page, vm_normal_page);
+
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
+# if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11))
+ INIT_MOD_DEP_VAR(put_task_struct, put_task_struct);
+# else
+ INIT_MOD_DEP_VAR(put_task_struct, __put_task_struct);
+# endif
+#else /*2.6.16 */
+ INIT_MOD_DEP_VAR(put_task_struct, __put_task_struct_cb);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+ INIT_MOD_DEP_VAR(do_mmap, do_mmap);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ INIT_MOD_DEP_VAR(do_mmap_pgoff, do_mmap_pgoff);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
+
+ return 0;
+}
+
+
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+
+static int do_access_process_vm(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long addr, void *buf, int len,
+ int write)
+{
+ struct vm_area_struct *vma;
+ void *old_buf = buf;
+
+ while (len) {
+ int bytes, ret, offset;
+ void *maddr;
+ struct page *page = NULL;
+
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+ ret = get_user_pages(tsk, mm, addr, 1, write, 1, &page, &vma);
+# else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */
+ ret = get_user_pages_remote(tsk, mm, addr, 1,
+ FOLL_WRITE | FOLL_FORCE,
+ &page, &vma);
+# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */
+
+ if (ret <= 0) {
+#ifndef CONFIG_HAVE_IOREMAP_PROT
+ break;
+#else
+ /*
+ * Check if this is a VM_IO | VM_PFNMAP VMA, which
+ * we can access using slightly different code.
+ */
+ vma = find_vma(mm, addr);
+ if (!vma || vma->vm_start > addr)
+ break;
+ if (vma->vm_ops && vma->vm_ops->access)
+ ret = vma->vm_ops->access(vma, addr, buf, len,
+ write);
+ if (ret <= 0)
+ break;
+ bytes = ret;
+#endif
+ } else {
+ bytes = len;
+ offset = addr & (PAGE_SIZE-1);
+ if (bytes > PAGE_SIZE-offset)
+ bytes = PAGE_SIZE-offset;
+
+ maddr = kmap(page);
+ if (write) {
+ swap_copy_to_user_page(vma, page, addr,
+ maddr + offset,
+ buf, bytes);
+ set_page_dirty_lock(page);
+ } else {
+ copy_from_user_page(vma, page, addr,
+ buf, maddr + offset, bytes);
+ }
+ kunmap(page);
+ put_page(page);
+ }
+ len -= bytes;
+ buf += bytes;
+ addr += bytes;
+ }
+
+ return buf - old_buf;
+}
+
+int swap_access_process_vm(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, int write)
+{
+ int ret;
+ struct mm_struct *mm;
+
+ mm = get_task_mm(tsk);
+ if (!mm)
+ return 0;
+
+ ret = do_access_process_vm(tsk, mm, addr, buf, len, write);
+ mmput(mm);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_access_process_vm);
+
+#else /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) /* FIXME: must be < 32 */
+#define GUP_FLAGS_WRITE 0x1
+#define GUP_FLAGS_FORCE 0x2
+#define GUP_FLAGS_IGNORE_VMA_PERMISSIONS 0x4
+#define GUP_FLAGS_IGNORE_SIGKILL 0x8
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+static inline int use_zero_page(struct vm_area_struct *vma)
+{
+ /*
+ * We don't want to optimize FOLL_ANON for make_pages_present()
+ * when it tries to page in a VM_LOCKED region. As to VM_SHARED,
+ * we want to get the page from the page tables to make sure
+ * that we serialize and update with any other user of that
+ * mapping.
+ */
+ if (vma->vm_flags & (VM_LOCKED | VM_SHARED))
+ return 0;
+ /*
+ * And if we have a fault routine, it's not an anonymous region.
+ */
+ return !vma->vm_ops || !vma->vm_ops->fault;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+
+#ifdef __HAVE_COLOR_ZERO_PAGE
+
+static inline int swap_is_zero_pfn(unsigned long pfn)
+{
+ unsigned long offset_from_zero_pfn = pfn - swap_zero_pfn;
+ return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT);
+}
+
+#else /* __HAVE_COLOR_ZERO_PAGE */
+
+static inline int swap_is_zero_pfn(unsigned long pfn)
+{
+ return pfn == swap_zero_pfn;
+}
+#endif /* __HAVE_COLOR_ZERO_PAGE */
+
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) */
+
+static inline int swap_is_zero_pfn(unsigned long pfn)
+{
+#ifndef is_zero_pfn
+ return pfn == swap_zero_pfn;
+#else /* is_zero_pfn */
+ return is_zero_pfn(pfn);
+#endif /* is_zero_pfn */
+}
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) */
+
+static inline int stack_guard_page(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ return stack_guard_page_start(vma, addr) ||
+ stack_guard_page_end(vma, addr+PAGE_SIZE);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+
+/**
+ * @brief Gets user pages uprobe.
+ *
+ * @param tsk Pointer to the task_struct.
+ * @param mm Pointer to the mm_struct.
+ * @param start Starting address.
+ * @param nr_pages Pages number.
+ * @param gup_flags Flags.
+ * @param pages Pointer to the array of pointers to the target page structs.
+ * @param vmas Pointer to the array of pointers to the target vm_area_struct.
+ * @param nonblocking Pointer to int.
+ * @return negative error code on error, positive result otherwise.
+ */
+long __get_user_pages_uprobe(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages,
+ struct vm_area_struct **vmas, int *nonblocking)
+{
+ long i;
+ unsigned long vm_flags;
+ unsigned int page_mask;
+
+ if (!nr_pages)
+ return 0;
+
+ VM_BUG_ON(!!pages != !!(gup_flags & FOLL_GET));
+
+ /*
+ * Require read or write permissions.
+ * If FOLL_FORCE is set, we only require the "MAY" flags.
+ */
+ vm_flags = (gup_flags & FOLL_WRITE) ?
+ (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
+ vm_flags &= (gup_flags & FOLL_FORCE) ?
+ (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
+
+ /*
+ * If FOLL_FORCE and FOLL_NUMA are both set, handle_mm_fault
+ * would be called on PROT_NONE ranges. We must never invoke
+ * handle_mm_fault on PROT_NONE ranges or the NUMA hinting
+ * page faults would unprotect the PROT_NONE ranges if
+ * _PAGE_NUMA and _PAGE_PROTNONE are sharing the same pte/pmd
+ * bitflag. So to avoid that, don't set FOLL_NUMA if
+ * FOLL_FORCE is set.
+ */
+ if (!(gup_flags & FOLL_FORCE))
+ gup_flags |= FOLL_NUMA;
+
+ i = 0;
+
+ do {
+ struct vm_area_struct *vma;
+
+ vma = swap_find_extend_vma(mm, start);
+ if (!vma && swap_in_gate_area(tsk, start)) {
+ unsigned long pg = start & PAGE_MASK;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /* user gate pages are read-only */
+ if (gup_flags & FOLL_WRITE)
+ return i ? : -EFAULT;
+ if (pg > TASK_SIZE)
+ pgd = pgd_offset_k(pg);
+ else
+ pgd = pgd_offset_gate(mm, pg);
+ BUG_ON(pgd_none(*pgd));
+ pud = pud_offset(pgd, pg);
+ BUG_ON(pud_none(*pud));
+ pmd = pmd_offset(pud, pg);
+ if (pmd_none(*pmd))
+ return i ? : -EFAULT;
+ VM_BUG_ON(pmd_trans_huge(*pmd));
+ pte = pte_offset_map(pmd, pg);
+ if (pte_none(*pte)) {
+ pte_unmap(pte);
+ return i ? : -EFAULT;
+ }
+ vma = swap_get_gate_vma(mm);
+ if (pages) {
+ struct page *page;
+
+ page = swap_vm_normal_page(vma, start, *pte);
+ if (!page) {
+ if (!(gup_flags & FOLL_DUMP) &&
+ swap_is_zero_pfn(pte_pfn(*pte)))
+ page = pte_page(*pte);
+ else {
+ pte_unmap(pte);
+ return i ? : -EFAULT;
+ }
+ }
+ pages[i] = page;
+ get_page(page);
+ }
+ pte_unmap(pte);
+ page_mask = 0;
+ goto next_page;
+ }
+
+ if (!vma ||
+ (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
+ !(vm_flags & vma->vm_flags))
+ return i ? : -EFAULT;
+
+ if (is_vm_hugetlb_page(vma)) {
+ i = swap_follow_hugetlb_page(mm, vma, pages, vmas,
+ &start, &nr_pages, i, gup_flags);
+ continue;
+ }
+
+ do {
+ struct page *page;
+ unsigned int foll_flags = gup_flags;
+ unsigned int page_increm;
+
+ /*
+ * If we have a pending SIGKILL, don't keep faulting
+ * pages and potentially allocating memory.
+ */
+ if (unlikely(fatal_signal_pending(current)))
+ return i ? i : -ERESTARTSYS;
+
+ /* cond_resched(); */
+ while (!(page = swap_follow_page_mask(vma, start,
+ foll_flags, &page_mask))) {
+ int ret;
+ unsigned int fault_flags = 0;
+
+ /* For mlock, just skip the stack guard page. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
+ if (foll_flags & FOLL_POPULATE) {
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) */
+ if (foll_flags & FOLL_MLOCK) {
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) */
+ if (stack_guard_page(vma, start))
+ goto next_page;
+ }
+ if (foll_flags & FOLL_WRITE)
+ fault_flags |= FAULT_FLAG_WRITE;
+ if (nonblocking)
+ fault_flags |= FAULT_FLAG_ALLOW_RETRY;
+ if (foll_flags & FOLL_NOWAIT)
+ fault_flags |=
+ (FAULT_FLAG_ALLOW_RETRY |
+ FAULT_FLAG_RETRY_NOWAIT);
+
+ ret = swap_handle_mm_fault(mm, vma, start,
+ fault_flags);
+
+ if (ret & VM_FAULT_ERROR) {
+ if (ret & VM_FAULT_OOM)
+ return i ? i : -ENOMEM;
+ if (ret & (VM_FAULT_HWPOISON |
+ VM_FAULT_HWPOISON_LARGE)) {
+ if (i)
+ return i;
+ else if (gup_flags &
+ FOLL_HWPOISON)
+ return -EHWPOISON;
+ else
+ return -EFAULT;
+ }
+ if (ret & VM_FAULT_SIGBUS)
+ return i ? i : -EFAULT;
+ BUG();
+ }
+
+ if (tsk) {
+ if (ret & VM_FAULT_MAJOR)
+ tsk->maj_flt++;
+ else
+ tsk->min_flt++;
+ }
+
+ if (ret & VM_FAULT_RETRY) {
+ if (nonblocking)
+ *nonblocking = 0;
+ return i;
+ }
+
+ /*
+ * The VM_FAULT_WRITE bit tells us that
+ * do_wp_page has broken COW when necessary,
+ * even if maybe_mkwrite decided not to set
+ * pte_write. We can thus safely do subsequent
+ * page lookups as if they were reads. But only
+ * do so when looping for pte_write is futile:
+ * in some cases userspace may also be wanting
+ * to write to the gotten user page, which a
+ * read fault here might prevent (a readonly
+ * page might get reCOWed by userspace write).
+ */
+ if ((ret & VM_FAULT_WRITE) &&
+ !(vma->vm_flags & VM_WRITE))
+ foll_flags &= ~FOLL_WRITE;
+
+ /* cond_resched(); */
+ }
+ if (IS_ERR(page))
+ return i ? i : PTR_ERR(page);
+ if (pages) {
+ pages[i] = page;
+
+ swap_flush_anon_page(vma, page, start);
+ flush_dcache_page(page);
+ page_mask = 0;
+ }
+next_page:
+ if (vmas) {
+ vmas[i] = vma;
+ page_mask = 0;
+ }
+ page_increm = 1 + (~(start >> PAGE_SHIFT) & page_mask);
+ if (page_increm > nr_pages)
+ page_increm = nr_pages;
+ i += page_increm;
+ start += page_increm * PAGE_SIZE;
+ nr_pages -= page_increm;
+ } while (nr_pages && start < vma->vm_end);
+ } while (nr_pages);
+ return i;
+}
+
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+
+static int __get_user_pages_uprobe(struct task_struct *tsk,
+ struct mm_struct *mm, unsigned long start,
+ int nr_pages, unsigned int gup_flags,
+ struct page **pages,
+ struct vm_area_struct **vmas,
+ int *nonblocking)
+{
+ int i;
+ unsigned long vm_flags;
+
+ if (nr_pages <= 0)
+ return 0;
+
+ VM_BUG_ON(!!pages != !!(gup_flags & FOLL_GET));
+
+ /*
+ * Require read or write permissions.
+ * If FOLL_FORCE is set, we only require the "MAY" flags.
+ */
+ vm_flags = (gup_flags & FOLL_WRITE) ?
+ (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
+ vm_flags &= (gup_flags & FOLL_FORCE) ?
+ (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
+ i = 0;
+
+ do {
+ struct vm_area_struct *vma;
+
+ vma = swap_find_extend_vma(mm, start);
+ if (!vma && swap_in_gate_area_no_xxx(start)) {
+ unsigned long pg = start & PAGE_MASK;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /* user gate pages are read-only */
+ if (gup_flags & FOLL_WRITE)
+ return i ? : -EFAULT;
+ if (pg > TASK_SIZE)
+ pgd = pgd_offset_k(pg);
+ else
+ pgd = pgd_offset_gate(mm, pg);
+ BUG_ON(pgd_none(*pgd));
+ pud = pud_offset(pgd, pg);
+ BUG_ON(pud_none(*pud));
+ pmd = pmd_offset(pud, pg);
+ if (pmd_none(*pmd))
+ return i ? : -EFAULT;
+ VM_BUG_ON(pmd_trans_huge(*pmd));
+ pte = pte_offset_map(pmd, pg);
+ if (pte_none(*pte)) {
+ pte_unmap(pte);
+ return i ? : -EFAULT;
+ }
+ vma = swap_get_gate_vma(mm);
+ if (pages) {
+ struct page *page;
+
+ page = swap_vm_normal_page(vma, start, *pte);
+ if (!page) {
+ if (!(gup_flags & FOLL_DUMP) &&
+ swap_is_zero_pfn(pte_pfn(*pte)))
+ page = pte_page(*pte);
+ else {
+ pte_unmap(pte);
+ return i ? : -EFAULT;
+ }
+ }
+ pages[i] = page;
+ get_page(page);
+ }
+ pte_unmap(pte);
+ goto next_page;
+ }
+
+ if (!vma ||
+ (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
+ !(vm_flags & vma->vm_flags)) {
+ return i ? : -EFAULT;
+ }
+
+ if (is_vm_hugetlb_page(vma)) {
+ i = swap_follow_hugetlb_page(mm, vma, pages, vmas,
+ &start, &nr_pages, i, gup_flags);
+ continue;
+ }
+
+ do {
+ struct page *page;
+ unsigned int foll_flags = gup_flags;
+
+ /*
+ * If we have a pending SIGKILL, don't keep faulting
+ * pages and potentially allocating memory.
+ */
+ if (unlikely(fatal_signal_pending(current)))
+ return i ? i : -ERESTARTSYS;
+
+ /* cond_resched(); */
+ while (!(page = swap_follow_page(vma, start,
+ foll_flags))) {
+ int ret;
+ unsigned int fault_flags = 0;
+
+ /* For mlock, just skip the stack guard page. */
+ if (foll_flags & FOLL_MLOCK) {
+ if (stack_guard_page(vma, start))
+ goto next_page;
+ }
+ if (foll_flags & FOLL_WRITE)
+ fault_flags |= FAULT_FLAG_WRITE;
+ if (nonblocking)
+ fault_flags |= FAULT_FLAG_ALLOW_RETRY;
+ if (foll_flags & FOLL_NOWAIT)
+ fault_flags |=
+ (FAULT_FLAG_ALLOW_RETRY |
+ FAULT_FLAG_RETRY_NOWAIT);
+
+ ret = swap_handle_mm_fault(mm, vma, start,
+ fault_flags);
+
+ if (ret & VM_FAULT_ERROR) {
+ if (ret & VM_FAULT_OOM)
+ return i ? i : -ENOMEM;
+ if (ret & (VM_FAULT_HWPOISON |
+ VM_FAULT_HWPOISON_LARGE)) {
+ if (i)
+ return i;
+ else if (gup_flags &
+ FOLL_HWPOISON)
+ return -EHWPOISON;
+ else
+ return -EFAULT;
+ }
+ if (ret & VM_FAULT_SIGBUS)
+ return i ? i : -EFAULT;
+ BUG();
+ }
+
+ if (tsk) {
+ if (ret & VM_FAULT_MAJOR)
+ tsk->maj_flt++;
+ else
+ tsk->min_flt++;
+ }
+
+ if (ret & VM_FAULT_RETRY) {
+ if (nonblocking)
+ *nonblocking = 0;
+ return i;
+ }
+
+ /*
+ * The VM_FAULT_WRITE bit tells us that
+ * do_wp_page has broken COW when necessary,
+ * even if maybe_mkwrite decided not to set
+ * pte_write. We can thus safely do subsequent
+ * page lookups as if they were reads. But only
+ * do so when looping for pte_write is futile:
+ * in some cases userspace may also be wanting
+ * to write to the gotten user page, which a
+ * read fault here might prevent (a readonly
+ * page might get reCOWed by userspace write).
+ */
+ if ((ret & VM_FAULT_WRITE) &&
+ !(vma->vm_flags & VM_WRITE))
+ foll_flags &= ~FOLL_WRITE;
+
+ /* cond_resched(); */
+ }
+ if (IS_ERR(page))
+ return i ? i : PTR_ERR(page);
+ if (pages) {
+ pages[i] = page;
+
+ swap_flush_anon_page(vma, page, start);
+ flush_dcache_page(page);
+ }
+next_page:
+ if (vmas)
+ vmas[i] = vma;
+ i++;
+ start += PAGE_SIZE;
+ nr_pages--;
+ } while (nr_pages && start < vma->vm_end);
+ } while (nr_pages);
+
+ return i;
+}
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+
+static int __get_user_pages_uprobe(struct task_struct *tsk,
+ struct mm_struct *mm,
+ unsigned long start, int len, int flags,
+ struct page **pages,
+ struct vm_area_struct **vmas)
+{
+ int i;
+ unsigned int vm_flags = 0;
+ int write = !!(flags & GUP_FLAGS_WRITE);
+ int force = !!(flags & GUP_FLAGS_FORCE);
+ int ignore = !!(flags & GUP_FLAGS_IGNORE_VMA_PERMISSIONS);
+
+ if (len <= 0)
+ return 0;
+ /*
+ * Require read or write permissions.
+ * If 'force' is set, we only require the "MAY" flags.
+ */
+ vm_flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
+ vm_flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
+ i = 0;
+
+ do {
+ struct vm_area_struct *vma;
+ unsigned int foll_flags;
+
+ vma = find_vma(mm, start);
+ if (!vma && swap_in_gate_area(tsk, start)) {
+ unsigned long pg = start & PAGE_MASK;
+ struct vm_area_struct *gate_vma =
+ swap_get_gate_vma(tsk);
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /* user gate pages are read-only */
+ if (!ignore && write)
+ return i ? : -EFAULT;
+ if (pg > TASK_SIZE)
+ pgd = pgd_offset_k(pg);
+ else
+ pgd = pgd_offset_gate(mm, pg);
+ BUG_ON(pgd_none(*pgd));
+ pud = pud_offset(pgd, pg);
+ BUG_ON(pud_none(*pud));
+ pmd = pmd_offset(pud, pg);
+ if (pmd_none(*pmd))
+ return i ? : -EFAULT;
+ pte = pte_offset_map(pmd, pg);
+ if (pte_none(*pte)) {
+ pte_unmap(pte);
+ return i ? : -EFAULT;
+ }
+ if (pages) {
+ struct page *page =
+ swap_vm_normal_page(gate_vma, start,
+ *pte);
+ pages[i] = page;
+ if (page)
+ get_page(page);
+ }
+ pte_unmap(pte);
+ if (vmas)
+ vmas[i] = gate_vma;
+ i++;
+ start += PAGE_SIZE;
+ len--;
+ continue;
+ }
+
+ if (!vma ||
+ (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
+ (!ignore && !(vm_flags & vma->vm_flags)))
+ return i ? : -EFAULT;
+
+ if (is_vm_hugetlb_page(vma)) {
+ i = swap_follow_hugetlb_page(mm, vma, pages, vmas,
+ &start, &len, i, write);
+ continue;
+ }
+
+ foll_flags = FOLL_TOUCH;
+ if (pages)
+ foll_flags |= FOLL_GET;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
+ if (!write && use_zero_page(vma))
+ foll_flags |= FOLL_ANON;
+#endif
+#endif
+
+ do {
+ struct page *page;
+
+ if (write)
+ foll_flags |= FOLL_WRITE;
+
+
+ /* cond_resched(); */
+
+ DBPRINTF("pages = %p vma = %p\n", pages, vma);
+ while (!(page = swap_follow_page(vma, start,
+ foll_flags))) {
+ int ret;
+ ret = swap_handle_mm_fault(mm, vma, start,
+ foll_flags & FOLL_WRITE);
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
+ if (ret & VM_FAULT_WRITE)
+ foll_flags &= ~FOLL_WRITE;
+
+ switch (ret & ~VM_FAULT_WRITE) {
+ case VM_FAULT_MINOR:
+ tsk->min_flt++;
+ break;
+ case VM_FAULT_MAJOR:
+ tsk->maj_flt++;
+ break;
+ case VM_FAULT_SIGBUS:
+ return i ? i : -EFAULT;
+ case VM_FAULT_OOM:
+ return i ? i : -ENOMEM;
+ default:
+ BUG();
+ }
+
+#else
+ if (ret & VM_FAULT_ERROR) {
+ if (ret & VM_FAULT_OOM)
+ return i ? i : -ENOMEM;
+ else if (ret & VM_FAULT_SIGBUS)
+ return i ? i : -EFAULT;
+ BUG();
+ }
+ if (ret & VM_FAULT_MAJOR)
+ tsk->maj_flt++;
+ else
+ tsk->min_flt++;
+
+ /*
+ * The VM_FAULT_WRITE bit tells us that
+ * do_wp_page has broken COW when necessary,
+ * even if maybe_mkwrite decided not to set
+ * pte_write. We can thus safely do subsequent
+ * page lookups as if they were reads. But only
+ * do so when looping for pte_write is futile:
+ * in some cases userspace may also be wanting
+ * to write to the gotten user page, which a
+ * read fault here might prevent (a readonly
+ * page might get reCOWed by userspace write).
+ */
+ if ((ret & VM_FAULT_WRITE) &&
+ !(vma->vm_flags & VM_WRITE))
+ foll_flags &= ~FOLL_WRITE;
+
+ /* cond_resched(); */
+#endif
+
+ }
+
+ if (IS_ERR(page))
+ return i ? i : PTR_ERR(page);
+ if (pages) {
+ pages[i] = page;
+
+ swap_flush_anon_page(vma, page, start);
+ flush_dcache_page(page);
+ }
+ if (vmas)
+ vmas[i] = vma;
+ i++;
+ start += PAGE_SIZE;
+ len--;
+ } while (len && start < vma->vm_end);
+ } while (len);
+ return i;
+}
+#endif
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+
+/**
+ * @brief Gets user pages uprobe.
+ *
+ * @param tsk Pointer to the task_struct.
+ * @param mm Pointer to the mm_struct.
+ * @param start Starting address.
+ * @param len Length.
+ * @param write Write flag.
+ * @param force Force flag.
+ * @param pages Pointer to the array of pointers to the target page structs.
+ * @param vmas Pointer to the array of pointers to the target vm_area_struct.
+ * @return negative error code on error, positive result otherwise.
+ */
+int get_user_pages_uprobe(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long start, int len, int write, int force,
+ struct page **pages, struct vm_area_struct **vmas)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) /* FIXME: must be >= 32! */
+ int flags = FOLL_TOUCH;
+
+ if (pages)
+ flags |= FOLL_GET;
+ if (write)
+ flags |= FOLL_WRITE;
+ if (force)
+ flags |= FOLL_FORCE;
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+ int flags = 0;
+
+ if (write)
+ flags |= GUP_FLAGS_WRITE;
+ if (force)
+ flags |= GUP_FLAGS_FORCE;
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+
+ return __get_user_pages_uprobe(tsk, mm,
+ start, len, flags,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+ pages, vmas, NULL);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+ pages, vmas);
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */
+#else
+ return get_user_pages(tsk, mm, start, len, write, force, pages, vmas);
+#endif
+}
+
+#define ACCESS_PROCESS_OPTIMIZATION 0
+
+#if ACCESS_PROCESS_OPTIMIZATION
+
+#define GET_STEP_X(LEN, STEP) (((LEN) >= (STEP)) ? (STEP) : (LEN) % (STEP))
+#define GET_STEP_4(LEN) GET_STEP_X((LEN), 4)
+
+static void read_data_current(unsigned long addr, void *buf, int len)
+{
+ int step;
+ int pos = 0;
+
+ for (step = GET_STEP_4(len); len; len -= step) {
+ switch (GET_STEP_4(len)) {
+ case 1:
+ get_user(*(u8 *)(buf + pos),
+ (unsigned long *)(addr + pos));
+ step = 1;
+ break;
+
+ case 2:
+ case 3:
+ get_user(*(u16 *)(buf + pos),
+ (unsigned long *)(addr + pos));
+ step = 2;
+ break;
+
+ case 4:
+ get_user(*(u32 *)(buf + pos),
+ (unsigned long *)(addr + pos));
+ step = 4;
+ break;
+ }
+
+ pos += step;
+ }
+}
+
+/* not working */
+static void write_data_current(unsigned long addr, void *buf, int len)
+{
+ int step;
+ int pos = 0;
+
+ for (step = GET_STEP_4(len); len; len -= step) {
+ switch (GET_STEP_4(len)) {
+ case 1:
+ put_user(*(u8 *)(buf + pos),
+ (unsigned long *)(addr + pos));
+ step = 1;
+ break;
+
+ case 2:
+ case 3:
+ put_user(*(u16 *)(buf + pos),
+ (unsigned long *)(addr + pos));
+ step = 2;
+ break;
+
+ case 4:
+ put_user(*(u32 *)(buf + pos),
+ (unsigned long *)(addr + pos));
+ step = 4;
+ break;
+ }
+
+ pos += step;
+ }
+}
+#endif
+
+/**
+ * @brief Read-write task memory.
+ *
+ * @param tsk Pointer to the target task task_struct.
+ * @param addr Address to read-write.
+ * @param buf Pointer to buffer where to put-get data.
+ * @param len Buffer length.
+ * @param write Write flag. If 0 - reading, if 1 - writing.
+ * @return Read-write size, error code on error.
+ */
+int access_process_vm_atomic(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, int write)
+{
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+ void *old_buf = buf;
+ int atomic;
+
+ if (len <= 0)
+ return -1;
+
+#if ACCESS_PROCESS_OPTIMIZATION
+ if (write == 0 && tsk == current) {
+ read_data_current(addr, buf, len);
+ return len;
+ }
+#endif
+
+ mm = tsk->mm; /* function 'get_task_mm' is to be called */
+ if (!mm)
+ return 0;
+
+ /* FIXME: danger: write memory in atomic context */
+ atomic = in_atomic();
+ WARN_ON(atomic);
+
+ /* ignore errors, just check how much was successfully transferred */
+ while (len) {
+ int bytes, ret, offset;
+ void *maddr;
+ struct page *page = NULL;
+
+ ret = get_user_pages_uprobe(tsk, mm, addr, 1,
+ write, 1, &page, &vma);
+
+ if (ret <= 0) {
+ /*
+ * Check if this is a VM_IO | VM_PFNMAP VMA, which
+ * we can access using slightly different code.
+ */
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ vma = find_vma(mm, addr);
+ if (!vma)
+ break;
+ if (vma->vm_ops && vma->vm_ops->access)
+ ret = vma->vm_ops->access(vma, addr, buf,
+ len, write);
+ if (ret <= 0)
+#endif
+ break;
+ bytes = ret;
+ } else {
+ bytes = len;
+ offset = addr & (PAGE_SIZE-1);
+ if (bytes > PAGE_SIZE-offset)
+ bytes = PAGE_SIZE-offset;
+
+ maddr = atomic ? swap_kmap_atomic(page) : kmap(page);
+
+ if (write) {
+ swap_copy_to_user_page(vma, page, addr,
+ maddr + offset,
+ buf, bytes);
+ set_page_dirty_lock(page);
+ } else {
+ copy_from_user_page(vma, page, addr,
+ buf, maddr + offset,
+ bytes);
+ }
+
+ atomic ? swap_kunmap_atomic(maddr) : kunmap(page);
+ page_cache_release(page);
+ }
+ len -= bytes;
+ buf += bytes;
+ addr += bytes;
+ }
+
+ return buf - old_buf;
+}
+EXPORT_SYMBOL_GPL(access_process_vm_atomic);
+
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+
+/**
+ * @brief Page present.
+ *
+ * @param mm Pointer to the target mm_struct.
+ * @param address Address.
+ */
+int page_present(struct mm_struct *mm, unsigned long address)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+ unsigned long pfn;
+
+ pgd = pgd_offset(mm, address);
+ if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
+ goto out;
+
+ pud = pud_offset(pgd, address);
+ if (pud_none(*pud) || unlikely(pud_bad(*pud)))
+ goto out;
+
+ pmd = pmd_offset(pud, address);
+ if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
+ goto out;
+
+ ptep = pte_offset_map(pmd, address);
+ if (pte_none(*ptep)) {
+ pte_unmap(ptep);
+ goto out;
+ }
+
+ pte = *ptep;
+ pte_unmap(ptep);
+ if (pte_present(pte)) {
+ pfn = pte_pfn(pte);
+ if (pfn_valid(pfn))
+ return 1;
+ }
+
+out:
+ return 0;
+}
+EXPORT_SYMBOL_GPL(page_present);
+
--- /dev/null
+/**
+ * @file kprobe/swap_kprobes_deps.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kprobe kernel-dependent dependencies.
+ */
+
+#ifndef _SWAP_KPROBES_DEPS_H
+#define _SWAP_KPROBES_DEPS_H
+
+#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/mempolicy.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <ksyms/ksyms.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+#define DECLARE_NODE_PTR_FOR_HLIST(var_name)
+#define swap_hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ hlist_for_each_entry_rcu(tpos, head, member)
+#define swap_hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ hlist_for_each_entry_safe(tpos, n, head, member)
+#define swap_hlist_for_each_entry(tpos, pos, head, member) \
+ hlist_for_each_entry(tpos, head, member)
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+#define DECLARE_NODE_PTR_FOR_HLIST(var_name) struct hlist_node *var_name
+#define swap_hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ hlist_for_each_entry_rcu(tpos, pos, head, member)
+#define swap_hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ hlist_for_each_entry_safe(tpos, pos, n, head, member)
+#define swap_hlist_for_each_entry(tpos, pos, head, member) \
+ hlist_for_each_entry(tpos, pos, head, member)
+
+#define list_first_entry_or_null(ptr, type, member) \
+ (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12))
+#define synchronize_sched synchronize_kernel
+#endif
+
+
+/*
+ * swap_preempt_enable_no_resched()
+ */
+#if (defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)))
+
+#ifdef CONFIG_PREEMPT_COUNT
+#define swap_preempt_enable_no_resched() \
+do { \
+ barrier(); \
+ preempt_count_dec(); \
+} while (0)
+#else /* !CONFIG_PREEMPT_COUNT */
+#define swap_preempt_enable_no_resched() barrier()
+#endif /* CONFIG_PREEMPT_COUNT */
+
+#else /* !(defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) */
+#define swap_preempt_enable_no_resched() preempt_enable_no_resched()
+#endif /* !(defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) */
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 1, 0)
+ #define task_job(task) (task->jobctl)
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 1, 0) */
+ #define task_job(task) (task->group_stop)
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 1, 0) */
+
+
+
+/* --------------------- Declaration of module dependencies ----------------- */
+
+#define DECLARE_MOD_FUNC_DEP(name, ret, ...) ret(*__ref_##name)(__VA_ARGS__)
+#define DECLARE_MOD_CB_DEP(name, ret, ...) ret(*name)(__VA_ARGS__)
+
+
+/* ---------------- Implementation of module dependencies wrappers ---------- */
+
+#define DECLARE_MOD_DEP_WRAPPER(name, ret, ...) ret name(__VA_ARGS__)
+#define IMP_MOD_DEP_WRAPPER(name, ...) \
+{ \
+ return __ref_##name(__VA_ARGS__); \
+}
+
+
+/* --------------------- Module dependencies initialization ----------------- */
+
+#define INIT_MOD_DEP_VAR(dep, name) \
+{ \
+ __ref_##dep = (void *) swap_ksyms(#name); \
+ if (!__ref_##dep) { \
+ DBPRINTF(#name " is not found! Oops. Where is it?"); \
+ return -ESRCH; \
+ } \
+}
+
+#define INIT_MOD_DEP_CB(dep, name) \
+{ \
+ dep = (void *) swap_ksyms(#name); \
+ if (!dep) { \
+ DBPRINTF(#name " is not found! Oops. Where is it?"); \
+ return -ESRCH; \
+ } \
+}
+
+
+int init_module_dependencies(void);
+
+
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+
+int swap_access_process_vm(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, int write);
+
+# define read_proc_vm_atomic(tsk, addr, buf, len) \
+ swap_access_process_vm(tsk, addr, buf, len, 0)
+# define write_proc_vm_atomic(tsk, addr, buf, len) \
+ swap_access_process_vm(tsk, addr, buf, len, 1)
+
+#else /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+
+int access_process_vm_atomic(struct task_struct *tsk, unsigned long addr,
+ void *buf, int len, int write);
+
+# define read_proc_vm_atomic(tsk, addr, buf, len) \
+ access_process_vm_atomic(tsk, addr, buf, len, 0)
+# define write_proc_vm_atomic(tsk, addr, buf, len) \
+ access_process_vm_atomic(tsk, addr, buf, len, 1)
+
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+
+int page_present(struct mm_struct *mm, unsigned long addr);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+unsigned long swap_do_mmap(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+ unsigned long flags, vm_flags_t vm_flags,
+ unsigned long pgoff, unsigned long *populate);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+unsigned long swap_do_mmap_pgoff(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff,
+ unsigned long *populate);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+unsigned long swap_do_mmap_pgoff(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0)
+#define swap_hlist_add_after(node, prev) hlist_add_behind(node, prev)
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
+#define swap_hlist_add_after(node, prev) hlist_add_after(node, prev)
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 18, 0)
+#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 18, 0) */
+
+#endif /* _SWAP_KPROBES_DEPS_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <ksyms/ksyms.h>
+#include "swap_ktd.h"
+#include "swap_td_raw.h"
+
+
+#define KTD_PREFIX "[SWAP_KTD] "
+#define kTD_JOBCTL_PREPARE (1 << 31)
+#define KTD_BIT_MAX (sizeof(unsigned long) * 8)
+
+
+struct td {
+ struct list_head list;
+ struct task_struct *task;
+
+ spinlock_t flags_lock;
+ unsigned long init_flags;
+};
+
+
+static struct td_raw td_raw;
+static LIST_HEAD(prepare_list);
+static DEFINE_RWLOCK(prepare_lock);
+static int preparing_cnt = 0;
+
+
+static DEFINE_MUTEX(mutex_ktd_nr);
+static struct ktask_data *ktd_array[KTD_BIT_MAX];
+
+
+static bool ktd_init_is(struct ktask_data *ktd, struct td *td)
+{
+ return !!(td->init_flags & (1 << ktd->nr_bit));
+}
+
+static void ktd_init(struct ktask_data *ktd, struct td *td,
+ struct task_struct *task)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&td->flags_lock, flags);
+ if (ktd_init_is(ktd, td))
+ goto unlock;
+
+ if (ktd->init)
+ ktd->init(task, swap_td_raw(&ktd->td_raw, task));
+
+ td->init_flags |= 1 << ktd->nr_bit;
+
+unlock:
+ spin_unlock_irqrestore(&td->flags_lock, flags);
+}
+
+static void ktd_exit_no_lock(struct ktask_data *ktd, struct td *td,
+ struct task_struct *task)
+{
+ if (ktd_init_is(ktd, td)) {
+ if (ktd->exit)
+ ktd->exit(task, swap_td_raw(&ktd->td_raw, task));
+
+ td->init_flags &= ~(1 << ktd->nr_bit);
+ }
+}
+
+static void ktd_exit_all(struct td *td, struct task_struct *task)
+{
+ unsigned long flags;
+ unsigned long init_flags;
+ int nr_bit = 0;
+
+ spin_lock_irqsave(&td->flags_lock, flags);
+ init_flags = td->init_flags;
+ do {
+ if (init_flags & 1)
+ ktd_exit_no_lock(ktd_array[nr_bit], td, task);
+
+ ++nr_bit;
+ init_flags >>= 1;
+ } while (init_flags);
+ td->init_flags = 0;
+ spin_unlock_irqrestore(&td->flags_lock, flags);
+}
+
+
+static bool task_prepare_is(struct task_struct *task)
+{
+ return !!(task_job(task) & kTD_JOBCTL_PREPARE);
+}
+
+static void task_prepare_set(struct task_struct *task)
+{
+ if (!(task_job(task) & kTD_JOBCTL_PREPARE))
+ task_job(task) |= kTD_JOBCTL_PREPARE;
+ else
+ WARN(1, KTD_PREFIX "already prepare");
+
+ ++preparing_cnt;
+}
+
+static void task_prepare_clear(struct task_struct *task)
+{
+ if (task_job(task) & kTD_JOBCTL_PREPARE)
+ task_job(task) &= ~kTD_JOBCTL_PREPARE;
+ else
+ WARN(1, KTD_PREFIX "is not prepare");
+
+ --preparing_cnt;
+}
+
+static struct task_struct *task_by_td(struct td *td)
+{
+ return td->task;
+}
+
+static struct td *td_by_task(struct task_struct *task)
+{
+ return (struct td *)swap_td_raw(&td_raw, task);
+}
+
+
+static void task_prepare(struct task_struct *task, struct td *td,
+ struct ktask_data *ktd)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&prepare_lock, flags);
+
+ /* skip multi-preparing task */
+ if (task_prepare_is(task))
+ goto unlock;
+
+ task_prepare_set(task);
+
+ INIT_LIST_HEAD(&td->list);
+ td->task = task;
+ spin_lock_init(&td->flags_lock);
+ td->init_flags = 0;
+
+ /* add to prepare_list */
+ list_add(&td->list, &prepare_list);
+
+unlock:
+ write_unlock_irqrestore(&prepare_lock, flags);
+}
+
+static void ktd_exit_all(struct td *td, struct task_struct *task);
+
+static void td_prepare_clear_no_lock(struct td *td, struct task_struct *task)
+{
+ if (task_prepare_is(task)) {
+ task_prepare_clear(task);
+
+ ktd_exit_all(td, task);
+
+ /* delete from prepare_list */
+ list_del(&td->list);
+ }
+}
+
+static void td_prepare_clear(struct td *td, struct task_struct *task)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&prepare_lock, flags);
+ td_prepare_clear_no_lock(td, task);
+ write_unlock_irqrestore(&prepare_lock, flags);
+}
+
+void *swap_ktd(struct ktask_data *ktd, struct task_struct *task)
+{
+ struct td *td = td_by_task(task);
+
+ if (!likely(task_prepare_is(task)))
+ task_prepare(task, td, ktd);
+
+ if (!likely(ktd_init_is(ktd, td)))
+ ktd_init(ktd, td, task);
+
+ return swap_td_raw(&ktd->td_raw, task);
+}
+EXPORT_SYMBOL_GPL(swap_ktd);
+
+
+static int ktd_nr_get_free_bit(void)
+{
+ int bit;
+
+ for (bit = 0; bit < KTD_BIT_MAX; ++bit) {
+ if (ktd_array[bit] == NULL)
+ return bit;
+ }
+
+ return -ENOMEM;
+}
+
+int swap_ktd_reg(struct ktask_data *ktd)
+{
+ int ret;
+ int free_bit;
+
+ mutex_lock(&mutex_ktd_nr);
+ free_bit = ktd_nr_get_free_bit();
+
+ if (free_bit < 0) {
+ ret = free_bit;
+ goto unlock;
+ }
+
+ ret = swap_td_raw_reg(&ktd->td_raw, ktd->size);
+ if (ret)
+ goto unlock;
+
+ ktd->nr_bit = free_bit;
+ ktd_array[free_bit] = ktd;
+unlock:
+ mutex_unlock(&mutex_ktd_nr);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_ktd_reg);
+
+void swap_ktd_unreg(struct ktask_data *ktd)
+{
+ struct td *td;
+ unsigned long flags;
+
+ /* exit all task */
+ read_lock_irqsave(&prepare_lock, flags);
+ list_for_each_entry(td, &prepare_list, list) {
+ spin_lock(&td->flags_lock);
+ ktd_exit_no_lock(ktd, td, task_by_td(td));
+ spin_unlock(&td->flags_lock);
+ }
+ read_unlock_irqrestore(&prepare_lock, flags);
+
+ mutex_lock(&mutex_ktd_nr);
+
+ ktd_array[ktd->nr_bit] = NULL;
+ swap_td_raw_unreg(&ktd->td_raw);
+
+ mutex_unlock(&mutex_ktd_nr);
+}
+EXPORT_SYMBOL_GPL(swap_ktd_unreg);
+
+
+static void do_put_task(struct task_struct *task)
+{
+ if (task_prepare_is(task))
+ td_prepare_clear(td_by_task(task), task);
+}
+
+#ifdef CONFIG_SWAP_HOOK_TASKDATA
+
+#include <swap/hook_taskdata.h>
+
+static struct hook_taskdata hook_taskdata = {
+ .owner = THIS_MODULE,
+ .put_task = do_put_task,
+};
+
+static int taskdata_init(void)
+{
+ return hook_taskdata_reg(&hook_taskdata);
+}
+
+static void taskdata_uninit(void)
+{
+ hook_taskdata_unreg(&hook_taskdata);
+}
+
+void swap_ktd_put_task(struct task_struct *task)
+{
+}
+
+#else /* CONFIG_SWAP_HOOK_TASKDATA */
+
+static int taskdata_init(void)
+{
+ return 0;
+}
+
+static void taskdata_uninit(void)
+{
+}
+
+void swap_ktd_put_task(struct task_struct *task)
+{
+ do_put_task(task);
+}
+
+#endif /* CONFIG_SWAP_HOOK_TASKDATA */
+
+int swap_ktd_init(void)
+{
+ int ret;
+
+ WARN(preparing_cnt, KTD_PREFIX "preparing_cnt=%d", preparing_cnt);
+
+ preparing_cnt = 0;
+
+ ret = swap_td_raw_reg(&td_raw, sizeof(struct td));
+ if (ret) {
+ pr_err(KTD_PREFIX "registration failed, ret=%d", ret);
+ return ret;
+ }
+
+ ret = taskdata_init();
+ if (ret) {
+ swap_td_raw_unreg(&td_raw);
+ pr_err(KTD_PREFIX "failed to initialize, ret=%d\n", ret);
+ }
+
+ return ret;
+}
+
+void swap_ktd_uninit_top(void)
+{
+ struct td *td;
+ unsigned long flags;
+
+ /* get injected tasks */
+ write_lock_irqsave(&prepare_lock, flags);
+ list_for_each_entry(td, &prepare_list, list) {
+ get_task_struct(task_by_td(td));
+ }
+ write_unlock_irqrestore(&prepare_lock, flags);
+}
+
+void swap_ktd_uninit_bottom(void)
+{
+ struct td *td, *n;
+ unsigned long flags;
+
+ /* remove td injection from tasks and put tasks */
+ write_lock_irqsave(&prepare_lock, flags);
+ list_for_each_entry_safe(td, n, &prepare_list, list) {
+ struct task_struct *task = task_by_td(td);
+
+ td_prepare_clear_no_lock(td, task);
+ put_task_struct(task);
+ }
+ write_unlock_irqrestore(&prepare_lock, flags);
+
+ taskdata_uninit();
+ swap_td_raw_unreg(&td_raw);
+
+ WARN(preparing_cnt, KTD_PREFIX "preparing_cnt=%d", preparing_cnt);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_TD_H
+#define _SWAP_TD_H
+
+
+#include <linux/list.h>
+#include "swap_td_raw.h"
+
+struct task_struct;
+
+struct ktask_data {
+ int nr_bit;
+ struct td_raw td_raw;
+
+ /* init() and exit() may be called in atomic context */
+ void (*init)(struct task_struct *, void *);
+ void (*exit)(struct task_struct *, void *);
+ unsigned long size;
+};
+
+
+int swap_ktd_reg(struct ktask_data *ktd);
+void swap_ktd_unreg(struct ktask_data *ktd);
+
+void *swap_ktd(struct ktask_data *ktd, struct task_struct *task);
+
+int swap_ktd_init(void);
+void swap_ktd_uninit_top(void);
+void swap_ktd_uninit_bottom(void);
+void swap_ktd_put_task(struct task_struct *task);
+
+
+#endif /* _SWAP_TD_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <kprobe/swap_ktd.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <master/swap_initializer.h>
+
+
+static void ktd_uninit(void)
+{
+ swap_ktd_uninit_top();
+ swap_ktd_uninit_bottom();
+}
+
+SWAP_LIGHT_INIT_MODULE(init_module_dependencies, swap_ktd_init, ktd_uninit, NULL, NULL);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/**
+ * kprobe/swap_slots.c
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ * Copyright (C) Samsung Electronics, 2006-2012
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP slots implementation.
+ */
+
+
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "swap_slots.h"
+#include "swap_kprobes_deps.h"
+
+
+/**
+ * @struct chunk
+ * @brief Chunk of memory for trampolines.
+ * @var chunk::data
+ * Chunk data.
+ * @var chunk::first_available
+ * Index of the first available block.
+ * @var chunk::count_available
+ * Count of the blocks.
+ * @var chunk::lock
+ * Chunk's lock.
+ * @var chunk::size
+ * Count of the blocks in chunk.
+ * @var chunk::index
+ * Pointer to allocated memory.
+ */
+struct chunk {
+ unsigned long *data;
+ unsigned long first_available;
+ unsigned long count_available;
+
+ spinlock_t lock;
+ unsigned long size;
+ unsigned long *index;
+};
+
+/**
+ * @struct fixed_alloc
+ * @brief Item of fixed allocs list.
+ * @var fixed_alloc::hlist
+ * Fixed alloc hash list node.
+ * @var fixed_alloc::chunk
+ * Chunk.
+ */
+struct fixed_alloc {
+ struct hlist_node hlist;
+ struct chunk chunk;
+};
+
+static int chunk_init(struct chunk *chunk, void *data,
+ size_t size, size_t size_block)
+{
+ unsigned long i;
+ unsigned long *p;
+
+ spin_lock_init(&chunk->lock);
+ chunk->data = (unsigned long *)data;
+ chunk->first_available = 0;
+ chunk->count_available = size / size_block;
+ chunk->size = chunk->count_available;
+
+ chunk->index = kmalloc(sizeof(*chunk->index)*chunk->count_available,
+ GFP_ATOMIC);
+
+ if (chunk->index == NULL) {
+ pr_err("%s: failed to allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ p = chunk->index;
+ for (i = 0; i != chunk->count_available; ++p)
+ *p = ++i;
+
+ return 0;
+}
+
+static void chunk_uninit(struct chunk *chunk)
+{
+ kfree(chunk->index);
+}
+
+static void *chunk_allocate(struct chunk *chunk, size_t size_block)
+{
+ unsigned long *ret;
+
+ if (!chunk->count_available)
+ return NULL;
+
+ spin_lock(&chunk->lock);
+ ret = chunk->data + chunk->first_available*size_block;
+ chunk->first_available = chunk->index[chunk->first_available];
+ --chunk->count_available;
+ spin_unlock(&chunk->lock);
+
+ return ret;
+}
+
+static void chunk_deallocate(struct chunk *chunk, void *p, size_t size_block)
+{
+ unsigned long idx = ((unsigned long *)p - chunk->data)/size_block;
+
+ spin_lock(&chunk->lock);
+ chunk->index[idx] = chunk->first_available;
+ chunk->first_available = idx;
+ ++chunk->count_available;
+ spin_unlock(&chunk->lock);
+}
+
+static inline int chunk_check_ptr(struct chunk *chunk, void *p, size_t size)
+{
+ if ((chunk->data <= (unsigned long *)p) &&
+ ((chunk->data + size/sizeof(chunk->data)) > (unsigned long *)p))
+ return 1;
+
+ return 0;
+}
+
+static inline int chunk_free(struct chunk *chunk)
+{
+ return (chunk->count_available == chunk->size);
+}
+
+static struct fixed_alloc *create_fixed_alloc(struct slot_manager *sm)
+{
+ int ret;
+ void *data;
+ struct fixed_alloc *fa;
+
+ fa = kmalloc(sizeof(*fa), GFP_ATOMIC);
+ if (fa == NULL)
+ return NULL;
+
+ data = sm->alloc(sm);
+ if (data == NULL)
+ goto free_fa;
+
+ ret = chunk_init(&fa->chunk, data,
+ PAGE_SIZE / sizeof(unsigned long), sm->slot_size);
+ if (ret)
+ goto free_sm;
+
+ return fa;
+
+free_sm:
+ sm->free(sm, data);
+free_fa:
+ kfree(fa);
+ return NULL;
+}
+
+static void free_fixed_alloc(struct slot_manager *sm, struct fixed_alloc *fa)
+{
+ chunk_uninit(&fa->chunk);
+ sm->free(sm, fa->chunk.data);
+ kfree(fa);
+}
+
+
+/**
+ * @brief Allocates slot for slot manager.
+ *
+ * @param[in,out] sm Slot manager that should be filled.
+ * @return Pointer to allocated slot.
+ */
+void *swap_slot_alloc(struct slot_manager *sm)
+{
+ void *free_slot;
+ struct fixed_alloc *fa;
+ DECLARE_NODE_PTR_FOR_HLIST(pos);
+
+ swap_hlist_for_each_entry_rcu(fa, pos, &sm->page_list, hlist) {
+ free_slot = chunk_allocate(&fa->chunk, sm->slot_size);
+ if (free_slot)
+ return free_slot;
+ }
+
+ fa = create_fixed_alloc(sm);
+ if (fa == NULL)
+ return NULL;
+
+ INIT_HLIST_NODE(&fa->hlist);
+ hlist_add_head_rcu(&fa->hlist, &sm->page_list);
+
+ return chunk_allocate(&fa->chunk, sm->slot_size);
+}
+EXPORT_SYMBOL_GPL(swap_slot_alloc);
+
+/**
+ * @brief Releases allocated slot.
+ *
+ * @param sm Pointer to the target slot manager.
+ * @param slot Pointer to the target slot.
+ * @return Void.
+ */
+void swap_slot_free(struct slot_manager *sm, void *slot)
+{
+ struct fixed_alloc *fa;
+ DECLARE_NODE_PTR_FOR_HLIST(pos);
+
+ if (slot == NULL)
+ return;
+
+ swap_hlist_for_each_entry_rcu(fa, pos, &sm->page_list, hlist) {
+ if (!chunk_check_ptr(&fa->chunk, slot, PAGE_SIZE))
+ continue;
+
+ chunk_deallocate(&fa->chunk, slot, sm->slot_size);
+
+ if (chunk_free(&fa->chunk)) {
+ hlist_del_rcu(&fa->hlist);
+ free_fixed_alloc(sm, fa);
+ }
+
+ return;
+ }
+
+ panic("%s: slot=%p is not data base\n", __func__, slot);
+}
+EXPORT_SYMBOL_GPL(swap_slot_free);
--- /dev/null
+/**
+ * @file kprobe/swap_slots.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial implementation;
+ * Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP slots interface declaration.
+ */
+
+#ifndef _SWAP_SLOTS_H
+#define _SWAP_SLOTS_H
+
+#include <linux/types.h>
+
+/**
+ * @struct slot_manager
+ * @brief Manage slots.
+ * @var slot_manager::slot_size
+ * Size of the slot.
+ * @var slot_manager::alloc
+ * Memory allocation callback.
+ * @var slot_manager::free
+ * Memory release callback.
+ * @var slot_manager::page_list
+ * List of pages.
+ * @var slot_manager::data
+ * Slot manager data. task_struct pointer usually stored here.
+ */
+struct slot_manager {
+ unsigned long slot_size; /* FIXME: allocated in long (4 byte) */
+ void *(*alloc)(struct slot_manager *sm);
+ void (*free)(struct slot_manager *sm, void *ptr);
+ struct hlist_head page_list;
+ void *data;
+};
+
+void *swap_slot_alloc(struct slot_manager *sm);
+void swap_slot_free(struct slot_manager *sm, void *slot);
+
+#endif /* _SWAP_SLOTS_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2014 Vasiliy Ulyanov <v.ulyanov@samsung.com>
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include "swap_td_raw.h"
+
+
+#define TD_OFFSET 1 /* skip STACK_END_MAGIC */
+#define TD_PREFIX "[SWAP_TD_RAW] "
+#define TD_STACK_USAGE_MAX 0x200
+#define TD_CHUNK_MIN sizeof(long)
+
+
+static DEFINE_MUTEX(mutex_stack_usage);
+static unsigned long stack_usage = 0;
+static LIST_HEAD(td_raw_list);
+
+
+/*
+ * take small area from stack
+ *
+ * 0x00 +--------------------------+
+ * | STACK_END_MAGIC |
+ * +--------------------------+ <-- bottom of stack;
+ * | |
+ * | stack |
+ * | |
+ * 0xff
+ *
+ */
+
+static void *bottom_of_stack(struct task_struct *task)
+{
+ return (void *)(end_of_stack(task) + TD_OFFSET);
+}
+
+int swap_td_raw_reg(struct td_raw *raw, unsigned long size)
+{
+ int ret = 0;
+
+ size = (size / TD_CHUNK_MIN + !!(size % TD_CHUNK_MIN)) * TD_CHUNK_MIN;
+
+ mutex_lock(&mutex_stack_usage);
+ if (stack_usage + size > TD_STACK_USAGE_MAX) {
+ pr_warn(TD_PREFIX "free stack ended: usage=%ld size=%ld\n",
+ stack_usage, size);
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ raw->offset = stack_usage;
+
+ INIT_LIST_HEAD(&raw->list);
+ list_add(&raw->list, &td_raw_list);
+
+ stack_usage += size;
+
+unlock:
+ mutex_unlock(&mutex_stack_usage);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_td_raw_reg);
+
+void swap_td_raw_unreg(struct td_raw *raw)
+{
+ mutex_lock(&mutex_stack_usage);
+
+ list_del(&raw->list);
+ if (list_empty(&td_raw_list))
+ stack_usage = 0;
+
+ mutex_unlock(&mutex_stack_usage);
+}
+EXPORT_SYMBOL_GPL(swap_td_raw_unreg);
+
+void *swap_td_raw(struct td_raw *raw, struct task_struct *task)
+{
+ return bottom_of_stack(task) + raw->offset;
+}
+EXPORT_SYMBOL_GPL(swap_td_raw);
+
+int swap_td_raw_init(void)
+{
+ WARN_ON(stack_usage);
+
+ stack_usage = 0;
+
+ return 0;
+}
+
+void swap_td_raw_uninit(void)
+{
+ WARN_ON(!list_empty(&td_raw_list));
+ WARN_ON(stack_usage);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2014 Vasiliy Ulyanov <v.ulyanov@samsung.com>
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_TD_RAW_H
+#define _SWAP_TD_RAW_H
+
+
+#include <linux/list.h>
+
+
+struct td_raw {
+ struct list_head list;
+ unsigned long offset;
+};
+
+
+int swap_td_raw_reg(struct td_raw *raw, unsigned long size);
+void swap_td_raw_unreg(struct td_raw *raw);
+
+void *swap_td_raw(struct td_raw *raw, struct task_struct *task);
+
+int swap_td_raw_init(void);
+void swap_td_raw_uninit(void);
+
+
+#endif /* _SWAP_TD_RAW_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers \
+ $(src)/../writer/Module.symvers
+
+obj-m := swap_ks_features.o
+swap_ks_features-y := ks_features.o \
+ ks_features_data.o \
+ ks_map.o \
+ file_ops.o \
+ ksf_msg.o
--- /dev/null
+/**
+ * ks_features/features_data.c
+ * @author Vyacheslav Cherkashin: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features
+ */
+
+
+#include "ksf_msg.h"
+#include "syscall_list.h"
+
+
+/**
+ * @struct feature
+ * Feature description.
+ * @var feature::cnt
+ * Syscalls count.
+ * @var feature::feature_list
+ * Pointer to feature's syscall list.
+ * @var feature::type
+ * Featue subtype.
+ * @var feature::enable
+ * Is feature enable.
+ */
+struct feature {
+ size_t cnt;
+ enum syscall_id *feature_list;
+ enum probe_t type;
+
+ unsigned enable:1;
+};
+
+/**
+ * @def X
+ * X-macros for syscall list.
+ */
+#define X(name, args) id_sys_##name,
+/**
+ * @enum syscall_id
+ * Syscall list
+ */
+enum syscall_id {
+ SYSCALL_LIST
+};
+#undef X
+
+static enum syscall_id id_none[] = {};
+
+static enum syscall_id id_file[] = {
+ id_sys_acct,
+ id_sys_mount,
+/* TODO:
+ * id_sys_umount,
+ */
+ id_sys_truncate,
+/* TODO:
+ * id_sys_stat,
+ */
+ id_sys_statfs,
+ id_sys_statfs64,
+/* TODO:
+ * id_sys_lstat,
+ */
+ id_sys_stat64,
+ id_sys_fstat64,
+ id_sys_lstat64,
+ id_sys_truncate64,
+ id_sys_ftruncate64,
+ id_sys_setxattr,
+ id_sys_getxattr,
+ id_sys_listxattr,
+ id_sys_removexattr,
+ id_sys_chroot,
+ id_sys_mknod,
+ id_sys_link,
+ id_sys_symlink,
+ id_sys_unlink,
+ id_sys_rename,
+ id_sys_chmod,
+ id_sys_readlink,
+ id_sys_creat,
+ id_sys_open,
+ id_sys_access,
+ id_sys_chown,
+/* TODO:
+ * id_sys_chown16,
+ * id_sys_utime,
+ */
+ id_sys_utimes,
+ id_sys_pread64,
+ id_sys_pwrite64,
+ id_sys_preadv,
+ id_sys_pwritev,
+ id_sys_getcwd,
+ id_sys_mkdir,
+ id_sys_chdir,
+ id_sys_rmdir,
+ id_sys_swapon,
+ id_sys_swapoff,
+ id_sys_uselib,
+ id_sys_mknodat,
+ id_sys_mkdirat,
+ id_sys_unlinkat,
+ id_sys_symlinkat,
+ id_sys_linkat,
+ id_sys_renameat,
+ id_sys_futimesat,
+ id_sys_faccessat,
+ id_sys_fchmodat,
+ id_sys_fchownat,
+ id_sys_openat,
+/* TODO:
+ * id_sys_newfstatat,
+ */
+ id_sys_readlinkat,
+ id_sys_utimensat,
+ id_sys_fanotify_mark,
+ id_sys_execve,
+ id_sys_name_to_handle_at,
+ id_sys_open_by_handle_at
+};
+
+static enum syscall_id id_ipc[] = {
+ id_sys_msgget,
+ id_sys_msgsnd,
+ id_sys_msgrcv,
+ id_sys_msgctl,
+ id_sys_semget,
+ id_sys_semop,
+ id_sys_semctl,
+ id_sys_semtimedop,
+ id_sys_shmat,
+ id_sys_shmget,
+ id_sys_shmdt,
+ id_sys_shmctl,
+/* TODO:
+ * id_sys_ipc
+ */
+};
+
+static enum syscall_id id_net[] = {
+ id_sys_shutdown,
+ id_sys_sendfile,
+ id_sys_sendfile64,
+ id_sys_setsockopt,
+ id_sys_getsockopt,
+ id_sys_bind,
+ id_sys_connect,
+ id_sys_accept,
+ id_sys_accept4,
+ id_sys_getsockname,
+ id_sys_getpeername,
+ id_sys_send,
+ id_sys_sendto,
+ id_sys_sendmsg,
+ id_sys_sendmmsg,
+ id_sys_recv,
+ id_sys_recvfrom,
+ id_sys_recvmsg,
+ id_sys_recvmmsg,
+ id_sys_socket,
+ id_sys_socketpair,
+/* TODO:
+ * id_sys_socketcall,
+ */
+ id_sys_listen
+};
+
+static enum syscall_id id_process[] = {
+ id_sys_exit,
+ id_sys_exit_group,
+ id_sys_wait4,
+ id_sys_waitid,
+/* TODO:
+ * id_sys_waitpid,
+ */
+ id_sys_rt_tgsigqueueinfo,
+ id_sys_unshare,
+ id_sys_fork,
+ id_sys_vfork,
+/* TODO: add support CONFIG_CLONE_BACKWARDS
+ * id_sys_clone,
+ * id_sys_clone,
+ */
+ id_sys_execve
+};
+
+static enum syscall_id id_signal[] = {
+ id_sys_sigpending,
+ id_sys_sigprocmask,
+/* TODO:
+ * id_sys_sigaltstack,
+ */
+/* TODO: add support CONFIG_OLD_SIGSUSPEND and CONFIG_OLD_SIGSUSPEND3
+ * id_sys_sigsuspend,
+ * id_sys_sigsuspend,
+ */
+ id_sys_rt_sigsuspend,
+ id_sys_sigaction,
+ id_sys_rt_sigaction,
+ id_sys_rt_sigprocmask,
+ id_sys_rt_sigtimedwait,
+ id_sys_rt_tgsigqueueinfo,
+ id_sys_kill,
+ id_sys_tgkill,
+/* TODO:
+ * id_sys_signal,
+ */
+ id_sys_pause,
+ id_sys_signalfd,
+ id_sys_signalfd4
+};
+
+static enum syscall_id id_desc[] = {
+ id_sys_fgetxattr,
+ id_sys_flistxattr,
+ id_sys_fremovexattr,
+/* TODO:
+ * id_sys_fadvise64_64,
+ */
+ id_sys_pipe2,
+ id_sys_dup3,
+ id_sys_sendfile,
+ id_sys_sendfile64,
+ id_sys_preadv,
+ id_sys_pwritev,
+ id_sys_epoll_create1,
+ id_sys_epoll_ctl,
+ id_sys_epoll_wait,
+ id_sys_epoll_pwait,
+ id_sys_inotify_init,
+ id_sys_inotify_init1,
+ id_sys_inotify_add_watch,
+ id_sys_inotify_rm_watch,
+ id_sys_mknodat,
+ id_sys_mkdirat,
+ id_sys_unlinkat,
+ id_sys_symlinkat,
+ id_sys_linkat,
+ id_sys_renameat,
+ id_sys_futimesat,
+ id_sys_faccessat,
+ id_sys_fchmodat,
+ id_sys_fchownat,
+ id_sys_openat,
+/* TODO:
+ * id_sys_newfstatat,
+ */
+ id_sys_readlinkat,
+ id_sys_utimensat,
+ id_sys_splice,
+ id_sys_vmsplice,
+ id_sys_tee,
+ id_sys_signalfd,
+ id_sys_signalfd4,
+ id_sys_timerfd_create,
+ id_sys_timerfd_settime,
+ id_sys_timerfd_gettime,
+ id_sys_eventfd,
+ id_sys_eventfd2,
+ id_sys_fallocate,
+ id_sys_pselect6,
+ id_sys_ppoll,
+ id_sys_fanotify_init,
+ id_sys_fanotify_mark,
+ id_sys_syncfs,
+/* TODO:
+ * id_sys_mmap_pgoff,
+ * id_sys_old_mmap,
+ */
+ id_sys_name_to_handle_at,
+ id_sys_setns
+};
+
+/**
+ * @def CREATE_FEATURE
+ * Feature initialization.
+ */
+#define CREATE_FEATURE(x, _type) \
+{ \
+ .cnt = sizeof(x) / sizeof(enum syscall_id), \
+ .feature_list = x, \
+ .type = _type, \
+ .enable = 0 \
+}
+
+static struct feature features[] = {
+ CREATE_FEATURE(id_none, PT_KS_NONE),
+ CREATE_FEATURE(id_file, PT_KS_FILE),
+ CREATE_FEATURE(id_ipc, PT_KS_IPC),
+ CREATE_FEATURE(id_process, PT_KS_PROCESS),
+ CREATE_FEATURE(id_signal, PT_KS_SIGNAL),
+ CREATE_FEATURE(id_net, PT_KS_NETWORK),
+ CREATE_FEATURE(id_desc, PT_KS_DESC)
+};
+
+/**
+ * @enum
+ * Defines feature_cnt.
+ */
+enum {
+ feature_cnt = sizeof(features) / sizeof(struct feature)
+};
+
+static int feature_index(struct feature *f)
+{
+ return f - features;
+}
--- /dev/null
+#include <linux/kconfig.h>
+
+#ifndef CONFIG_SWAP_HOOK_SYSCALL
+
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/percpu.h>
+#include <linux/namei.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <kprobe/swap_kprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <writer/event_filter.h>
+#include "ks_map.h"
+#include "ksf_msg.h"
+#include "file_ops.h"
+
+#define FOPS_PREFIX "[FILE_OPS] "
+
+#define PT_FILE 0x4 /* probe type FILE(04) */
+
+/* path buffer size */
+enum { PATH_LEN = 512 };
+
+struct file_probe {
+ int id;
+ const char *args;
+ int subtype;
+ struct kretprobe rp;
+};
+
+#define to_file_probe(_rp) container_of(_rp, struct file_probe, rp)
+
+/* common private data */
+struct file_private {
+ struct dentry *dentry;
+};
+
+/* open/creat private data */
+struct open_private {
+ int dfd;
+ const char __user *name;
+ int ret;
+};
+
+/* locks private data */
+struct flock_private {
+ struct dentry *dentry;
+ int subtype;
+};
+
+#define DECLARE_HANDLER(_name) \
+ int _name(struct kretprobe_instance *, struct pt_regs *)
+
+/* generic handlers forward declaration */
+static DECLARE_HANDLER(generic_entry_handler);
+static DECLARE_HANDLER(generic_ret_handler);
+/* open/creat handlers */
+static DECLARE_HANDLER(open_entry_handler);
+static DECLARE_HANDLER(open_ret_handler);
+/* lock handlers */
+static DECLARE_HANDLER(lock_entry_handler);
+static DECLARE_HANDLER(lock_ret_handler);
+/* filp_close helper handlers */
+static DECLARE_HANDLER(filp_close_entry_handler);
+static DECLARE_HANDLER(filp_close_ret_handler);
+
+#define FILE_OPS_OPEN_LIST \
+ X(sys_open, sdd), \
+ X(sys_openat, dsdd), \
+ X(sys_creat, sd)
+
+#define FILE_OPS_CLOSE_LIST \
+ X(sys_close, d)
+
+#define FILE_OPS_READ_LIST \
+ X(sys_read, dpd), \
+ X(sys_readv, dpd), \
+ X(sys_pread64, dpxx), \
+ X(sys_preadv, dpxxx)
+
+#define FILE_OPS_WRITE_LIST \
+ X(sys_write, dpd), \
+ X(sys_writev, dpd), \
+ X(sys_pwrite64, dpxx), \
+ X(sys_pwritev, dpxxx)
+
+#define FILE_OPS_LOCK_LIST \
+ X(sys_fcntl, ddd), \
+ X(sys_fcntl64, ddd), \
+ X(sys_flock, dd)
+
+#define FILE_OPS_LIST \
+ FILE_OPS_OPEN_LIST, \
+ FILE_OPS_CLOSE_LIST, \
+ FILE_OPS_READ_LIST, \
+ FILE_OPS_WRITE_LIST, \
+ FILE_OPS_LOCK_LIST
+
+#define X(_name, _args) \
+ id_##_name
+enum {
+ FILE_OPS_LIST
+};
+#undef X
+
+#define __FILE_PROBE_INITIALIZER(_name, _args, _subtype, _dtype, _entry, _ret) \
+ { \
+ .id = id_##_name, \
+ .args = #_args, \
+ .subtype = _subtype, \
+ .rp = { \
+ .kp.symbol_name = #_name, \
+ .data_size = sizeof(_dtype), \
+ .entry_handler = _entry, \
+ .handler = _ret, \
+ } \
+ }
+
+static struct file_probe fprobes[] = {
+#define X(_name, _args) \
+ [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_OPEN, \
+ struct open_private, \
+ open_entry_handler, \
+ open_ret_handler)
+ FILE_OPS_OPEN_LIST,
+#undef X
+
+#define X(_name, _args) \
+ [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_CLOSE, \
+ struct file_private, \
+ generic_entry_handler, \
+ generic_ret_handler)
+ FILE_OPS_CLOSE_LIST,
+#undef X
+
+#define X(_name, _args) \
+ [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_READ, \
+ struct file_private, \
+ generic_entry_handler, \
+ generic_ret_handler)
+ FILE_OPS_READ_LIST,
+#undef X
+
+#define X(_name, _args) \
+ [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_WRITE, \
+ struct file_private, \
+ generic_entry_handler, \
+ generic_ret_handler)
+ FILE_OPS_WRITE_LIST,
+#undef X
+
+#define X(_name, _args) \
+ [id_##_name] = __FILE_PROBE_INITIALIZER(_name, _args, FOPS_OTHER, \
+ struct flock_private, \
+ lock_entry_handler, \
+ lock_ret_handler)
+ FILE_OPS_LOCK_LIST
+#undef X
+};
+
+static void *fops_key_func(void *);
+static int fops_cmp_func(void *, void *);
+
+/* percpu buffer to hold the filepath inside handlers */
+static DEFINE_PER_CPU(char[PATH_LEN], __path_buf);
+
+/* map to hold 'interesting' files */
+static DEFINE_MAP(__map, fops_key_func, fops_cmp_func);
+static DEFINE_RWLOCK(__map_lock);
+
+/* enabled/disabled flag */
+static int fops_enabled;
+static DEFINE_MUTEX(fops_lock);
+
+/* GET/PUT debug stuff */
+static int file_get_put_balance;
+static int dentry_get_put_balance;
+
+/* helper probe */
+static struct kretprobe filp_close_krp = {
+ .kp.symbol_name = "filp_close",
+ .data_size = 0,
+ .entry_handler = filp_close_entry_handler,
+ .handler = filp_close_ret_handler
+};
+
+/* should be called only from handlers (with preemption disabled) */
+static inline char *fops_path_buf(void)
+{
+ return __get_cpu_var(__path_buf);
+}
+
+static inline unsigned fops_dcount(const struct dentry *dentry)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
+ return dentry->d_count;
+#else
+ return d_count(dentry);
+#endif
+}
+
+/* kernel function args */
+#define fops_karg(_type, _regs, _idx) ((_type)swap_get_karg(_regs, _idx))
+/* syscall args */
+#define fops_sarg(_type, _regs, _idx) ((_type)swap_get_sarg(_regs, _idx))
+/* retval */
+#define fops_ret(_type, _regs) ((_type)regs_return_value(_regs))
+
+#define F_ADDR(_rp) ((unsigned long)(_rp)->kp.addr) /* function address */
+#define R_ADDR(_ri) ((unsigned long)(_ri)->ret_addr) /* return adress */
+
+static void *fops_key_func(void *data)
+{
+ /* use ((struct dentry *)data)->d_inode pointer as map key to handle
+ * symlinks/hardlinks the same way as the original file */
+ return data;
+}
+
+static int fops_cmp_func(void *key_a, void *key_b)
+{
+ return key_a - key_b;
+}
+
+static inline struct map *__get_map(void)
+{
+ return &__map;
+}
+
+static inline struct map *get_map_read(void)
+{
+ read_lock(&__map_lock);
+
+ return __get_map();
+}
+
+static inline void put_map_read(struct map *map)
+{
+ read_unlock(&__map_lock);
+}
+
+static inline struct map *get_map_write(void)
+{
+ write_lock(&__map_lock);
+
+ return __get_map();
+}
+
+static inline void put_map_write(struct map *map)
+{
+ write_unlock(&__map_lock);
+}
+
+static struct file *__fops_fget(int fd)
+{
+ struct file *file;
+
+ file = fget(fd);
+ if (IS_ERR_OR_NULL(file))
+ file = NULL;
+ else
+ file_get_put_balance++;
+
+ return file;
+}
+
+static void __fops_fput(struct file *file)
+{
+ file_get_put_balance--;
+ fput(file);
+}
+
+static struct dentry *__fops_dget(struct dentry *dentry)
+{
+ dentry_get_put_balance++;
+
+ return dget(dentry);
+}
+
+static void __fops_dput(struct dentry *dentry)
+{
+ dentry_get_put_balance--;
+ dput(dentry);
+}
+
+static int fops_dinsert(struct dentry *dentry)
+{
+ struct map *map;
+ int ret;
+
+ map = get_map_write();
+ ret = insert(map, __fops_dget(dentry));
+ put_map_write(map);
+
+ if (ret)
+ __fops_dput(dentry);
+
+ /* it's ok if dentry is already inserted */
+ return ret == -EEXIST ? 0 : ret;
+}
+
+static struct dentry *fops_dsearch(struct dentry *dentry)
+{
+ struct dentry *found;
+ struct map *map;
+
+ map = get_map_read();
+ found = search(map, map->key_f(dentry));
+ put_map_read(map);
+
+ return found;
+}
+
+static struct dentry *fops_dremove(struct dentry *dentry)
+{
+ struct dentry *removed;
+ struct map *map;
+
+ map = get_map_write();
+ removed = remove(map, map->key_f(dentry));
+ put_map_write(map);
+
+ if (removed)
+ __fops_dput(removed);
+
+ return removed;
+}
+
+static int fops_fcheck(struct task_struct *task, struct file *file)
+{
+ struct dentry *dentry;
+
+ if (!task || !file)
+ return -EINVAL;
+
+ dentry = file->f_path.dentry;
+
+ /* check if it is a regular file */
+ if (!S_ISREG(dentry->d_inode->i_mode))
+ return -EBADF;
+
+ if (check_event(task))
+ /* it is 'our' task: just add the dentry to the map */
+ return fops_dinsert(dentry) ? : -EAGAIN;
+ else
+ /* not 'our' task: check if the file is 'interesting' */
+ return fops_dsearch(dentry) ? 0 : -ESRCH;
+}
+
+static char *fops_fpath(struct file *file, char *buf, int buflen)
+{
+ char *filename;
+
+ path_get(&file->f_path);
+ filename = d_path(&file->f_path, buf, buflen);
+ path_put(&file->f_path);
+
+ if (IS_ERR_OR_NULL(filename)) {
+ printk(FOPS_PREFIX "d_path FAILED: %ld\n", PTR_ERR(filename));
+ buf[0] = '\0';
+ filename = buf;
+ }
+
+ return filename;
+}
+
+static int generic_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+
+ if (rp) {
+ struct file_probe *fprobe = to_file_probe(rp);
+ struct file_private *priv = (struct file_private *)ri->data;
+ int fd = fops_sarg(int, regs, 0);
+ struct file *file = __fops_fget(fd);
+
+ if (fops_fcheck(current, file) == 0) {
+ char *buf = fops_path_buf();
+
+ ksf_msg_file_entry(fd, fprobe->subtype,
+ fops_fpath(file, buf, PATH_LEN));
+
+ priv->dentry = file->f_path.dentry;
+ } else {
+ priv->dentry = NULL;
+ }
+
+ if (file)
+ __fops_fput(file);
+ }
+
+ return 0;
+}
+
+static int generic_ret_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+ struct file_private *priv = (struct file_private *)ri->data;
+
+ if (rp && priv->dentry)
+ ksf_msg_file_exit(regs, 'x');
+
+ return 0;
+}
+
+static int open_private_init(const char *args, struct pt_regs *regs,
+ struct open_private *priv)
+{
+ int ret = 0;
+
+ switch (args[0]) {
+ case 'd': /* file name: relative to fd */
+ if (args[1] != 's') {
+ ret = -EINVAL;
+ break;
+ }
+ priv->dfd = fops_sarg(int, regs, 0);
+ priv->name = fops_sarg(const char __user *, regs, 1);
+ break;
+ case 's': /* file name: absolute or relative to CWD */
+ priv->dfd = AT_FDCWD;
+ priv->name = fops_sarg(const char __user *, regs, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ priv->ret = ret;
+
+ return ret;
+}
+
+static int open_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+
+ if (rp) {
+ struct file_probe *fprobe = to_file_probe(rp);
+ struct open_private *priv = (struct open_private *)ri->data;
+
+ open_private_init(fprobe->args, regs, priv);
+ /* FIXME entry event will be sent in open_ret_handler: cannot
+ * perform a file lookup in atomic context */
+ }
+
+ return 0;
+}
+
+static int open_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+ struct open_private *priv = (struct open_private *)ri->data;
+
+ if (rp && priv->ret == 0) {
+ struct file_probe *fprobe = to_file_probe(rp);
+ int fd = fops_ret(int, regs);
+ struct file *file = __fops_fget(fd);
+
+ if (fops_fcheck(current, file) == 0) {
+ char *buf = fops_path_buf();
+ const char *path = fops_fpath(file, buf, PATH_LEN);
+
+ ksf_msg_file_entry_open(fd, fprobe->subtype,
+ path, priv->name);
+ ksf_msg_file_exit(regs, 'x');
+ }
+
+ if (file)
+ __fops_fput(file);
+ }
+
+ return 0;
+}
+
+/* wrapper for 'struct flock*' data */
+struct lock_arg {
+ int type;
+ int whence;
+ s64 start;
+ s64 len;
+};
+
+/* TODO copy_from_user */
+#define __lock_arg_init(_type, _regs, _arg) \
+ do { \
+ _type __user *flock = fops_sarg(_type __user *, _regs, 2); \
+ _arg->type = flock->l_type; \
+ _arg->whence = flock->l_whence; \
+ _arg->start = flock->l_start; \
+ _arg->len = flock->l_len; \
+ } while (0)
+
+static int lock_arg_init(int id, struct pt_regs *regs, struct lock_arg *arg)
+{
+ unsigned int cmd = fops_sarg(unsigned int, regs, 1);
+ int ret = 0;
+
+ switch (id) {
+ case id_sys_fcntl:
+ if (cmd == F_SETLK || cmd == F_SETLKW)
+ __lock_arg_init(struct flock, regs, arg);
+ else
+ ret = -EINVAL;
+ break;
+ case id_sys_fcntl64:
+ if (cmd == F_SETLK64 || cmd == F_SETLKW64)
+ __lock_arg_init(struct flock64, regs, arg);
+ else if (cmd == F_SETLK || cmd == F_SETLKW)
+ __lock_arg_init(struct flock, regs, arg);
+ else
+ ret = -EINVAL;
+ break;
+ case id_sys_flock: /* TODO is it really needed? */
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int lock_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+
+ if (rp) {
+ struct file_probe *fprobe = to_file_probe(rp);
+ struct flock_private *priv = (struct flock_private *)ri->data;
+ int fd = fops_sarg(int, regs, 0);
+ struct file *file = __fops_fget(fd);
+
+ if (fops_fcheck(current, file) == 0) {
+ int subtype = fprobe->subtype;
+ struct lock_arg arg;
+ char *buf, *filepath;
+
+ buf = fops_path_buf();
+ filepath = fops_fpath(file, buf, PATH_LEN);
+
+ if (lock_arg_init(fprobe->id, regs, &arg) == 0) {
+ subtype = arg.type == F_UNLCK ?
+ FOPS_LOCK_RELEASE :
+ FOPS_LOCK_START;
+ ksf_msg_file_entry_lock(fd, subtype, filepath,
+ arg.type, arg.whence,
+ arg.start, arg.len);
+ } else {
+ ksf_msg_file_entry(fd, subtype, filepath);
+ }
+
+ priv->dentry = file->f_path.dentry;
+ priv->subtype = subtype;
+ } else {
+ priv->dentry = NULL;
+ }
+
+ if (file)
+ __fops_fput(file);
+ }
+
+ return 0;
+}
+
+static int lock_ret_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+ struct flock_private *priv = (struct flock_private *)ri->data;
+
+ if (rp && priv->dentry)
+ ksf_msg_file_exit(regs, 'x');
+
+ return 0;
+}
+
+static int filp_close_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+ struct file *file = fops_karg(struct file *, regs, 0);
+
+ if (rp && file && file_count(file)) {
+ struct dentry *dentry = file->f_path.dentry;
+
+ /* release the file if it is going to be removed soon */
+ if (dentry && fops_dcount(dentry) == 2)
+ fops_dremove(dentry);
+ }
+
+ return 0;
+}
+
+static int filp_close_ret_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ return 0;
+}
+
+static void fops_unregister_probes(struct file_probe *fprobes, int cnt)
+{
+ int i = cnt;
+
+ /* probes are unregistered in reverse order */
+ while (--i >= 0) {
+ struct kretprobe *rp = &fprobes[i].rp;
+
+ swap_unregister_kretprobe(rp);
+ printk(FOPS_PREFIX "'%s/%08lx' kretprobe unregistered (%d)\n",
+ rp->kp.symbol_name, F_ADDR(rp), i);
+ }
+
+ /* unregister helper probes */
+ swap_unregister_kretprobe(&filp_close_krp);
+}
+
+static int fops_register_probes(struct file_probe *fprobes, int cnt)
+{
+ struct kretprobe *rp = &filp_close_krp;
+ int ret, i = 0;
+
+ /* register helper probes */
+ ret = swap_register_kretprobe(rp);
+ if (ret)
+ goto fail;
+
+ /* register syscalls */
+ for (i = 0; i < cnt; i++) {
+ rp = &fprobes[i].rp;
+
+ if (!rp->entry_handler)
+ rp->entry_handler = generic_entry_handler;
+
+ if (!rp->handler)
+ rp->handler = generic_ret_handler;
+
+ ret = swap_register_kretprobe(rp);
+ if (ret)
+ goto fail_unreg;
+
+ printk(FOPS_PREFIX "'%s/%08lx' kretprobe registered (%d)\n",
+ rp->kp.symbol_name, F_ADDR(rp), i);
+ }
+
+ return 0;
+
+fail_unreg:
+ fops_unregister_probes(fprobes, i);
+
+fail:
+ printk(FOPS_PREFIX "Failed to register probe: %s\n",
+ rp->kp.symbol_name);
+
+ return ret;
+}
+
+static char *__fops_dpath(struct dentry *dentry, char *buf, int buflen)
+{
+ static const char *NA = "N/A";
+ char *filename = dentry_path_raw(dentry, buf, buflen);
+
+ if (IS_ERR_OR_NULL(filename)) {
+ printk(FOPS_PREFIX "dentry_path_raw FAILED: %ld\n",
+ PTR_ERR(filename));
+ strncpy(buf, NA, buflen);
+ filename = buf;
+ }
+
+ return filename;
+}
+
+/* just a simple wrapper for passing to clear function */
+static int __fops_dput_wrapper(void *data, void *arg)
+{
+ static char buf[PATH_LEN]; /* called under write lock => static is ok */
+ struct dentry *dentry = data;
+ struct inode *inode = dentry->d_inode;
+
+ printk(FOPS_PREFIX "Releasing dentry(%p/%p/%d): %s\n",
+ dentry, inode, inode ? inode->i_nlink : 0,
+ __fops_dpath(dentry, buf, PATH_LEN));
+ __fops_dput(dentry);
+
+ return 0;
+}
+
+bool file_ops_is_init(void)
+{
+ return fops_enabled;
+}
+
+int file_ops_init(void)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&fops_lock);
+
+ if (fops_enabled) {
+ printk(FOPS_PREFIX "Handlers already enabled\n");
+ goto unlock;
+ }
+
+ ret = fops_register_probes(fprobes, ARRAY_SIZE(fprobes));
+ if (ret == 0)
+ fops_enabled = 1;
+
+unlock:
+ mutex_unlock(&fops_lock);
+
+ return ret;
+}
+
+void file_ops_exit(void)
+{
+ struct map *map;
+
+ mutex_lock(&fops_lock);
+
+ if (!fops_enabled) {
+ printk(FOPS_PREFIX "Handlers not enabled\n");
+ goto unlock;
+ }
+
+ /* 1. unregister probes */
+ fops_unregister_probes(fprobes, ARRAY_SIZE(fprobes));
+
+ /* 2. clear the map */
+ map = get_map_write();
+ printk(FOPS_PREFIX "Clearing map: entries(%d)\n", map->size);
+ clear(map, __fops_dput_wrapper, NULL);
+ WARN(file_get_put_balance, "File GET/PUT balance: %d\n",
+ file_get_put_balance);
+ WARN(dentry_get_put_balance, "Dentry GET/PUT balance: %d\n",
+ dentry_get_put_balance);
+ put_map_write(map);
+
+ /* 3. drop the flag */
+ fops_enabled = 0;
+
+unlock:
+ mutex_unlock(&fops_lock);
+}
+
+#endif /* CONFIG_SWAP_HOOK_SYSCALL */
--- /dev/null
+#ifndef __FILE_OPS__
+#define __FILE_OPS__
+
+#include <linux/types.h>
+#include <linux/printk.h>
+
+
+#define FOPS_PREFIX "[FILE_OPS] "
+
+
+/* TODO: add support CONFIG_SWAP_HOOK_SYSCALL */
+#ifdef CONFIG_SWAP_HOOK_SYSCALL
+
+static inline bool file_ops_is_init(void)
+{
+ return 0;
+}
+
+static inline bool file_ops_init(void)
+{
+ pr_info(FOPS_PREFIX "file_ops is not supported\n");
+ return 0;
+}
+
+void file_ops_exit(void)
+{
+}
+
+#else /* CONFIG_SWAP_HOOK_SYSCALL */
+
+bool file_ops_is_init(void);
+int file_ops_init(void);
+void file_ops_exit(void);
+
+#endif /* CONFIG_SWAP_HOOK_SYSCALL */
+
+#endif /* __FILE_OPS__ */
--- /dev/null
+/**
+ * @author Vyacheslav Cherkashin: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features
+ */
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <ksyms/ksyms.h>
+#include <master/swap_initializer.h>
+#include <writer/event_filter.h>
+#include <swap/hook_syscall.h>
+#include <asm/syscall.h>
+#include "ksf_msg.h"
+#include "ks_features.h"
+#include "syscall_list.h"
+#include "features_data.c"
+#include "writer/kernel_operations.h"
+#include "ks_features_data.h"
+
+
+static void syscall_entry_hendler(struct hook_syscall *self,
+ struct pt_regs *regs)
+{
+ if (check_event(current)) {
+ struct ks_probe *ksp = container_of(self, struct ks_probe, hook);
+ const char *fmt = ksp->args;
+ const unsigned long func_addr = ksp->sys_addr;
+ enum probe_t type = ksp->type;
+
+ ksf_msg_entry(regs, func_addr, type, fmt);
+ }
+}
+
+static void syscall_exit_hendler(struct hook_syscall *self,
+ struct pt_regs *regs)
+{
+ if (check_event(current)) {
+ struct ks_probe *ksp = container_of(self, struct ks_probe, hook);
+ const unsigned long func_addr = ksp->sys_addr;
+ const unsigned long ret_addr = get_regs_ret_func(regs);
+ enum probe_t type = ksp->type;
+
+ ksf_msg_exit(regs, func_addr, ret_addr, type, 'x');
+ }
+}
+
+static int register_syscall(size_t id)
+{
+ int ret = 0;
+
+ if (id >= syscall_cnt)
+ return -EINVAL;
+
+ ksp[id].hook.entry = syscall_entry_hendler;
+ ksp[id].hook.exit = syscall_exit_hendler;
+#ifdef CONFIG_COMPAT
+ /* FIXME: add hook_syscall_reg() */
+ ret = hook_syscall_reg_compat(&ksp[id].hook, ksp[id].id);
+#else
+ ret = hook_syscall_reg(&ksp[id].hook, ksp[id].id);
+#endif
+
+ if (ret) {
+ pr_err("ERROR: cannot register hook '%s' id=%zd sysid=%u\n",
+ get_sys_name(id), id, ksp[id].id);
+ }
+
+ return ret;
+}
+
+static int unregister_syscall(size_t id)
+{
+ if (id >= syscall_cnt)
+ return -EINVAL;
+
+#ifdef CONFIG_COMPAT
+ /* FIXME: add hook_syscall_unreg() */
+ hook_syscall_unreg_compat(&ksp[id].hook);
+#else
+ hook_syscall_unreg(&ksp[id].hook);
+#endif
+
+ return 0;
+}
+
+static int unregister_multiple_syscalls(size_t *id_p, size_t cnt)
+{
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < cnt; i++) {
+ ret = unregister_syscall(id_p[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int init_syscalls(void)
+{
+ size_t i, sys_id;
+ unsigned long addr;
+ const char *name;
+ const char *sys_call_table_name;
+ unsigned long *syscall_table;
+
+#ifdef CONFIG_COMPAT
+ sys_call_table_name = "compat_sys_call_table";
+#else
+ sys_call_table_name = "sys_call_table";
+#endif
+
+ syscall_table = (unsigned long *)swap_ksyms(sys_call_table_name);
+ if (syscall_table == NULL) {
+ pr_warn("WARN: '%s' not found\n", sys_call_table_name);
+ return 0;
+ }
+
+ for (i = 0; i < syscall_cnt; ++i) {
+ name = get_sys_name(i);
+ sys_id = ksp[i].id;
+ addr = syscall_table[sys_id];
+ if (addr == 0)
+ pr_warn("WARN: %s() not found\n", name);
+
+ ksp[i].sys_addr = addr;
+ }
+
+ return 0;
+}
--- /dev/null
+/**
+ * @author Vyacheslav Cherkashin: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features
+ */
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <ksyms/ksyms.h>
+#include <kprobe/swap_kprobes.h>
+#include <master/swap_initializer.h>
+#include <writer/event_filter.h>
+#include "ksf_msg.h"
+#include "ks_features.h"
+#include "features_data.c"
+#include "ks_features_data.h"
+
+
+/* ========================= HANDLERS ========================= */
+static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+
+ if (rp && check_event(current)) {
+ struct ks_probe *ksp = container_of(rp, struct ks_probe, rp);
+ const char *fmt = ksp->args;
+ const unsigned long addr = ksp->rp.kp.addr;
+ enum probe_t type = ksp->type;
+
+ ksf_msg_entry(regs, addr, type, fmt);
+ }
+
+ return 0;
+}
+
+static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct kretprobe *rp = ri->rp;
+
+ if (rp && check_event(current)) {
+ struct ks_probe *ksp = container_of(rp, struct ks_probe, rp);
+ const unsigned long func_addr = rp->kp.addr;
+ const unsigned long ret_addr = (unsigned long)ri->ret_addr;
+ enum probe_t type = ksp->type;
+
+ ksf_msg_exit(regs, func_addr, ret_addr, type, 'x');
+ }
+
+ return 0;
+}
+
+/* ========================= HANDLERS ========================= */
+
+
+
+
+
+static int register_syscall(size_t id)
+{
+ int ret;
+
+ if (ksp[id].rp.kp.addr == 0)
+ return 0;
+
+ ksp[id].rp.entry_handler = entry_handler;
+ ksp[id].rp.handler = ret_handler;
+
+ ret = swap_register_kretprobe(&ksp[id].rp);
+
+ return ret;
+}
+
+static int unregister_syscall(size_t id)
+{
+ if (ksp[id].rp.kp.addr == 0)
+ return 0;
+
+ swap_unregister_kretprobe(&ksp[id].rp);
+
+ return 0;
+}
+
+static int unregister_multiple_syscalls(size_t *id_p, size_t cnt)
+{
+ struct kretprobe **rpp;
+ const size_t end = ((size_t) 0) - 1;
+ size_t i = 0, id;
+ int ret = 0;
+
+ if (cnt == 1)
+ return unregister_syscall(id_p[0]);
+
+ rpp = kmalloc(sizeof(*rpp) * cnt, GFP_KERNEL);
+ --cnt;
+ if (rpp == NULL) {
+ for (; cnt != end; --cnt) {
+ ret = unregister_syscall(id_p[cnt]);
+ if (ret)
+ return ret;
+ }
+ return ret;
+ }
+
+ for (; cnt != end; --cnt) {
+ id = id_p[cnt];
+ if (ksp[id].rp.kp.addr) {
+ rpp[i] = &ksp[id].rp;
+ ++i;
+ }
+ }
+
+ swap_unregister_kretprobes(rpp, i);
+ kfree(rpp);
+
+ return 0;
+}
+
+static int init_syscalls(void)
+{
+ size_t i;
+ unsigned long addr, ni_syscall;
+ const char *name;
+
+ ni_syscall = swap_ksyms("sys_ni_syscall");
+
+ for (i = 0; i < syscall_cnt; ++i) {
+ name = get_sys_name(i);
+ addr = swap_ksyms(name);
+ if (addr == 0) {
+ pr_err("ERROR: %s() not found\n", name);
+ } else if (ni_syscall == addr) {
+ pr_err("WARN: %s is not install\n", name);
+ addr = 0;
+ }
+
+ ksp[i].rp.kp.addr = addr;
+ }
+
+ return 0;
+}
--- /dev/null
+/**
+ * ks_features/ks_features.c
+ * @author Vyacheslav Cherkashin: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features
+ */
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <ksyms/ksyms.h>
+#include <master/swap_initializer.h>
+#include <writer/event_filter.h>
+#include "ksf_msg.h"
+#include "ks_features.h"
+#include "file_ops.h"
+
+#ifdef CONFIG_SWAP_HOOK_SYSCALL
+# include "ks_feature_hook.c"
+#else /* CONFIG_SWAP_HOOK_SYSCALL */
+# include "ks_feature_kprobe.c"
+#endif /* CONFIG_SWAP_HOOK_SYSCALL */
+
+
+
+/* ====================== SWITCH_CONTEXT ======================= */
+static DEFINE_MUTEX(mutex_sc_enable);
+static int sc_enable;
+
+
+#ifdef CONFIG_SWAP_HOOK_SWITCH_TO
+
+#include <swap/hook_switch_to.h>
+
+
+static void ksf_switch(struct task_struct *prev, struct task_struct *next)
+{
+ if (check_event(prev))
+ ksf_switch_entry(prev);
+ if (check_event(next))
+ ksf_switch_exit(next);
+}
+
+static struct swap_hook_ctx hook_ctx = {
+ .hook = ksf_switch,
+};
+
+/**
+ * @brief Get scheduler address.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int init_switch_context(void)
+{
+ return 0;
+}
+
+static int register_ctx_handler(void)
+{
+ return swap_hook_ctx_reg(&hook_ctx);
+}
+
+static void unregister_ctx_handler(void)
+{
+ swap_hook_ctx_unreg(&hook_ctx);
+}
+
+#else /* CONFIG_SWAP_HOOK_SWITCH_TO */
+
+static int switch_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ if (check_event(current))
+ ksf_switch_entry(current);
+
+ return 0;
+}
+
+static int switch_ret_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ if (check_event(current))
+ ksf_switch_exit(current);
+
+ return 0;
+}
+
+/**
+ * @var switch_rp
+ * Kretprobe for scheduler.
+ */
+struct kretprobe switch_rp = {
+ .entry_handler = switch_entry_handler,
+ .handler = switch_ret_handler
+};
+
+/**
+ * @brief Get scheduler address.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int init_switch_context(void)
+{
+ switch_rp.kp.addr = swap_ksyms("__switch_to");
+ if (switch_rp.kp.addr == 0) {
+ printk(KERN_INFO "ERROR: not found '__switch_to'\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int register_ctx_handler(void)
+{
+ return swap_register_kretprobe(&switch_rp);
+}
+
+static void unregister_ctx_handler(void)
+{
+ swap_unregister_kretprobe(&switch_rp);
+}
+#endif /* CONFIG_SWAP_HOOK_SWITCH_TO */
+
+
+/**
+ * @brief Unregisters probe on context switching.
+ *
+ * @return Void.
+ */
+void exit_switch_context(void)
+{
+ if (sc_enable)
+ unregister_ctx_handler();
+}
+
+static int register_switch_context(void)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&mutex_sc_enable);
+ if (sc_enable) {
+ printk(KERN_INFO "switch context profiling is already run!\n");
+ goto unlock;
+ }
+
+ ret = register_ctx_handler();
+ if (ret == 0)
+ sc_enable = 1;
+
+unlock:
+ mutex_unlock(&mutex_sc_enable);
+
+ return ret;
+}
+
+static int unregister_switch_context(void)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_sc_enable);
+ if (sc_enable == 0) {
+ printk(KERN_INFO "switch context profiling is not running!\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ unregister_ctx_handler();
+
+ sc_enable = 0;
+unlock:
+ mutex_unlock(&mutex_sc_enable);
+
+ return ret;
+}
+/* ====================== SWITCH_CONTEXT ======================= */
+
+
+
+
+static void set_pst(struct feature *f, size_t id)
+{
+ ksp[id].type |= f->type;
+}
+
+static void unset_pst(struct feature *f, size_t id)
+{
+ ksp[id].type &= !f->type;
+}
+
+static void do_uninstall_features(struct feature *f, size_t i)
+{
+ int ret;
+ size_t *id_p;
+ size_t id;
+ size_t cnt = 0;
+ const size_t end = ((size_t) 0) - 1;
+
+ id_p = kmalloc(sizeof(id) * (i + 1), GFP_KERNEL);
+ /* NULL check is below in loop */
+
+ for (; i != end; --i) {
+ id = f->feature_list[i];
+
+ if (get_counter(id) == 0) {
+ printk(KERN_INFO "syscall %s not installed\n",
+ get_sys_name(id));
+ kfree(id_p);
+ BUG();
+ }
+
+ dec_counter(id);
+
+ if (get_counter(id) == 0) {
+ if (id_p != NULL) {
+ id_p[cnt] = id;
+ ++cnt;
+ } else {
+ ret = unregister_syscall(id);
+ if (ret)
+ printk(KERN_INFO "syscall %s uninstall error, ret=%d\n",
+ get_sys_name(id), ret);
+ }
+ }
+
+ unset_pst(f, id);
+ }
+
+ if (id_p != NULL) {
+ unregister_multiple_syscalls(id_p, cnt);
+ kfree(id_p);
+ }
+}
+
+static int do_install_features(struct feature *f)
+{
+ int ret;
+ size_t i, id;
+
+ for (i = 0; i < f->cnt; ++i) {
+ id = f->feature_list[i];
+ set_pst(f, id);
+
+ if (get_counter(id) == 0) {
+ ret = register_syscall(id);
+ if (ret) {
+ printk(KERN_INFO "syscall %s install error, ret=%d\n",
+ get_sys_name(id), ret);
+
+ do_uninstall_features(f, --i);
+ return ret;
+ }
+ }
+
+ inc_counter(id);
+ }
+
+ return 0;
+}
+
+static DEFINE_MUTEX(mutex_features);
+
+static int install_features(struct feature *f)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_features);
+ if (f->enable) {
+ printk(KERN_INFO "energy profiling is already run!\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = do_install_features(f);
+
+ f->enable = 1;
+unlock:
+ mutex_unlock(&mutex_features);
+ return ret;
+}
+
+static int uninstall_features(struct feature *f)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_features);
+ if (f->enable == 0) {
+ printk(KERN_INFO "feature[%d] is not running!\n",
+ feature_index(f));
+ ret = -EINVAL;
+ goto unlock;
+ }
+ do_uninstall_features(f, f->cnt - 1);
+ f->enable = 0;
+unlock:
+ mutex_unlock(&mutex_features);
+
+ return ret;
+}
+
+static struct feature *get_feature(enum feature_id id)
+{
+ if (id < 0 || id >= (int)feature_cnt)
+ return NULL;
+
+ return &features[id];
+}
+
+/**
+ * @brief Sets probes related to specified feature.
+ *
+ * @param id Feature id.
+ * @return 0 on success, negative error code on error.
+ */
+int set_feature(enum feature_id id)
+{
+ struct feature *f;
+ int ret;
+
+ switch (id) {
+ case FID_SYSFILE_ACTIVITY:
+ ret = file_ops_init();
+ break;
+ case FID_SWITCH:
+ ret = register_switch_context();
+ break;
+ default:
+ f = get_feature(id);
+ ret = f ? install_features(f) : -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(set_feature);
+
+/**
+ * @brief Unsets probes related to specified feature.
+ *
+ * @param id Feature id.
+ * @return 0 on success, negative error code on error.
+ */
+int unset_feature(enum feature_id id)
+{
+ struct feature *f;
+ int ret = 0;
+
+ switch (id) {
+ case FID_SYSFILE_ACTIVITY:
+ file_ops_exit();
+ break;
+ case FID_SWITCH:
+ ret = unregister_switch_context();
+ break;
+ default:
+ f = get_feature(id);
+ ret = f ? uninstall_features(f) : -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(unset_feature);
+
+static int init_syscall_features(void)
+{
+ return init_syscalls();
+}
+
+static void uninit_syscall_features(void)
+{
+ size_t id;
+
+ for (id = 0; id < syscall_cnt; ++id) {
+ if (get_counter(id) > 0)
+ unregister_syscall(id);
+ }
+}
+
+
+static int once(void)
+{
+ int ret;
+
+ ret = init_switch_context();
+ if (ret)
+ return ret;
+
+ ret = init_syscall_features();
+
+ return ret;
+}
+
+static void core_uninit(void)
+{
+ uninit_syscall_features();
+ exit_switch_context();
+
+ if (file_ops_is_init())
+ file_ops_exit();
+}
+
+SWAP_LIGHT_INIT_MODULE(once, NULL, core_uninit, NULL, NULL);
+
+MODULE_LICENSE("GPL");
+
+/* debug */
+static void print_feature(struct feature *f)
+{
+ size_t i;
+
+ for (i = 0; i < f->cnt; ++i)
+ printk(KERN_INFO " feature[%3zu]: %s\n", i,
+ get_sys_name(f->feature_list[i]));
+}
+
+/**
+ * @brief Prints features.
+ *
+ * @return Void.
+ */
+void print_features(void)
+{
+ int i;
+
+ printk(KERN_INFO "print_features:\n");
+ for (i = 0; i < feature_cnt; ++i) {
+ printk(KERN_INFO "feature: %d\n", i);
+ print_feature(&features[i]);
+ }
+}
+
+/**
+ * @brief Prints all syscalls.
+ *
+ * @return Void.
+ */
+void print_all_syscall(void)
+{
+ int i;
+
+ printk(KERN_INFO "SYSCALL:\n");
+ for (i = 0; i < syscall_cnt; ++i)
+ printk(KERN_INFO " [%2d] %s\n",
+ get_counter(i), get_sys_name(i));
+}
+/* debug */
--- /dev/null
+/**
+ * @file ks_features/ks_features.h
+ * @author Vyacheslav Cherkashin: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features interface declaration.
+ */
+
+
+#ifndef _KS_FEATURES_H
+#define _KS_FEATURES_H
+
+/**
+ * @enum feature_id
+ * Features ids
+ */
+enum feature_id {
+ FID_FILE = 1, /**< File probes */
+ FID_IPC = 2, /**< Hz probes */
+ FID_PROCESS = 3, /**< Process probes */
+ FID_SIGNAL = 4, /**< Signal probes */
+ FID_NET = 5, /**< Network probes */
+ FID_DESC = 6, /**< Description probes */
+ FID_SWITCH = 7, /**< Switch context probes */
+ FID_SYSFILE_ACTIVITY = 8 /**< System file activity */
+};
+
+int set_feature(enum feature_id id);
+int unset_feature(enum feature_id id);
+
+/* debug */
+void print_features(void);
+void print_all_syscall(void);
+/* debug */
+
+#endif /* _KS_FEATURES_H */
--- /dev/null
+/**
+ * @author Vitaliy Cherepanov: SWAP ks_features_data implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features
+ */
+
+#include "syscall_list.h"
+#include "ks_features_data.h"
+#include "ksf_msg.h"
+
+
+/**
+ * @struct ks_probe
+ * @brief Kernel-space probe. Struct used as a container of syscall probes.
+ * @var ks_probe::rp
+ * Pointer to kretprobe.
+ * @var ks_probe::counter
+ * Installed probes counter.
+ * @var ks_probe::args
+ * Pointer to args format string.
+ * @var ks_probe::type
+ * Probe sub type.
+ */
+
+#define CREATE_RP(name) \
+{ \
+ .entry_handler = NULL, \
+ .handler = NULL \
+}
+
+#define CREATE_HOOK_SYSCALL(name) \
+{ \
+ .entry = NULL, \
+ .exit = NULL \
+}
+
+#define SYSCALL_NAME_STR(name) #name
+
+#ifdef CONFIG_SWAP_HOOK_SYSCALL
+
+#include <asm/unistd32.h> /* FIXME: for only arm64 compat mode */
+
+#define X(name__, args__) \
+{ \
+ .hook = CREATE_HOOK_SYSCALL(name__), \
+ .sys_addr = 0xdeadbeef, \
+ .counter = 0, \
+ .args = #args__, \
+ .type = PT_KS_NONE, \
+ .name = SYSCALL_NAME_STR(sys_ ## name__), \
+ .id = __NR_ ## name__, \
+},
+#else /* !CONFIG_SWAP_HOOK_SYSCALL */
+#define X(name__, args__) \
+{ \
+ .rp = CREATE_RP(name__), \
+ .counter = 0, \
+ .args = #args__, \
+ .type = PT_KS_NONE, \
+ .name = SYSCALL_NAME_STR(sys_ ## name__), \
+},
+#endif /* CONFIG_SWAP_HOOK_SYSCALL */
+
+struct ks_probe ksp[syscall_cnt] = {
+ SYSCALL_LIST
+};
+
+#undef X
+
+const char *get_sys_name(size_t id)
+{
+ return ksp[id].name;
+}
+
+int get_counter(size_t id)
+{
+ return ksp[id].counter;
+}
+
+void inc_counter(size_t id)
+{
+ ++ksp[id].counter;
+}
+
+void dec_counter(size_t id)
+{
+ --ksp[id].counter;
+}
+
--- /dev/null
+/**
+ * ks_features/ks_features_data.h
+ * @author Vitaliy Cherepanov: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP kernel features
+ */
+
+/**
+ * @struct ks_probe
+ * @brief Kernel-space probe. Struct used as a container of syscall probes.
+ * @var ks_probe::rp
+ * Pointer to kretprobe.
+ * @var ks_probe::counter
+ * Installed probes counter.
+ * @var ks_probe::args
+ * Pointer to args format string.
+ * @var ks_probe::type
+ * Probe sub type.
+ */
+#ifndef __KS_FEATURE_DATA_H__
+#define __KS_FEATURE_DATA_H__
+
+#include "syscall_list.h"
+
+#ifdef CONFIG_SWAP_HOOK_SYSCALL
+# include <swap/hook_syscall.h>
+#else
+# include "kprobe/swap_kprobes.h"
+#endif
+
+struct ks_probe {
+#ifdef CONFIG_SWAP_HOOK_SYSCALL
+ struct hook_syscall hook;
+ u64 sys_addr;
+#else /* CONFIG_SWAP_HOOK_SYSCALL */
+ struct kretprobe rp;
+#endif /* CONFIG_SWAP_HOOK_SYSCALL */
+
+ int counter;
+ char *args;
+ int type;
+
+ const char *name;
+ unsigned int id;
+};
+
+/**
+ * @enum
+ * Syscall name count defenition
+ */
+#define X(name__, args__) + 1
+enum {
+ syscall_cnt = 0 SYSCALL_LIST
+};
+#undef X
+
+extern struct ks_probe ksp[syscall_cnt];
+
+const char *get_sys_name(size_t id);
+int get_counter(size_t id);
+void inc_counter(size_t id);
+void dec_counter(size_t id);
+
+#endif /* __KS_FEATURE_DATA_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include "ks_map.h"
+
+struct entry {
+ struct rb_node node;
+ void *data;
+};
+
+static inline void *entry_data(struct entry *entry)
+{
+ return entry->data;
+}
+
+static struct entry *alloc_entry(struct map *map, void *data)
+{
+ struct entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+
+ if (entry) {
+ entry->data = data;
+ RB_CLEAR_NODE(&entry->node);
+ }
+
+ return entry;
+}
+
+static void *free_entry(struct map *map, struct entry *entry)
+{
+ void *data = entry_data(entry);
+
+ kfree(entry);
+
+ return data;
+}
+
+static struct entry *__search(struct map *map, void *key)
+{
+ struct rb_root *root = &map->root;
+ struct rb_node *node = root->rb_node;
+ key_func_t key_f = map->key_f;
+ cmp_func_t cmp_f = map->cmp_f;
+
+ while (node) {
+ struct entry *entry = rb_entry(node, struct entry, node);
+ int result = cmp_f(key_f(entry_data(entry)), key);
+
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else
+ return entry;
+ }
+
+ return NULL;
+}
+
+void *search(struct map *map, void *key)
+{
+ struct entry *entry = __search(map, key);
+
+ return entry ? entry_data(entry) : NULL;
+}
+
+static void *__remove(struct map *map, struct entry *entry)
+{
+ struct rb_root *root = &map->root;
+
+ rb_erase(&entry->node, root);
+ RB_CLEAR_NODE(&entry->node);
+ map->size--;
+
+ return free_entry(map, entry);
+}
+
+void *remove(struct map *map, void *key)
+{
+ struct entry *entry = __search(map, key);
+
+ /* Removes entry from the tree but does not free the data */
+ return entry ? __remove(map, entry) : NULL;
+}
+
+static void *__replace(struct map *map, struct entry *old, struct entry *new)
+{
+ struct rb_root *root = &map->root;
+
+ rb_replace_node(&old->node, &new->node, root);
+
+ return free_entry(map, old);
+}
+
+void *replace(struct map *map, void *data)
+{
+ struct entry *old, *new;
+
+ old = __search(map, map->key_f(data));
+ if (old) {
+ new = alloc_entry(map, data);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ return __replace(map, old, new);
+ }
+
+ return ERR_PTR(-ESRCH);
+}
+
+int insert(struct map *map, void *data)
+{
+ struct rb_root *root = &map->root;
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+ key_func_t key_f = map->key_f;
+ cmp_func_t cmp_f = map->cmp_f;
+ void *key = key_f(data);
+ struct entry *entry;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct entry *this = rb_entry(*new, struct entry, node);
+ int result = cmp_f(key_f(entry_data(this)), key);
+
+ parent = *new;
+ if (result < 0)
+ new = &((*new)->rb_left);
+ else if (result > 0)
+ new = &((*new)->rb_right);
+ else /* entry already inserted */
+ return -EEXIST;
+ }
+
+ entry = alloc_entry(map, data);
+ if (!entry)
+ return -ENOMEM;
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&entry->node, parent, new);
+ rb_insert_color(&entry->node, root);
+ map->size++;
+
+ return 0;
+}
+
+int for_each_entry(struct map *map, act_func_t func, void *arg)
+{
+ struct rb_root *root = &map->root;
+ struct rb_node *node = rb_first(root);
+ int ret = 0;
+
+ while (node) {
+ struct entry *entry = rb_entry(node, struct entry, node);
+
+ /* Stop iteration if actor returns non zero */
+ ret = func(entry_data(entry), arg);
+ if (ret)
+ break;
+
+ node = rb_next(node);
+ }
+
+ return ret;
+}
+
+int for_each_entry_reverse(struct map *map, act_func_t func, void *arg)
+{
+ struct rb_root *root = &map->root;
+ struct rb_node *node = rb_last(root);
+ int ret = 0;
+
+ while (node) {
+ struct entry *entry = rb_entry(node, struct entry, node);
+
+ /* Stop iteration if actor returns non zero */
+ ret = func(entry_data(entry), arg);
+ if (ret)
+ break;
+
+ node = rb_prev(node);
+ }
+
+ return ret;
+}
+
+void clear(struct map *map, act_func_t destructor, void *arg)
+{
+ struct rb_root *root = &map->root;
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct entry *entry = rb_entry(node, struct entry, node);
+ void *data = __remove(map, entry);
+
+ /* call the data 'destructor' if supplied */
+ if (destructor)
+ destructor(data, arg);
+
+ node = root->rb_node;
+ }
+
+ WARN(map->size, "ks_map size: %d\n", map->size);
+ map->root = RB_ROOT;
+}
--- /dev/null
+#ifndef __KS_MAP__
+#define __KS_MAP__
+
+#include <linux/rbtree.h>
+
+typedef void *(*key_func_t)(void *);
+typedef int (*cmp_func_t)(void *, void *);
+typedef int (*act_func_t)(void *, void *);
+
+struct map {
+ struct rb_root root;
+ int size;
+ key_func_t key_f;
+ cmp_func_t cmp_f;
+};
+
+#define __MAP_INITIALIZER(_key_f, _cmp_f) \
+ { \
+ .root = RB_ROOT, \
+ .size = 0, \
+ .key_f = _key_f, \
+ .cmp_f = _cmp_f \
+ }
+
+#define DEFINE_MAP(_name, _key_f, _cmp_f) \
+ struct map _name = __MAP_INITIALIZER(_key_f, _cmp_f)
+
+void *search(struct map *map, void *key);
+void *remove(struct map *map, void *key);
+void *replace(struct map *map, void *data);
+int insert(struct map *map, void *data);
+int for_each_entry(struct map *map, act_func_t func, void *arg);
+int for_each_entry_reverse(struct map *map, act_func_t act, void *arg);
+void clear(struct map *map, act_func_t destructor, void *arg);
+
+#endif /* __KS_MAP__ */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <writer/swap_msg.h>
+#include <writer/kernel_operations.h>
+#include "ksf_msg.h"
+
+
+#define KSF_PREFIX KERN_INFO "[KSF] "
+
+
+
+
+
+/* ============================================================================
+ * = MSG_SYSCALL_* (ENTRY/EXIT) =
+ * ============================================================================
+ */
+struct msg_sys_header {
+ u32 pid;
+ u32 tid;
+ u32 probe_type;
+ u64 pc_addr;
+ u64 caller_pc_addr;
+ u32 cpu_num;
+} __packed;
+
+struct msg_sys_entry {
+ struct msg_sys_header h;
+ u32 cnt_args;
+ char args[0];
+} __packed;
+
+struct msg_sys_exit {
+ struct msg_sys_header h;
+ char ret_val[0];
+} __packed;
+
+
+static void pack_header(struct msg_sys_header *h, unsigned long func_addr,
+ unsigned long ret_addr, enum probe_t type)
+{
+ struct task_struct *task = current;
+
+ h->pid = task->tgid;
+ h->tid = task->pid;
+ h->probe_type = (u32)type;
+ h->pc_addr = func_addr;
+ h->caller_pc_addr = ret_addr;
+ h->cpu_num = raw_smp_processor_id();
+}
+
+static void pack_entry_header(struct msg_sys_entry *e, struct pt_regs *regs,
+ unsigned long func_addr, enum probe_t type,
+ const char *fmt)
+{
+ pack_header(&e->h, func_addr, get_regs_ret_func(regs), type);
+ e->cnt_args = strlen(fmt);
+}
+
+static void pack_exit_header(struct msg_sys_exit *e, unsigned long func_addr,
+ unsigned long ret_addr, enum probe_t type)
+{
+ pack_header(&e->h, func_addr, ret_addr, type);
+}
+
+void ksf_msg_entry(struct pt_regs *regs, unsigned long func_addr,
+ enum probe_t type, const char *fmt)
+{
+ int ret;
+ struct swap_msg *m;
+ struct msg_sys_entry *ent;
+ size_t size;
+
+ m = swap_msg_get(MSG_SYSCALL_ENTRY);
+
+ ent = swap_msg_payload(m);
+ pack_entry_header(ent, regs, func_addr, type, fmt);
+
+ size = swap_msg_size(m) - sizeof(*ent);
+ ret = swap_msg_pack_args(ent->args, size, fmt, regs);
+ if (ret < 0) {
+ printk(KSF_PREFIX "ERROR: arguments packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, sizeof(*ent) + ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+void ksf_msg_exit(struct pt_regs *regs, unsigned long func_addr,
+ unsigned long ret_addr, enum probe_t type, char ret_type)
+{
+ int ret;
+ struct swap_msg *m;
+ struct msg_sys_exit *ext;
+ size_t size;
+
+ m = swap_msg_get(MSG_SYSCALL_EXIT);
+
+ ext = swap_msg_payload(m);
+ pack_exit_header(ext, func_addr, ret_addr, type);
+
+ size = swap_msg_size(m) - sizeof(*ext);
+ ret = swap_msg_pack_ret_val(ext->ret_val, size, ret_type, regs);
+ if (ret < 0) {
+ printk(KSF_PREFIX "ERROR: ret value packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, sizeof(*ext) + ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_FILE_FUNCTION_* (ENTRY/EXIT) =
+ * ============================================================================
+ */
+struct msg_file_entry {
+ u32 pid;
+ u32 tid;
+ u32 fd;
+ u32 event_type;
+ char file_path[0];
+} __packed;
+
+enum file_info {
+ FI_GENIRAL = 0,
+ FI_OPEN = 1,
+ FI_LOCK = 2
+};
+
+static int pack_file_entry_head(void *data, size_t size, enum file_info info,
+ int fd, enum file_api_t api, const char *path)
+{
+ struct msg_file_entry *ent = (struct msg_file_entry *)data;
+ struct task_struct *task = current;
+ size_t len, old_size = size;
+
+ ent->pid = task->tgid;
+ ent->tid = task->pid;
+ ent->fd = fd;
+ ent->event_type = api;
+
+ size -= sizeof(*ent);
+ len = strlen(path);
+ if (size < len + 1)
+ return -ENOMEM;
+
+ memcpy(ent->file_path, path, len);
+ ent->file_path[len] = '\0';
+
+ size -= len + 1;
+ data += old_size - size;
+
+ if (size < 4)
+ return -ENOMEM;
+
+ *((u32 *)data) = (u32)info;
+ size -= 4;
+
+ return old_size - size;
+}
+
+
+
+void ksf_msg_file_entry(int fd, enum file_api_t api, const char *path)
+{
+ int ret;
+ void *p;
+ size_t size;
+ struct swap_msg *m;
+
+ m = swap_msg_get(MSG_FILE_FUNCTION_ENTRY);
+ p = swap_msg_payload(m);
+ size = swap_msg_size(m);
+
+ ret = pack_file_entry_head(p, size, FI_GENIRAL, fd, api, path);
+ if (ret < 0) {
+ printk(KSF_PREFIX "buffer is too small\n");
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+void ksf_msg_file_entry_open(int fd, enum file_api_t api, const char *path,
+ const char __user *ofile)
+{
+ long n;
+ int ret;
+ void *p;
+ size_t size;
+ struct swap_msg *m;
+
+ m = swap_msg_get(MSG_FILE_FUNCTION_ENTRY);
+ p = swap_msg_payload(m);
+ size = swap_msg_size(m);
+
+ ret = pack_file_entry_head(p, size, FI_OPEN, fd, api, path);
+ if (ret < 0) {
+ printk(KSF_PREFIX "buffer is too small\n");
+ goto put_msg;
+ }
+
+ size -= ret;
+ p += ret;
+
+ n = strncpy_from_user(p, ofile, size);
+ if (n < 0) {
+ printk(KSF_PREFIX "cannot copy ofile\n");
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret + n + 1);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+struct lock_arg {
+ u32 type;
+ u32 whence;
+ u64 start;
+ u64 len;
+} __packed;
+
+void ksf_msg_file_entry_lock(int fd, enum file_api_t api, const char *path,
+ int type, int whence, s64 start, s64 len)
+{
+ int ret;
+ void *p;
+ size_t size;
+ struct swap_msg *m;
+ struct lock_arg *arg;
+
+ m = swap_msg_get(MSG_FILE_FUNCTION_ENTRY);
+ p = swap_msg_payload(m);
+ size = swap_msg_size(m);
+
+ ret = pack_file_entry_head(p, size, FI_LOCK, fd, api, path);
+ if (ret < 0) {
+ printk(KSF_PREFIX "buffer is too small\n");
+ goto put_msg;
+ }
+
+ size -= ret;
+ p += ret;
+
+ if (size < sizeof(*arg)) {
+ printk(KSF_PREFIX "buffer is too small\n");
+ goto put_msg;
+ }
+
+ arg = (struct lock_arg *)p;
+ arg->type = (u32)type;
+ arg->whence = (u32)whence;
+ arg->start = (u64)start;
+ arg->len = (u64)len;
+
+ swap_msg_flush(m, ret + sizeof(*arg));
+
+put_msg:
+ swap_msg_put(m);
+}
+
+
+struct msg_file_exit {
+ u32 pid;
+ u32 tid;
+ char ret_val[0];
+} __packed;
+
+void ksf_msg_file_exit(struct pt_regs *regs, char ret_type)
+{
+ struct task_struct *task = current;
+ int ret;
+ struct swap_msg *m;
+ struct msg_file_exit *ext;
+ size_t size;
+
+ m = swap_msg_get(MSG_FILE_FUNCTION_EXIT);
+
+ ext = swap_msg_payload(m);
+ ext->pid = task->tgid;
+ ext->tid = task->pid;
+
+ size = swap_msg_size(m) - sizeof(*ext);
+ ret = swap_msg_pack_ret_val(ext->ret_val, size, ret_type, regs);
+ if (ret < 0) {
+ printk(KSF_PREFIX "ERROR: ret value packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, sizeof(*ext) + ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_FILE_FUNCTION_* (ENTRY/EXIT) =
+ * ============================================================================
+ */
+struct msg_context_switch {
+ u64 pc_addr;
+ u32 pid;
+ u32 tid;
+ u32 cpu_num;
+} __packed;
+
+static void context_switch(struct task_struct *task, enum swap_msg_id id)
+{
+ struct swap_msg *m;
+ struct msg_context_switch *mcs;
+ void *p;
+
+ m = swap_msg_get(id);
+ p = swap_msg_payload(m);
+
+ mcs = p;
+ mcs->pc_addr = 0;
+ mcs->pid = task->tgid;
+ mcs->tid = task->pid;
+ mcs->cpu_num = raw_smp_processor_id();
+
+ swap_msg_flush_wakeupoff(m, sizeof(*mcs));
+ swap_msg_put(m);
+}
+
+void ksf_switch_entry(struct task_struct *task)
+{
+ context_switch(task, MSG_CONTEXT_SWITCH_ENTRY);
+}
+
+void ksf_switch_exit(struct task_struct *task)
+{
+ context_switch(task, MSG_CONTEXT_SWITCH_EXIT);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _KSF_MSG_H
+#define _KSF_MSG_H
+
+
+enum probe_t {
+ PT_KS_NONE = 0x00,
+ PT_KS_FILE = 0x01,
+ PT_KS_IPC = 0x02,
+ PT_KS_PROCESS = 0x04,
+ PT_KS_SIGNAL = 0x08,
+ PT_KS_NETWORK = 0x10,
+ PT_KS_DESC = 0x20
+};
+
+
+enum file_api_t {
+ FOPS_OPEN = 0,
+ FOPS_CLOSE = 1,
+ FOPS_READ_BEGIN = 2,
+ FOPS_READ_END = 3,
+ FOPS_READ = FOPS_READ_BEGIN,
+ FOPS_WRITE_BEGIN = 4,
+ FOPS_WRITE_END = 5,
+ FOPS_WRITE = FOPS_WRITE_BEGIN,
+ FOPS_DIRECTORY = 6,
+ FOPS_PERMS = 7,
+ FOPS_OTHER = 8,
+ FOPS_SEND = 9,
+ FOPS_RECV = 10,
+ FOPS_OPTION = 11,
+ FOPS_MANAGE = 12,
+ FOPS_LOCK_START = 14, /* 13 */
+ FOPS_LOCK_END = 15,
+ FOPS_LOCK_RELEASE = 16
+};
+
+
+struct pt_regs;
+
+
+void ksf_msg_entry(struct pt_regs *regs, unsigned long func_addr,
+ enum probe_t type, const char *fmt);
+void ksf_msg_exit(struct pt_regs *regs, unsigned long func_addr,
+ unsigned long ret_addr, enum probe_t type, char ret_type);
+
+void ksf_msg_file_entry(int fd, enum file_api_t api, const char *path);
+void ksf_msg_file_entry_open(int fd, enum file_api_t api, const char *path,
+ const char __user *ofile);
+void ksf_msg_file_entry_lock(int fd, enum file_api_t api, const char *path,
+ int type, int whence, s64 start, s64 len);
+void ksf_msg_file_exit(struct pt_regs *regs, char ret_type);
+
+void ksf_switch_entry(struct task_struct *task);
+void ksf_switch_exit(struct task_struct *task);
+
+
+#endif /* _KSF_MSG_H */
--- /dev/null
+/**
+ * @file ks_features/syscall_list.h
+ * @author Vyacheslav Cherkashin: SWAP ks_features implement
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Syscalls list.
+ */
+
+
+#ifndef _SYSCALL_LIST_H
+#define _SYSCALL_LIST_H
+
+#define SYSCALL_LIST \
+ X(accept4, dpdd) \
+ X(accept, dpd) \
+ X(access, sd) \
+ X(acct, s) \
+ X(bind, dpd) \
+ X(chdir, s) \
+ X(chmod, sd) \
+/* TODO: X(chown16, sdd) */ \
+ X(chown, sdd) \
+ X(chroot, s) \
+ X(clone, ddddd) \
+ X(connect, dpd) \
+ X(creat, sd) \
+ X(dup3, ddd) \
+ X(epoll_create1, d) \
+ X(epoll_ctl, dddp) \
+ X(epoll_pwait, dpddpx) \
+ X(epoll_wait, dpdd) \
+ X(eventfd2, dd) \
+ X(eventfd, d) \
+ X(execve, spp) \
+ X(exit, d) \
+ X(exit_group, d) \
+ X(faccessat, dsd) \
+/* TODO: X(fadvise64_64, dxxd) */ \
+ X(fallocate, ddxx) \
+ X(fanotify_init, dd) \
+ X(fanotify_mark, ddxds) \
+ X(fchmodat, dsd) \
+ X(fchownat, dsddd) \
+ X(fgetxattr, dspx) \
+ X(flistxattr, dpx) \
+ X(fork, /* empty */) \
+ X(fremovexattr, ds) \
+ X(fstat64, xp) \
+ X(ftruncate64, dx) \
+ X(futimesat, dsp) \
+ X(getcwd, px) \
+ X(getpeername, dpd) \
+ X(getsockname, dpd) \
+ X(getsockopt, dddpd) \
+ X(getxattr, sspx) \
+ X(inotify_add_watch, dsd) \
+ X(inotify_init, /* empty */) \
+ X(inotify_init1, d) \
+ X(inotify_rm_watch, dd) \
+/* TODO: X(ipc, ddxxpx) */\
+ X(kill, dd) \
+ X(linkat, dsdsd) \
+ X(link, ss) \
+ X(listen, dd) \
+ X(listxattr, spx) \
+ X(lstat64, sp) \
+/* TODO: X(lstat, sp) */ \
+ X(mkdirat, dsd) \
+ X(mkdir, sd) \
+ X(mknodat, dsdd) \
+ X(mknod, sdd) \
+/* TODO: X(mmap_pgoff, xxxxxx) */ \
+ X(mount, pppxp) \
+ X(msgctl, ddp) \
+ X(msgget, dd) \
+ X(msgrcv, dpxxd) \
+ X(msgsnd, dpxd) \
+ X(name_to_handle_at, dspdd) \
+/* TODO: X(newfstatat, dspd) */ \
+/* TODO: X(old_mmap, p) */ \
+ X(openat, dsdd) \
+ X(open_by_handle_at, dpd) \
+ X(open, sdd) \
+ X(pause, /* empty */) \
+ X(pipe2, dd) \
+ X(ppoll, pdpp) \
+ X(pread64, dpxx) \
+ X(preadv, xpxxx) \
+ X(pselect6, dxxxpp) \
+ X(pwrite64, dsxx) \
+ X(pwritev, xpxxx) \
+ X(readlinkat, dspd) \
+ X(readlink, spd) \
+ X(recv, dpxd) \
+ X(recvfrom, dpxdpd) \
+ X(recvmmsg, dpddp) \
+ X(recvmsg, dpd) \
+ X(removexattr, ss) \
+ X(renameat, dsds) \
+ X(rename, ss) \
+ X(rmdir, s) \
+ X(rt_sigaction, dpp) \
+ X(rt_sigprocmask, dppx) \
+ X(rt_sigsuspend, px) \
+ X(rt_sigtimedwait, pppx) \
+ X(rt_tgsigqueueinfo, dddp) \
+ X(semctl, dddx) \
+ X(semget, ddd) \
+ X(semop, dpd) \
+ X(semtimedop, dpdp) \
+ X(send, dpxd) \
+ X(sendfile64, ddlxx) \
+ X(sendfile, ddxx) \
+ X(sendmmsg, dpdd) \
+ X(sendmsg, dpd) \
+ X(sendto, dpxdpd) \
+ X(setns, dd) \
+ X(setsockopt, dddpd) \
+ X(setxattr, sspxd) \
+ X(shmat, dpd) \
+ X(shmctl, ddp) \
+ X(shmdt, p) \
+ X(shmget, dxd) \
+ X(shutdown, dd) \
+ X(sigaction, dpp) \
+/* TODO: X(sigaltstack, pp) */ \
+/* TODO: X(signal, dp) */ \
+ X(signalfd4, dpxd) \
+ X(signalfd, dpx) \
+ X(sigpending, p) \
+ X(sigprocmask, dpp) \
+/* TODO: X(sigsuspend, ddp) */ \
+/* TODO: X(sigsuspend, p) */ \
+/* TODO: X(socketcall, dx) */\
+ X(socket, ddd) \
+ X(socketpair, dddd) \
+ X(splice, dxdxxd) \
+ X(stat64, sp) \
+ X(statfs64, sxp) \
+ X(statfs, sp) \
+/* TODO: X(stat, sp) */ \
+ X(swapoff, s) \
+ X(swapon, sd) \
+ X(symlinkat, sds) \
+ X(symlink, ss) \
+ X(syncfs, d) \
+ X(tee, ddxd) \
+ X(tgkill, ddd) \
+ X(timerfd_create, dd) \
+ X(timerfd_gettime, dp) \
+ X(timerfd_settime, ddpp) \
+ X(truncate64, sx) \
+ X(truncate, sx) \
+/* TODO: X(umount, pd) */\
+ X(unlinkat, dsd) \
+ X(unlink, s) \
+ X(unshare, x) \
+ X(uselib, s) \
+ X(utimensat, dspd) \
+/* TODO: X(utime, pp) */\
+ X(utimes, pp) \
+ X(vfork, /* empty */) \
+ X(vmsplice, dpxd) \
+ X(wait4, dddp) \
+ X(waitid, ddpdp)
+/* TODO: X(waitpid, ddd) */
+
+#endif /* _SYSCALL_LIST_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
+
+obj-m := swap_ks_manager.o
+swap_ks_manager-y := ks_manager.o
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/ks_manager/ks_manager.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <kprobe/swap_kprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include "ks_manager.h"
+
+struct probe {
+ struct hlist_node hlist;
+ struct kern_probe p;
+};
+
+static HLIST_HEAD(list_probes);
+
+static struct probe *create_probe(unsigned long addr, void *pre_handler,
+ void *jp_handler, void *rp_handler)
+{
+ struct probe *p = kzalloc(sizeof(*p), GFP_KERNEL);
+
+ if (p == NULL)
+ return NULL;
+
+ p->p.jp.kp.addr = p->p.rp.kp.addr = addr;
+ p->p.jp.pre_entry = pre_handler;
+ p->p.jp.entry = jp_handler;
+ p->p.rp.handler = rp_handler;
+ INIT_HLIST_NODE(&p->hlist);
+
+ return p;
+}
+
+static void free_probe(struct probe *p)
+{
+ kfree(p);
+}
+
+static void add_probe_to_list(struct probe *p)
+{
+ hlist_add_head(&p->hlist, &list_probes);
+}
+
+static void remove_probe_to_list(struct probe *p)
+{
+ hlist_del(&p->hlist);
+}
+
+static struct probe *find_probe(unsigned long addr)
+{
+ struct probe *p;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ /* check if such probe does exist */
+ swap_hlist_for_each_entry(p, node, &list_probes, hlist)
+ if ((unsigned long)p->p.jp.kp.addr == addr)
+ return p;
+
+ return NULL;
+}
+
+int ksm_register_probe(unsigned long addr, void *pre_handler,
+ void *jp_handler, void *rp_handler)
+{
+ int ret;
+ struct probe *p;
+
+ p = create_probe(addr, pre_handler, jp_handler, rp_handler);
+ if (!p)
+ return -ENOMEM;
+
+ ret = swap_register_jprobe(&p->p.jp);
+ if (ret)
+ goto free;
+
+ ret = swap_register_kretprobe(&p->p.rp);
+ if (ret)
+ goto unregister_jprobe;
+
+ add_probe_to_list(p);
+ return 0;
+
+unregister_jprobe:
+ swap_unregister_jprobe(&p->p.jp);
+free:
+ free_probe(p);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ksm_register_probe);
+
+static void do_ksm_unregister_probe(struct probe *p)
+{
+ remove_probe_to_list(p);
+ swap_unregister_kretprobe(&p->p.rp);
+ swap_unregister_jprobe(&p->p.jp);
+ free_probe(p);
+}
+
+int ksm_unregister_probe(unsigned long addr)
+{
+ struct probe *p;
+
+ p = find_probe(addr);
+ if (p == NULL)
+ return -EINVAL;
+
+ do_ksm_unregister_probe(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ksm_unregister_probe);
+
+int ksm_unregister_probe_all(void)
+{
+ struct probe *p;
+ struct hlist_node *n;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry_safe(p, node, n, &list_probes, hlist) {
+ do_ksm_unregister_probe(p);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ksm_unregister_probe_all);
+
+static int __init init_ks_manager(void)
+{
+ return 0;
+}
+
+static void __exit exit_ks_manager(void)
+{
+ ksm_unregister_probe_all();
+}
+
+module_init(init_ks_manager);
+module_exit(exit_ks_manager);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+#ifndef _KS_MANAGER_H
+#define _KS_MANAGER_H
+
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/ks_manager/ks_manager.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include <kprobe/swap_kprobes.h>
+
+struct kern_probe {
+ struct jprobe jp;
+ struct kretprobe rp;
+};
+
+int ksm_register_probe(unsigned long addr, void *pre_handler,
+ void *jp_handler, void *rp_handler);
+int ksm_unregister_probe(unsigned long addr);
+
+int ksm_unregister_probe_all(void);
+
+#endif /* _KS_MANAGER_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_ksyms.o
+swap_ksyms-y := ksyms_module.o
+
+ifeq ($(CONFIG_KALLSYMS),y)
+ swap_ksyms-y += ksyms.o
+else
+ swap_ksyms-y += no_ksyms.o
+endif
--- /dev/null
+/**
+ * @file ksyms/ksyms.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP ksyms module.
+ */
+
+
+#include "ksyms.h"
+#include "ksyms_init.h"
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/percpu.h>
+
+/**
+ * @struct symbol_data
+ * @brief Stores symbols data.
+ * @var symbol_data::name
+ * Pointer to symbol name string.
+ * @var symbol_data::len
+ * Symbol name length.
+ * @var symbol_data::addr
+ * Symbol address.
+ */
+struct symbol_data {
+ const char *name;
+ size_t len;
+ unsigned long addr;
+};
+
+static int symbol_cb(void *data, const char *sym, struct module *mod,
+ unsigned long addr)
+{
+ struct symbol_data *sym_data_p = (struct symbol_data *)data;
+
+ /* We expect that real symbol name should have at least the same
+ * length as symbol name we are looking for. */
+ if (strncmp(sym_data_p->name, sym, sym_data_p->len) == 0) {
+ sym_data_p->addr = addr;
+ /* Return != 0 to stop loop over the symbols */
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Search of symbol address based on substring.
+ *
+ * @param name Pointer to the substring.
+ * @return Symbol address.
+ */
+unsigned long swap_ksyms_substr(const char *name)
+{
+ struct symbol_data sym_data = {
+ .name = name,
+ .len = strlen(name),
+ .addr = 0
+ };
+ kallsyms_on_each_symbol(symbol_cb, (void *)&sym_data);
+
+ return sym_data.addr;
+}
+EXPORT_SYMBOL_GPL(swap_ksyms_substr);
--- /dev/null
+/**
+ * @file ksyms/ksyms.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @sectoin DESCRIPTION
+ *
+ * SWAP symbols searching module.
+ */
+
+#ifndef __KSYMS_H__
+#define __KSYMS_H__
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
+#include <linux/autoconf.h>
+#else
+#include <generated/autoconf.h>
+#endif
+
+#include <linux/kallsyms.h>
+
+#ifdef CONFIG_KALLSYMS
+
+static inline int swap_get_ksyms(void)
+{
+ return 0;
+}
+
+static inline void swap_put_ksyms(void)
+{
+}
+
+static inline unsigned long swap_ksyms(const char *name)
+{
+ return kallsyms_lookup_name(name);
+}
+
+#else /* !CONFIG_KALLSYMS */
+
+int swap_get_ksyms(void);
+void swap_put_ksyms(void);
+unsigned long swap_ksyms(const char *name);
+
+#endif /*CONFIG_KALLSYMS*/
+
+unsigned long swap_ksyms_substr(const char *name);
+
+#endif /*__KSYMS_H__*/
--- /dev/null
+/**
+ * @file ksyms/ksyms_init.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section LICENSE
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP symbols searching module initialization interface.
+ */
+
+#ifndef __KSYMS_INIT_H__
+#define __KSYMS_INIT_H__
+
+#ifdef CONFIG_KALLSYMS
+
+static inline int ksyms_init(void)
+{
+ return 0;
+}
+
+static inline void ksyms_exit(void)
+{
+}
+
+#else /* CONFIG_KALLSYMS */
+
+int ksyms_init(void);
+void ksyms_exit(void);
+
+#endif /* CONFIG_KALLSYMS */
+
+#endif /* __KSYMS_INIT_H__ */
--- /dev/null
+/**
+ * @file ksyms/ksyms_module.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP symbols searching module initialization implementation.
+ */
+
+#include "ksyms_init.h"
+
+#include <linux/module.h>
+
+/**
+ * @brief Init ksyms module.
+ *
+ * @return 0 on success.
+ */
+int __init swap_ksyms_init(void)
+{
+ int ret = ksyms_init();
+
+ printk(KERN_INFO "SWAP_KSYMS: Module initialized\n");
+
+ return ret;
+}
+
+/**
+ * @brief Exit ksyms module.
+ *
+ * @return Void.
+ */
+void __exit swap_ksyms_exit(void)
+{
+ ksyms_exit();
+
+ printk(KERN_INFO "SWAP_KSYMS: Module uninitialized\n");
+}
+
+module_init(swap_ksyms_init);
+module_exit(swap_ksyms_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP ksyms module");
+MODULE_AUTHOR("Vyacheslav Cherkashin <v.cherkashin@samaung.com>");
--- /dev/null
+/**
+ * @file ksyms/no_ksyms.c
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP symbols searching implementation.
+ */
+
+#include "ksyms.h"
+#include "ksyms_init.h"
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/semaphore.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+
+/**
+ * @def KSYMS_ERR
+ * Error message define.
+ */
+#define KSYMS_ERR(format, args...) \
+ do { \
+ char *f = __FILE__; \
+ char *n = strrchr(f, '/'); \
+ printk(KERN_INFO "%s:%u \'%s\' ERROR: " format "\n" , \
+ (n) ? n+1 : f, __LINE__, __func__, ##args); \
+ } while (0)
+
+/**
+ * @struct sys_map_item
+ * @brief System map list item info.
+ * @var sys_map_item::list
+ * List pointer.
+ * @var sys_map_item::addr
+ * Symbol address.
+ * @var sys_map_item::name
+ * Symbol name.
+ */
+struct sys_map_item {
+ struct list_head list;
+
+ unsigned long addr;
+ char *name;
+};
+
+static char *sm_path;
+module_param(sm_path, charp, 0);
+
+/**
+ * @var smi_list
+ * List of sys_map_item.
+ */
+LIST_HEAD(smi_list);
+static struct file *file;
+
+static int cnt_init_sm;
+
+/**
+ * @var cnt_init_sm_lock
+ * System map items list lock.
+ */
+DEFINE_SEMAPHORE(cnt_init_sm_lock);
+
+static int file_open(void)
+{
+ struct file *f = filp_open(sm_path, O_RDONLY, 0);
+
+ if (IS_ERR(f)) {
+ KSYMS_ERR("cannot open file \'%s\'", sm_path);
+ return PTR_ERR(f);
+ }
+
+ file = f;
+
+ return 0;
+}
+
+static void file_close(void)
+{
+ if (file) {
+ int ret = filp_close(file, NULL);
+ file = NULL;
+
+ if (ret) {
+ KSYMS_ERR("while closing file \'%s\' err=%d",
+ sm_path, ret);
+ }
+ }
+}
+
+static int file_check(void)
+{
+ int ret = file_open();
+ if (ret == 0)
+ file_close();
+
+ return ret;
+}
+
+static long file_size(struct file *file)
+{
+ struct kstat st;
+ if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
+ return -1;
+
+ if (!S_ISREG(st.mode))
+ return -1;
+
+ if (st.size != (long)st.size)
+ return -1;
+
+ return st.size;
+}
+
+static struct sys_map_item *create_smi(unsigned long addr, const char *name)
+{
+ struct sys_map_item *smi = kmalloc(sizeof(*smi), GFP_KERNEL);
+
+ if (smi == NULL) {
+ KSYMS_ERR("not enough memory");
+ return NULL;
+ }
+
+ smi->name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (smi->name == NULL) {
+ kfree(smi);
+ KSYMS_ERR("not enough memory");
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&smi->list);
+ smi->addr = addr;
+ strcpy(smi->name, name);
+
+ return smi;
+}
+
+static void free_smi(struct sys_map_item *smi)
+{
+ kfree(smi->name);
+ kfree(smi);
+}
+
+static void add_smi(struct sys_map_item *smi)
+{
+ list_add_tail(&smi->list, &smi_list);
+}
+
+static int is_endline(char c)
+{
+ return c == '\n' || c == '\r' || c == '\0';
+}
+
+static int is_symbol_attr(char c)
+{
+ return c == 't' || c == 'T';
+}
+
+static struct sys_map_item *get_sys_map_item(char *begin, char *end)
+{
+ struct sys_map_item *smi = NULL;
+ int n, len = end - begin;
+ unsigned long addr;
+ char attr, name[128], *line;
+
+ line = kmalloc(len + 1, GFP_KERNEL);
+ memcpy(line, begin, len);
+ line[len] = '\0';
+
+ n = sscanf(line, "%lx %c %127s", &addr, &attr, name);
+ name[127] = '\0';
+
+ if (n != 3) {
+ KSYMS_ERR("parsing line: \"%s\"", line);
+ attr = '\0';
+ }
+
+ kfree(line);
+
+ if (is_symbol_attr(attr))
+ smi = create_smi(addr, name);
+
+ return smi;
+}
+
+
+static void parsing(char *buf, int size)
+{
+ struct sys_map_item *smi;
+ char *start, *end, *c;
+
+ start = buf;
+ end = buf + size;
+
+ for (c = start; c < end; ++c) {
+ if (is_endline(*c)) {
+ smi = get_sys_map_item(start, c);
+ if (smi)
+ add_smi(smi);
+
+ for (start = c; c < end; ++c) {
+ if (!is_endline(*c)) {
+ start = c;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static int create_sys_map(void)
+{
+ char *data;
+ long size;
+ int ret = file_open();
+
+ if (ret)
+ return ret;
+
+ size = file_size(file);
+ if (size < 0) {
+ KSYMS_ERR("cannot get file size");
+ ret = size;
+ goto close;
+ }
+
+ data = vmalloc(size);
+ if (data == NULL) {
+ KSYMS_ERR("not enough memory");
+ ret = -1;
+ goto close;
+ }
+
+ if (kernel_read(file, 0, data, size) != size) {
+ KSYMS_ERR("reading file %s", sm_path);
+ ret = -1;
+ goto free;
+ }
+
+ parsing(data, size);
+
+free:
+ vfree(data);
+
+close:
+ file_close();
+
+ return 0;
+}
+
+static void free_sys_map(void)
+{
+ struct sys_map_item *smi, *n;
+ list_for_each_entry_safe(smi, n, &smi_list, list) {
+ list_del(&smi->list);
+ free_smi(smi);
+ }
+}
+
+/**
+ * @brief Generates symbols list.
+ *
+ * @return 0 on success.
+ */
+int swap_get_ksyms(void)
+{
+ int ret = 0;
+
+ down(&cnt_init_sm_lock);
+ if (cnt_init_sm == 0)
+ ret = create_sys_map();
+
+ ++cnt_init_sm;
+ up(&cnt_init_sm_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_get_ksyms);
+
+/**
+ * @brief Frees symbols list.
+ *
+ * @return Void.
+ */
+void swap_put_ksyms(void)
+{
+ down(&cnt_init_sm_lock);
+ --cnt_init_sm;
+ if (cnt_init_sm == 0)
+ free_sys_map();
+
+ if (cnt_init_sm < 0) {
+ KSYMS_ERR("cnt_init_sm=%d", cnt_init_sm);
+ cnt_init_sm = 0;
+ }
+
+ up(&cnt_init_sm_lock);
+}
+EXPORT_SYMBOL_GPL(swap_put_ksyms);
+
+/**
+ * @brief Searches for symbol by its exact name.
+ *
+ * @param name Pointer the name string.
+ * @return Symbol's address.
+ */
+unsigned long swap_ksyms(const char *name)
+{
+ struct sys_map_item *smi;
+
+ list_for_each_entry(smi, &smi_list, list) {
+ if (strcmp(name, smi->name) == 0)
+ return smi->addr;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swap_ksyms);
+
+/**
+ * @brief Searches for symbol by substring of its name.
+ *
+ * @param name Pointer to the name substring.
+ * @return Symbol's address.
+ */
+unsigned long swap_ksyms_substr(const char *name)
+{
+ struct sys_map_item *smi;
+ size_t len = strlen(name);
+
+ list_for_each_entry(smi, &smi_list, list) {
+ if (strncmp(name, smi->name, len) == 0)
+ return smi->addr;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swap_ksyms_substr);
+
+/**
+ * @brief SWAP ksyms module initialization.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int ksyms_init(void)
+{
+ int ret = 0;
+
+ if (sm_path == NULL) {
+ KSYMS_ERR("sm_path=NULL");
+ return -EINVAL;
+ }
+
+ ret = file_check();
+ if (ret)
+ return -EINVAL;
+
+ /* TODO: calling func 'swap_get_ksyms' in
+ * module used func 'swap_ksyms' */
+ swap_get_ksyms();
+
+ return 0;
+}
+
+/**
+ * @brief SWAP ksyms module deinitialization.
+ *
+ * @return Void.
+ */
+void ksyms_exit(void)
+{
+ down(&cnt_init_sm_lock);
+
+ if (cnt_init_sm > 0)
+ free_sys_map();
+
+ up(&cnt_init_sm_lock);
+}
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_loader.o
+swap_loader-y := loader_module.o \
+ loader_debugfs.o \
+ loader_storage.o \
+ loader_pd.o
--- /dev/null
+#ifndef __LOADER__
+#define __LOADER__
+
+struct pd_t;
+struct hd_t;
+struct uretprobe_instance;
+struct task_struct;
+
+/* process loader states */
+enum ps_t {
+ NOT_LOADED,
+ LOADING,
+ LOADED,
+ FAILED,
+ ERROR
+};
+
+void loader_module_prepare_ujump(struct uretprobe_instance *ri,
+ struct pt_regs *regs, unsigned long addr);
+
+unsigned long loader_not_loaded_entry(struct uretprobe_instance *ri,
+ struct pt_regs *regs, struct pd_t *pd,
+ struct hd_t *hd);
+void loader_loading_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd);
+void loader_failed_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd);
+
+void loader_set_rp_data_size(struct uretprobe *rp);
+void loader_set_priv_origin(struct uretprobe_instance *ri, unsigned long addr);
+unsigned long loader_get_priv_origin(struct uretprobe_instance *ri);
+int loader_add_handler(const char *path);
+
+
+struct pd_t *lpd_get(struct sspt_proc *proc);
+struct pd_t *lpd_get_by_task(struct task_struct *task);
+struct hd_t *lpd_get_hd(struct pd_t *pd, struct dentry *dentry);
+
+bool lpd_get_init_state(struct pd_t *pd);
+void lpd_set_init_state(struct pd_t *pd, bool state);
+
+struct dentry *lpd_get_dentry(struct hd_t *hd);
+struct pd_t *lpd_get_parent_pd(struct hd_t *hd);
+enum ps_t lpd_get_state(struct hd_t *hd);
+unsigned long lpd_get_handlers_base(struct hd_t *hd);
+void *lpd_get_handle(struct hd_t *hd);
+long lpd_get_attempts(struct hd_t *hd);
+void lpd_dec_attempts(struct hd_t *hd);
+
+
+#endif /* __LOADER__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/limits.h>
+#include <asm/uaccess.h>
+#include <master/swap_debugfs.h>
+#include <us_manager/us_common_file.h>
+#include "loader_defs.h"
+#include "loader_debugfs.h"
+#include "loader_module.h"
+#include "loader_storage.h"
+
+static const char LOADER_FOLDER[] = "loader";
+static const char LOADER_LOADER[] = "loader";
+static const char LOADER_LOADER_OFFSET[] = "loader_offset";
+static const char LOADER_LOADER_PATH[] = "loader_path";
+static const char LOADER_LINKER_DATA[] = "linker";
+static const char LOADER_LINKER_PATH[] = "linker_path";
+static const char LOADER_LINKER_R_STATE_OFFSET[] = "r_state_offset";
+
+struct loader_info {
+ char *path;
+ unsigned long offset;
+ struct dentry *dentry;
+};
+
+static struct dentry *loader_root;
+static struct loader_info __loader_info;
+
+static unsigned long r_state_offset = 0;
+static DEFINE_SPINLOCK(__dentry_lock);
+
+static inline void dentry_lock(void)
+{
+ spin_lock(&__dentry_lock);
+}
+
+static inline void dentry_unlock(void)
+{
+ spin_unlock(&__dentry_lock);
+}
+
+
+static void set_loader_file(char *path)
+{
+ __loader_info.path = path;
+ dentry_lock();
+ __loader_info.dentry = swap_get_dentry(__loader_info.path);
+ dentry_unlock();
+}
+
+struct dentry *ld_get_loader_dentry(void)
+{
+ struct dentry *dentry;
+
+ dentry_lock();
+ dentry = __loader_info.dentry;
+ dentry_unlock();
+
+ return dentry;
+}
+
+unsigned long ld_get_loader_offset(void)
+{
+ /* TODO Think about sync */
+ return __loader_info.offset;
+}
+
+static void clean_loader_info(void)
+{
+ if (__loader_info.path != NULL)
+ kfree(__loader_info.path);
+ __loader_info.path = NULL;
+
+ dentry_lock();
+ if (__loader_info.dentry != NULL)
+ swap_put_dentry(__loader_info.dentry);
+
+ __loader_info.dentry = NULL;
+ __loader_info.offset = 0;
+
+ dentry_unlock();
+}
+
+struct dentry *swap_debugfs_create_ptr(const char *name, mode_t mode,
+ struct dentry *parent,
+ unsigned long *value)
+{
+ struct dentry *dentry;
+
+#if BITS_PER_LONG == 32
+ dentry = swap_debugfs_create_x32(name, mode, parent, (u32 *)value);
+#elif BITS_PER_LONG == 64
+ dentry = swap_debugfs_create_x64(name, mode, parent, (u64 *)value);
+#else
+#error Unsupported BITS_PER_LONG value
+#endif
+
+ return dentry;
+}
+
+
+/* ===========================================================================
+ * = LOADER PATH =
+ * ===========================================================================
+ */
+
+static ssize_t loader_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ if (loader_module_is_running())
+ return -EBUSY;
+
+ clean_loader_info();
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (path == NULL) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(path, buf, len)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ path[len - 1] = '\0';
+ set_loader_file(path);
+
+ ret = len;
+
+ return ret;
+err:
+ kfree(path);
+ return ret;
+}
+
+
+static const struct file_operations loader_path_file_ops = {
+ .owner = THIS_MODULE,
+ .write = loader_path_write,
+};
+
+
+/* ===========================================================================
+ * = LINKER PATH =
+ * ===========================================================================
+ */
+
+
+static ssize_t linker_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (path == NULL) {
+ ret = -ENOMEM;
+ goto linker_path_write_out;
+ }
+
+ if (copy_from_user(path, buf, len)) {
+ ret = -EINVAL;
+ goto linker_path_write_out;
+ }
+
+ path[len - 1] = '\0';
+
+ if (ls_set_linker_info(path) != 0) {
+ printk(LOADER_PREFIX "Cannot set linker path %s\n", path);
+ ret = -EINVAL;
+ goto linker_path_write_out;
+ }
+
+ ret = len;
+
+linker_path_write_out:
+ kfree(path);
+
+ return ret;
+}
+
+static const struct file_operations linker_path_file_ops = {
+ .owner = THIS_MODULE,
+ .write = linker_path_write,
+};
+
+
+
+
+
+unsigned long ld_r_state_offset(void)
+{
+ return r_state_offset;
+}
+
+int ld_init(void)
+{
+ struct dentry *swap_dentry, *root, *loader, *open_p, *lib_path,
+ *linker_dir, *linker_path, *r_state_path;
+ int ret;
+
+ ret = -ENODEV;
+ if (!debugfs_initialized())
+ goto fail;
+
+ ret = -ENOENT;
+ swap_dentry = swap_debugfs_getdir();
+ if (!swap_dentry)
+ goto fail;
+
+ ret = -ENOMEM;
+ root = swap_debugfs_create_dir(LOADER_FOLDER, swap_dentry);
+ if (IS_ERR_OR_NULL(root))
+ goto fail;
+
+ loader_root = root;
+
+ loader = swap_debugfs_create_dir(LOADER_LOADER, root);
+ if (IS_ERR_OR_NULL(root)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ open_p = swap_debugfs_create_ptr(LOADER_LOADER_OFFSET,
+ LOADER_DEFAULT_PERMS, loader,
+ &__loader_info.offset);
+ if (IS_ERR_OR_NULL(open_p)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ lib_path = swap_debugfs_create_file(LOADER_LOADER_PATH,
+ LOADER_DEFAULT_PERMS, loader, NULL,
+ &loader_path_file_ops);
+ if (IS_ERR_OR_NULL(lib_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ linker_dir = swap_debugfs_create_dir(LOADER_LINKER_DATA, root);
+ if (IS_ERR_OR_NULL(linker_dir)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ linker_path = swap_debugfs_create_file(LOADER_LINKER_PATH,
+ LOADER_DEFAULT_PERMS, linker_dir,
+ NULL, &linker_path_file_ops);
+ if (IS_ERR_OR_NULL(linker_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ r_state_path = swap_debugfs_create_ptr(LOADER_LINKER_R_STATE_OFFSET,
+ LOADER_DEFAULT_PERMS, linker_dir,
+ &r_state_offset);
+ if (IS_ERR_OR_NULL(r_state_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+
+ debugfs_remove_recursive(root);
+
+fail:
+ printk(LOADER_PREFIX "Debugfs initialization failure: %d\n", ret);
+
+ return ret;
+}
+
+void ld_exit(void)
+{
+ if (loader_root)
+ debugfs_remove_recursive(loader_root);
+ loader_root = NULL;
+
+ loader_module_set_not_ready();
+ clean_loader_info();
+}
--- /dev/null
+#ifndef __LOADER_DEBUGFS_H__
+#define __LOADER_DEBUGFS_H__
+
+struct dentry;
+
+int ld_init(void);
+void ld_exit(void);
+
+struct dentry *ld_get_loader_dentry(void);
+unsigned long ld_get_loader_offset(void);
+
+unsigned long ld_r_state_offset(void);
+
+#endif /* __LOADER_DEBUGFS_H__ */
--- /dev/null
+#ifndef __LOADER_DEFS_H__
+#define __LOADER_DEFS_H__
+
+#define LOADER_PREFIX "SWAP_LOADER: "
+#define LOADER_MAX_ATTEMPTS 10
+#define LOADER_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
+
+#endif /* __LOADER_DEFS_H__ */
--- /dev/null
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/mman.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/callbacks.h>
+#include <us_manager/usm_hook.h>
+#include <writer/kernel_operations.h>
+#include <master/swap_initializer.h>
+#include "loader_defs.h"
+#include "loader_debugfs.h"
+#include "loader_module.h"
+#include "loader.h"
+#include "loader_storage.h"
+#include "loader_pd.h"
+
+
+struct us_priv {
+ struct pt_regs regs;
+ unsigned long arg0;
+ unsigned long arg1;
+ unsigned long raddr;
+ unsigned long origin;
+};
+
+static atomic_t dentry_balance = ATOMIC_INIT(0);
+
+enum loader_status_t {
+ SWAP_LOADER_NOT_READY = 0,
+ SWAP_LOADER_READY = 1,
+ SWAP_LOADER_RUNNING = 2
+};
+
+static enum loader_status_t __loader_status = SWAP_LOADER_NOT_READY;
+
+static int __loader_cbs_start_h = -1;
+static int __loader_cbs_stop_h = -1;
+
+
+
+bool loader_module_is_running(void)
+{
+ if (__loader_status == SWAP_LOADER_RUNNING)
+ return true;
+
+ return false;
+}
+
+bool loader_module_is_ready(void)
+{
+ if (__loader_status == SWAP_LOADER_READY)
+ return true;
+
+ return false;
+}
+
+bool loader_module_is_not_ready(void)
+{
+ if (__loader_status == SWAP_LOADER_NOT_READY)
+ return true;
+
+ return false;
+}
+
+void loader_module_set_ready(void)
+{
+ __loader_status = SWAP_LOADER_READY;
+}
+
+void loader_module_set_running(void)
+{
+ __loader_status = SWAP_LOADER_RUNNING;
+}
+
+void loader_module_set_not_ready(void)
+{
+ __loader_status = SWAP_LOADER_NOT_READY;
+}
+
+static inline void __prepare_ujump(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ unsigned long vaddr)
+{
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ ri->preload.use = true;
+ ri->preload.thumb = !!thumb_mode(regs);
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+
+ swap_set_upc(regs, vaddr);
+}
+
+static inline void __save_uregs(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+
+ priv->regs = *regs;
+ priv->arg0 = swap_get_uarg(regs, 0);
+ priv->arg1 = swap_get_uarg(regs, 1);
+ priv->raddr = swap_get_uret_addr(regs);
+}
+
+static inline void __restore_uregs(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+
+ *regs = priv->regs;
+ swap_put_uarg(regs, 0, priv->arg0);
+ swap_put_uarg(regs, 1, priv->arg1);
+ swap_set_uret_addr(regs, priv->raddr);
+#ifdef CONFIG_X86_32
+ /* need to do it only on x86 */
+ regs->EREG(ip) -= 1;
+#endif /* CONFIG_X86_32 */
+ /* we have just restored the registers => no need to do it in
+ * trampoline_uprobe_handler */
+ ri->ret_addr = NULL;
+}
+
+static inline void print_regs(const char *prefix, struct pt_regs *regs,
+ struct uretprobe_instance *ri, struct hd_t *hd)
+{
+ struct dentry *dentry = lpd_get_dentry(hd);
+
+#if defined(CONFIG_ARM)
+ printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%08lx), "
+ "r0(%08lx), r1(%08lx), r2(%08lx), r3(%08lx), "
+ "r4(%08lx), r5(%08lx), r6(%08lx), r7(%08lx), "
+ "sp(%08lx), lr(%08lx), pc(%08lx)\n",
+ current->comm, current->tgid, current->pid,
+ dentry != NULL ? (char *)(dentry->d_name.name) :
+ (char *)("NULL"),
+ (int)lpd_get_state(hd),
+ prefix, (unsigned long)ri->rp->up.addr,
+ regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3,
+ regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7,
+ regs->ARM_sp, regs->ARM_lr, regs->ARM_pc);
+#elif defined(CONFIG_X86_32)
+ printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%08lx), "
+ "ip(%08lx), arg0(%08lx), arg1(%08lx), raddr(%08lx)\n",
+ current->comm, current->tgid, current->pid,
+ dentry != NULL ? (char *)(dentry->d_name.name) :
+ (char *)("NULL"),
+ (int)lpd_get_state(hd),
+ prefix, (unsigned long)ri->rp->up.addr,
+ regs->EREG(ip), swap_get_uarg(regs, 0), swap_get_uarg(regs, 1),
+ swap_get_uret_addr(regs));
+#elif defined(CONFIG_ARM64)
+ printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%016lx), "
+ "x0(%016lx), x1(%016lx), x2(%016lx), x3(%016lx), "
+ "x4(%016lx), x5(%016lx), x6(%016lx), x7(%016lx), "
+ "sp(%016lx), lr(%016lx), pc(%016lx)\n",
+ current->comm, current->tgid, current->pid,
+ dentry != NULL ? (char *)(dentry->d_name.name) :
+ (char *)("NULL"),
+ (int)lpd_get_state(hd),
+ prefix, (unsigned long)ri->rp->up.addr,
+ (long)regs->regs[0], (long)regs->regs[1],
+ (long)regs->regs[2], (long)regs->regs[3],
+ (long)regs->regs[4], (long)regs->regs[5],
+ (long)regs->regs[6], (long)regs->regs[7],
+ (long)regs->sp, swap_get_uret_addr(regs), (long)regs->pc);
+ (void)dentry;
+#else /* CONFIG_arch */
+# error "this architecture is not supported"
+#endif /* CONFIG_arch */
+}
+
+static inline unsigned long get_r_state_addr(struct vm_area_struct *linker_vma)
+{
+ unsigned long start_addr;
+ unsigned long offset = ld_r_state_offset();
+
+ if (linker_vma == NULL)
+ return 0;
+
+ start_addr = linker_vma->vm_start;
+
+ return (offset ? start_addr + offset : 0);
+}
+
+static struct vm_area_struct *__get_linker_vma(struct task_struct *task)
+{
+ struct vm_area_struct *vma = NULL;
+ struct bin_info *ld_info;
+
+ ld_info = ls_get_linker_info();
+ if (ld_info == NULL) {
+ printk(LOADER_PREFIX "Cannot get linker info [%u %u %s]!\n",
+ task->tgid, task->pid, task->comm);
+ return NULL;
+ }
+
+ for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
+ if (vma->vm_file && vma->vm_flags & VM_EXEC
+ && vma->vm_file->f_path.dentry == ld_info->dentry) {
+ ls_put_linker_info(ld_info);
+ return vma;
+ }
+ }
+
+ ls_put_linker_info(ld_info);
+ return NULL;
+}
+
+
+
+
+
+
+
+
+
+static bool __is_proc_mmap_mappable(struct task_struct *task)
+{
+ struct vm_area_struct *linker_vma = __get_linker_vma(task);
+ struct sspt_proc *proc;
+ unsigned long r_state_addr;
+ unsigned int state;
+
+ if (linker_vma == NULL)
+ return false;
+
+ r_state_addr = get_r_state_addr(linker_vma);
+ if (r_state_addr == 0)
+ return false;
+
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ proc->r_state_addr = r_state_addr;
+ sspt_proc_put(proc);
+ }
+
+ if (get_user(state, (unsigned long *)r_state_addr))
+ return false;
+
+ return !state;
+}
+
+static bool __should_we_load_handlers(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ if (!__is_proc_mmap_mappable(task))
+ return false;
+
+ return true;
+}
+
+
+
+
+
+static void mmap_handler(struct sspt_proc *proc, struct vm_area_struct *vma)
+{
+ struct pd_t *pd;
+ unsigned long vaddr = vma->vm_start;
+ struct dentry *dentry = vma->vm_file->f_path.dentry;
+
+ pd = lpd_get(proc);
+ if (pd == NULL) {
+ printk(LOADER_PREFIX "%d: No process data! Current %d %s\n",
+ __LINE__, current->tgid, current->comm);
+ return;
+ }
+
+ if (dentry == ld_get_loader_dentry()) {
+ lpd_set_loader_base(pd, vaddr);
+ } else {
+ struct hd_t *hd;
+
+ hd = lpd_get_hd(pd, dentry);
+ if (hd)
+ lpd_set_handlers_base(hd, vaddr);
+ }
+}
+
+
+static struct usm_hook usm_hook = {
+ .owner = THIS_MODULE,
+ .mmap = mmap_handler,
+};
+
+static bool mmap_rp_inst = false;
+static DEFINE_MUTEX(mmap_rp_mtx);
+
+static void loader_start_cb(void)
+{
+ int res;
+
+ mutex_lock(&mmap_rp_mtx);
+ res = usm_hook_reg(&usm_hook);
+ if (res != 0)
+ pr_err(LOADER_PREFIX "Cannot register usm_hook\n");
+ else
+ mmap_rp_inst = true;
+ mutex_unlock(&mmap_rp_mtx);
+}
+
+static void loader_stop_cb(void)
+{
+ mutex_lock(&mmap_rp_mtx);
+ if (mmap_rp_inst) {
+ usm_hook_unreg(&usm_hook);
+ mmap_rp_inst = false;
+ }
+ mutex_unlock(&mmap_rp_mtx);
+}
+
+static unsigned long __not_loaded_entry(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd)
+{
+ char __user *path = NULL;
+ unsigned long vaddr = 0;
+ unsigned long base;
+
+ /* if linker is still doing its work, we do nothing */
+ if (!__should_we_load_handlers(current, regs))
+ return 0;
+
+ base = lpd_get_loader_base(pd);
+ if (base == 0)
+ return 0; /* loader isn't mapped */
+
+ /* jump to loader code if ready */
+ vaddr = base + ld_get_loader_offset();
+ if (vaddr) {
+ /* save original regs state */
+ __save_uregs(ri, regs);
+ print_regs("ORIG", regs, ri, hd);
+
+ path = lpd_get_path(pd, hd);
+
+ /* set dlopen args: filename, flags */
+ swap_put_uarg(regs, 0, (unsigned long)path);
+ swap_put_uarg(regs, 1, 2 /* RTLD_NOW */);
+
+ /* do the jump to dlopen */
+ __prepare_ujump(ri, regs, vaddr);
+ /* set new state */
+ lpd_set_state(hd, LOADING);
+ }
+
+ return vaddr;
+}
+
+static void __loading_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+ unsigned long vaddr = 0;
+
+ /* check if loading has been completed */
+ vaddr = lpd_get_loader_base(pd) +
+ ld_get_loader_offset();
+ if (vaddr && (priv->origin == vaddr)) {
+ lpd_set_handle(hd,
+ (void __user *)regs_return_value(regs));
+
+ /* restore original regs state */
+ __restore_uregs(ri, regs);
+ print_regs("REST", regs, ri, hd);
+ /* check if loading done */
+
+ if (lpd_get_handle(hd)) {
+ lpd_set_state(hd, LOADED);
+ } else {
+ lpd_dec_attempts(hd);
+ lpd_set_state(hd, FAILED);
+ }
+ }
+}
+
+static void __failed_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd)
+{
+ if (lpd_get_attempts(hd))
+ lpd_set_state(hd, NOT_LOADED);
+}
+
+
+
+unsigned long loader_not_loaded_entry(struct uretprobe_instance *ri,
+ struct pt_regs *regs, struct pd_t *pd,
+ struct hd_t *hd)
+{
+ return __not_loaded_entry(ri, regs, pd, hd);
+}
+EXPORT_SYMBOL_GPL(loader_not_loaded_entry);
+
+void loader_loading_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd)
+{
+ __loading_ret(ri, regs, pd, hd);
+}
+EXPORT_SYMBOL_GPL(loader_loading_ret);
+
+void loader_failed_ret(struct uretprobe_instance *ri, struct pt_regs *regs,
+ struct pd_t *pd, struct hd_t *hd)
+{
+ __failed_ret(ri, regs, pd, hd);
+}
+EXPORT_SYMBOL_GPL(loader_failed_ret);
+
+void loader_module_prepare_ujump(struct uretprobe_instance *ri,
+ struct pt_regs *regs, unsigned long addr)
+{
+ __prepare_ujump(ri, regs, addr);
+}
+EXPORT_SYMBOL_GPL(loader_module_prepare_ujump);
+
+void loader_set_rp_data_size(struct uretprobe *rp)
+{
+ rp->data_size = sizeof(struct us_priv);
+}
+EXPORT_SYMBOL_GPL(loader_set_rp_data_size);
+
+void loader_set_priv_origin(struct uretprobe_instance *ri, unsigned long addr)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+
+ priv->origin = addr;
+}
+EXPORT_SYMBOL_GPL(loader_set_priv_origin);
+
+unsigned long loader_get_priv_origin(struct uretprobe_instance *ri)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+
+ return priv->origin;
+}
+EXPORT_SYMBOL_GPL(loader_get_priv_origin);
+
+
+int loader_set(void)
+{
+ if (loader_module_is_running())
+ return -EBUSY;
+
+ return 0;
+}
+
+void loader_unset(void)
+{
+ mutex_lock(&mmap_rp_mtx);
+ if (mmap_rp_inst) {
+ usm_hook_unreg(&usm_hook);
+ mmap_rp_inst = false;
+ }
+ mutex_unlock(&mmap_rp_mtx);
+
+ /*module_put(THIS_MODULE);*/
+ loader_module_set_not_ready();
+}
+
+int loader_add_handler(const char *path)
+{
+ return ls_add_handler(path);
+}
+EXPORT_SYMBOL_GPL(loader_add_handler);
+
+
+static int loader_module_init(void)
+{
+ int ret;
+
+ ret = ld_init();
+ if (ret)
+ goto out_err;
+
+ ret = ls_init();
+ if (ret)
+ goto exit_debugfs;
+
+ ret = lpd_init();
+ if (ret)
+ goto exit_storage;
+
+ /* TODO do not forget to remove set (it is just for debugging) */
+ ret = loader_set();
+ if (ret)
+ goto exit_pd;
+
+ __loader_cbs_start_h = us_manager_reg_cb(START_CB, loader_start_cb);
+ if (__loader_cbs_start_h < 0)
+ goto exit_start_cb;
+
+ __loader_cbs_stop_h = us_manager_reg_cb(STOP_CB, loader_stop_cb);
+ if (__loader_cbs_stop_h < 0)
+ goto exit_stop_cb;
+
+ return 0;
+
+exit_stop_cb:
+ us_manager_unreg_cb(__loader_cbs_start_h);
+
+exit_start_cb:
+ loader_unset();
+
+exit_pd:
+ lpd_uninit();
+
+exit_storage:
+ ls_exit();
+
+exit_debugfs:
+ ld_exit();
+
+out_err:
+ return ret;
+}
+
+static void loader_module_exit(void)
+{
+ int balance;
+
+ us_manager_unreg_cb(__loader_cbs_start_h);
+ us_manager_unreg_cb(__loader_cbs_stop_h);
+ loader_unset();
+ lpd_uninit();
+ ls_exit();
+ ld_exit();
+
+ balance = atomic_read(&dentry_balance);
+ atomic_set(&dentry_balance, 0);
+
+ WARN(balance, "Bad GET/PUT dentry balance: %d\n", balance);
+}
+
+SWAP_LIGHT_INIT_MODULE(NULL, loader_module_init, loader_module_exit,
+ NULL, NULL);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP Loader Module");
+MODULE_AUTHOR("Vasiliy Ulyanov <v.ulyanov@samsung.com>"
+ "Alexander Aksenov <a.aksenov@samsung.com>");
--- /dev/null
+#ifndef __LOADER_MODULE_H__
+#define __LOADER_MODULE_H__
+
+#include <linux/types.h>
+
+struct dentry;
+
+bool loader_module_is_ready(void);
+bool loader_module_is_running(void);
+bool loader_module_is_not_ready(void);
+void loader_module_set_ready(void);
+void loader_module_set_running(void);
+void loader_module_set_not_ready(void);
+
+struct dentry *get_dentry(const char *filepath);
+void put_dentry(struct dentry *dentry);
+
+
+
+#endif /* __LOADER_MODULE_H__ */
--- /dev/null
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/module.h>
+#include <linux/hardirq.h>
+#include <linux/list.h>
+#include <us_manager/us_manager_common.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include "loader_pd.h"
+#include "loader.h"
+#include "loader_debugfs.h"
+#include "loader_storage.h"
+#include "loader_defs.h"
+
+
+struct pd_t {
+ unsigned long loader_base;
+ unsigned long data_page;
+ struct list_head handlers;
+ bool is_pthread_init;
+};
+
+struct hd_t {
+ struct list_head list;
+ struct dentry *dentry;
+ enum ps_t state;
+ struct pd_t *parent;
+ unsigned long base;
+ unsigned long offset;
+ void __user *handle;
+ long attempts;
+};
+
+
+static inline bool check_vma(struct vm_area_struct *vma, struct dentry *dentry)
+{
+ struct file *file = vma->vm_file;
+
+ return (file && (vma->vm_flags & VM_EXEC) &&
+ (file->f_path.dentry == dentry));
+}
+
+static inline unsigned long __get_loader_base(struct pd_t *pd)
+{
+ return pd->loader_base;
+}
+
+static inline void __set_loader_base(struct pd_t *pd,
+ unsigned long addr)
+{
+ pd->loader_base = addr;
+}
+
+static inline unsigned long __get_data_page(struct pd_t *pd)
+{
+ return pd->data_page;
+}
+
+static inline void __set_data_page(struct pd_t *pd, unsigned long page)
+{
+ pd->data_page = page;
+}
+
+
+
+static inline enum ps_t __get_state(struct hd_t *hd)
+{
+ return hd->state;
+}
+
+static inline void __set_state(struct hd_t *hd, enum ps_t state)
+{
+ hd->state = state;
+}
+
+static inline unsigned long __get_handlers_base(struct hd_t *hd)
+{
+ return hd->base;
+}
+
+static inline void __set_handlers_base(struct hd_t *hd,
+ unsigned long addr)
+{
+ hd->base = addr;
+}
+
+static inline unsigned long __get_offset(struct hd_t *hd)
+{
+ return hd->offset;
+}
+
+static inline void *__get_handle(struct hd_t *hd)
+{
+ return hd->handle;
+}
+
+static inline void __set_handle(struct hd_t *hd, void __user *handle)
+{
+ hd->handle = handle;
+}
+
+static inline long __get_attempts(struct hd_t *hd)
+{
+ return hd->attempts;
+}
+
+static inline void __set_attempts(struct hd_t *hd, long attempts)
+{
+ hd->attempts = attempts;
+}
+
+
+
+static struct vm_area_struct *find_vma_by_dentry(struct mm_struct *mm,
+ struct dentry *dentry)
+{
+ struct vm_area_struct *vma;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next)
+ if (check_vma(vma, dentry))
+ return vma;
+
+ return NULL;
+}
+
+static struct pd_t *__create_pd(void)
+{
+ struct pd_t *pd;
+ unsigned long page;
+
+ pd = kzalloc(sizeof(*pd), GFP_ATOMIC);
+ if (pd == NULL)
+ return NULL;
+
+ down_write(¤t->mm->mmap_sem);
+ page = __swap_do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+ up_write(¤t->mm->mmap_sem);
+ if (IS_ERR_VALUE(page)) {
+ printk(KERN_ERR LOADER_PREFIX
+ "Cannot alloc page for %u\n", current->tgid);
+ goto create_pd_fail;
+ }
+
+ __set_data_page(pd, page);
+ pd->is_pthread_init = true;
+ INIT_LIST_HEAD(&pd->handlers);
+
+ return pd;
+
+create_pd_fail:
+ kfree(pd);
+
+ return NULL;
+}
+
+static size_t __copy_path(char *src, unsigned long page, unsigned long offset)
+{
+ unsigned long dest = page + offset;
+ size_t len = strnlen(src, PATH_MAX);
+
+ /* set handler path */
+ if (copy_to_user((void __user *)dest, src, len) != 0) {
+ printk(KERN_ERR LOADER_PREFIX
+ "Cannot copy string to user!\n");
+ return 0;
+ }
+
+ return len;
+}
+
+static void __set_ld_mapped(struct pd_t *pd, struct mm_struct *mm)
+{
+ struct vm_area_struct *vma;
+ struct dentry *ld = ld_get_loader_dentry();
+
+ down_read(&mm->mmap_sem);
+ if (ld) {
+ vma = find_vma_by_dentry(mm, ld);
+ if (vma)
+ __set_loader_base(pd, vma->vm_start);
+ }
+ up_read(&mm->mmap_sem);
+}
+
+static void __set_handler_mapped(struct hd_t *hd, struct mm_struct *mm)
+{
+ struct vm_area_struct *vma;
+ struct dentry *handlers = hd->dentry;
+
+ down_read(&mm->mmap_sem);
+ if (handlers) {
+ vma = find_vma_by_dentry(mm, handlers);
+ if (vma) {
+ __set_handlers_base(hd, vma->vm_start);
+ __set_state(hd, LOADED);
+ goto set_handler_mapped_out;
+ }
+ }
+ __set_state(hd, NOT_LOADED);
+
+set_handler_mapped_out:
+ up_read(&mm->mmap_sem);
+}
+
+
+static int __get_handlers(struct pd_t *pd, struct task_struct *task)
+{
+ struct list_head *handlers = NULL;
+ struct bin_info_el *bin;
+ struct hd_t *hd;
+ unsigned long offset = 0;
+ size_t len;
+ int ret = 0;
+
+ handlers = ls_get_handlers();
+ if (handlers == NULL)
+ return -EINVAL;
+
+ list_for_each_entry(bin, handlers, list) {
+ len = __copy_path(bin->path, pd->data_page, offset);
+ if (len == 0) {
+ ret = -EINVAL;
+ goto get_handlers_out;
+ }
+
+ hd = kzalloc(sizeof(*hd), GFP_ATOMIC);
+ if (hd == NULL) {
+ printk(KERN_ERR LOADER_PREFIX "No atomic mem!\n");
+ ret = -ENOMEM;
+ goto get_handlers_out;
+ }
+
+
+ INIT_LIST_HEAD(&hd->list);
+ hd->parent = pd;
+ hd->dentry = bin->dentry;
+ hd->offset = offset;
+ __set_handler_mapped(hd, task->mm);
+ __set_attempts(hd, LOADER_MAX_ATTEMPTS);
+ list_add_tail(&hd->list, &pd->handlers);
+
+ /* inc handlers path's on page */
+ offset += len + 1;
+ }
+
+get_handlers_out:
+ /* TODO Cleanup already created */
+ ls_put_handlers();
+
+ return ret;
+}
+
+
+
+enum ps_t lpd_get_state(struct hd_t *hd)
+{
+ if (hd == NULL)
+ return 0;
+
+ return __get_state(hd);
+}
+EXPORT_SYMBOL_GPL(lpd_get_state);
+
+void lpd_set_state(struct hd_t *hd, enum ps_t state)
+{
+ if (hd == NULL) {
+ printk(LOADER_PREFIX "%d: No handler data! Current %d %s\n",
+ __LINE__, current->tgid, current->comm);
+ return;
+ }
+
+ __set_state(hd, state);
+}
+
+unsigned long lpd_get_loader_base(struct pd_t *pd)
+{
+ if (pd == NULL)
+ return 0;
+
+ return __get_loader_base(pd);
+}
+
+void lpd_set_loader_base(struct pd_t *pd, unsigned long vaddr)
+{
+ __set_loader_base(pd, vaddr);
+}
+
+unsigned long lpd_get_handlers_base(struct hd_t *hd)
+{
+ if (hd == NULL)
+ return 0;
+
+ return __get_handlers_base(hd);
+}
+EXPORT_SYMBOL_GPL(lpd_get_handlers_base);
+
+void lpd_set_handlers_base(struct hd_t *hd, unsigned long vaddr)
+{
+ __set_handlers_base(hd, vaddr);
+}
+
+char __user *lpd_get_path(struct pd_t *pd, struct hd_t *hd)
+{
+ unsigned long page = __get_data_page(pd);
+ unsigned long offset = __get_offset(hd);
+
+ return (char __user *)(page + offset);
+}
+
+
+
+void *lpd_get_handle(struct hd_t *hd)
+{
+ if (hd == NULL)
+ return NULL;
+
+ return __get_handle(hd);
+}
+
+void lpd_set_handle(struct hd_t *hd, void __user *handle)
+{
+ if (hd == NULL) {
+ printk(LOADER_PREFIX "%d: No handler data! Current %d %s\n",
+ __LINE__, current->tgid, current->comm);
+ return;
+ }
+
+ __set_handle(hd, handle);
+}
+
+long lpd_get_attempts(struct hd_t *hd)
+{
+ if (hd == NULL)
+ return -EINVAL;
+
+ return __get_attempts(hd);
+}
+
+void lpd_dec_attempts(struct hd_t *hd)
+{
+ long attempts;
+
+ if (hd == NULL) {
+ printk(LOADER_PREFIX "%d: No handler data! Current %d %s\n",
+ __LINE__, current->tgid, current->comm);
+ return;
+ }
+
+ attempts = __get_attempts(hd);
+ attempts--;
+ __set_attempts(hd, attempts);
+}
+
+struct dentry *lpd_get_dentry(struct hd_t *hd)
+{
+ return hd->dentry;
+}
+
+struct pd_t *lpd_get_parent_pd(struct hd_t *hd)
+{
+ return hd->parent;
+}
+EXPORT_SYMBOL_GPL(lpd_get_parent_pd);
+
+struct pd_t *lpd_get(struct sspt_proc *proc)
+{
+ return (struct pd_t *)proc->private_data;
+}
+EXPORT_SYMBOL_GPL(lpd_get);
+
+struct pd_t *lpd_get_by_task(struct task_struct *task)
+{
+ struct sspt_proc *proc = sspt_proc_by_task(task);
+
+ return lpd_get(proc);
+}
+EXPORT_SYMBOL_GPL(lpd_get_by_task);
+
+struct hd_t *lpd_get_hd(struct pd_t *pd, struct dentry *dentry)
+{
+ struct hd_t *hd;
+
+ list_for_each_entry(hd, &pd->handlers, list) {
+ if (hd->dentry == dentry)
+ return hd;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(lpd_get_hd);
+
+/* TODO Move it to GOT patcher, only it uses this */
+bool lpd_get_init_state(struct pd_t *pd)
+{
+ return pd->is_pthread_init;
+}
+EXPORT_SYMBOL_GPL(lpd_get_init_state);
+
+/* TODO Move it to GOT patcher, only it uses this */
+void lpd_set_init_state(struct pd_t *pd, bool state)
+{
+ pd->is_pthread_init = state;
+}
+EXPORT_SYMBOL_GPL(lpd_set_init_state);
+
+static struct pd_t *do_create_pd(struct task_struct *task)
+{
+ struct pd_t *pd;
+ int ret;
+
+ pd = __create_pd();
+ if (pd == NULL) {
+ ret = -ENOMEM;
+ goto create_pd_exit;
+ }
+
+ ret = __get_handlers(pd, task);
+ if (ret)
+ goto free_pd;
+
+ __set_ld_mapped(pd, task->mm);
+
+ return pd;
+
+free_pd:
+ kfree(pd);
+
+create_pd_exit:
+ printk(KERN_ERR LOADER_PREFIX "do_pd_create_pd: error=%d\n", ret);
+ return NULL;
+}
+
+static void *pd_create(struct sspt_proc *proc)
+{
+ struct pd_t *pd;
+
+ pd = do_create_pd(proc->leader);
+
+ return (void *)pd;
+}
+
+static void pd_destroy(struct sspt_proc *proc, void *data)
+{
+ /* FIXME: to be implemented */
+}
+
+struct sspt_proc_cb pd_cb = {
+ .priv_create = pd_create,
+ .priv_destroy = pd_destroy
+};
+
+int lpd_init(void)
+{
+ int ret;
+
+ ret = sspt_proc_cb_set(&pd_cb);
+
+ return ret;
+}
+
+void lpd_uninit(void)
+{
+ sspt_proc_cb_set(NULL);
+
+ /* TODO Cleanup */
+}
--- /dev/null
+#ifndef __LOADER_PD_H__
+#define __LOADER_PD_H__
+
+#include <loader/loader.h>
+
+struct pd_t;
+struct hd_t;
+struct sspt_proc;
+struct dentry;
+struct list_head;
+
+
+unsigned long lpd_get_loader_base(struct pd_t *pd);
+void lpd_set_loader_base(struct pd_t *pd, unsigned long vaddr);
+
+void lpd_set_state(struct hd_t *hd, enum ps_t state);
+void lpd_set_handlers_base(struct hd_t *hd, unsigned long vaddr);
+void lpd_set_handle(struct hd_t *hd, void __user *handle);
+
+char __user *lpd_get_path(struct pd_t *pd, struct hd_t *hd);
+
+int lpd_init(void);
+void lpd_uninit(void);
+
+
+#endif /* __LOADER_PD_H__*/
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <ks_features/ks_map.h>
+#include <us_manager/us_common_file.h>
+#include "loader_defs.h"
+#include "loader_module.h"
+#include "loader_storage.h"
+
+static struct bin_info __linker_info = { NULL, NULL };
+
+static LIST_HEAD(handlers_list);
+
+
+static bool __check_dentry_already_exist(struct dentry *dentry)
+{
+ struct bin_info_el *bin;
+ bool ret = false;
+
+ list_for_each_entry(bin, &handlers_list, list) {
+ if (bin->dentry == dentry) {
+ ret = true;
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static inline int __add_handler(const char *path)
+{
+ struct dentry *dentry;
+ size_t len = strnlen(path, PATH_MAX);
+ struct bin_info_el *bin;
+ int ret = 0;
+
+ dentry = swap_get_dentry(path);
+ if (!dentry) {
+ ret = -ENOENT;
+ goto add_handler_out;
+ }
+
+ if (__check_dentry_already_exist(dentry)) {
+ ret = 1;
+ goto add_handler_fail_release_dentry;
+ }
+
+ bin = kmalloc(sizeof(*bin), GFP_KERNEL);
+ if (bin == NULL) {
+ ret = -ENOMEM;
+ goto add_handler_fail_release_dentry;
+ }
+
+ bin->path = kmalloc(len + 1, GFP_KERNEL);
+ if (bin->path == NULL) {
+ ret = -ENOMEM;
+ goto add_handler_fail_free_bin;
+ }
+
+ INIT_LIST_HEAD(&bin->list);
+ strncpy(bin->path, path, len);
+ bin->path[len] = '\0';
+ bin->dentry = dentry;
+ list_add_tail(&bin->list, &handlers_list);
+
+ return ret;
+
+add_handler_fail_free_bin:
+ kfree(bin);
+
+add_handler_fail_release_dentry:
+ swap_put_dentry(dentry);
+
+add_handler_out:
+ return ret;
+}
+
+static inline void __remove_handler(struct bin_info_el *bin)
+{
+ list_del(&bin->list);
+ swap_put_dentry(bin->dentry);
+ kfree(bin->path);
+ kfree(bin);
+}
+
+static inline void __remove_handlers(void)
+{
+ struct bin_info_el *bin, *tmp;
+
+ list_for_each_entry_safe(bin, tmp, &handlers_list, list)
+ __remove_handler(bin);
+}
+
+static inline struct bin_info *__get_linker_info(void)
+{
+ return &__linker_info;
+}
+
+static inline bool __check_linker_info(void)
+{
+ return (__linker_info.dentry != NULL); /* TODO */
+}
+
+static inline int __init_linker_info(char *path)
+{
+ struct dentry *dentry;
+ size_t len = strnlen(path, PATH_MAX);
+ int ret = 0;
+
+
+ __linker_info.path = kmalloc(len + 1, GFP_KERNEL);
+ if (__linker_info.path == NULL) {
+ ret = -ENOMEM;
+ goto init_linker_fail;
+ }
+
+ dentry = swap_get_dentry(path);
+ if (!dentry) {
+ ret = -ENOENT;
+ goto init_linker_fail_free;
+ }
+
+ strncpy(__linker_info.path, path, len);
+ __linker_info.path[len] = '\0';
+ __linker_info.dentry = dentry;
+
+ return ret;
+
+init_linker_fail_free:
+ kfree(__linker_info.path);
+
+init_linker_fail:
+
+ return ret;
+}
+
+static inline void __drop_linker_info(void)
+{
+ kfree(__linker_info.path);
+ __linker_info.path = NULL;
+
+ if (__linker_info.dentry)
+ swap_put_dentry(__linker_info.dentry);
+ __linker_info.dentry = NULL;
+}
+
+
+
+
+int ls_add_handler(const char *path)
+{
+ int ret;
+
+ /* If ret is positive - handler was not added, because it is
+ * already exists */
+ ret = __add_handler(path);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+struct list_head *ls_get_handlers(void)
+{
+ /* TODO counter, syncs */
+ return &handlers_list;
+}
+
+void ls_put_handlers(void)
+{
+ /* TODO dec counter, release sync */
+}
+
+int ls_set_linker_info(char *path)
+{
+ return __init_linker_info(path);
+}
+
+struct bin_info *ls_get_linker_info(void)
+{
+ struct bin_info *info = __get_linker_info();
+
+ if (__check_linker_info())
+ return info;
+
+ return NULL;
+}
+
+void ls_put_linker_info(struct bin_info *info)
+{
+}
+
+int ls_init(void)
+{
+ return 0;
+}
+
+void ls_exit(void)
+{
+ __drop_linker_info();
+ __remove_handlers();
+}
--- /dev/null
+#ifndef __LOADER_STORAGE_H__
+#define __LOADER_STORAGE_H__
+
+struct list_head;
+struct dentry;
+
+struct bin_info {
+ char *path;
+ /* ghot */
+ struct dentry *dentry;
+};
+
+struct bin_info_el {
+ struct list_head list;
+ char *path;
+ /* ghot */
+ struct dentry *dentry;
+};
+
+
+
+int ls_add_handler(const char *path);
+struct list_head *ls_get_handlers(void);
+void ls_put_handlers(void);
+
+int ls_set_linker_info(char *path);
+struct bin_info *ls_get_linker_info(void);
+void ls_put_linker_info(struct bin_info *info);
+
+int ls_init(void);
+void ls_exit(void);
+
+#endif /* __LOADER_HANDLERS_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_master.o
+swap_master-y := master_module.o \
+ swap_debugfs.o \
+ swap_initializer.o \
+ swap_deps.o \
+ wait.o
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include "swap_debugfs.h"
+#include "swap_initializer.h"
+
+
+static int __init master_init(void)
+{
+ return swap_debugfs_init();
+}
+
+static void __exit master_exit(void)
+{
+ swap_debugfs_uninit();
+}
+
+module_init(master_init);
+module_exit(master_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include "swap_initializer.h"
+#include "swap_debugfs.h"
+
+
+static int change_permission(struct dentry *dentry)
+{
+ const int system_fw = 202;
+
+ /* set UNIX permissions */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ dentry->d_inode->i_uid = KUIDT_INIT(system_fw);
+ dentry->d_inode->i_gid = KGIDT_INIT(system_fw);
+#else
+ dentry->d_inode->i_uid = system_fw;
+ dentry->d_inode->i_gid = system_fw;
+#endif
+
+ return 0;
+}
+
+struct dentry *swap_debugfs_create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
+{
+ struct dentry *dentry;
+
+ dentry = debugfs_create_file(name, mode, parent, data, fops);
+ if (dentry) {
+ if (change_permission(dentry)) {
+ debugfs_remove(dentry);
+ return NULL;
+ }
+ }
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_file);
+
+struct dentry *swap_debugfs_create_dir(const char *name, struct dentry *parent)
+{
+ struct dentry *dentry;
+
+ dentry = debugfs_create_dir(name, parent);
+ if (dentry) {
+ if (change_permission(dentry)) {
+ debugfs_remove(dentry);
+ return NULL;
+ }
+ }
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_dir);
+
+struct dentry *swap_debugfs_create_x32(const char *name, umode_t mode,
+ struct dentry *parent, u32 *value)
+{
+ struct dentry *dentry;
+
+ dentry = debugfs_create_x32(name, mode, parent, value);
+ if (dentry) {
+ if (change_permission(dentry)) {
+ debugfs_remove(dentry);
+ return NULL;
+ }
+ }
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_x32);
+
+struct dentry *swap_debugfs_create_x64(const char *name, umode_t mode,
+ struct dentry *parent, u64 *value)
+{
+ struct dentry *dentry;
+
+ dentry = debugfs_create_x64(name, mode, parent, value);
+ if (dentry) {
+ if (change_permission(dentry)) {
+ debugfs_remove(dentry);
+ return NULL;
+ }
+ }
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_x64);
+
+struct dentry *swap_debugfs_create_u64(const char *name, umode_t mode,
+ struct dentry *parent, u64 *value)
+{
+ struct dentry *dentry;
+
+ dentry = debugfs_create_u64(name, mode, parent, value);
+ if (dentry) {
+ if (change_permission(dentry)) {
+ debugfs_remove(dentry);
+ return NULL;
+ }
+ }
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_u64);
+
+
+/* based on define DEFINE_SIMPLE_ATTRIBUTE */
+#define SWAP_DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \
+static int __fops ## _open(struct inode *inode, struct file *file) \
+{ \
+ int ret; \
+ \
+ ret = swap_init_simple_open(inode, file); \
+ if (ret) \
+ return ret; \
+ \
+ __simple_attr_check_format(__fmt, 0ull); \
+ ret = simple_attr_open(inode, file, __get, __set, __fmt); \
+ if (ret) \
+ swap_init_simple_release(inode, file); \
+ \
+ return ret; \
+} \
+static int __fops ## _release(struct inode *inode, struct file *file) \
+{ \
+ simple_attr_release(inode, file); \
+ swap_init_simple_release(inode, file); \
+ \
+ return 0; \
+} \
+static const struct file_operations __fops = { \
+ .owner = THIS_MODULE, \
+ .open = __fops ## _open, \
+ .release = __fops ## _release, \
+ .read = simple_attr_read, \
+ .write = simple_attr_write, \
+ .llseek = generic_file_llseek, \
+}
+
+
+static int fset_u64(void *data, u64 val)
+{
+ struct dfs_setget_64 *setget = data;
+
+ return setget->set(val);
+}
+
+static int fget_u64(void *data, u64 *val)
+{
+ struct dfs_setget_64 *setget = data;
+
+ *val = setget->get();
+ return 0;
+}
+
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_u64, fget_u64, fset_u64, "%llu\n");
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_u64_ro, fget_u64, NULL, "%llu\n");
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_u64_wo, NULL, fset_u64, "%llu\n");
+
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_x64, fget_u64, fset_u64, "0x%08llx\n");
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_x64_ro, fget_u64, NULL, "0x%08llx\n");
+SWAP_DEFINE_SIMPLE_ATTRIBUTE(fops_setget_x64_wo, NULL, fset_u64, "0x%08llx\n");
+
+static struct dentry *do_create_dfs_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops,
+ const struct file_operations *fops_ro,
+ const struct file_operations *fops_wo)
+{
+ /* if there are no write bits set, make read only */
+ if (!(mode & S_IWUGO))
+ return swap_debugfs_create_file(name, mode, parent,
+ data, fops_ro);
+ /* if there are no read bits set, make write only */
+ if (!(mode & S_IRUGO))
+ return swap_debugfs_create_file(name, mode, parent,
+ data, fops_wo);
+
+ return swap_debugfs_create_file(name, mode, parent, data, fops);
+}
+
+struct dentry *swap_debugfs_create_setget_u64(const char *name, umode_t mode,
+ struct dentry *parent,
+ struct dfs_setget_64 *setget)
+{
+ return do_create_dfs_file(name, mode, parent, setget,
+ &fops_setget_u64,
+ &fops_setget_u64_ro,
+ &fops_setget_u64_wo);
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_setget_u64);
+
+struct dentry *swap_debugfs_create_setget_x64(const char *name, umode_t mode,
+ struct dentry *parent,
+ struct dfs_setget_64 *setget)
+{
+ return do_create_dfs_file(name, mode, parent, setget,
+ &fops_setget_x64,
+ &fops_setget_x64_ro,
+ &fops_setget_x64_wo);
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_create_setget_x64);
+
+
+static int set_enable(int enable)
+{
+ int ret = 0, change, enable_current;
+
+ enable_current = swap_init_stat_get();
+
+ change = ((!!enable_current) << 1) | (!!enable);
+ switch (change) {
+ case 0b01: /* init */
+ ret = swap_init_init();
+ break;
+ case 0b10: /* uninit */
+ ret = swap_init_uninit();
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ swap_init_stat_put();
+
+ return ret;
+}
+
+static ssize_t read_enable(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[3];
+ int enable;
+
+ enable = swap_init_stat_get();
+ swap_init_stat_put();
+
+ if (enable)
+ buf[0] = '1';
+ else
+ buf[0] = '0';
+ buf[1] = '\n';
+ buf[2] = '\0';
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static int do_write_enable(const char *buf, size_t size)
+{
+ if (size < 1)
+ return -EINVAL;
+
+ switch (buf[0]) {
+ case '1':
+ return set_enable(1);
+ case '0':
+ return set_enable(0);
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t write_enable(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ char buf[32];
+ size_t buf_size;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ ret = do_write_enable(buf, buf_size);
+
+ return ret ? ret : count;
+}
+
+static const struct file_operations fops_enable = {
+ .owner = THIS_MODULE,
+ .read = read_enable,
+ .write = write_enable,
+ .llseek = default_llseek,
+};
+
+
+static struct dentry *swap_dir;
+
+/**
+ * @brief Get debugfs dir.
+ *
+ * @return Pointer to dentry stuct.
+ */
+struct dentry *swap_debugfs_getdir(void)
+{
+ return swap_dir;
+}
+EXPORT_SYMBOL_GPL(swap_debugfs_getdir);
+
+static int debugfs_dir_init(void)
+{
+ swap_dir = swap_debugfs_create_dir("swap", NULL);
+ if (swap_dir == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void debugfs_dir_exit(void)
+{
+ struct dentry *dir = swap_dir;
+
+ swap_dir = NULL;
+ debugfs_remove_recursive(dir);
+}
+
+/**
+ * @brief Initializes SWAP debugfs.
+ *
+ * @return 0 on success, negative error code on error.
+ */
+int swap_debugfs_init(void)
+{
+ int ret;
+ struct dentry *dentry;
+
+ ret = debugfs_dir_init();
+ if (ret)
+ return ret;
+
+ dentry = swap_debugfs_create_file("enable", 0600, swap_dir, NULL,
+ &fops_enable);
+ if (dentry == NULL) {
+ debugfs_dir_exit();
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Deinitializes SWAP debugfs and recursively removes all its files.
+ *
+ * @return Void.
+ */
+void swap_debugfs_uninit(void)
+{
+ debugfs_dir_exit();
+}
--- /dev/null
+/**
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP debugfs interface definition.
+ */
+
+#ifndef _SWAP_DEBUGFS_H
+#define _SWAP_DEBUGFS_H
+
+
+#include <linux/types.h>
+
+
+struct dfs_setget_64 {
+ int (*set)(u64 val);
+ u64 (*get)(void);
+};
+
+struct dentry;
+struct file_operations;
+
+struct dentry *swap_debugfs_create_setget_u64(const char *name, umode_t mode,
+ struct dentry *parent,
+ struct dfs_setget_64 *setget);
+
+struct dentry *swap_debugfs_create_setget_x64(const char *name, umode_t mode,
+ struct dentry *parent,
+ struct dfs_setget_64 *setget);
+
+struct dentry *swap_debugfs_create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops);
+struct dentry *swap_debugfs_create_dir(const char *name, struct dentry *parent);
+
+struct dentry *swap_debugfs_create_x32(const char *name, umode_t mode,
+ struct dentry *parent, u32 *value);
+struct dentry *swap_debugfs_create_x64(const char *name, umode_t mode,
+ struct dentry *parent, u64 *value);
+struct dentry *swap_debugfs_create_u64(const char *name, umode_t mode,
+ struct dentry *parent, u64 *value);
+
+struct dentry *swap_debugfs_getdir(void);
+
+int swap_debugfs_init(void);
+void swap_debugfs_uninit(void);
+
+
+#endif /* _SWAP_DEBUGFS_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <ksyms/ksyms.h>
+#include "swap_deps.h"
+
+
+static struct files_struct *(*__get_files_struct)(struct task_struct *);
+static void (*__put_files_struct)(struct files_struct *fs);
+
+struct files_struct *swap_get_files_struct(struct task_struct *task)
+{
+ return __get_files_struct(task);
+}
+EXPORT_SYMBOL_GPL(swap_get_files_struct);
+
+void swap_put_files_struct(struct files_struct *fs)
+{
+ __put_files_struct(fs);
+}
+EXPORT_SYMBOL_GPL(swap_put_files_struct);
+
+
+int chef_once(void)
+{
+ const char *sym;
+ static unsigned once_flag = 0;
+
+ if (once_flag)
+ return 0;
+
+ sym = "get_files_struct";
+ __get_files_struct = (void *)swap_ksyms(sym);
+ if (__get_files_struct == NULL)
+ goto not_found;
+
+ sym = "put_files_struct";
+ __put_files_struct = (void *)swap_ksyms(sym);
+ if (__put_files_struct == NULL)
+ goto not_found;
+
+ once_flag = 1;
+ return 0;
+
+not_found:
+ printk("ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_DEPS_H
+#define _SWAP_DEPS_H
+
+
+struct task_struct;
+struct files_struct;
+
+
+struct files_struct *swap_get_files_struct(struct task_struct *task);
+void swap_put_files_struct(struct files_struct *fs);
+
+
+int chef_once(void);
+
+
+#endif /* _SWAP_DEPS_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include "swap_initializer.h"
+#include "swap_deps.h"
+
+
+enum init_level {
+ IL_CORE,
+ IL_FS
+};
+
+static swap_init_t sis_get_fn_init(struct swap_init_struct *init,
+ enum init_level level)
+{
+ switch (level) {
+ case IL_CORE:
+ return init->core_init;
+ case IL_FS:
+ return init->fs_init;
+ default:
+ return NULL;
+ }
+}
+
+static swap_uninit_t sis_get_fn_uninit(struct swap_init_struct *init,
+ enum init_level level)
+{
+ switch (level) {
+ case IL_CORE:
+ return init->core_uninit;
+ case IL_FS:
+ return init->fs_uninit;
+ }
+
+ return NULL;
+}
+
+static void sis_set_flag(struct swap_init_struct *init,
+ enum init_level level, bool val)
+{
+ switch (level) {
+ case IL_CORE:
+ init->core_flag = val;
+ break;
+ case IL_FS:
+ init->fs_flag = val;
+ break;
+ }
+}
+
+static bool sis_get_flag(struct swap_init_struct *init, enum init_level level)
+{
+ switch (level) {
+ case IL_CORE:
+ return init->core_flag;
+ case IL_FS:
+ return init->fs_flag;
+ }
+
+ return false;
+}
+
+static int sis_once(struct swap_init_struct *init)
+{
+ swap_init_t once;
+
+ once = init->once;
+ if (!init->once_flag && once) {
+ int ret;
+
+ ret = once();
+ if (ret)
+ return ret;
+
+ init->once_flag = true;
+ }
+
+ return 0;
+}
+
+static int sis_init_level(struct swap_init_struct *init, enum init_level level)
+{
+ int ret;
+ swap_init_t fn;
+
+ if (sis_get_flag(init, level))
+ return -EPERM;
+
+ fn = sis_get_fn_init(init, level);
+ if (fn) {
+ ret = fn();
+ if (ret)
+ return ret;
+ }
+
+ sis_set_flag(init, level, true);
+ return 0;
+}
+
+static void sis_uninit_level(struct swap_init_struct *init,
+ enum init_level level)
+{
+ if (sis_get_flag(init, level)) {
+ swap_uninit_t fn = sis_get_fn_uninit(init, level);
+ if (fn)
+ fn();
+ sis_set_flag(init, level, false);
+ }
+}
+
+static int sis_init(struct swap_init_struct *init)
+{
+ int ret;
+
+ ret = sis_once(init);
+ if (ret)
+ return ret;
+
+ ret = sis_init_level(init, IL_CORE);
+ if (ret)
+ return ret;
+
+ ret = sis_init_level(init, IL_FS);
+ if (ret)
+ sis_uninit_level(init, IL_CORE);
+
+ return ret;
+}
+
+static void sis_uninit(struct swap_init_struct *init)
+{
+ sis_uninit_level(init, IL_FS);
+ sis_uninit_level(init, IL_CORE);
+}
+
+static LIST_HEAD(init_list);
+static DEFINE_MUTEX(inst_mutex);
+static unsigned init_flag;
+
+static int do_once(void)
+{
+ int ret;
+ struct swap_init_struct *init;
+
+ ret = chef_once();
+ if (ret)
+ return ret;
+
+ list_for_each_entry(init, &init_list, list) {
+ ret = sis_once(init);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void do_uninit_level(enum init_level level)
+{
+ struct swap_init_struct *init;
+
+ list_for_each_entry_reverse(init, &init_list, list)
+ sis_uninit_level(init, level);
+}
+
+static int do_init_level(enum init_level level)
+{
+ int ret;
+ struct swap_init_struct *init;
+
+ list_for_each_entry(init, &init_list, list) {
+ ret = sis_init_level(init, level);
+ if (ret) {
+ do_uninit_level(level);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int do_init(void)
+{
+ int ret;
+
+ ret = do_once();
+ if (ret)
+ return ret;
+
+ ret = do_init_level(IL_CORE);
+ if (ret)
+ return ret;
+
+ ret = do_init_level(IL_FS);
+ if (ret)
+ do_uninit_level(IL_CORE);
+
+ init_flag = 1;
+
+ return 0;
+}
+
+static void do_uninit(void)
+{
+ do_uninit_level(IL_FS);
+ do_uninit_level(IL_CORE);
+
+ init_flag = 0;
+}
+
+
+static atomic_t init_use = ATOMIC_INIT(0);
+
+enum init_stat_t {
+ IS_OFF,
+ IS_SWITCHING,
+ IS_ON,
+};
+
+static enum init_stat_t init_stat;
+static DEFINE_SPINLOCK(init_stat_lock);
+
+
+static bool swap_init_try_get(void)
+{
+ spin_lock(&init_stat_lock);
+ if (init_stat != IS_ON) {
+ spin_unlock(&init_stat_lock);
+ return false;
+ }
+ spin_unlock(&init_stat_lock);
+
+ atomic_inc(&init_use);
+
+ return true;
+}
+
+static void swap_init_put(void)
+{
+ atomic_dec(&init_use);
+}
+
+int swap_init_simple_open(struct inode *inode, struct file *file)
+{
+ if (swap_init_try_get() == false)
+ return -EBUSY;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swap_init_simple_open);
+
+int swap_init_simple_release(struct inode *inode, struct file *file)
+{
+ swap_init_put();
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swap_init_simple_release);
+
+int swap_init_init(void)
+{
+ int ret;
+
+ spin_lock(&init_stat_lock);
+ init_stat = IS_SWITCHING;
+ spin_unlock(&init_stat_lock);
+
+ ret = do_init();
+
+ spin_lock(&init_stat_lock);
+ init_stat = ret ? IS_OFF : IS_ON;
+ spin_unlock(&init_stat_lock);
+
+ return ret;
+}
+
+int swap_init_uninit(void)
+{
+ spin_lock(&init_stat_lock);
+ init_stat = IS_SWITCHING;
+ if (atomic_read(&init_use)) {
+ init_stat = IS_ON;
+ spin_unlock(&init_stat_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&init_stat_lock);
+
+ do_uninit();
+
+ spin_lock(&init_stat_lock);
+ init_stat = IS_OFF;
+ spin_unlock(&init_stat_lock);
+
+ return 0;
+}
+
+
+int swap_init_stat_get(void)
+{
+ mutex_lock(&inst_mutex);
+
+ return init_flag;
+}
+
+void swap_init_stat_put(void)
+{
+ mutex_unlock(&inst_mutex);
+}
+
+int swap_init_register(struct swap_init_struct *init)
+{
+ int ret = 0;
+
+ mutex_lock(&inst_mutex);
+ if (init_flag)
+ ret = sis_init(init);
+
+ if (ret == 0)
+ list_add_tail(&init->list, &init_list);
+ mutex_unlock(&inst_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_init_register);
+
+void swap_init_unregister(struct swap_init_struct *init)
+{
+ mutex_lock(&inst_mutex);
+ list_del(&init->list);
+ sis_uninit(init);
+ mutex_unlock(&inst_mutex);
+}
+EXPORT_SYMBOL_GPL(swap_init_unregister);
--- /dev/null
+/**
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * SWAP event notification interface.
+ */
+
+#ifndef _SWAP_INITIALIZER_H
+#define _SWAP_INITIALIZER_H
+
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/module.h>
+
+
+struct file;
+struct inode;
+
+
+typedef int (*swap_init_t)(void);
+typedef void (*swap_uninit_t)(void);
+
+
+struct swap_init_struct {
+ swap_init_t once; /* to call only on the first initialization */
+
+ swap_init_t core_init;
+ swap_uninit_t core_uninit;
+
+ swap_init_t fs_init;
+ swap_uninit_t fs_uninit;
+
+ /* private fields */
+ struct list_head list;
+ unsigned once_flag:1;
+ unsigned core_flag:1;
+ unsigned fs_flag:1;
+};
+
+
+int swap_init_simple_open(struct inode *inode, struct file *file);
+int swap_init_simple_release(struct inode *inode, struct file *file);
+
+int swap_init_init(void);
+int swap_init_uninit(void);
+
+int swap_init_stat_get(void);
+void swap_init_stat_put(void);
+
+int swap_init_register(struct swap_init_struct *init);
+void swap_init_unregister(struct swap_init_struct *init);
+
+
+#define SWAP_LIGHT_INIT_MODULE(_once, _init, _uninit, _fs_init, _fs_uninit) \
+ static struct swap_init_struct __init_struct = { \
+ .once = _once, \
+ .core_init = _init, \
+ .core_uninit = _uninit, \
+ .fs_init = _fs_init, \
+ .fs_uninit = _fs_uninit, \
+ .list = LIST_HEAD_INIT(__init_struct.list), \
+ .once_flag = false, \
+ .core_flag = false, \
+ .fs_flag = false \
+ }; \
+ static int __init __init_mod(void) \
+ { \
+ return swap_init_register(&__init_struct); \
+ } \
+ static void __exit __exit_mod(void) \
+ { \
+ swap_init_unregister(&__init_struct); \
+ } \
+ module_init(__init_mod); \
+ module_exit(__exit_mod)
+
+
+#endif /* _SWAP_INITIALIZER_H */
--- /dev/null
+/*
+ * This code copied from linux kernel 3.11+
+ * commit cb65537ee1134d3cc55c1fa83952bc8eb1212833
+ */
+
+
+#include "wait.h"
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+
+#include <linux/wait.h>
+#include <linux/module.h>
+
+
+#define WAIT_ATOMIC_T_BIT_NR -1
+#define __WAIT_ATOMIC_T_KEY_INITIALIZER(p) \
+ { .flags = p, .bit_nr = WAIT_ATOMIC_T_BIT_NR, }
+
+
+/*
+ * Manipulate the atomic_t address to produce a better bit waitqueue table hash
+ * index (we're keying off bit -1, but that would produce a horrible hash
+ * value).
+ */
+static inline wait_queue_head_t *atomic_t_waitqueue(atomic_t *p)
+{
+ if (BITS_PER_LONG == 64) {
+ unsigned long q = (unsigned long)p;
+ return bit_waitqueue((void *)(q & ~1), q & 1);
+ }
+ return bit_waitqueue(p, 0);
+}
+
+static int wake_atomic_t_function(wait_queue_t *wait, unsigned mode, int sync,
+ void *arg)
+{
+ struct wait_bit_key *key = arg;
+ struct wait_bit_queue *wait_bit
+ = container_of(wait, struct wait_bit_queue, wait);
+ atomic_t *val = key->flags;
+
+ if (wait_bit->key.flags != key->flags ||
+ wait_bit->key.bit_nr != key->bit_nr ||
+ atomic_read(val) != 0)
+ return 0;
+ return autoremove_wake_function(wait, mode, sync, key);
+}
+
+/*
+ * To allow interruptible waiting and asynchronous (i.e. nonblocking) waiting,
+ * the actions of __wait_on_atomic_t() are permitted return codes. Nonzero
+ * return codes halt waiting and return.
+ */
+static int __wait_on_atomic_t(wait_queue_head_t *wq, struct wait_bit_queue *q,
+ int (*action)(atomic_t *), unsigned mode)
+{
+ atomic_t *val;
+ int ret = 0;
+
+ do {
+ prepare_to_wait(wq, &q->wait, mode);
+ val = q->key.flags;
+ if (atomic_read(val) == 0)
+ ret = (*action)(val);
+ } while (!ret && atomic_read(val) != 0);
+ finish_wait(wq, &q->wait);
+ return ret;
+}
+
+#define DEFINE_WAIT_ATOMIC_T(name, p) \
+ struct wait_bit_queue name = { \
+ .key = __WAIT_ATOMIC_T_KEY_INITIALIZER(p), \
+ .wait = { \
+ .private = current, \
+ .func = wake_atomic_t_function, \
+ .task_list = \
+ LIST_HEAD_INIT((name).wait.task_list), \
+ }, \
+ }
+
+int out_of_line_wait_on_atomic_t(atomic_t *p, int (*action)(atomic_t *),
+ unsigned mode)
+{
+ wait_queue_head_t *wq = atomic_t_waitqueue(p);
+ DEFINE_WAIT_ATOMIC_T(wait, p);
+
+ return __wait_on_atomic_t(wq, &wait, action, mode);
+}
+EXPORT_SYMBOL(out_of_line_wait_on_atomic_t);
+
+/**
+ * wake_up_atomic_t - Wake up a waiter on a atomic_t
+ * @word: The word being waited on, a kernel virtual address
+ * @bit: The bit of the word being waited on
+ *
+ * Wake up anyone waiting for the atomic_t to go to zero.
+ *
+ * Abuse the bit-waker function and its waitqueue hash table set (the atomic_t
+ * check is done by the waiter's wake function, not the by the waker itself).
+ */
+void wake_up_atomic_t(atomic_t *p)
+{
+ __wake_up_bit(atomic_t_waitqueue(p), p, WAIT_ATOMIC_T_BIT_NR);
+}
+EXPORT_SYMBOL(wake_up_atomic_t);
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) */
--- /dev/null
+/*
+ * This code copied from linux kernel 3.11+
+ * commit cb65537ee1134d3cc55c1fa83952bc8eb1212833
+ */
+
+
+#ifndef _SWAP_WAIT_H
+#define _SWAP_WAIT_H
+
+
+#include <linux/version.h>
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+
+
+#include <linux/atomic.h>
+
+
+void wake_up_atomic_t(atomic_t *);
+int out_of_line_wait_on_atomic_t(atomic_t *, int (*)(atomic_t *), unsigned);
+
+static inline
+int wait_on_atomic_t(atomic_t *val, int (*action)(atomic_t *), unsigned mode)
+{
+ if (atomic_read(val) == 0)
+ return 0;
+ return out_of_line_wait_on_atomic_t(val, action, mode);
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) */
+
+#include <linux/wait.h>
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) */
+
+#endif /* _SWAP_WAIT_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_nsp.o
+swap_nsp-y := \
+ nsp_module.o \
+ nsp.o \
+ nsp_msg.o \
+ nsp_debugfs.o
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <writer/swap_msg.h>
+#include <kprobe/swap_ktd.h>
+#include <uprobe/swap_uaccess.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/probes/probe_info_new.h>
+#include "nsp.h"
+#include "nsp_msg.h"
+#include "nsp_print.h"
+#include "nsp_debugfs.h"
+
+
+/* ============================================================================
+ * = probes =
+ * ============================================================================
+ */
+
+/* dlopen@plt */
+static int dlopen_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static int dlopen_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static struct probe_desc pin_dlopen = MAKE_URPROBE(dlopen_eh, dlopen_rh, 0);
+static struct probe_new p_dlopen = {
+ .desc = &pin_dlopen
+};
+
+/* dlsym@plt */
+static int dlsym_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static int dlsym_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static struct probe_desc pin_dlsym = MAKE_URPROBE(dlsym_eh, dlsym_rh, 0);
+static struct probe_new p_dlsym = {
+ .desc = &pin_dlsym
+};
+
+/* main */
+static int main_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static int main_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static struct probe_desc pin_main = MAKE_URPROBE(main_eh, main_rh, 0);
+
+/* appcore_efl_main */
+static int ac_efl_main_h(struct uprobe *p, struct pt_regs *regs);
+static struct probe_desc pin_ac_efl_main = MAKE_UPROBE(ac_efl_main_h);
+static struct probe_new p_ac_efl_main = {
+ .desc = &pin_ac_efl_main
+};
+
+/* appcore_init@plt */
+static int ac_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static struct probe_desc pin_ac_init = MAKE_URPROBE(NULL, ac_init_rh, 0);
+static struct probe_new p_ac_init = {
+ .desc = &pin_ac_init
+};
+
+/* elm_run@plt */
+static int elm_run_h(struct uprobe *p, struct pt_regs *regs);
+static struct probe_desc pin_elm_run = MAKE_UPROBE(elm_run_h);
+static struct probe_new p_elm_run = {
+ .desc = &pin_elm_run
+};
+
+/* __do_app */
+static int do_app_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static int do_app_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static struct probe_desc pin_do_app = MAKE_URPROBE(do_app_eh, do_app_rh, 0);
+static struct probe_new p_do_app = {
+ .desc = &pin_do_app
+};
+
+
+
+
+
+/* ============================================================================
+ * = the variables are initialized by the user =
+ * ============================================================================
+ */
+static const char *lpad_path;
+static struct dentry *lpad_dentry;
+
+static const char *libappcore_path;
+static struct dentry *libappcore_dentry;
+static const char *libcapi_path;
+static struct dentry *libcapi_dentry;
+
+static void uninit_variables(void)
+{
+ kfree(lpad_path);
+ lpad_path = NULL;
+ lpad_dentry = NULL;
+
+ kfree(libappcore_path);
+ libappcore_path = NULL;
+ libappcore_dentry = NULL;
+
+ kfree(libcapi_path);
+ libcapi_path = NULL;
+ libcapi_dentry = NULL;
+}
+
+static bool is_init(void)
+{
+ return lpad_dentry && libappcore_dentry && libcapi_dentry;
+}
+
+static int do_set_lpad_info(const char *path, unsigned long dlopen,
+ unsigned long dlsym)
+{
+ struct dentry *dentry;
+ const char *new_path;
+
+ dentry = dentry_by_path(path);
+ if (dentry == NULL) {
+ pr_err("dentry not found (path='%s')\n", path);
+ return -EINVAL;
+ }
+
+ new_path = kstrdup(path, GFP_KERNEL);
+ if (new_path == NULL) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ kfree(lpad_path);
+
+ lpad_path = new_path;
+ lpad_dentry = dentry;
+ p_dlopen.offset = dlopen;
+ p_dlsym.offset = dlsym;
+
+ return 0;
+}
+
+static int do_set_appcore_info(struct nsp_info_data *info)
+{
+ struct dentry *dentry;
+ const char *new_path;
+ int ret = 0;
+
+ new_path = kstrdup(info->appcore_path, GFP_KERNEL);
+ if (!new_path)
+ return -ENOMEM;
+ kfree(libappcore_path);
+ libappcore_path = new_path;
+
+ new_path = kstrdup(info->capi_path, GFP_KERNEL);
+ if (!new_path) {
+ ret = -ENOMEM;
+ goto fail_alloc;
+ }
+ kfree(libcapi_path);
+ libcapi_path = new_path;
+
+ dentry = dentry_by_path(info->appcore_path);
+ if (!dentry) {
+ pr_err("dentry not found (path='%s')\n", info->appcore_path);
+ ret = -EINVAL;
+ goto fail;
+ }
+ libappcore_dentry = dentry;
+
+ dentry = dentry_by_path(info->capi_path);
+ if (!dentry) {
+ pr_err("dentry not found (path='%s')\n", info->capi_path);
+ ret = -EINVAL;
+ goto fail;
+ }
+ libcapi_dentry = dentry;
+
+ p_ac_efl_main.offset = info->ac_efl_init;
+ p_do_app.offset = info->do_app;
+ p_ac_init.offset = info->ac_init;
+ p_elm_run.offset = info->elm_run;
+
+ return 0;
+
+fail:
+ kfree(libcapi_path);
+ libcapi_path = NULL;
+fail_alloc:
+ kfree(libappcore_path);
+ libappcore_path = NULL;
+
+ return ret;
+}
+
+
+
+
+
+/* ============================================================================
+ * = nsp_data =
+ * ============================================================================
+ */
+struct nsp_data {
+ struct list_head list;
+
+ const char *app_path;
+ struct dentry *app_dentry;
+ struct probe_new p_main;
+
+ struct pf_group *pfg;
+};
+
+static LIST_HEAD(nsp_data_list);
+
+static struct nsp_data *nsp_data_create(const char *app_path,
+ unsigned long main_addr)
+{
+ struct dentry *dentry;
+ struct nsp_data *data;
+
+ dentry = dentry_by_path(app_path);
+ if (dentry == NULL)
+ return ERR_PTR(-ENOENT);
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ data->app_path = kstrdup(app_path, GFP_KERNEL);
+ if (data->app_path == NULL) {
+ kfree(data);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ data->app_dentry = dentry;
+ data->p_main.desc = &pin_main;
+ data->p_main.offset = main_addr;
+ data->pfg = NULL;
+
+ return data;
+}
+
+static void nsp_data_destroy(struct nsp_data *data)
+{
+ kfree(data->app_path);
+ kfree(data);
+}
+
+static struct nsp_data *nsp_data_find(const struct dentry *dentry)
+{
+ struct nsp_data *data;
+
+ list_for_each_entry(data, &nsp_data_list, list) {
+ if (data->app_dentry == dentry)
+ return data;
+ }
+
+ return NULL;
+}
+
+static struct nsp_data *nsp_data_find_by_path(const char *path)
+{
+ struct nsp_data *data;
+
+ list_for_each_entry(data, &nsp_data_list, list) {
+ if (strcmp(data->app_path, path) == 0)
+ return data;
+ }
+
+ return NULL;
+}
+
+static void nsp_data_add(struct nsp_data *data)
+{
+ list_add(&data->list, &nsp_data_list);
+}
+
+static void nsp_data_rm(struct nsp_data *data)
+{
+ list_del(&data->list);
+}
+
+static int nsp_data_inst(struct nsp_data *data)
+{
+ int ret;
+ struct pf_group *pfg;
+
+ pfg = get_pf_group_by_dentry(lpad_dentry, (void *)data->app_dentry);
+ if (pfg == NULL)
+ return -ENOMEM;
+
+ ret = pin_register(&p_dlsym, pfg, lpad_dentry);
+ if (ret)
+ goto put_g;
+
+ ret = pin_register(&p_dlopen, pfg, lpad_dentry);
+ if (ret)
+ goto ur_dlsym;
+
+ ret = pin_register(&data->p_main, pfg, data->app_dentry);
+ if (ret)
+ goto ur_dlopen;
+
+ ret = pin_register(&p_ac_efl_main, pfg, libappcore_dentry);
+ if (ret)
+ goto ur_main;
+
+ ret = pin_register(&p_ac_init, pfg, libcapi_dentry);
+ if (ret)
+ goto ur_ac_efl_main;
+
+ ret = pin_register(&p_elm_run, pfg, libcapi_dentry);
+ if (ret)
+ goto ur_ac_init;
+
+ ret = pin_register(&p_do_app, pfg, libappcore_dentry);
+ if (ret)
+ goto ur_elm_run;
+
+ data->pfg = pfg;
+
+ return 0;
+
+ur_elm_run:
+ pin_unregister(&p_elm_run, pfg);
+ur_ac_init:
+ pin_unregister(&p_ac_init, pfg);
+ur_ac_efl_main:
+ pin_unregister(&p_ac_efl_main, pfg);
+ur_main:
+ pin_unregister(&data->p_main, pfg);
+ur_dlopen:
+ pin_unregister(&p_dlopen, pfg);
+ur_dlsym:
+ pin_unregister(&p_dlsym, pfg);
+put_g:
+ put_pf_group(pfg);
+ return ret;
+}
+
+static void nsp_data_uninst(struct nsp_data *data)
+{
+ struct pf_group *pfg = data->pfg;
+
+ pin_unregister(&p_do_app, pfg);
+ pin_unregister(&p_elm_run, pfg);
+ pin_unregister(&p_ac_init, pfg);
+ pin_unregister(&p_ac_efl_main, pfg);
+ pin_unregister(&data->p_main, pfg);
+ pin_unregister(&p_dlopen, pfg);
+ pin_unregister(&p_dlsym, pfg);
+ put_pf_group(pfg);
+
+ data->pfg = NULL;
+}
+
+static int __nsp_add(const char *app_path, unsigned long main_addr)
+{
+ struct nsp_data *data;
+
+ if (nsp_data_find_by_path(app_path))
+ return -EEXIST;
+
+ data = nsp_data_create(app_path, main_addr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ nsp_data_add(data);
+
+ return 0;
+}
+
+static int __nsp_rm(const char *path)
+{
+ struct dentry *dentry;
+ struct nsp_data *data;
+
+ dentry = dentry_by_path(path);
+ if (dentry == NULL)
+ return -ENOENT;
+
+ data = nsp_data_find(dentry);
+ if (data == NULL)
+ return -ESRCH;
+
+ nsp_data_rm(data);
+ nsp_data_destroy(data);
+
+ return 0;
+}
+
+static int __nsp_rm_all(void)
+{
+ struct nsp_data *data, *n;
+
+ list_for_each_entry_safe(data, n, &nsp_data_list, list) {
+ nsp_data_rm(data);
+ nsp_data_destroy(data);
+ }
+
+ return 0;
+}
+
+static void __nsp_disabel(void)
+{
+ struct nsp_data *data;
+
+ list_for_each_entry(data, &nsp_data_list, list) {
+ if (data->pfg)
+ nsp_data_uninst(data);
+ }
+}
+
+static int __nsp_enable(void)
+{
+ int ret;
+ struct nsp_data *data;
+
+ list_for_each_entry(data, &nsp_data_list, list) {
+ ret = nsp_data_inst(data);
+ if (ret)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ __nsp_disabel();
+ return ret;
+}
+
+
+
+
+
+
+
+/* ============================================================================
+ * = set parameters =
+ * ============================================================================
+ */
+#define F_ARG1(m, t, a) m(t, a)
+#define F_ARG2(m, t, a, ...) m(t, a), F_ARG1(m, __VA_ARGS__)
+#define F_ARG3(m, t, a, ...) m(t, a), F_ARG2(m, __VA_ARGS__)
+#define F_ARG(n, m, ...) F_ARG##n(m, __VA_ARGS__)
+
+#define M_TYPE_AND_ARG(t, a) t a
+#define M_ARG(t, a) a
+
+#define DECLARE_SAFE_FUNC(n, func_name, do_func, ...) \
+int func_name(F_ARG(n, M_TYPE_AND_ARG, __VA_ARGS__)) \
+{ \
+ int ret; \
+ mutex_lock(&stat_mutex); \
+ if (stat == NS_ON) { \
+ ret = -EBUSY; \
+ goto unlock; \
+ } \
+ ret = do_func(F_ARG(n, M_ARG, __VA_ARGS__)); \
+unlock: \
+ mutex_unlock(&stat_mutex); \
+ return ret; \
+}
+
+#define DECLARE_SAFE_FUNC0(name, _do) DECLARE_SAFE_FUNC(1, name, _do, void, /* */);
+#define DECLARE_SAFE_FUNC1(name, _do, ...) DECLARE_SAFE_FUNC(1, name, _do, __VA_ARGS__);
+#define DECLARE_SAFE_FUNC2(name, _do, ...) DECLARE_SAFE_FUNC(2, name, _do, __VA_ARGS__);
+#define DECLARE_SAFE_FUNC3(name, _do, ...) DECLARE_SAFE_FUNC(3, name, _do, __VA_ARGS__);
+
+
+static DEFINE_MUTEX(stat_mutex);
+static enum nsp_stat stat = NS_OFF;
+
+DECLARE_SAFE_FUNC2(nsp_add, __nsp_add, const char *, app_path,
+ unsigned long, main_addr);
+DECLARE_SAFE_FUNC1(nsp_rm, __nsp_rm, const char *, app_path);
+DECLARE_SAFE_FUNC0(nsp_rm_all, __nsp_rm_all);
+DECLARE_SAFE_FUNC3(nsp_set_lpad_info, do_set_lpad_info,
+ const char *, path, unsigned long, dlopen,
+ unsigned long, dlsym);
+DECLARE_SAFE_FUNC1(nsp_set_appcore_info, do_set_appcore_info,
+ struct nsp_info_data *, info);
+
+
+
+
+
+/* ============================================================================
+ * = set stat =
+ * ============================================================================
+ */
+static int set_stat_off(void)
+{
+ if (stat == NS_OFF)
+ return -EINVAL;
+
+ __nsp_disabel();
+
+ stat = NS_OFF;
+
+ return 0;
+}
+
+static int set_stat_on(void)
+{
+ if (is_init() == false)
+ return -EPERM;
+
+ if (stat == NS_ON)
+ return -EINVAL;
+
+ __nsp_enable();
+
+ stat = NS_ON;
+
+ return 0;
+}
+
+int nsp_set_stat(enum nsp_stat st)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&stat_mutex);
+ switch (st) {
+ case NS_OFF:
+ ret = set_stat_off();
+ break;
+ case NS_ON:
+ ret = set_stat_on();
+ break;
+ }
+ mutex_unlock(&stat_mutex);
+
+ return ret;
+}
+
+enum nsp_stat nsp_get_stat(void)
+{
+ return stat;
+}
+
+
+
+
+
+/* ============================================================================
+ * = tdata =
+ * ============================================================================
+ */
+enum nsp_proc_stat {
+ NPS_OPEN_E, /* mapping begin */
+ NPS_OPEN_R,
+ NPS_SYM_E,
+ NPS_SYM_R, /* mapping end */
+ NPS_MAIN_E, /* main begin */
+ NPS_AC_EFL_MAIN_E, /* main end */
+ NPS_AC_INIT_R, /* create begin */
+ NPS_ELM_RUN_E, /* create end */
+ NPS_DO_APP_E, /* reset begin */
+ NPS_DO_APP_R /* reset end */
+};
+
+struct tdata {
+ enum nsp_proc_stat stat;
+ struct nsp_data *nsp_data;
+ u64 time;
+ void __user *handle;
+ struct probe_new p_main;
+};
+
+
+static void ktd_init(struct task_struct *task, void *data)
+{
+ struct tdata *tdata = (struct tdata *)data;
+
+ tdata->nsp_data = NULL;
+}
+
+static struct ktask_data ktd = {
+ .init = ktd_init,
+ .size = sizeof(struct tdata),
+};
+
+static struct tdata *tdata_get(struct task_struct *task)
+{
+ return (struct tdata *)swap_ktd(&ktd, task);
+}
+
+
+
+
+
+/* ============================================================================
+ * = handlers =
+ * ============================================================================
+ */
+static int dlopen_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ const char __user *user_s = (const char __user *)swap_get_uarg(regs, 0);
+ const char *path;
+ struct nsp_data *nsp_data;
+
+ path = strdup_from_user(user_s, GFP_ATOMIC);
+ if (path == NULL)
+ return 0;
+
+ nsp_data = nsp_data_find_by_path(path);
+ if (nsp_data) {
+ struct tdata *tdata = tdata_get(current);
+
+ /* init tdata */
+ if (tdata->nsp_data == NULL) {
+ tdata->stat = NPS_OPEN_E;
+ tdata->time = swap_msg_current_time();
+ tdata->nsp_data = nsp_data;
+ }
+ }
+
+ kfree(path);
+ return 0;
+}
+
+static int dlopen_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct tdata *tdata = tdata_get(current);
+ void *handle = (void *)regs_return_value(regs);
+
+ if ((tdata->stat == NPS_OPEN_E) && handle) {
+ tdata->stat = NPS_OPEN_R;
+ tdata->handle = handle;
+ } else {
+ tdata->handle = NULL;
+ }
+
+ return 0;
+}
+
+static int dlsym_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct tdata *tdata = tdata_get(current);
+ void __user *handle = (void __user *)swap_get_uarg(regs, 0);
+ const char __user *str = (const char __user *)swap_get_uarg(regs, 1);
+
+ if (handle == tdata->handle && tdata->stat == NPS_OPEN_R) {
+ const char *name;
+
+ name = strdup_from_user(str, GFP_ATOMIC);
+ if (name && (strcmp(name, "main") == 0))
+ tdata->stat = NPS_SYM_E;
+
+ kfree(name);
+ }
+
+ return 0;
+}
+
+static int dlsym_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct tdata *tdata = tdata_get(current);
+
+ if (tdata->handle && tdata->stat == NPS_SYM_E)
+ tdata->stat = NPS_SYM_R;
+
+ return 0;
+}
+
+static void stage_begin(enum nsp_proc_stat priv, enum nsp_proc_stat cur)
+{
+ struct tdata *tdata = tdata_get(current);
+
+ if (tdata->handle && tdata->stat == priv) {
+ tdata->stat = cur;
+ tdata->time = swap_msg_current_time();
+ }
+}
+
+static void stage_end(enum nsp_proc_stat priv, enum nsp_proc_stat cur,
+ enum nsp_msg_stage st)
+{
+ struct tdata *tdata = tdata_get(current);
+ u64 time_start;
+ u64 time_end;
+
+ if (tdata->handle && tdata->stat == priv) {
+ tdata->stat = cur;
+ time_start = tdata->time;
+
+ time_end = swap_msg_current_time();
+ nsp_msg(st, time_start, time_end);
+ }
+}
+
+static int main_h(struct uprobe *p, struct pt_regs *regs)
+{
+ struct tdata *tdata = tdata_get(current);
+ u64 time_start;
+ u64 time_end;
+
+ if (tdata->handle && tdata->stat == NPS_SYM_R) {
+ tdata->stat = NPS_MAIN_E;
+ time_start = tdata->time;
+ time_end = swap_msg_current_time();
+ tdata->time = time_end;
+
+ nsp_msg(NMS_MAPPING, time_start, time_end);
+ }
+
+ return 0;
+}
+
+/* FIXME: workaround for simultaneously nsp and main() function profiling */
+#include <retprobe/rp_msg.h>
+#include <us_manager/us_manager.h>
+
+static int main_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct uretprobe *rp = ri->rp;
+
+ if (rp) {
+ main_h(&rp->up, regs);
+
+ if (get_quiet() == QT_OFF) {
+ struct sspt_ip *ip;
+ unsigned long func_addr;
+
+ ip = container_of(rp, struct sspt_ip, retprobe);
+ func_addr = (unsigned long)ip->orig_addr;
+ rp_msg_entry(regs, func_addr, "p");
+ }
+ }
+
+ return 0;
+}
+
+static int main_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct uretprobe *rp = ri->rp;
+
+ if (rp && get_quiet() == QT_OFF) {
+ struct sspt_ip *ip;
+ unsigned long func_addr;
+ unsigned long ret_addr;
+
+ ip = container_of(rp, struct sspt_ip, retprobe);
+ func_addr = (unsigned long)ip->orig_addr;
+ ret_addr = (unsigned long)ri->ret_addr;
+ rp_msg_exit(regs, func_addr, 'n', ret_addr);
+ }
+
+ return 0;
+}
+
+static int ac_efl_main_h(struct uprobe *p, struct pt_regs *regs)
+{
+ stage_end(NPS_MAIN_E, NPS_AC_EFL_MAIN_E, NMS_MAIN);
+ return 0;
+}
+
+static int ac_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ stage_begin(NPS_AC_EFL_MAIN_E, NPS_AC_INIT_R);
+ return 0;
+}
+
+static int elm_run_h(struct uprobe *p, struct pt_regs *regs)
+{
+ stage_end(NPS_AC_INIT_R, NPS_ELM_RUN_E, NMS_CREATE);
+ return 0;
+}
+
+static int do_app_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ int event = swap_get_uarg(regs, 0);
+ enum { AE_RESET = 5 }; /* FIXME: hardcode */
+
+ if (event == AE_RESET)
+ stage_begin(NPS_ELM_RUN_E, NPS_DO_APP_E);
+
+ return 0;
+}
+
+static int do_app_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ stage_end(NPS_DO_APP_E, NPS_DO_APP_R, NMS_RESET);
+ return 0;
+}
+
+
+
+
+
+int nsp_init(void)
+{
+ return swap_ktd_reg(&ktd);
+}
+
+void nsp_exit(void)
+{
+ if (stat == NS_ON)
+ set_stat_off();
+
+ uninit_variables();
+ swap_ktd_unreg(&ktd);
+}
--- /dev/null
+#ifndef _NSP_H
+#define _NSP_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+enum offset_t {
+ OS_CREATE,
+ OS_RESET
+};
+
+enum nsp_stat {
+ NS_OFF,
+ NS_ON
+};
+
+struct nsp_info_data {
+ const char *appcore_path;
+ unsigned long ac_efl_init;
+ unsigned long do_app;
+
+ const char *capi_path;
+ unsigned long ac_init;
+ unsigned long elm_run;
+};
+
+int nsp_init(void);
+void nsp_exit(void);
+
+int nsp_set_lpad_info(const char *path, unsigned long dlopen,
+ unsigned long dlsym);
+int nsp_set_appcore_info(struct nsp_info_data *info);
+
+int nsp_set_stat(enum nsp_stat st);
+enum nsp_stat nsp_get_stat(void);
+
+int nsp_add(const char *app_path, unsigned long main_addr);
+int nsp_rm(const char *app_path);
+int nsp_rm_all(void);
+
+
+#endif /* _NSP_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/limits.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <master/swap_debugfs.h>
+#include "nsp.h"
+
+
+/* remove end-line symbols */
+static void rm_endline_symbols(char *buf, size_t len)
+{
+ char *p, *buf_end;
+
+ buf_end = buf + len;
+ for (p = buf; p != buf_end; ++p)
+ if (*p == '\n' || *p == '\r')
+ *p = '\0';
+}
+
+/*
+ * format:
+ * main:app_path
+ *
+ * sample:
+ * 0x00000d60:/bin/app_sample
+ */
+static int do_add(const char *buf, size_t len)
+{
+ int n, ret;
+ char *app_path;
+ unsigned long main_addr;
+ const char fmt[] = "%%lx:/%%%ds";
+ char fmt_buf[64];
+
+ n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2);
+ if (n <= 0)
+ return -EINVAL;
+
+ app_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (app_path == NULL)
+ return -ENOMEM;
+
+ n = sscanf(buf, fmt_buf, &main_addr, app_path + 1);
+ if (n != 2) {
+ ret = -EINVAL;
+ goto free_app_path;
+ }
+ app_path[0] = '/';
+
+ ret = nsp_add(app_path, main_addr);
+
+free_app_path:
+ kfree(app_path);
+ return ret;
+}
+
+/*
+ * format:
+ * path
+ *
+ * sample:
+ * /tmp/sample
+ */
+static int do_rm(const char *buf, size_t len)
+{
+ return nsp_rm(buf);
+}
+
+static int do_rm_all(const char *buf, size_t len)
+{
+ return nsp_rm_all();
+}
+
+/*
+ * format:
+ * dlopen_addr@plt:dlsym_addr@plt:launchpad_path
+ *
+ * sample:
+ * 0x000234:0x000342:/usr/bin/launchpad-loader
+ */
+static int do_set_lpad_info(const char *data, size_t len)
+{
+ int n, ret;
+ unsigned long dlopen_addr;
+ unsigned long dlsym_addr;
+ char *lpad_path;
+ const char fmt[] = "%%lx:%%lx:/%%%ds";
+ char fmt_buf[64];
+
+ n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2);
+ if (n <= 0)
+ return -EINVAL;
+
+ lpad_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (lpad_path == NULL)
+ return -ENOMEM;
+
+ n = sscanf(data, fmt_buf, &dlopen_addr, &dlsym_addr, lpad_path + 1);
+ if (n != 3) {
+ ret = -EINVAL;
+ goto free_lpad_path;
+ }
+ lpad_path[0] = '/';
+
+ ret = nsp_set_lpad_info(lpad_path, dlopen_addr, dlsym_addr);
+
+free_lpad_path:
+ kfree(lpad_path);
+ return ret;
+}
+
+/*
+ * format:
+ * appcore_efl_init:__do_app:libappcore-efl ui_app_init:elm_run@plt:libcapi-appfw-application
+ *
+ * sample:
+ * 0x2000:0x2ed0:/usr/lib/libappcore-efl.so.1 0x2b20:0x11f0:/usr/lib/libcapi-appfw-application.so.0
+ */
+static int do_set_appcore_info(const char *data, size_t len)
+{
+ int n, ret;
+ struct nsp_info_data info;
+ const char fmt[] = "%%lx:%%lx:/%%%ds %%lx:%%lx:/%%%ds";
+ char fmt_buf[64];
+ char *appcore_path, *capi_path;
+
+ n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2, PATH_MAX - 2);
+ if (n <= 0)
+ return -EINVAL;
+
+ appcore_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!appcore_path)
+ return -ENOMEM;
+
+ capi_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!capi_path) {
+ ret = -ENOMEM;
+ goto fail_alloc;
+ }
+
+ n = sscanf(data, fmt_buf,
+ &info.ac_efl_init, &info.do_app, appcore_path + 1,
+ &info.ac_init, &info.elm_run, capi_path + 1);
+ if (n != 6) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ appcore_path[0] = '/';
+ capi_path[0] = '/';
+
+ info.appcore_path = appcore_path;
+ info.capi_path = capi_path;
+ ret = nsp_set_appcore_info(&info);
+
+fail:
+ kfree(capi_path);
+fail_alloc:
+ kfree(appcore_path);
+ return ret;
+}
+
+/*
+ * format:
+ * 0 byte - type
+ * 1 byte - ' '
+ * 2.. bytes - data
+ */
+static int do_cmd(const char *data, size_t len)
+{
+ char type;
+ size_t len_data;
+ const char *cmd_data;
+
+ if (len) {
+ if (data[0] == 'c')
+ return do_rm_all(data + 1, len - 1);
+ }
+ /*
+ * 0 byte - type
+ * 1 byte - ' '
+ */
+ if (len < 2 || data[1] != ' ')
+ return -EINVAL;
+
+ len_data = len - 2;
+ cmd_data = data + 2;
+ type = data[0];
+ switch (type) {
+ case 'a':
+ return do_add(cmd_data, len_data);
+ case 'b':
+ return do_set_lpad_info(cmd_data, len_data);
+ case 'l':
+ return do_set_appcore_info(cmd_data, len_data);
+ case 'r':
+ return do_rm(cmd_data, len_data);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+
+/* ============================================================================
+ * === DEBUGFS FOR CMD ===
+ * ============================================================================
+ */
+static ssize_t write_cmd(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ ssize_t ret = count;
+
+ buf = kmalloc(count + 1, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, user_buf, count)) {
+ ret = -EFAULT;
+ goto free_buf;
+ }
+
+ buf[count] = '\0';
+ rm_endline_symbols(buf, count);
+
+ if (do_cmd(buf, count))
+ ret = -EINVAL;
+
+free_buf:
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t read_cmd(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char help[] =
+ "use:\n"
+ "\ta $app_path - add\n"
+ "\tr $app_path - remove\n"
+ "\tc - remove all\n"
+ "\tb dlopen_addr@plt:dlsym_addr@plt:launchpad_path\n"
+ "\tl appcore_efl_init:__do_app:libappcore-efl_path ui_app_init:elm_run@plt:libcapi-appfw-application_path\n";
+ ssize_t ret;
+
+ ret = simple_read_from_buffer(user_buf, count, ppos,
+ help, sizeof(help));
+
+ return ret;
+}
+
+static const struct file_operations fops_cmd = {
+ .read = read_cmd,
+ .write = write_cmd,
+ .llseek = default_llseek
+};
+
+
+
+
+/* ============================================================================
+ * === DEBUGFS FOR ENABLE ===
+ * ============================================================================
+ */
+static ssize_t read_enabled(struct file *file, char /*__user*/ *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[2];
+
+ buf[0] = nsp_get_stat() == NS_OFF ? '0' : '1';
+ buf[1] = '\n';
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t write_enabled(struct file *file, const char /*__user*/ *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ char buf[32];
+ size_t buf_size;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ switch (buf[0]) {
+ case '1':
+ ret = nsp_set_stat(NS_ON);
+ break;
+ case '0':
+ ret = nsp_set_stat(NS_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_enabled = {
+ .read = read_enabled,
+ .write = write_enabled,
+ .llseek = default_llseek,
+};
+
+
+
+
+static struct dentry *nsp_dir = NULL;
+
+void nsp_debugfs_exit(void)
+{
+ if (nsp_dir)
+ debugfs_remove_recursive(nsp_dir);
+
+ nsp_dir = NULL;
+}
+
+int nsp_debugfs_init(void)
+{
+ struct dentry *dentry;
+
+ dentry = swap_debugfs_getdir();
+ if (dentry == NULL)
+ return -ENOENT;
+
+ nsp_dir = swap_debugfs_create_dir("nsp", dentry);
+ if (nsp_dir == NULL)
+ return -ENOMEM;
+
+ dentry = swap_debugfs_create_file("cmd", 0600, nsp_dir, NULL,
+ &fops_cmd);
+ if (dentry == NULL)
+ goto fail;
+
+ dentry = swap_debugfs_create_file("enabled", 0600, nsp_dir, NULL,
+ &fops_enabled);
+ if (dentry == NULL)
+ goto fail;
+
+ return 0;
+
+fail:
+ nsp_debugfs_exit();
+ return -ENOMEM;
+}
--- /dev/null
+#ifndef _NSP_DEBUGFS_H
+#define _NSP_DEBUGFS_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+int nsp_debugfs_init(void);
+void nsp_debugfs_exit(void);
+
+
+#endif /* _NSP_DEBUGFS_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <master/swap_initializer.h>
+#include "nsp.h"
+#include "nsp_debugfs.h"
+
+
+SWAP_LIGHT_INIT_MODULE(NULL, nsp_init, nsp_exit,
+ nsp_debugfs_init, nsp_debugfs_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <writer/swap_msg.h>
+#include "nsp_msg.h"
+
+
+struct nsp_msg_struct {
+ u32 pid;
+ u32 stage;
+ u64 begin_time;
+ u64 end_time;
+} __packed;
+
+
+void nsp_msg(enum nsp_msg_stage stage, u64 begin_time, u64 end_time)
+{
+ struct swap_msg *m;
+ struct nsp_msg_struct *nsp;
+
+ m = swap_msg_get(MSG_NSP);
+
+ nsp = (struct nsp_msg_struct *)swap_msg_payload(m);
+ nsp->pid = (u32)current->tgid;
+ nsp->stage = (u32)stage;
+ nsp->begin_time = begin_time;
+ nsp->end_time = end_time;
+
+ swap_msg_flush(m, sizeof(*nsp));
+ swap_msg_put(m);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#ifndef _NSP_MSG_H
+#define _NSP_MSG_H
+
+
+#include <linux/types.h>
+
+
+enum nsp_msg_stage {
+ NMS_MAPPING = 0x00,
+ NMS_MAIN = 0x01,
+ NMS_CREATE = 0x02,
+ NMS_RESET = 0x03,
+};
+
+
+void nsp_msg(enum nsp_msg_stage stage, u64 begin_time, u64 end_time);
+
+
+#endif /* _NSP_MSG_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#ifndef _NSP_PRINT_H
+#define _NSP_PRINT_H
+
+
+#include <linux/printk.h>
+
+
+#define NSP_PREFIX "[NSP] "
+
+#define nsp_print(...) printk(NSP_PREFIX __VA_ARGS__)
+
+
+#endif /* _NSP_PRINT_H */
--- /dev/null
+#!/bin/bash -ex
+
+if [ $# -ne 1 ]; then
+ echo "Usage $0 <dest_dir>"
+ exit 0
+fi
+
+scrdir=$(dirname $(readlink -f $0))
+
+dest=$1
+mkdir -p $dest
+
+mode=0666
+
+modules="master/swap_master.ko
+ buffer/swap_buffer.ko
+ ksyms/swap_ksyms.ko
+ driver/swap_driver.ko
+ writer/swap_writer.ko
+ kprobe/swap_kprobe.ko
+ ks_manager/swap_ks_manager.ko
+ uprobe/swap_uprobe.ko
+ us_manager/swap_us_manager.ko
+ ks_features/swap_ks_features.ko
+ sampler/swap_sampler.ko
+ energy/swap_energy.ko
+ parser/swap_message_parser.ko
+ retprobe/swap_retprobe.ko
+ loader/swap_loader.ko
+ preload/swap_preload.ko
+ uihv/swap_uihv.ko
+ fbiprobe/swap_fbiprobe.ko
+ wsp/swap_wsp.ko
+ nsp/swap_nsp.ko
+ task_ctx/swap_taskctx.ko
+ got_patcher/swap_gtp.ko"
+
+for i in $modules; do
+ mv $scrdir/$i $dest/
+ chmod 0666 $dest/$(basename $i)
+done
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../energy/Module.symvers \
+ $(src)/../ks_features/Module.symvers \
+ $(src)/../driver/Module.symvers \
+ $(src)/../writer/Module.symvers \
+ $(src)/../us_manager/Module.symvers \
+ $(src)/../sampler/Module.symvers
+
+obj-m := swap_message_parser.o
+swap_message_parser-y := swap_msg_parser.o \
+ msg_parser.o \
+ msg_buf.o \
+ msg_cmd.o \
+ features.o \
+ us_inst.o \
+ cpu_ctrl.o \
+ usm_msg.o
--- /dev/null
+/**
+ * parser/cpu_ctrl.c
+ * @author Vasiliy Ulyanov <v.ulyanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * CPU controls implementation.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/cpu.h>
+#include <ksyms/ksyms.h>
+
+#ifdef CONFIG_SMP
+static void (*swap_cpu_maps_update_begin)(void);
+static void (*swap_cpu_maps_update_done)(void);
+static int (*swap_cpu_down)(unsigned int, int);
+static int (*swap_cpu_up)(unsigned int, int);
+
+/**
+ * @brief Disables nonboot CPUs lock.
+ *
+ * @param mask Pointer to CPU mask struct.
+ * @return 0 on success, error code on error.
+ */
+int swap_disable_nonboot_cpus_lock(struct cpumask *mask)
+{
+ int boot_cpu, cpu;
+ int ret = 0;
+
+ swap_cpu_maps_update_begin();
+ cpumask_clear(mask);
+
+ boot_cpu = cpumask_first(cpu_online_mask);
+
+ for_each_online_cpu(cpu) {
+ if (cpu == boot_cpu)
+ continue;
+ ret = swap_cpu_down(cpu, 0);
+ if (ret == 0)
+ cpumask_set_cpu(cpu, mask);
+ printk(KERN_INFO "===> SWAP CPU[%d] down(%d)\n", cpu, ret);
+ }
+
+ WARN_ON(num_online_cpus() > 1);
+ return ret;
+}
+
+/**
+ * @brief Enables nonboot CPUs unlock.
+ *
+ * @param mask Pointer to CPU mask struct.
+ * @return 0 on success, error code on error.
+ */
+int swap_enable_nonboot_cpus_unlock(struct cpumask *mask)
+{
+ int cpu, ret = 0;
+
+ if (cpumask_empty(mask))
+ goto out;
+
+ for_each_cpu(cpu, mask) {
+ ret = swap_cpu_up(cpu, 0);
+ printk(KERN_INFO "===> SWAP CPU[%d] up(%d)\n", cpu, ret);
+ }
+
+out:
+ swap_cpu_maps_update_done();
+
+ return ret;
+}
+
+/**
+ * @brief Intializes CPU controls.
+ *
+ * @return 0 on success, error code on error.
+ */
+int init_cpu_deps(void)
+{
+ const char *sym = "cpu_maps_update_begin";
+
+ swap_cpu_maps_update_begin = (void *)swap_ksyms(sym);
+ if (!swap_cpu_maps_update_begin)
+ goto not_found;
+
+ sym = "cpu_maps_update_done";
+ swap_cpu_maps_update_done = (void *)swap_ksyms(sym);
+ if (!swap_cpu_maps_update_done)
+ goto not_found;
+
+ sym = "_cpu_up";
+ swap_cpu_up = (void *)swap_ksyms(sym);
+ if (!swap_cpu_up)
+ goto not_found;
+
+ sym = "_cpu_down";
+ swap_cpu_down = (void *)swap_ksyms(sym);
+ if (!swap_cpu_down)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ printk(KERN_INFO "ERROR: symbol %s(...) not found\n", sym);
+ return -ESRCH;
+}
+
+#endif /* CONFIG_SMP */
--- /dev/null
+/**
+ * @file parser/cpu_ctrl.h
+ * @author Vasiliy Ulyanov <v.ulyanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * CPU controls interface.
+ */
+
+#ifndef _CPU_CTRL_H_
+#define _CPU_CTRL_H_
+
+struct cpumask;
+
+#ifdef CONFIG_SMP
+int swap_disable_nonboot_cpus_lock(struct cpumask *mask);
+int swap_enable_nonboot_cpus_unlock(struct cpumask *mask);
+
+int init_cpu_deps(void);
+
+#else /* CONFIG_SMP */
+
+static inline int swap_disable_nonboot_cpus_lock(struct cpumask *mask)
+{
+ return 0;
+}
+
+static inline int swap_enable_nonboot_cpus_unlock(struct cpumask *mask)
+{
+ return 0;
+}
+
+static inline int init_cpu_deps(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SMP */
+
+#endif /* _CPU_CTRL_H_ */
--- /dev/null
+/**
+ * parser/features.c
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Features control implementation.
+ */
+
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <ks_features/ks_features.h>
+#include <us_manager/us_manager.h>
+#include "parser_defs.h"
+#include "features.h"
+#include "msg_parser.h"
+
+#include <writer/swap_msg.h>
+#include <writer/event_filter.h>
+#include <sampler/swap_sampler_module.h>
+#include <energy/energy.h>
+
+/**
+ * @enum features_list
+ * List of supported features.
+ */
+enum features_list {
+ syscall_file = (1 << 10), /**< File operation syscalls tracing */
+ syscall_ipc = (1 << 11), /**< IPC syscall tracing */
+ syscall_process = (1 << 12), /**< Process syscalls tracing */
+ syscall_signal = (1 << 13), /**< Signal syscalls tracing */
+ syscall_network = (1 << 14), /**< Network syscalls tracing */
+ syscall_desc = (1 << 15), /**< Descriptor syscalls tracing */
+ context_switch = (1 << 16), /**< Context switch tracing */
+ func_sampling = (1 << 19) /**< Function sampling */
+};
+
+/**
+ * @brief Start user-space instrumentation event handling.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return 0.
+ */
+int set_us_inst(struct conf_data *conf)
+{
+ set_quiet(QT_OFF);
+
+ return 0;
+}
+
+/**
+ * @brief Stop user-space instrumentation event handling.
+ *
+ * @return 0.
+ */
+int unset_us_inst(void)
+{
+ set_quiet(QT_ON);
+
+ return 0;
+}
+
+/**
+ * @brief Set syscall file feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_syscall_file(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_FILE);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall file feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_syscall_file(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_FILE);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall ipc feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_syscall_ipc(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_IPC);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall ipc feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_syscall_ipc(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_IPC);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall process feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_syscall_process(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_PROCESS);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall process feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_syscall_process(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_PROCESS);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall signal feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_syscall_signal(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_SIGNAL);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall signal feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_syscall_signal(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_SIGNAL);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall network feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_syscall_network(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_NET);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall network feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_syscall_network(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_NET);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall desc feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_syscall_desc(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_DESC);
+
+ return ret;
+}
+
+/**
+ * @brief Set syscall desc feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_syscall_desc(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_DESC);
+
+ return ret;
+}
+
+/**
+ * @brief Set context switch feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_context_switch(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_feature(FID_SWITCH);
+
+ return ret;
+}
+/**
+ * @brief Set context switch feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_context_switch(void)
+{
+ int ret;
+
+ ret = unset_feature(FID_SWITCH);
+
+ return ret;
+}
+
+
+
+
+
+struct sample {
+ u32 pid;
+ u64 pc_addr;
+ u32 tid;
+ u32 cpu_num;
+} __packed;
+
+static void sample_msg(struct pt_regs *regs)
+{
+ struct swap_msg *m;
+ struct sample *s;
+ struct task_struct *task = current;
+
+ m = swap_msg_get(MSG_SAMPLE);
+
+ s = swap_msg_payload(m);
+ s->pid = task->tgid;
+ s->pc_addr = instruction_pointer(regs);
+ s->tid = task->pid;
+ s->cpu_num = raw_smp_processor_id();
+
+ swap_msg_flush(m, sizeof(*s));
+ swap_msg_put(m);
+}
+
+static void sampler_cb(struct pt_regs *regs)
+{
+ if (check_event(current))
+ sample_msg(regs);
+}
+
+/**
+ * @brief Set sampling feature on.
+ *
+ * @param conf Pointer to conf_data config.
+ * @return set_feature results.
+ */
+int set_func_sampling(struct conf_data *conf)
+{
+ int ret;
+
+ ret = swap_sampler_start(conf->data_msg_period, sampler_cb);
+
+ return ret;
+}
+
+/**
+ * @brief Set sampling feature off.
+ *
+ * @return unset_feature results.
+ */
+int unset_func_sampling(void)
+{
+ int ret;
+
+ ret = swap_sampler_stop();
+
+ return ret;
+}
+
+static int set_func_energy(struct conf_data *conf)
+{
+ return set_energy();
+}
+
+static int unset_func_energy(void)
+{
+ return unset_energy();
+}
+
+static int set_sysfile_activity(struct conf_data *conf)
+{
+ return set_feature(FID_SYSFILE_ACTIVITY);
+}
+
+static int unset_sysfile_activity(void)
+{
+ return unset_feature(FID_SYSFILE_ACTIVITY);
+}
+
+/**
+ * @struct feature_item
+ * @brief Struct that describes feature.
+ * @var feature_item::name
+ * Feature name.
+ * @var feature_item::set
+ * Set feature callback.
+ * @var feature_item::unset
+ * Unset feature callback.
+ */
+struct feature_item {
+ char *name;
+ int (*set)(struct conf_data *conf);
+ int (*unset)(void);
+};
+
+static struct feature_item feature_us_inst = {
+ .name = "user space instrumentation",
+ .set = set_us_inst,
+ .unset = unset_us_inst
+};
+
+static struct feature_item feature_syscall_file = {
+ .name = "file operation syscalls",
+ .set = set_syscall_file,
+ .unset = unset_syscall_file
+};
+
+static struct feature_item feature_syscall_ipc = {
+ .name = "IPC syscall",
+ .set = set_syscall_ipc,
+ .unset = unset_syscall_ipc
+};
+
+static struct feature_item feature_syscall_process = {
+ .name = "process syscalls",
+ .set = set_syscall_process,
+ .unset = unset_syscall_process
+};
+
+static struct feature_item feature_syscall_signal = {
+ .name = "signal syscalls",
+ .set = set_syscall_signal,
+ .unset = unset_syscall_signal
+};
+
+static struct feature_item feature_syscall_network = {
+ .name = "network syscalls",
+ .set = set_syscall_network,
+ .unset = unset_syscall_network
+};
+
+static struct feature_item feature_syscall_desc = {
+ .name = "descriptor syscalls",
+ .set = set_syscall_desc,
+ .unset = unset_syscall_desc
+};
+
+static struct feature_item feature_context_switch = {
+ .name = "context switch",
+ .set = set_context_switch,
+ .unset = unset_context_switch
+};
+
+static struct feature_item feature_func_sampling = {
+ .name = "function sampling",
+ .set = set_func_sampling,
+ .unset = unset_func_sampling
+};
+
+static struct feature_item feature_func_energy = {
+ .name = "energy",
+ .set = set_func_energy,
+ .unset = unset_func_energy
+};
+
+static struct feature_item feature_sysfile_activity = {
+ .name = "system file activity",
+ .set = set_sysfile_activity,
+ .unset = unset_sysfile_activity
+};
+
+static struct feature_item *feature_list[] = {
+ /* 0 */ NULL,
+ /* 1 */ NULL,
+ /* 2 */ &feature_us_inst,
+ /* 3 */ NULL,
+ /* 4 */ NULL,
+ /* 5 */ NULL,
+ /* 6 */ NULL,
+ /* 7 */ NULL,
+ /* 8 */ NULL,
+ /* 9 */ NULL,
+ /* 10 */ &feature_syscall_file,
+ /* 11 */ &feature_syscall_ipc,
+ /* 12 */ &feature_syscall_process,
+ /* 13 */ &feature_syscall_signal,
+ /* 14 */ &feature_syscall_network,
+ /* 15 */ &feature_syscall_desc,
+ /* 16 */ &feature_context_switch,
+ /* 17 */ NULL,
+ /* 18 */ NULL,
+ /* 19 */ &feature_func_sampling,
+ /* 20 */ NULL,
+ /* 21 */ NULL,
+ /* 22 */ NULL,
+ /* 23 */ NULL,
+ /* 24 */ NULL,
+ /* 25 */ NULL,
+ /* 26 */ &feature_func_energy,
+ /* 27 */ NULL,
+ /* 28 */ NULL,
+ /* 29 */ NULL,
+ /* 30 */ NULL,
+ /* 31 */ NULL,
+ /* 32 */ NULL,
+ /* 33 */ NULL,
+ /* 34 */ NULL,
+ /* 35 */ NULL,
+ /* 36 */ NULL,
+ /* 37 */ NULL,
+ /* 38 */ NULL,
+ /* 39 */ NULL,
+ /* 40 */ NULL,
+ /* 41 */ NULL,
+ /* 42 */ NULL,
+ /* 43 */ NULL,
+ /* 44 */ NULL,
+ /* 45 */ NULL,
+ /* 46 */ NULL,
+ /* 47 */ NULL,
+ /* 48 */ &feature_sysfile_activity,
+};
+
+/**
+ * @brief SIZE_FEATURE_LIST definition.
+ */
+enum {
+ SIZE_FEATURE_LIST =
+ sizeof(feature_list) / sizeof(struct feature_item *),
+};
+
+static u64 feature_inst;
+static u64 feature_mask;
+
+/**
+ * @brief Inits features list.
+ *
+ * @return 0.
+ */
+int once_features(void)
+{
+ int i;
+ for (i = 0; i < SIZE_FEATURE_LIST; ++i) {
+ printk(KERN_INFO "### f init_feature_mask[%2d]=%p\n", i,
+ feature_list[i]);
+ if (feature_list[i] != NULL) {
+ feature_mask |= ((u64)1) << i;
+ printk(KERN_INFO "### f name=%s\n",
+ feature_list[i]->name);
+ }
+ }
+
+ return 0;
+}
+
+static int do_set_features(struct conf_data *conf)
+{
+ int i, ret;
+ u64 feature_XOR;
+ u64 features, features_backup;
+
+ /* TODO: field use_features1 is not used*/
+ features_backup = features = conf->use_features0;
+
+ features &= feature_mask;
+ feature_XOR = features ^ feature_inst;
+
+ for (i = 0; feature_XOR && i < SIZE_FEATURE_LIST; ++i) {
+ if ((feature_XOR & 1) && feature_list[i] != NULL) {
+ u64 f_mask;
+ if (features & 1)
+ ret = feature_list[i]->set(conf);
+ else
+ ret = feature_list[i]->unset();
+
+ if (ret) {
+ char *func = features & 1 ? "set" : "unset";
+ print_err("%s '%s' ret=%d\n",
+ func, feature_list[i]->name, ret);
+
+ return ret;
+ }
+ f_mask = ~((u64)1 << i);
+ feature_inst = (feature_inst & f_mask) |
+ (features_backup & ~f_mask);
+ }
+
+ features >>= 1;
+ feature_XOR >>= 1;
+ }
+
+ return 0;
+}
+
+static DEFINE_MUTEX(mutex_features);
+
+/**
+ * @brief Wrapper for do_set_features with locking.
+ *
+ * @param conf Pointer to the current config.
+ * @return do_set_features result.
+ */
+int set_features(struct conf_data *conf)
+{
+ int ret;
+
+ mutex_lock(&mutex_features);
+ ret = do_set_features(conf);
+ mutex_unlock(&mutex_features);
+
+ return ret;
+}
+
+void disable_all_features(void)
+{
+ struct conf_data conf;
+
+ conf.use_features0 = 0;
+ conf.use_features1 = 0;
+ conf.data_msg_period = 0;
+ conf.sys_trace_period = 0;
+
+ BUG_ON(set_features(&conf));
+ BUG_ON(feature_inst);
+}
--- /dev/null
+/**
+ * @file parser/features.h
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Features control interface declaration.
+ */
+
+
+#ifndef _FEATURES_H
+#define _FEATURES_H
+
+struct conf_data;
+
+int once_features(void);
+
+int set_features(struct conf_data *conf);
+void disable_all_features(void);
+
+#endif /* _FEATURES_H */
--- /dev/null
+/**
+ * parser/msg_buf.c
+ * @author Vyacheslav Cherkashin
+ * @author Vitaliy Cherepanov
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Message buffer controls implementation.
+ */
+
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include "msg_buf.h"
+#include "parser_defs.h"
+
+/**
+ * @brief Initializes message buffer.
+ *
+ * @param mb Pointer to message buffer struct.
+ * @param size Size of the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int init_mb(struct msg_buf *mb, size_t size)
+{
+ if (size) {
+ mb->begin = vmalloc(size);
+ if (mb->begin == NULL) {
+ printk(KERN_INFO "Cannot alloc memory!\n");
+ return -ENOMEM;
+ }
+
+ mb->ptr = mb->begin;
+ mb->end = mb->begin + size;
+ } else
+ mb->begin = mb->end = mb->ptr = NULL;
+
+ return 0;
+}
+
+/**
+ * @brief Uninitializes message buffer.
+ *
+ * @param mb Pointer to message buffer struct.
+ * @return Void.
+ */
+void uninit_mb(struct msg_buf *mb)
+{
+ vfree(mb->begin);
+}
+
+/**
+ * @brief Checks if current buffer data pointer is at the end.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @param size Size of the message buffer.
+ * @return 1 - buffer ptr is not at the end,
+ * 0 - buffer ptr is at the end,
+ * -1 - buffer ptr is invalid (far away of the end).
+ */
+int cmp_mb(struct msg_buf *mb, size_t size)
+{
+ char *tmp;
+
+ tmp = mb->ptr + size;
+ if (mb->end > tmp)
+ return 1;
+ else if (mb->end < tmp)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * @brief Gets remainder part of message buffer.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @return Remainder part of message buffer.
+ */
+size_t remained_mb(struct msg_buf *mb)
+{
+ return mb->end - mb->ptr;
+}
+
+/**
+ * @brief Checks whether we reached the end of the message buffer or not.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @return 1 - if message buffer end has been reached \n
+ * 0 - otherwise.
+ */
+int is_end_mb(struct msg_buf *mb)
+{
+ return mb->ptr == mb->end;
+}
+
+/**
+ * @brief Reads 8 bits from message buffer and updates buffer's pointer.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @param val Pointer to the target variable where to put read value.
+ * @return 0 on success, negative error code on error.
+ */
+int get_u8(struct msg_buf *mb, u8 *val)
+{
+ if (cmp_mb(mb, sizeof(*val)) < 0)
+ return -EINVAL;
+
+ *val = *((u8 *)mb->ptr);
+ mb->ptr += sizeof(*val);
+
+ print_parse_debug("u8 ->%d;%08X\n", *val, *val);
+
+ return 0;
+}
+
+/**
+ * @brief Reads 32 bits from message buffer and updates buffer's pointer.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @param val Pointer to the target variable where to put read value.
+ * @return 0 on success, negative error code on error.
+ */
+int get_u32(struct msg_buf *mb, u32 *val)
+{
+ if (cmp_mb(mb, sizeof(*val)) < 0)
+ return -EINVAL;
+
+ *val = *((u32 *)mb->ptr);
+ mb->ptr += sizeof(*val);
+
+ print_parse_debug("u32->%d;%08X\n", *val, *val);
+
+ return 0;
+}
+
+/**
+ * @brief Reads 64 bits from message buffer and updates buffer's pointer.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @param val Pointer to the target variable where to put read value.
+ * @return 0 on success, negative error code on error.
+ */
+int get_u64(struct msg_buf *mb, u64 *val)
+{
+ if (cmp_mb(mb, sizeof(*val)) < 0)
+ return -EINVAL;
+
+ *val = *((u64 *)mb->ptr);
+ mb->ptr += sizeof(*val);
+ print_parse_debug("u64->%llu; 0x%016llX\n", *val, *val);
+
+ return 0;
+}
+
+/**
+ * @brief Reads null-terminated string from message buffer and updates
+ * buffer's pointer.
+ *
+ * @param mb Pointer to the message buffer struct.
+ * @param str Pointer to the target variable where to put read string.
+ * @return 0 on success, negative error code on error.
+ */
+int get_string(struct msg_buf *mb, char **str)
+{
+ size_t len, len_max;
+ enum { min_len_str = 1 };
+
+ if (cmp_mb(mb, min_len_str) < 0)
+ return -EINVAL;
+
+ len_max = remained_mb(mb) - 1;
+ len = strnlen(mb->ptr, len_max);
+
+ *str = kmalloc(len + 1, GFP_KERNEL);
+ if (*str == NULL)
+ return -ENOMEM;
+
+ memcpy(*str, mb->ptr, len);
+ (*str)[len] = '\0';
+
+ mb->ptr += len + 1;
+
+ print_parse_debug("str->'%s'\n", *str);
+ return 0;
+}
+
+/**
+ * @brief Releases string memory allocated in get_string.
+ *
+ * @param str Pointer to the target string.
+ * @return Void.
+ */
+void put_string(const char *str)
+{
+ kfree(str);
+}
--- /dev/null
+/**
+ * @file parser/msg_buf.h
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Message buffer interface declaration.
+ */
+
+
+#ifndef _MSG_BUF_H
+#define _MSG_BUF_H
+
+#include <linux/types.h>
+
+/**
+ * @struct msg_buf
+ * @brief Stores pointers to the message buffer.
+ */
+struct msg_buf {
+ char *begin; /**< Pointer to the beginning of the buffer. */
+ char *end; /**< Pointer to the end of the buffer. */
+ char *ptr; /**< Buffer iterator. */
+};
+
+int init_mb(struct msg_buf *mb, size_t size);
+void uninit_mb(struct msg_buf *mb);
+
+int cmp_mb(struct msg_buf *mb, size_t size);
+size_t remained_mb(struct msg_buf *mb);
+int is_end_mb(struct msg_buf *mb);
+
+int get_u8(struct msg_buf *mb, u8 *val);
+int get_u32(struct msg_buf *mb, u32 *val);
+int get_u64(struct msg_buf *mb, u64 *val);
+
+int get_string(struct msg_buf *mb, char **str);
+void put_string(const char *str);
+
+#endif /* _MSG_BUF_H */
--- /dev/null
+/**
+ * parser/msg_cmd.c
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Module's messages parsing implementation.
+ */
+
+
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include "msg_parser.h"
+#include "msg_buf.h"
+#include "features.h"
+#include "parser_defs.h"
+#include "us_inst.h"
+#include <writer/swap_msg.h>
+#include <us_manager/us_manager.h>
+
+
+static int wrt_launcher_port;
+
+static int set_config(struct conf_data *conf)
+{
+ int ret;
+
+ ret = set_features(conf);
+
+ return ret;
+}
+
+/**
+ * @brief Message "keep alive" handling.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int msg_keep_alive(struct msg_buf *mb)
+{
+ if (!is_end_mb(mb)) {
+ print_err("to long message, remained=%zu", remained_mb(mb));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Message "start" handling.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int msg_start(struct msg_buf *mb)
+{
+ int ret = 0;
+ struct conf_data conf;
+ LIST_HEAD(app_head);
+
+ swap_msg_seq_num_reset();
+ swap_msg_discard_reset();
+
+ if (!create_us_inst_data(mb, &app_head))
+ return -EINVAL;
+
+ if (!is_end_mb(mb)) {
+ pr_info("Too long message, remained=%zu", remained_mb(mb));
+ destroy_us_inst_data(&app_head);
+ return -EINVAL;
+ }
+
+ ret = app_list_reg(&app_head);
+ if (ret) {
+ pr_info("Cannot mod us inst, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = usm_start();
+ if (ret) {
+ app_list_unreg_all();
+ return ret;
+ }
+
+ restore_config(&conf);
+ set_config(&conf);
+
+ return 0;
+}
+
+/**
+ * @brief Message "stop" handling.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int msg_stop(struct msg_buf *mb)
+{
+ int ret = 0;
+ int discarded;
+
+ if (!is_end_mb(mb)) {
+ print_err("to long message, remained=%zu", remained_mb(mb));
+ return -EINVAL;
+ }
+
+ ret = usm_stop();
+ if (ret)
+ return ret;
+
+ app_list_unreg_all();
+ disable_all_features();
+
+ discarded = swap_msg_discard_get();
+ printk(KERN_INFO "discarded messages: %d\n", discarded);
+ swap_msg_discard_reset();
+
+ return ret;
+}
+
+/**
+ * @brief Message "config" handling.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int msg_config(struct msg_buf *mb)
+{
+ int ret = 0;
+ struct conf_data *conf;
+ enum status_type st;
+
+ conf = create_conf_data(mb);
+ if (conf == NULL)
+ return -EINVAL;
+
+ if (!is_end_mb(mb)) {
+ print_err("to long message, remained=%zu", remained_mb(mb));
+ ret = -EINVAL;
+ goto free_conf_data;
+ }
+
+ st = usm_get_status();
+ if (st == ST_ON)
+ set_config(conf);
+
+ save_config(conf);
+ usm_put_status(st);
+
+free_conf_data:
+ destroy_conf_data(conf);
+
+ return ret;
+}
+
+/**
+ * @brief Message "swap inst add" handling.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int msg_swap_inst_add(struct msg_buf *mb)
+{
+ LIST_HEAD(app_head);
+
+ if (!create_us_inst_data(mb, &app_head))
+ return -EINVAL;
+
+ if (!is_end_mb(mb)) {
+ pr_info("Too long message, remained=%zu", remained_mb(mb));
+ destroy_us_inst_data(&app_head);
+ return -EINVAL;
+ }
+
+ return app_list_reg(&app_head);
+}
+
+/**
+ * @brief Message "swap inst remove" handling.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return 0 on success, negative error code on error.
+ */
+int msg_swap_inst_remove(struct msg_buf *mb)
+{
+ LIST_HEAD(app_head);
+
+ INIT_LIST_HEAD(&app_head);
+ if (!create_us_inst_data(mb, &app_head))
+ return -EINVAL;
+
+ if (!is_end_mb(mb)) {
+ pr_info("Too long message, remained=%zu", remained_mb(mb));
+ destroy_us_inst_data(&app_head);
+ return -EINVAL;
+ }
+
+ return app_list_unreg(&app_head);
+}
+
+void set_wrt_launcher_port(int port)
+{
+ wrt_launcher_port = port;
+}
+EXPORT_SYMBOL_GPL(set_wrt_launcher_port);
+
+#define GET_PORT_DELAY 100 /* msec */
+#define GET_PORT_TIMEOUT 10000 /* msec */
+
+int get_wrt_launcher_port(void)
+{
+ int port;
+ int timeout = GET_PORT_TIMEOUT;
+
+ do {
+ port = wrt_launcher_port;
+ timeout -= GET_PORT_DELAY;
+ mdelay(GET_PORT_DELAY);
+ } while (!port && timeout > 0);
+
+ set_wrt_launcher_port(0);
+
+ return port;
+}
+
+/**
+ * @brief Initializes commands handling.
+ *
+ * @return Initialization results.
+ */
+int once_cmd(void)
+{
+ return once_features();
+}
--- /dev/null
+/**
+ * @file parser/msg_cmd.h
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Module's message handling interface declaration.
+ */
+
+#ifndef _MSG_CMD_H
+#define _MSG_CMD_H
+
+struct msg_buf;
+
+int once_cmd(void);
+
+int msg_keep_alive(struct msg_buf *mb);
+int msg_start(struct msg_buf *mb);
+int msg_stop(struct msg_buf *mb);
+int msg_config(struct msg_buf *mb);
+int msg_swap_inst_add(struct msg_buf *mb);
+int msg_swap_inst_remove(struct msg_buf *mb);
+int get_wrt_launcher_port(void);
+void set_wrt_launcher_port(int port);
+
+#endif /* _MSG_CMD_H */
--- /dev/null
+/**
+ * parser/msg_parser.c
+ *
+ * @author Vyacheslav Cherkashin
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @sectionLICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Message parsing implementation.
+ */
+
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <us_manager/probes/probes.h>
+#include "msg_parser.h"
+#include "msg_buf.h"
+#include "parser_defs.h"
+
+/* ============================================================================
+ * == APP_INFO ==
+ * ============================================================================
+ */
+
+/**
+ * @brief Creates and fills pr_app_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param ai Pointer to the target app_inst_data.
+ * @return 0 on success, error code on error.
+ */
+struct pr_app_info *pr_app_info_create(struct msg_buf *mb)
+{
+ int ret;
+ struct pr_app_info *app_info;
+ u32 app_type;
+ char *str_id, *exec_path;
+
+ app_info = kmalloc(sizeof(*app_info), GFP_KERNEL);
+ if (!app_info)
+ return ERR_PTR(-ENOMEM);
+
+ print_parse_debug("app_info:\n");
+ print_parse_debug("type:");
+ ret = get_u32(mb, &app_type);
+ if (ret) {
+ print_err("failed to read target application type\n");
+ goto free_app_info;
+ }
+
+ print_parse_debug("id:");
+ ret = get_string(mb, &str_id);
+ if (ret) {
+ print_err("failed to read target application ID\n");
+ goto free_app_info;
+ }
+
+ print_parse_debug("exec path:");
+ ret = get_string(mb, &exec_path);
+ if (ret) {
+ print_err("failed to read executable path\n");
+ goto free_id;
+ }
+
+ switch (app_type) {
+ case AT_TIZEN_NATIVE_APP:
+ case AT_TIZEN_WEB_APP:
+ case AT_COMMON_EXEC:
+ app_info->tgid = 0;
+ break;
+ case AT_PID: {
+ u32 tgid = 0;
+
+ if (*str_id != '\0') {
+ ret = kstrtou32(str_id, 10, &tgid);
+ if (ret) {
+ print_err("converting string to PID, "
+ "str='%s'\n", str_id);
+ goto free_exec_path;
+ }
+ }
+
+ app_info->tgid = tgid;
+ break;
+ }
+ default:
+ print_err("wrong application type(%u)\n", app_type);
+ ret = -EINVAL;
+ goto free_exec_path;
+ }
+
+ app_info->type = (enum APP_TYPE)app_type;
+ app_info->id = str_id;
+ app_info->path = exec_path;
+
+ return app_info;
+
+free_exec_path:
+ put_string(exec_path);
+free_id:
+ put_string(str_id);
+free_app_info:
+ kfree(app_info);
+ return ERR_PTR(ret);
+}
+
+void pr_app_info_free(struct pr_app_info *app_info)
+{
+ put_string(app_info->path);
+ put_string(app_info->id);
+ kfree(app_info);
+}
+
+int pr_app_info_cmp(struct pr_app_info *app0, struct pr_app_info *app1)
+{
+ print_parse_debug("app0: %d, %d, %s, %s\n",
+ app0->type, app0->tgid, app0->id, app0->path);
+
+ print_parse_debug("app1: %d, %d, %s, %s\n",
+ app1->type, app1->tgid, app1->id, app1->path);
+
+ if ((app0->type == app1->type) &&
+ (app0->tgid == app1->tgid) &&
+ !strcmp(app0->id, app1->id) &&
+ !strcmp(app0->path, app1->path)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+
+/* ============================================================================
+ * == CONFIG ==
+ * ============================================================================
+ */
+
+/**
+ * @brief Creates and fills conf_data struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return Pointer to the filled conf_data struct on success;\n
+ * NULL on error.
+ */
+struct conf_data *create_conf_data(struct msg_buf *mb)
+{
+ struct conf_data *conf;
+ u64 use_features0, use_features1;
+ u32 stp, dmp;
+
+ print_parse_debug("conf_data:\n");
+
+ print_parse_debug("features:");
+ if (get_u64(mb, &use_features0)) {
+ print_err("failed to read use_features\n");
+ return NULL;
+ }
+
+ if (get_u64(mb, &use_features1)) {
+ print_err("failed to read use_features\n");
+ return NULL;
+ }
+
+ print_parse_debug("sys trace period:");
+ if (get_u32(mb, &stp)) {
+ print_err("failed to read sys trace period\n");
+ return NULL;
+ }
+
+ print_parse_debug("data msg period:");
+ if (get_u32(mb, &dmp)) {
+ print_err("failed to read data message period\n");
+ return NULL;
+ }
+
+ conf = kmalloc(sizeof(*conf), GFP_KERNEL);
+ if (conf == NULL) {
+ print_err("out of memory\n");
+ return NULL;
+ }
+
+ conf->use_features0 = use_features0;
+ conf->use_features1 = use_features1;
+ conf->sys_trace_period = stp;
+ conf->data_msg_period = dmp;
+
+ return conf;
+}
+
+/**
+ * @brief conf_data cleanup.
+ *
+ * @param conf Pointer to the target conf_data.
+ * @return Void.
+ */
+void destroy_conf_data(struct conf_data *conf)
+{
+ kfree(conf);
+}
+
+static struct conf_data config;
+
+/**
+ * @brief Saves config to static config variable.
+ *
+ * @param conf Variable to save.
+ * @return Void.
+ */
+void save_config(const struct conf_data *conf)
+{
+ memcpy(&config, conf, sizeof(config));
+}
+
+/**
+ * @brief Restores config from static config variable.
+ *
+ * @param conf Variable to restore.
+ * @return Void.
+ */
+void restore_config(struct conf_data *conf)
+{
+ memcpy(conf, &config, sizeof(*conf));
+}
+
+
+
+/* ============================================================================
+ * == PROBES PARSING ==
+ * ============================================================================
+ */
+
+/**
+ * @brief Gets retprobe data and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pd Pointer to the probe_desc struct.
+ * @return 0 on success, error code on error.
+ */
+int get_retprobe(struct msg_buf *mb, struct probe_desc *pd)
+{
+ char *args;
+ char ret_type;
+
+ print_parse_debug("funct args:");
+ if (get_string(mb, &args)) {
+ print_err("failed to read data function arguments\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("funct ret type:");
+ if (get_u8(mb, (u8 *)&ret_type)) {
+ print_err("failed to read data function arguments\n");
+ goto free_args;
+ }
+
+ pd->type = SWAP_RETPROBE;
+ pd->info.rp_i.args = args;
+ pd->info.rp_i.ret_type = ret_type;
+
+ return 0;
+
+free_args:
+ put_string(args);
+ return -EINVAL;
+}
+
+/**
+ * @brief Retprobe data cleanup.
+ *
+ * @param pi Pointer to the probe_info comprising retprobe.
+ * @return Void.
+ */
+void put_retprobe(struct probe_info *pi)
+{
+ put_string(pi->rp_i.args);
+}
+
+static int cmp_retprobe(struct probe_info *p0, struct probe_info *p1)
+{
+ if (p0->rp_i.ret_type == p1->rp_i.ret_type &&
+ !strcmp(p0->rp_i.args, p1->rp_i.args)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Gets preload data and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pd Pointer to the probe_desc struct.
+ * @return 0 on success, error code on error.
+ */
+int get_preload_probe(struct msg_buf *mb, struct probe_desc *pd)
+{
+ u64 handler;
+ u8 flags;
+ char *path;
+
+ print_parse_debug("funct handler:");
+ if (get_u64(mb, &handler)) {
+ print_err("failed to read function handler\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("collect events flag:");
+ if (get_u8(mb, &flags)) {
+ print_err("failed to read collect events type\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("handler library:");
+ if (get_string(mb, &path)) {
+ print_err("failed to read handler library path\n");
+ return -EINVAL;
+ }
+
+ pd->type = SWAP_PRELOAD_PROBE;
+ pd->info.pl_i.handler = handler;
+ pd->info.pl_i.flags = flags;
+ pd->info.pl_i.path = path;
+
+ return 0;
+}
+
+/**
+ * @brief Preload probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info struct.
+ * @return Void.
+ */
+void put_preload_probe(struct probe_info *pi)
+{
+ put_string((char *)pi->pl_i.path);
+}
+
+static int cmp_preload_probe(struct probe_info *p0, struct probe_info *p1)
+{
+ if (p0->pl_i.handler == p1->pl_i.handler &&
+ p0->pl_i.flags == p1->pl_i.flags) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Gets preload get_caller and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pd Pointer to the probe_desc struct.
+ * @return 0 on success, error code on error.
+ */
+
+int get_get_caller_probe(struct msg_buf *mb, struct probe_desc *pd)
+{
+ pd->type = SWAP_GET_CALLER;
+
+ return 0;
+}
+
+/**
+ * @brief Preload get_caller probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info struct.
+ * @return Void.
+ */
+void put_get_caller_probe(struct probe_info *pi)
+{
+}
+
+static int cmp_get_caller_probe(struct probe_info *p0, struct probe_info *p1)
+{
+ return 0;
+}
+
+/**
+ * @brief Gets preload get_call_type and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pd Pointer to the probe_desc struct.
+ * @return 0 on success, error code on error.
+ */
+int get_get_call_type_probe(struct msg_buf *mb, struct probe_desc *pd)
+{
+ pd->type = SWAP_GET_CALL_TYPE;
+
+ return 0;
+}
+
+/**
+ * @brief Preload get_call type probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info struct.
+ * @return Void.
+ */
+void put_get_call_type_probe(struct probe_info *pi)
+{
+}
+
+static int cmp_get_caller_type_probe(struct probe_info *p0,
+ struct probe_info *p1)
+{
+ return 0;
+}
+
+/**
+ * @brief Gets preload write_msg and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pi Pointer to the probe_info struct.
+ * @return 0 on success, error code on error.
+ */
+int get_write_msg_probe(struct msg_buf *mb, struct probe_desc *pd)
+{
+ pd->type = SWAP_WRITE_MSG;
+
+ return 0;
+}
+
+/**
+ * @brief Preload write_msg type probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info comprising retprobe.
+ * @return Void.
+ */
+void put_write_msg_probe(struct probe_info *pi)
+{
+}
+
+static int cmp_write_msg_probe(struct probe_info *p0, struct probe_info *p1)
+{
+ return 0;
+}
+
+/**
+ * @brief Gets FBI probe data and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pi Pointer to the probe_info struct.
+ * @return 0 on success, error code on error.
+ */
+int get_fbi_data(struct msg_buf *mb, struct fbi_var_data *vd)
+{
+ u64 var_id;
+ u64 reg_offset;
+ u8 reg_n;
+ u32 data_size;
+ u8 steps_count, i;
+ struct fbi_step *steps = NULL;
+
+ print_parse_debug("var ID:");
+ if (get_u64(mb, &var_id)) {
+ print_err("failed to read var ID\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("register offset:");
+ if (get_u64(mb, ®_offset)) {
+ print_err("failed to read register offset\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("register number:");
+ if (get_u8(mb, ®_n)) {
+ print_err("failed to read number of the register\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("data size:");
+ if (get_u32(mb, &data_size)) {
+ print_err("failed to read data size\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("steps count:");
+ if (get_u8(mb, &steps_count)) {
+ print_err("failed to read steps count\n");
+ return -EINVAL;
+ }
+
+ if (steps_count > 0) {
+ steps = kmalloc(steps_count * sizeof(*vd->steps),
+ GFP_KERNEL);
+ if (steps == NULL) {
+ print_err("MALLOC FAIL\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i != steps_count; i++) {
+ print_parse_debug("steps #%d ptr_order:", i);
+ if (get_u8(mb, &(steps[i].ptr_order))) {
+ print_err("failed to read pointer order(step #%d)\n",
+ i);
+ goto free_steps;
+ }
+ print_parse_debug("steps #%d data_offset:", i);
+ if (get_u64(mb, &(steps[i].data_offset))){
+ print_err("failed to read offset (steps #%d)\n",
+ i);
+ goto free_steps;
+ }
+ }
+ }
+
+ vd->reg_n = reg_n;
+ vd->reg_offset = reg_offset;
+ vd->data_size = data_size;
+ vd->var_id = var_id;
+ vd->steps_count = steps_count;
+ vd->steps = steps;
+
+ return 0;
+
+free_steps:
+ kfree(steps);
+ return -EINVAL;
+}
+
+int get_fbi_probe(struct msg_buf *mb, struct probe_desc *pd)
+{
+ uint8_t var_count, i;
+ struct fbi_var_data *vars;
+
+ print_parse_debug("var count:");
+ if (get_u8(mb, &var_count)) {
+ print_err("failed to read var ID\n");
+ return -EINVAL;
+ }
+
+ vars = kmalloc(var_count * sizeof(*vars), GFP_KERNEL);
+ if (vars == NULL) {
+ print_err("alloc vars error\n");
+ goto err;
+ }
+
+ for (i = 0; i != var_count; i++) {
+ if (get_fbi_data(mb, &vars[i]) != 0)
+ goto free_vars;
+ }
+
+ pd->type = SWAP_FBIPROBE;
+ pd->info.fbi_i.var_count = var_count;
+ pd->info.fbi_i.vars = vars;
+ return 0;
+
+free_vars:
+ kfree(vars);
+
+err:
+ return -EINVAL;
+
+}
+
+/**
+ * @brief FBI probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info comprising FBI probe.
+ * @return Void.
+ */
+void put_fbi_probe(struct probe_info *pi)
+{
+ /* FIXME: memory leak (vars) */
+ return;
+}
+
+static int cmp_fbi_probe(struct probe_info *p0, struct probe_info *p1)
+{
+ /* TODO: to implement */
+ return 0;
+}
+
+
+/* ============================================================================
+ * == PROBE ==
+ * ============================================================================
+ */
+
+/**
+ * @brief Creates and fills func_inst_data struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return Pointer to the filled func_inst_data struct on success;\n
+ * 0 on error.
+ */
+struct pr_probe_desc *pr_probe_desc_create(struct msg_buf *mb)
+{
+ int ret = -EINVAL;
+ struct pr_probe_desc *probe;
+ int (*get_probe)(struct msg_buf *mb, struct probe_desc *pd);
+ u64 addr;
+ u8 type;
+
+ print_parse_debug("func addr:");
+ if (get_u64(mb, &addr)) {
+ print_err("failed to read data function address\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ print_parse_debug("probe type:");
+ if (get_u8(mb, &type)) {
+ print_err("failed to read data probe type\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ probe = kmalloc(sizeof(*probe), GFP_KERNEL);
+ if (!probe) {
+ print_err("out of memory\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&probe->list);
+
+ switch (type) {
+ case SWAP_RETPROBE:
+ get_probe = get_retprobe;
+ break;
+ case SWAP_PRELOAD_PROBE:
+ get_probe = get_preload_probe;
+ break;
+ case SWAP_GET_CALLER:
+ get_probe = get_get_caller_probe;
+ break;
+ case SWAP_GET_CALL_TYPE:
+ get_probe = get_get_call_type_probe;
+ break;
+ case SWAP_FBIPROBE:
+ get_probe = get_fbi_probe;
+ break;
+ case SWAP_WRITE_MSG:
+ get_probe = get_write_msg_probe;
+ break;
+ default:
+ printk(KERN_WARNING "SWAP PARSER: Wrong probe type %d!\n",
+ type);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = get_probe(mb, &probe->p_desc);
+ if (ret)
+ goto err;
+
+ probe->addr = addr;
+ return probe;
+
+err:
+ kfree(probe);
+ return ERR_PTR(ret);
+}
+
+
+/**
+ * @brief func_inst_data cleanup.
+ *
+ * @param fi Pointer to the target func_inst_data.
+ * @return Void.
+ */
+void pr_probe_desc_free(struct pr_probe_desc *probe)
+{
+ switch (probe->p_desc.type) {
+ case SWAP_RETPROBE:
+ put_retprobe(&(probe->p_desc.info));
+ break;
+ case SWAP_PRELOAD_PROBE:
+ put_preload_probe(&(probe->p_desc.info));
+ break;
+ case SWAP_GET_CALLER:
+ put_get_caller_probe(&(probe->p_desc.info));
+ break;
+ case SWAP_GET_CALL_TYPE:
+ put_get_call_type_probe(&(probe->p_desc.info));
+ break;
+ case SWAP_FBIPROBE:
+ put_fbi_probe(&(probe->p_desc.info));
+ break;
+ case SWAP_WRITE_MSG:
+ put_write_msg_probe(&(probe->p_desc.info));
+ break;
+ default:
+ pr_err("SWAP PARSER: Wrong probe type %d!\n",
+ probe->p_desc.type);
+ }
+
+ kfree(probe);
+}
+
+int probe_inst_info_cmp(struct pr_probe_desc *p0, struct pr_probe_desc *p1)
+{
+ enum probe_t type;
+ int (*cmp_probe)(struct probe_info *p0, struct probe_info *p1);
+
+ if (p0->addr != p1->addr &&
+ p0->p_desc.type != p1->p_desc.type)
+ return 1;
+
+ type = p0->p_desc.type;
+ switch (type) {
+ case SWAP_RETPROBE:
+ cmp_probe = cmp_retprobe;
+ break;
+ case SWAP_PRELOAD_PROBE:
+ cmp_probe = cmp_preload_probe;
+ break;
+ case SWAP_GET_CALLER:
+ cmp_probe = cmp_get_caller_probe;
+ break;
+ case SWAP_GET_CALL_TYPE:
+ cmp_probe = cmp_get_caller_type_probe;
+ break;
+ case SWAP_FBIPROBE:
+ cmp_probe = cmp_fbi_probe;
+ break;
+ case SWAP_WRITE_MSG:
+ cmp_probe = cmp_write_msg_probe;
+ break;
+ default:
+ pr_err("SWAP PARSER: Wrong probe type %d!\n", type);
+ return 1;
+ }
+
+ return cmp_probe(&p0->p_desc.info, &p1->p_desc.info);
+}
+
+static struct pr_bin_info *pr_bin_info_create(const char *path)
+{
+ struct pr_bin_info *info;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->path = kstrdup(path, GFP_KERNEL);
+ if (!info->path) {
+ kfree(info);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return info;
+}
+
+static void pr_bin_info_free(struct pr_bin_info *info)
+{
+ kfree(info->path);
+ kfree(info);
+}
+
+
+struct pr_bin_desc *pr_bin_desc_create(const char *path,
+ struct list_head *probe_list)
+{
+ struct pr_bin_desc *bin;
+
+ bin = kmalloc(sizeof(*bin), GFP_KERNEL);
+ if (!bin)
+ return ERR_PTR(-ENOMEM);
+
+ bin->info = pr_bin_info_create(path);
+ if (IS_ERR(bin->info)) {
+ long err = PTR_ERR(bin->info);
+
+ kfree(bin);
+ return ERR_PTR(err);
+ }
+
+ INIT_LIST_HEAD(&bin->list);
+ INIT_LIST_HEAD(&bin->probe_head);
+ list_splice_init(probe_list, &bin->probe_head);
+
+ return bin;
+}
+
+void pr_bin_desc_free(struct pr_bin_desc *bin)
+{
+ struct pr_probe_desc *p, *n;
+
+ list_for_each_entry_safe(p, n, &bin->probe_head, list) {
+ list_del(&p->list);
+ pr_probe_desc_free(p);
+ }
+
+ pr_bin_info_free(bin->info);
+ kfree(bin);
+}
+
+int pr_bin_info_cmp(struct pr_bin_info *b0, struct pr_bin_info *b1)
+{
+ return strcmp(b0->path, b1->path);
+}
+
+
+static void pr_probe_desc_list_free(struct list_head *head)
+{
+ struct pr_probe_desc *probe, *n;
+
+ list_for_each_entry_safe(probe, n, head, list) {
+ list_del(&probe->list);
+ pr_probe_desc_free(probe);
+ }
+}
+
+static int pr_probe_desc_list_create(struct msg_buf *mb, struct list_head *head)
+{
+ u32 i, cnt;
+
+ if (get_u32(mb, &cnt)) {
+ print_err("failed to read count of functions\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("probe count:%d", cnt);
+ if (remained_mb(mb) / MIN_SIZE_FUNC_INST < cnt) {
+ print_err("to match count of probes(%u)\n", cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cnt; ++i) {
+ struct pr_probe_desc *probe;
+
+ print_parse_debug("probe #%d:\n", i + 1);
+ probe = pr_probe_desc_create(mb);
+ if (IS_ERR(probe)) {
+ pr_probe_desc_list_free(head);
+ return PTR_ERR(probe);
+ }
+
+ list_add(&probe->list, head);
+ }
+
+ return cnt;
+}
+
+
+struct pr_bin_desc *bin_info_by_lib(struct msg_buf *mb)
+{
+ u32 cnt;
+ char *path;
+ struct pr_bin_desc *bin = ERR_PTR(-EINVAL);
+
+ print_parse_debug("bin path:");
+ if (get_string(mb, &path)) {
+ print_err("failed to read path of binary\n");
+ return bin;
+ }
+
+ print_parse_debug("func count:");
+ if (get_u32(mb, &cnt)) {
+ print_err("failed to read count of functions\n");
+ goto free_path;
+ }
+
+ if (remained_mb(mb) / MIN_SIZE_FUNC_INST < cnt) {
+ print_err("to match count of functions(%u)\n", cnt);
+ goto free_path;
+ }
+
+ if (cnt) {
+ u32 i;
+ LIST_HEAD(probe_head);
+
+ for (i = 0; i < cnt; ++i) {
+ struct pr_probe_desc *probe;
+ print_parse_debug("func #%d:\n", i + 1);
+ probe = pr_probe_desc_create(mb);
+ if (IS_ERR(probe)) {
+ /* set error to 'bin' */
+ bin = ERR_PTR(PTR_ERR(probe));
+ pr_probe_desc_list_free(&probe_head);
+ goto free_path;
+ }
+
+ list_add(&probe->list, &probe_head);
+ }
+
+ bin = pr_bin_desc_create(path, &probe_head);
+ if (IS_ERR(bin))
+ pr_probe_desc_list_free(&probe_head);
+ }
+
+free_path:
+ put_string(path);
+ return bin;
+}
+
+
+
+
+void bin_info_list_free(struct list_head *head)
+{
+ struct pr_bin_desc *bin, *n;
+
+ list_for_each_entry_safe(bin, n, head, list) {
+ list_del(&bin->list);
+ pr_bin_desc_free(bin);
+ }
+}
+
+int bin_info_list_create(struct msg_buf *mb, struct list_head *head)
+{
+ u32 i, cnt;
+
+ if (get_u32(mb, &cnt)) {
+ print_err("failed to read count of binaries\n");
+ return -EINVAL;
+ }
+ print_parse_debug("bin count:i%d", cnt);
+
+ if (remained_mb(mb) / MIN_SIZE_LIB_INST < cnt) {
+ print_err("to match count of binaries(%u)\n", cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cnt; ++i) {
+ struct pr_bin_desc *bin;
+
+ print_parse_debug("bin #%d:\n", i_lib + 1);
+ bin = bin_info_by_lib(mb);
+ if (IS_ERR(bin)) {
+ bin_info_list_free(head);
+ return PTR_ERR(bin);
+ } else if (bin) {
+ list_add(&bin->list, head);
+ }
+ }
+
+ return cnt;
+}
+
+
+/* ============================================================================
+ * == APP ==
+ * ============================================================================
+ */
+
+/**
+ * @brief Creates and fills pr_app_desc struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @return Pointer to the filled app_inst_data struct on success;\n
+ * 0 on error.
+ */
+struct pr_app_desc *pr_app_desc_create(struct msg_buf *mb)
+{
+ int cnt_probe, cnt_bin, ret = -EINVAL;
+ struct pr_app_info *app_info;
+ struct pr_app_desc *app;
+ LIST_HEAD(probe_head);
+ LIST_HEAD(bin_head);
+
+ app = kmalloc(sizeof(*app), GFP_KERNEL);
+ if (!app) {
+ print_err("%s: Out of memory\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ app_info = pr_app_info_create(mb);
+ if (IS_ERR(app_info)) {
+ ret = PTR_ERR(app_info);
+ goto free_app_inst;
+ }
+
+ app->info = app_info;
+ app->pfg = NULL;
+ INIT_LIST_HEAD(&app->list);
+ INIT_LIST_HEAD(&app->bin_head);
+
+ cnt_probe = pr_probe_desc_list_create(mb, &probe_head);
+ if (cnt_probe < 0) {
+ ret = cnt_probe;
+ goto free_app_info;
+ } else if (cnt_probe) {
+ struct pr_bin_desc *bin;
+
+ bin = pr_bin_desc_create(app_info->path, &probe_head);
+ if (IS_ERR(bin)) {
+ pr_probe_desc_list_free(&probe_head);
+ ret = PTR_ERR(bin);
+ goto free_app_info;
+ }
+
+ /* add pr_bin_desc */
+ list_add(&bin->list, &app->bin_head);
+ }
+
+ cnt_bin = bin_info_list_create(mb, &app->bin_head);
+ if (cnt_bin < 0) {
+ ret = cnt_bin;
+ goto free_bins;
+ }
+
+ return app;
+
+free_bins:
+ bin_info_list_free(&app->bin_head);
+free_app_info:
+ pr_app_info_free(app_info);
+free_app_inst:
+ kfree(app);
+ return ERR_PTR(ret);
+}
+
+/**
+ * @brief pr_app_desc cleanup.
+ *
+ * @param ai Pointer to the target app_inst_data.
+ * @return Void.
+ */
+void pr_app_desc_free(struct pr_app_desc *app)
+{
+ bin_info_list_free(&app->bin_head);
+ pr_app_info_free(app->info);
+ kfree(app);
+}
+
+
+/* ============================================================================
+ * == US_INST ==
+ * ============================================================================
+ */
+
+/**
+ * @brief Creates and fills us_inst_data struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param head Pointer to the list head.
+ * @return u32 count of created elements.
+ */
+u32 create_us_inst_data(struct msg_buf *mb,
+ struct list_head *head)
+{
+ u32 cnt, i;
+
+ print_parse_debug("us_inst_data:\n");
+
+ print_parse_debug("app count:");
+ if (get_u32(mb, &cnt)) {
+ print_err("failed to read count of applications\n");
+ return 0;
+ }
+
+ if (remained_mb(mb) / MIN_SIZE_APP_INST < cnt) {
+ print_err("to match count of applications(%u)\n", cnt);
+ return 0;
+ }
+
+ for (i = 0; i < cnt; ++i) {
+ struct pr_app_desc *app;
+
+ print_parse_debug("app #%d:\n", i + 1);
+ app = pr_app_desc_create(mb);
+ if (IS_ERR(app))
+ goto err;
+
+ list_add_tail(&app->list, head);
+ }
+
+ return cnt;
+
+err:
+ destroy_us_inst_data(head);
+ return 0;
+}
+
+/**
+ * @brief us_inst_data cleanup.
+ *
+ * @param head Pointer to the list head.
+ * @return Void.
+ */
+void destroy_us_inst_data(struct list_head *head)
+{
+ struct pr_app_desc *app, *n;
+
+ list_for_each_entry_safe(app, n, head, list) {
+ list_del(&app->list);
+ pr_app_desc_free(app);
+ }
+}
--- /dev/null
+/**
+ * @file parser/msg_parser.h
+ * @author Vyacheslav Cherkashin
+ * @author Vitaliy Cherepanov
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Message parsing interface declaration.
+ */
+
+
+#ifndef _MSG_PARSER_H
+#define _MSG_PARSER_H
+
+#include <linux/types.h>
+#include <us_manager/probes/probes.h>
+
+struct img_ip;
+struct msg_buf;
+
+/**
+ * @enum APP_TYPE
+ * Supported application types.
+ */
+enum APP_TYPE {
+ AT_TIZEN_NATIVE_APP = 0x01, /**< Tizen native application. */
+ AT_PID = 0x02, /**< App with specified PID. */
+ AT_COMMON_EXEC = 0x03, /**< Common application. */
+ AT_TIZEN_WEB_APP = 0x04 /**< Tizen web application. */
+};
+
+/**
+ * @brief App type size defenition.
+ */
+enum {
+ SIZE_APP_TYPE = 4
+};
+
+/**
+ * @struct conf_data
+ * @brief Configuration struct.
+ */
+struct conf_data {
+ u64 use_features0; /**< Feature flags. */
+ u64 use_features1; /**< Feature flags. */
+ u32 sys_trace_period; /**< Trace period. */
+ u32 data_msg_period; /**< Data message period. */
+};
+
+/**
+ * @brief The pr_app_info struct
+ */
+struct pr_app_info {
+ enum APP_TYPE type; /**< Application type. */
+ pid_t tgid; /**< Application PID. */
+ char *id; /**< Application ID */
+ char *path; /**< Application execution path. */
+};
+
+/**
+ * @brief The pr_bin_info struct
+ */
+struct pr_bin_info {
+ char *path;
+};
+
+/**
+ * @brief The pr_app_desc struct
+ */
+struct pr_app_desc {
+ struct list_head list;
+
+ struct pr_app_info *info;
+ struct pf_group *pfg;
+ struct list_head bin_head;
+};
+
+/**
+ * @brief The pr_bin_desc struct
+ */
+struct pr_bin_desc {
+ struct list_head list;
+
+ struct pr_bin_info *info;
+
+ struct list_head probe_head;
+};
+
+struct pr_probe_desc {
+ struct list_head list;
+
+ /* register info */
+ u64 addr;
+ struct probe_desc p_desc;
+
+ /* unregister info */
+ struct img_ip *ip;
+};
+
+
+struct pr_app_info *pr_app_info_create(struct msg_buf *mb);
+void pr_app_info_free(struct pr_app_info *app_info);
+int pr_app_info_cmp(struct pr_app_info *app0, struct pr_app_info *app1);
+
+struct pr_bin_desc *pr_bin_desc_create(const char *path,
+ struct list_head *probe_list);
+void pr_bin_desc_free(struct pr_bin_desc *bin);
+
+int pr_bin_info_cmp(struct pr_bin_info *b0, struct pr_bin_info *b1);
+
+struct pr_probe_desc *pr_probe_desc_create(struct msg_buf *mb);
+void pr_probe_desc_free(struct pr_probe_desc *probe_info);
+int probe_inst_info_cmp(struct pr_probe_desc *p0, struct pr_probe_desc *p1);
+
+struct conf_data *create_conf_data(struct msg_buf *mb);
+void destroy_conf_data(struct conf_data *conf);
+
+void save_config(const struct conf_data *conf);
+void restore_config(struct conf_data *conf);
+
+struct pr_app_desc *pr_app_desc_create(struct msg_buf *mb);
+void pr_app_desc_free(struct pr_app_desc *app);
+
+u32 create_us_inst_data(struct msg_buf *mb, struct list_head *head);
+void destroy_us_inst_data(struct list_head *head);
+
+
+/**
+ * @brief Constant defenitions.
+ */
+enum {
+ MIN_SIZE_STRING = 1,
+ MIN_SIZE_FUNC_INST = 8 /* address size */ +
+ MIN_SIZE_STRING,
+ MIN_SIZE_LIB_INST = MIN_SIZE_STRING +
+ 4 /* lib counter */,
+ MIN_SIZE_APP_INFO = SIZE_APP_TYPE + MIN_SIZE_STRING + MIN_SIZE_STRING,
+ MIN_SIZE_APP_INST = MIN_SIZE_APP_INFO +
+ 4 /* probe counter */ +
+ 4 /* lib counter */,
+};
+
+#endif /* _MSG_PARSER_H */
--- /dev/null
+/**
+ * @file modules/parser/parser_defs.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov:
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Parser defenitions.
+ */
+
+
+#ifndef __SWAP_DRIVER_DEVICE_DEFS_H__
+#define __SWAP_DRIVER_DEVICE_DEFS_H__
+
+#include <linux/kernel.h>
+
+/* #define PARSE_DEBUG */
+
+/** Prints debug message. */
+#define print_debug(msg, args...) \
+ printk(KERN_DEBUG "SWAP_PARSER DEBUG : " msg, ##args)
+/** Prints info message. */
+#define print_msg(msg, args...) \
+ printk(KERN_INFO "SWAP_PARSER : " msg, ##args)
+/** Prints warning message. */
+#define print_warn(msg, args...) \
+ printk(KERN_WARNING "SWAP_PARSER WARNING : " msg, ##args)
+/** Prints error message. */
+#define print_err(msg, args...) \
+ printk(KERN_ERR "SWAP_PARSER ERROR : " msg, ##args)
+/** Prints critical error message. */
+#define print_crit(msg, args...) \
+ printk(KERN_CRIT "SWAP_PARSER CRITICAL : " msg, ##args)
+
+/* debug parse */
+#ifdef PARSE_DEBUG
+#define print_parse_debug(msg, args...) \
+ printk(KERN_DEBUG "SWAP_PARSER DEBUG : " msg, ##args)
+#else
+#define print_parse_debug(msg, args...) \
+ do {} while (0)
+#endif /* PARSE_DEBUG */
+
+#endif /* __SWAP_DRIVER_DEVICE_DEFS_H__ */
--- /dev/null
+/**
+ * parser/swap_msg_parser.c
+ * @author Vyacheslav Cherkashin
+ * @author Vitaliy Cherepanov
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Parser module interface implementation.
+ */
+
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/cpumask.h>
+#include <linux/uaccess.h>
+
+#include "parser_defs.h"
+#include "us_inst.h"
+#include "msg_buf.h"
+#include "msg_cmd.h"
+#include "usm_msg.h"
+#include "cpu_ctrl.h"
+#include "features.h"
+
+#include <driver/driver_to_msg.h>
+#include <driver/swap_ioctl.h>
+#include <master/swap_initializer.h>
+
+/**
+ * @enum MSG_ID
+ * @brief Message IDs.
+ */
+enum MSG_ID {
+ MSG_KEEP_ALIVE = 0x0001, /**< Keep alive message. */
+ MSG_START = 0x0002, /**< Start message. */
+ MSG_STOP = 0x0003, /**< Stop message. */
+ MSG_CONFIG = 0x0004, /**< Config message. */
+ MSG_SWAP_INST_ADD = 0x0008, /**< Swap inst add message. */
+ MSG_SWAP_INST_REMOVE = 0x0009, /**< Swap inst remove message. */
+ MSG_WRT_LAUNCHER_PORT = 0x8001 /**< WRT launcher port. */
+};
+
+/**
+ * @struct basic_msg_fmt
+ * @brief Basic part of each message.
+ */
+struct basic_msg_fmt {
+ u32 msg_id; /**< Message ID. */
+ u32 len; /**< Message length. */
+} __packed;
+
+static int msg_handler(void __user *msg)
+{
+ int ret;
+ u32 size;
+ enum MSG_ID msg_id;
+ struct msg_buf mb;
+ void __user *payload;
+ struct basic_msg_fmt bmf;
+ enum { size_max = 128 * 1024 * 1024 };
+
+ ret = copy_from_user(&bmf, (void *)msg, sizeof(bmf));
+ if (ret)
+ return ret;
+
+ size = bmf.len;
+ if (size >= size_max) {
+ printk(KERN_INFO "%s: too large message, size=%u\n",
+ __func__, size);
+ return -ENOMEM;
+ }
+
+ ret = init_mb(&mb, size);
+ if (ret)
+ return ret;
+
+ payload = msg + sizeof(bmf);
+ if (size) {
+ ret = copy_from_user(mb.begin, (void *)payload, size);
+ if (ret)
+ goto uninit;
+ }
+
+ msg_id = bmf.msg_id;
+ switch (msg_id) {
+ case MSG_KEEP_ALIVE:
+ print_parse_debug("MSG_KEEP_ALIVE. size=%d\n", size);
+ ret = msg_keep_alive(&mb);
+ break;
+ case MSG_START:
+ print_parse_debug("MSG_START. size=%d\n", size);
+ ret = msg_start(&mb);
+ break;
+ case MSG_STOP:
+ print_parse_debug("MSG_STOP. size=%d\n", size);
+ ret = msg_stop(&mb);
+ break;
+ case MSG_CONFIG:
+ print_parse_debug("MSG_CONFIG. size=%d\n", size);
+ ret = msg_config(&mb);
+ break;
+ case MSG_SWAP_INST_ADD:
+ print_parse_debug("MSG_SWAP_INST_ADD. size=%d\n", size);
+ ret = msg_swap_inst_add(&mb);
+ break;
+ case MSG_SWAP_INST_REMOVE:
+ print_parse_debug("MSG_SWAP_INST_REMOVE. size=%d\n", size);
+ ret = msg_swap_inst_remove(&mb);
+ break;
+ case MSG_WRT_LAUNCHER_PORT: {
+ /* TODO: discuss wrt-launcher port transfer */
+ int port;
+ print_parse_debug("MSG_WRT_LAUNCHER_PORT. size=%d\n", size);
+ port = get_wrt_launcher_port();
+ if (copy_to_user(payload, &port, sizeof(port))) {
+ ret = -EIO;
+ break;
+ }
+ ret = port ? 0 : -EINVAL;
+ break;
+ }
+ default:
+ print_err("incorrect message ID [%u]. size=%d\n", msg_id, size);
+ ret = -EINVAL;
+ break;
+ }
+
+uninit:
+ uninit_mb(&mb);
+ return ret;
+}
+
+static struct driver_msg_handler dmsg_handler = {
+ .mod = THIS_MODULE,
+ .handler = msg_handler,
+};
+
+static int reg_msg_handler(void)
+{
+ driver_msg_handler_set(&dmsg_handler);
+ return 0;
+}
+
+static void unreg_msg_handler(void)
+{
+ driver_msg_handler_set(NULL);
+ app_list_unreg_all();
+
+ disable_all_features();
+}
+
+static int once(void)
+{
+ int ret;
+
+ ret = once_cmd();
+ if (ret)
+ return ret;
+
+ ret = init_cpu_deps();
+
+ return ret;
+}
+
+SWAP_LIGHT_INIT_MODULE(once, reg_msg_handler, unreg_msg_handler, NULL, NULL);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/**
+ * parser/us_inst.c
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * User-space instrumentation controls.
+ */
+
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/probes/probes.h>
+
+#include "msg_parser.h"
+#include "us_inst.h"
+#include "usm_msg.h"
+
+
+static struct pfg_msg_cb msg_cb = {
+ .msg_info = usm_msg_info,
+ .msg_status_info = usm_msg_status_info,
+ .msg_term = usm_msg_term,
+ .msg_map = usm_msg_map,
+ .msg_unmap = usm_msg_unmap
+};
+
+
+static int probe_inst_reg(struct pr_probe_desc *probe, struct pf_group *pfg,
+ struct dentry *dentry)
+{
+ probe->ip = pf_register_probe(pfg, dentry, probe->addr, &probe->p_desc);
+ if (IS_ERR(probe->ip))
+ return PTR_ERR(probe->ip);
+
+ return 0;
+}
+
+static void probe_inst_unreg(struct pr_probe_desc *probe, struct pf_group *pfg)
+{
+ pf_unregister_probe(pfg, probe->ip);
+}
+
+static void do_bin_inst_unreg(struct list_head *head, struct pf_group *pfg)
+{
+ struct pr_probe_desc *probe;
+
+ list_for_each_entry(probe, head, list) {
+ probe_inst_unreg(probe, pfg);
+ }
+}
+
+static void bin_inst_unreg(struct pr_bin_desc *bin, struct pf_group *pfg)
+{
+ do_bin_inst_unreg(&bin->probe_head, pfg);
+}
+
+static int bin_inst_reg(struct pr_bin_desc *bin, struct pf_group *pfg)
+{
+ struct pr_probe_desc *probe, *n;
+ struct dentry *dentry;
+ LIST_HEAD(reg_head);
+
+ dentry = dentry_by_path(bin->info->path);
+ if (dentry == NULL) {
+ pr_warn("Cannot get dentry by path %s\n", bin->info->path);
+ return -EINVAL;
+ }
+
+ list_for_each_entry_safe(probe, n, &bin->probe_head, list) {
+ int ret;
+
+ ret = probe_inst_reg(probe, pfg, dentry);
+ if (!ret) {
+ list_move(&probe->list, ®_head);
+ } else {
+ do_bin_inst_unreg(®_head, pfg);
+ return ret;
+ }
+ }
+
+ list_splice(®_head, &bin->probe_head);
+ return 0;
+}
+
+static struct pf_group *get_pfg_by_app_info(struct pr_app_info *app)
+{
+ struct pf_group *pfg = ERR_PTR(-EINVAL);
+ struct dentry *dentry;
+
+ dentry = dentry_by_path(app->path);
+ if (dentry == NULL)
+ return pfg;
+
+ switch (app->type) {
+ case AT_PID:
+ if (app->tgid == 0) {
+ if (app->path[0] == '\0')
+ pfg = get_pf_group_dumb(dentry);
+ else
+ goto pf_dentry;
+ } else {
+ pfg = get_pf_group_by_tgid(app->tgid, dentry);
+ }
+ break;
+ case AT_TIZEN_WEB_APP:
+ pfg = get_pf_group_by_comm(app->id, dentry);
+ break;
+ case AT_TIZEN_NATIVE_APP:
+ case AT_COMMON_EXEC:
+ pf_dentry:
+ pfg = get_pf_group_by_dentry(dentry, dentry);
+ break;
+ default:
+ pr_info("ERROR: app_type=0x%x\n", app->type);
+ break;
+ }
+
+ if (!pfg)
+ pfg = ERR_PTR(-ENOMEM);
+
+ if (!IS_ERR(pfg)) {
+ /* TODO: move to other location and chack return value */
+ pfg_msg_cb_set(pfg, &msg_cb);
+ }
+
+ return pfg;
+}
+
+static void do_us_app_inst_unreg(struct pr_app_desc *app,
+ struct list_head *head)
+{
+ struct pr_bin_desc *bin;
+
+ list_for_each_entry(bin, head, list) {
+ bin_inst_unreg(bin, app->pfg);
+ }
+ put_pf_group(app->pfg);
+ app->pfg = NULL;
+}
+
+static void us_app_inst_unreg(struct pr_app_desc *app)
+{
+ do_us_app_inst_unreg(app, &app->bin_head);
+}
+
+static int us_app_inst_reg(struct pr_app_desc *app)
+{
+ struct pf_group *pfg;
+ struct pr_bin_desc *bin, *n;
+ LIST_HEAD(reg_head);
+
+ pfg = get_pfg_by_app_info(app->info);
+ if (IS_ERR(pfg))
+ return PTR_ERR(pfg);
+
+ app->pfg = pfg;
+ list_for_each_entry_safe(bin, n, &app->bin_head, list) {
+ int ret;
+
+ ret = bin_inst_reg(bin, app->pfg);
+ if (!ret) {
+ list_move(&bin->list, ®_head);
+ } else {
+ do_us_app_inst_unreg(app, ®_head);
+ return ret;
+ }
+ }
+
+ list_splice(®_head, &app->bin_head);
+ return 0;
+}
+
+
+static struct pr_probe_desc *find_probe(struct list_head *head,
+ struct pr_probe_desc *probe)
+{
+ struct pr_probe_desc *p;
+
+ list_for_each_entry(p, head, list) {
+ if (!probe_inst_info_cmp(probe, p))
+ return p;
+ }
+
+ return NULL;
+}
+
+static struct pr_bin_desc *find_bin(struct list_head *head,
+ struct pr_bin_info *info)
+{
+ struct pr_bin_desc *bin;
+
+ list_for_each_entry(bin, head, list) {
+ if (!pr_bin_info_cmp(bin->info, info))
+ return bin;
+ }
+
+ return NULL;
+}
+
+static struct pr_app_desc *find_app(struct list_head *head,
+ struct pr_app_info *app_info)
+{
+ struct pr_app_desc *app;
+
+ list_for_each_entry(app, head, list) {
+ if (!pr_app_info_cmp(app->info, app_info))
+ return app;
+ }
+
+ return NULL;
+}
+
+static void us_probe_get_equal_elements(struct list_head *probe_head,
+ struct list_head *test_probe_head,
+ struct list_head *out_probe_head)
+{
+ struct pr_probe_desc *test_probe, *n;
+
+ list_for_each_entry_safe(test_probe, n, test_probe_head, list) {
+ struct pr_probe_desc *probe;
+
+ probe = find_probe(probe_head, test_probe);
+ if (probe) {
+ list_move(&probe->list, out_probe_head);
+
+ /* remove probe */
+ list_del(&test_probe->list);
+ pr_probe_desc_free(test_probe);
+ } else {
+ return;
+ }
+ }
+}
+
+static void us_bin_get_equal_elements(struct list_head *bin_head,
+ struct list_head *test_bin_head,
+ struct list_head *out_bin_head)
+{
+ struct pr_bin_desc *test_bin, *n;
+
+ list_for_each_entry_safe(test_bin, n, test_bin_head, list) {
+ struct pr_bin_desc *bin;
+ LIST_HEAD(out_probe_head);
+
+ bin = find_bin(bin_head, test_bin->info);
+ if (!bin)
+ return;
+
+ us_probe_get_equal_elements(&bin->probe_head,
+ &test_bin->probe_head,
+ &out_probe_head);
+
+ /* check all probes found */
+ if (list_empty(&test_bin->probe_head)) {
+ list_move(&test_bin->list, out_bin_head);
+ list_splice(&out_probe_head, &test_bin->probe_head);
+ } else {
+ list_splice(&out_probe_head, &bin->probe_head);
+ }
+ }
+}
+
+static void us_app_get_equal_elements(struct list_head *app_head,
+ struct list_head *test_app_head,
+ struct list_head *out_app_head)
+{
+ struct pr_app_desc *test_app, *n;
+
+ list_for_each_entry_safe(test_app, n, test_app_head, list) {
+ struct pr_app_desc *app;
+ LIST_HEAD(out_bin_head);
+
+ app = find_app(app_head, test_app->info);
+ if (!app)
+ return;
+
+ us_bin_get_equal_elements(&app->bin_head,
+ &test_app->bin_head,
+ &out_bin_head);
+
+ /* check all bins found */
+ if (list_empty(&test_app->bin_head)) {
+ list_move(&test_app->list, out_app_head);
+ list_splice(&out_bin_head, &test_app->bin_head);
+ } else {
+ list_splice(&out_bin_head, &app->bin_head);
+ }
+ }
+}
+
+
+static void bin_list_splice(struct list_head *list, struct list_head *head)
+{
+ struct pr_bin_desc *new_bin, *n;
+
+ list_for_each_entry_safe(new_bin, n, list, list) {
+ struct pr_bin_desc *bin;
+
+ bin = find_bin(head, new_bin->info);
+ if (bin) {
+ list_splice_init(&new_bin->probe_head,
+ &bin->probe_head);
+
+ list_del(&new_bin->list);
+ pr_bin_desc_free(new_bin);
+ } else {
+ list_move(&new_bin->list, head);
+ }
+ }
+}
+
+static void app_list_splice(struct list_head *list, struct list_head *head)
+{
+ struct pr_app_desc *new_app, *n;
+
+ list_for_each_entry_safe(new_app, n, list, list) {
+ struct pr_app_desc *app;
+
+ app = find_app(head, new_app->info);
+ if (app) {
+ bin_list_splice(&new_app->bin_head, &app->bin_head);
+
+ list_del(&new_app->list);
+ put_pf_group(app->pfg);
+ pr_app_desc_free(new_app);
+ } else {
+ list_move(&new_app->list, head);
+ }
+ }
+}
+
+static void app_list_free(struct list_head *head)
+{
+ struct pr_app_desc *app, *n;
+
+ list_for_each_entry_safe(app, n, head, list) {
+ list_del(&app->list);
+ pr_app_desc_free(app);
+ }
+}
+
+static void do_app_list_unreg(struct list_head *head)
+{
+ struct pr_app_desc *app;
+
+ list_for_each_entry(app, head, list) {
+ us_app_inst_unreg(app);
+ }
+}
+
+
+
+static LIST_HEAD(app_head);
+
+/* After call the 'head' list is empty, do not free it. */
+int app_list_unreg(struct list_head *head)
+{
+ LIST_HEAD(out_app_head);
+
+ us_app_get_equal_elements(&app_head, head, &out_app_head);
+
+ /* check all apps found */
+ if (!list_empty(head)) {
+ app_list_splice(&out_app_head, &app_head);
+ return -EINVAL;
+ }
+
+ do_app_list_unreg(&out_app_head);
+ app_list_free(&out_app_head);
+ return 0;
+}
+
+/* After call the 'head' list is empty, do not free it. */
+int app_list_reg(struct list_head *head)
+{
+ LIST_HEAD(reg_head);
+ struct pr_app_desc *app, *n;
+
+ list_for_each_entry_safe(app, n, head, list) {
+ int ret;
+
+ ret = us_app_inst_reg(app);
+ if (!ret) {
+ list_move(&app->list, ®_head);
+ } else {
+ do_app_list_unreg(®_head);
+ list_splice(®_head, head);
+ app_list_free(head);
+ return ret;
+ }
+ }
+
+ app_list_splice(®_head, &app_head);
+ return 0;
+}
+
+void app_list_unreg_all(void)
+{
+ do_app_list_unreg(&app_head);
+ app_list_free(&app_head);
+}
--- /dev/null
+/**
+ * @file parser/us_inst.h
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * User-space instrumentation controls interface.
+ */
+
+
+#ifndef _US_INST_H
+#define _US_INST_H
+
+int app_list_reg(struct list_head *head);
+int app_list_unreg(struct list_head *head);
+void app_list_unreg_all(void);
+
+#endif /* _US_INST_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/dcache.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/version.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/net.h>
+#include <writer/swap_msg.h>
+#include <master/swap_deps.h>
+#include <us_manager/sspt/sspt.h> /* ... check_vma() */
+
+
+#define USM_PREFIX KERN_INFO "[USM] "
+
+
+struct kmem_info {
+ const char *name;
+ unsigned long start;
+ unsigned long end;
+};
+
+#if defined(CONFIG_ARM64) || defined(CONFIG_X86_32)
+static void kmem_info_fill_common(struct kmem_info *info, struct task_struct *task)
+{
+ unsigned long vdso;
+ struct vm_area_struct *vma_vdso;
+
+ vdso = (unsigned long)task->mm->context.vdso;
+ vma_vdso = find_vma_intersection(task->mm, vdso, vdso + 1);
+ if (vma_vdso) {
+ info->name = "[vdso]";
+ info->start = vma_vdso->vm_start;
+ info->end = vma_vdso->vm_end;
+ } else {
+ pr_err(USM_PREFIX "Cannot get VDSO mapping\n");
+ info->name = NULL;
+ info->start = 0;
+ info->end = 0;
+ }
+}
+#endif /* defined(CONFIG_ARM64) || defined(CONFIG_X86_32) */
+
+static void kmem_info_fill(struct kmem_info *info, struct task_struct *task)
+{
+#if defined(CONFIG_ARM)
+ info->name = "[vectors]";
+ info->start = CONFIG_VECTORS_BASE;
+ info->end = CONFIG_VECTORS_BASE + PAGE_SIZE;
+#elif defined(CONFIG_ARM64)
+ if (test_ti_thread_flag(task_thread_info(task), TIF_32BIT)) {
+ info->name = "[vectors]";
+ info->start = AARCH32_VECTORS_BASE;
+ info->end = AARCH32_VECTORS_BASE + PAGE_SIZE;
+ } else {
+ kmem_info_fill_common(info, task);
+ }
+#elif defined(CONFIG_X86_32)
+ kmem_info_fill_common(info, task);
+#endif /* CONFIG_arch */
+}
+
+static inline struct timespec get_task_start_time(struct task_struct *task)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0)
+ return ns_to_timespec(task->real_start_time);
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
+ return task->real_start_time;
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
+}
+
+
+
+static int pack_path(void *data, size_t size, struct file *file)
+{
+ enum { TMP_BUF_LEN = 512 };
+ char tmp_buf[TMP_BUF_LEN];
+ const char NA[] = "N/A";
+ const char *filename;
+ size_t len = sizeof(NA);
+
+ if (file == NULL) {
+ filename = NA;
+ goto cp2buf;
+ }
+
+ path_get(&file->f_path);
+ filename = d_path(&file->f_path, tmp_buf, TMP_BUF_LEN);
+ path_put(&file->f_path);
+
+ if (IS_ERR_OR_NULL(filename)) {
+ filename = NA;
+ goto cp2buf;
+ }
+
+ len = strlen(filename) + 1;
+
+cp2buf:
+ if (size < len)
+ return -ENOMEM;
+
+ memcpy(data, filename, len);
+ return len;
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_PROCESS_INFO =
+ * ============================================================================
+ */
+struct proc_info_top {
+ u32 pid;
+ char comm[0];
+} __packed;
+
+struct proc_info_bottom {
+ u32 ppid;
+ u64 start_time;
+ u64 low_addr;
+ u64 high_addr;
+ char bin_path[0];
+} __packed;
+
+struct lib_obj {
+ u64 low_addr;
+ u64 high_addr;
+ char lib_path[0];
+} __packed;
+
+static int pack_lib_obj(void *data, size_t size, struct vm_area_struct *vma)
+{
+ int ret;
+ struct lib_obj *obj = (struct lib_obj *)data;
+
+ if (size < sizeof(*obj))
+ return -ENOMEM;
+
+ obj->low_addr = vma->vm_start;
+ obj->high_addr = vma->vm_end;
+ size -= sizeof(*obj);
+
+ ret = pack_path(obj->lib_path, size, vma->vm_file);
+ if (ret < 0)
+ return ret;
+
+ return ret + sizeof(*obj);
+}
+
+static int pack_shared_kmem(void *data, size_t size, struct task_struct *task)
+{
+ struct lib_obj *obj = (struct lib_obj *)data;
+ struct kmem_info info;
+ size_t name_len, obj_size;
+
+ if (size < sizeof(*obj))
+ return -ENOMEM;
+
+ kmem_info_fill(&info, task);
+
+ if (info.name == NULL)
+ return 0;
+
+ obj->low_addr = (u64)info.start;
+ obj->high_addr = (u64)info.end;
+
+ name_len = strlen(info.name) + 1;
+ obj_size = sizeof(*obj) + name_len;
+ if (size < obj_size)
+ return -ENOMEM;
+
+ memcpy(obj->lib_path, info.name, name_len);
+
+ return obj_size;
+}
+
+static int pack_libs(void *data, size_t size, struct task_struct *task)
+{
+ int ret;
+ struct vm_area_struct *vma;
+ u32 *lib_cnt = (u32 *)data;
+ const size_t old_size = size;
+
+ if (size < sizeof(*lib_cnt))
+ return -ENOMEM;
+
+ /* packing libraries count */
+ *lib_cnt = 0;
+ data += sizeof(*lib_cnt);
+ size -= sizeof(*lib_cnt);
+
+ /* packing libraries */
+ for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
+ if (check_vma(vma)) {
+ ret = pack_lib_obj(data, size, vma);
+ if (ret < 0)
+ return ret;
+
+ data += ret;
+ size -= ret;
+ ++(*lib_cnt);
+ }
+ }
+
+ /* packing shared kernel memory */
+ ret = pack_shared_kmem(data, size, task);
+ if (ret < 0)
+ return ret;
+
+ *lib_cnt += !!ret;
+ size -= ret;
+
+ return old_size - size;
+}
+
+static struct vm_area_struct *find_vma_exe_by_dentry(struct mm_struct *mm,
+ struct dentry *dentry)
+{
+ struct vm_area_struct *vma;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (vma->vm_file && (vma->vm_flags & VM_EXEC) &&
+ (vma->vm_file->f_path.dentry == dentry))
+ goto out;
+ }
+
+ vma = NULL;
+out:
+
+ return vma;
+}
+
+static int pack_proc_info_top(void *data, size_t size,
+ struct task_struct *task)
+{
+ struct proc_info_top *pit = (struct proc_info_top *)data;
+
+ if (size < sizeof(*pit) + sizeof(task->comm))
+ return -ENOMEM;
+
+ pit->pid = task->tgid;
+ get_task_comm(pit->comm, task);
+
+ return sizeof(*pit) + strlen(pit->comm) + 1;
+}
+
+static int pack_proc_info_bottom(void *data, size_t size,
+ struct task_struct *task,
+ struct dentry *dentry)
+{
+ struct proc_info_bottom *pib = (struct proc_info_bottom *)data;
+ struct vm_area_struct *vma = find_vma_exe_by_dentry(task->mm, dentry);
+ struct timespec boot_time;
+ struct timespec start_time;
+ int ret;
+
+ if (size < sizeof(*pib))
+ return -ENOMEM;
+
+ getboottime(&boot_time);
+ start_time = timespec_add(boot_time, get_task_start_time(task));
+
+ pib->ppid = task->real_parent->tgid;
+ pib->start_time = swap_msg_spec2time(&start_time);
+
+ if (vma) {
+ pib->low_addr = vma->vm_start;
+ pib->high_addr = vma->vm_end;
+ ret = pack_path(pib->bin_path, size, vma->vm_file);
+ } else {
+ pib->low_addr = 0;
+ pib->high_addr = 0;
+ ret = pack_path(pib->bin_path, size, NULL);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return sizeof(*pib) + ret;
+}
+
+static int pack_proc_info(void *data, size_t size, struct task_struct *task,
+ struct dentry *dentry)
+{
+ int ret;
+ const size_t old_size = size;
+
+ ret = pack_proc_info_top(data, size, task);
+ if (ret < 0)
+ return ret;
+
+ data += ret;
+ size -= ret;
+
+ ret = pack_proc_info_bottom(data, size, task, dentry);
+ if (ret < 0)
+ return ret;
+
+ data += ret;
+ size -= ret;
+
+ ret = pack_libs(data, size, task);
+ if (ret < 0)
+ return ret;
+
+ return old_size - size + ret;
+}
+
+/* Called with down\up\_read(&task->mm->mmap_sem). */
+void usm_msg_info(struct task_struct *task, struct dentry *dentry)
+{
+ int ret;
+ struct swap_msg *m;
+ void *p;
+ size_t size;
+
+ m = swap_msg_get(MSG_PROC_INFO);
+ p = swap_msg_payload(m);
+ size = swap_msg_size(m);
+
+ ret = pack_proc_info(p, size, task, dentry);
+ if (ret < 0) {
+ printk(USM_PREFIX "ERROR: message process info packing, "
+ "ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_TERMINATE =
+ * ============================================================================
+ */
+struct proc_terminate {
+ u32 pid;
+} __packed;
+
+void usm_msg_term(struct task_struct *task)
+{
+ struct swap_msg *m;
+ struct proc_terminate *term;
+
+ m = swap_msg_get(MSG_TERMINATE);
+
+ term = swap_msg_payload(m);
+ term->pid = task->pid;
+
+ swap_msg_flush(m, sizeof(*term));
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_PROCESS_MAP =
+ * ============================================================================
+ */
+struct proc_map {
+ u32 pid;
+ u64 low_addr;
+ u64 high_addr;
+ char bin_path[0];
+} __packed;
+
+static int pack_proc_map(void *data, size_t size, struct vm_area_struct *vma)
+{
+ struct proc_map *map = (struct proc_map *)data;
+ int ret;
+
+ map->pid = current->tgid;
+ map->low_addr = vma->vm_start;
+ map->high_addr = vma->vm_end;
+
+ ret = pack_path(map->bin_path, size - sizeof(*map), vma->vm_file);
+ if (ret < 0)
+ return ret;
+
+ return ret + sizeof(*map);
+}
+
+void usm_msg_map(struct vm_area_struct *vma)
+{
+ int ret;
+ struct swap_msg *m;
+ void *p;
+ size_t size;
+
+ m = swap_msg_get(MSG_PROC_MAP);
+ p = swap_msg_payload(m);
+ size = swap_msg_size(m);
+
+ ret = pack_proc_map(p, size, vma);
+ if (ret < 0) {
+ printk(USM_PREFIX "ERROR: message process mapping packing, "
+ "ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_PROCESS_UNMAP =
+ * ============================================================================
+ */
+struct proc_unmap {
+ u32 pid;
+ u64 low_addr;
+ u64 high_addr;
+} __packed;
+
+void usm_msg_unmap(unsigned long start, unsigned long end)
+{
+ struct swap_msg *m;
+ struct proc_unmap *unmap;
+
+ m = swap_msg_get(MSG_PROC_UNMAP);
+
+ unmap = swap_msg_payload(m);
+ unmap->pid = current->tgid;
+ unmap->low_addr = (u64)start;
+ unmap->high_addr = (u64)end;
+
+ swap_msg_flush(m, sizeof(*unmap));
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_PROCESS_COMM =
+ * ============================================================================
+ */
+struct proc_comm {
+ u32 pid;
+ char comm[0];
+} __packed;
+
+void usm_msg_comm(struct task_struct *task)
+{
+ struct swap_msg *m;
+ struct proc_comm *c;
+
+ m = swap_msg_get(MSG_PROC_COMM);
+
+ c = swap_msg_payload(m);
+ c->pid = task->tgid;
+ get_task_comm(c->comm, task);
+
+ swap_msg_flush(m, sizeof(*c) + strlen(c->comm) + 1);
+ swap_msg_put(m);
+}
+
+
+
+
+
+/* ============================================================================
+ * = MSG_PROCESS_STATUS_INFO =
+ * ============================================================================
+ */
+struct ofile {
+ u32 fd;
+ u64 size;
+ char path[0];
+} __packed;
+
+struct osock {
+ u32 fd;
+ u32 ip;
+ u32 port;
+} __packed;
+
+static int pack_ofile(void *data, size_t size, int fd, struct file *file,
+ loff_t fsize)
+{
+ int ret;
+ struct ofile *ofile;
+
+ if (size < sizeof(*ofile))
+ return -ENOMEM;
+
+ ofile = (struct ofile *)data;
+ ofile->fd = (u32)fd;
+ ofile->size = (u64)fsize;
+
+ ret = pack_path(ofile->path, size - sizeof(*ofile), file);
+ if (ret < 0)
+ return ret;
+
+ return sizeof(*ofile) + ret;
+}
+
+static int is_file_mode(umode_t mode)
+{
+ return S_ISREG(mode);
+}
+
+static int pack_osock(void *data, size_t size, int fd, struct file *file,
+ loff_t fsize)
+{
+ struct osock *pack_sock;
+ struct socket *sock;
+ struct sockaddr_storage addr;
+ struct sockaddr *saddr = (struct sockaddr *)&addr;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+ int ret_size = sizeof(*pack_sock);
+ int addrlen;
+ int err;
+
+ if (size < sizeof(*pack_sock))
+ return -ENOMEM;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ sock = sockfd_lookup(fd, &err);
+#else
+ sock = sock_from_file(file, &err);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) */
+ if (!sock)
+ return 0;
+
+ kernel_getsockname(sock, saddr, &addrlen);
+
+ pack_sock = (struct osock *)data;
+ pack_sock->fd = (u32)fd;
+ pack_sock->ip = (u32)0x0;
+ pack_sock->port = 0;
+
+ switch (saddr->sa_family) {
+ case AF_INET:
+ pack_sock->ip = (u32)(sin->sin_addr.s_addr);
+ pack_sock->port = ntohs(sin->sin_port);
+ break;
+ default:
+ pr_info("[USM] ignored unknown address type: %u\n",
+ (unsigned int)saddr->sa_family);
+ ret_size = 0;
+ break;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ sockfd_put(sock);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) */
+
+ return ret_size;
+}
+
+static int is_sock_mode(umode_t mode)
+{
+ return S_ISSOCK(mode);
+}
+
+typedef int (*pack_func_t)(void *data, size_t size, int fd, struct file *file,
+ loff_t fsize);
+typedef int (*check_mode_func_t)(umode_t mode);
+
+static int pack_descriptors(void *data, size_t size, pack_func_t pack_func,
+ check_mode_func_t check_mode,
+ struct files_struct *files)
+{
+ int ret, fd;
+ const size_t old_size = size;
+ u32 *desc_cnt;
+
+ /* pack descriptor count */
+ desc_cnt = (u32 *)data;
+ *desc_cnt = 0;
+ data += 4;
+ size -= 4;
+
+ /* pack descriptors list */
+ rcu_read_lock();
+ for (fd = 0; fd < files_fdtable(files)->max_fds; ++fd) {
+ struct file *file;
+ struct inode *inode;
+ loff_t fsize;
+
+ file = fcheck_files(files, fd);
+ if (file == NULL)
+ continue;
+
+ inode = file->f_path.dentry->d_inode;
+ /* check inode and if it is a regular file */
+ if (!inode || !check_mode(inode->i_mode))
+ continue;
+
+ fsize = inode->i_size;
+ rcu_read_unlock();
+
+ ret = pack_func(data, size, fd, file, fsize);
+ if (ret < 0) {
+ goto exit;
+ } else if (ret > 0) {
+ /* increment if data packed only */
+ data += ret;
+ size -= ret;
+ ++(*desc_cnt);
+ }
+
+ rcu_read_lock();
+ }
+
+ rcu_read_unlock();
+ ret = old_size - size;
+
+exit:
+ return ret;
+}
+
+static int pack_status_info(void *data, size_t size, struct task_struct *task)
+{
+ int ret;
+ const size_t old_size = size;
+ struct files_struct *files;
+
+ files = swap_get_files_struct(task);
+ if (!files)
+ return -EIO;
+
+ /* pack pid */
+ *((u32 *)data) = (u32)task->tgid;
+ data += 4;
+ size -= 4;
+
+ /* pack file descriptors */
+ ret = pack_descriptors(data, size, pack_ofile, is_file_mode, files);
+ if (ret < 0)
+ goto put_fstruct;
+ data += ret;
+ size -= ret;
+
+ /* pack sock descriptors */
+ ret = pack_descriptors(data, size, pack_osock, is_sock_mode, files);
+ if (ret < 0)
+ goto put_fstruct;
+ data += ret;
+ size -= ret;
+
+ ret = old_size - size;
+
+put_fstruct:
+ swap_put_files_struct(files);
+ return ret;
+}
+
+void usm_msg_status_info(struct task_struct *task)
+{
+ int ret;
+ void *data;
+ size_t size;
+ struct swap_msg *m;
+
+ m = swap_msg_get(MSG_PROCESS_STATUS_INFO);
+
+ data = swap_msg_payload(m);
+ size = swap_msg_size(m);
+ ret = pack_status_info(data, size, task);
+ if (ret < 0) {
+ printk(USM_PREFIX "ERROR: MSG_PROCESS_STATUS_INFO "
+ "packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret);
+
+put_msg:
+ swap_msg_put(m);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _USM_MSG_H
+#define _USM_MSG_H
+
+
+struct dentry;
+struct task_struct;
+struct vm_area_struct;
+
+
+void usm_msg_info(struct task_struct *task, struct dentry *dentry);
+void usm_msg_term(struct task_struct *task);
+void usm_msg_map(struct vm_area_struct *vma);
+void usm_msg_unmap(unsigned long start, unsigned long end);
+void usm_msg_comm(struct task_struct *task);
+void usm_msg_status_info(struct task_struct *task);
+
+
+#endif /* _USM_MSG_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_preload.o
+swap_preload-y := preload_module.o \
+ preload_control.o \
+ preload_debugfs.o \
+ preload_probe.o \
+ preload_threads.o \
+ preload_process.o
--- /dev/null
+#ifndef __PRELOAD__
+#define __PRELOAD__
+
+#define PRELOAD_PREFIX "SWAP_PRELOAD: "
+#define PRELOAD_MAX_ATTEMPTS 10
+#define PRELOAD_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
+
+#endif /* __PRELOAD__ */
--- /dev/null
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/us_common_file.h>
+
+#include "preload.h"
+#include "preload_module.h"
+#include "preload_control.h"
+#include "preload_probe.h"
+
+struct bin_desc {
+ struct list_head list;
+ struct dentry *dentry;
+ char *filename;
+};
+
+struct list_desc {
+ struct list_head list;
+ rwlock_t lock;
+ int cnt;
+};
+
+static struct list_desc target = {
+ .list = LIST_HEAD_INIT(target.list),
+ .lock = __RW_LOCK_UNLOCKED(&target.lock),
+ .cnt = 0
+};
+
+static struct list_desc ignored = {
+ .list = LIST_HEAD_INIT(ignored.list),
+ .lock = __RW_LOCK_UNLOCKED(&ignored.lock),
+ .cnt = 0
+};
+
+static inline struct task_struct *__get_task_struct(void)
+{
+ return current;
+}
+
+static struct bin_desc *__alloc_binary(struct dentry *dentry, char *name,
+ int namelen)
+{
+ struct bin_desc *p = NULL;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ INIT_LIST_HEAD(&p->list);
+ p->filename = kmalloc(namelen + 1, GFP_KERNEL);
+ if (!p->filename)
+ goto fail;
+ memcpy(p->filename, name, namelen);
+ p->filename[namelen] = '\0';
+ p->dentry = dentry;
+
+ return p;
+fail:
+ kfree(p);
+ return NULL;
+}
+
+static void __free_binary(struct bin_desc *p)
+{
+ kfree(p->filename);
+ kfree(p);
+}
+
+static void __free_binaries(struct list_desc *tl)
+{
+ struct bin_desc *p, *n;
+ struct list_head rm_head;
+
+ INIT_LIST_HEAD(&rm_head);
+ write_lock(&tl->lock);
+ list_for_each_entry_safe(p, n, &tl->list, list) {
+ list_move(&p->list, &rm_head);
+ }
+ tl->cnt = 0;
+ write_unlock(&tl->lock);
+
+ list_for_each_entry_safe(p, n, &rm_head, list) {
+ list_del(&p->list);
+ swap_put_dentry(p->dentry);
+ __free_binary(p);
+ }
+}
+
+static bool __check_dentry_already_exist(struct dentry *dentry,
+ struct list_desc *tl)
+{
+ struct bin_desc *p;
+ bool ret = false;
+
+ read_lock(&tl->lock);
+ list_for_each_entry(p, &tl->list, list) {
+ if (p->dentry == dentry) {
+ ret = true;
+ goto out;
+ }
+ }
+out:
+ read_unlock(&tl->lock);
+
+ return ret;
+}
+
+static int __add_binary(struct dentry *dentry, char *filename,
+ struct list_desc *tl)
+{
+ struct bin_desc *p;
+ size_t len;
+
+ if (__check_dentry_already_exist(dentry, tl)) {
+ printk(PRELOAD_PREFIX "Binary already exist\n");
+ return EALREADY;
+ }
+
+ /* Filename should be < PATH_MAX */
+ len = strnlen(filename, PATH_MAX);
+ if (len == PATH_MAX)
+ return -EINVAL;
+
+ p = __alloc_binary(dentry, filename, len);
+ if (!p)
+ return -ENOMEM;
+
+ write_lock(&tl->lock);
+ list_add_tail(&p->list, &tl->list);
+ tl->cnt++;
+ write_unlock(&tl->lock);
+
+ return 0;
+}
+
+static struct dentry *__get_caller_dentry(struct task_struct *task,
+ unsigned long caller)
+{
+ struct vm_area_struct *vma = NULL;
+
+ if (unlikely(task->mm == NULL))
+ goto get_caller_dentry_fail;
+
+ vma = find_vma_intersection(task->mm, caller, caller + 1);
+ if (unlikely(vma == NULL || vma->vm_file == NULL))
+ goto get_caller_dentry_fail;
+
+ return vma->vm_file->f_path.dentry;
+
+get_caller_dentry_fail:
+
+ return NULL;
+}
+
+static bool __check_if_instrumented(struct task_struct *task,
+ struct dentry *dentry)
+{
+ return __check_dentry_already_exist(dentry, &target);
+}
+
+static bool __is_instrumented(void *caller)
+{
+ struct task_struct *task = __get_task_struct();
+ struct dentry *caller_dentry = __get_caller_dentry(task,
+ (unsigned long) caller);
+
+ if (caller_dentry == NULL)
+ return false;
+
+ return __check_if_instrumented(task, caller_dentry);
+}
+
+static unsigned int __get_names(struct list_desc *tl, char ***filenames_p)
+{
+ unsigned int i, ret = 0;
+ struct bin_desc *p;
+ char **a = NULL;
+
+ read_lock(&tl->lock);
+ if (tl->cnt == 0)
+ goto out;
+
+ a = kmalloc(sizeof(*a) * tl->cnt, GFP_KERNEL);
+ if (!a)
+ goto out;
+
+ i = 0;
+ list_for_each_entry(p, &tl->list, list) {
+ if (i >= tl->cnt)
+ break;
+ a[i++] = p->filename;
+ }
+
+ *filenames_p = a;
+ ret = i;
+out:
+ read_unlock(&tl->lock);
+ return ret;
+}
+
+
+/* Called only form handlers. If we're there, then it is instrumented. */
+enum preload_call_type pc_call_type_always_inst(void *caller)
+{
+ if (__is_instrumented(caller))
+ return INTERNAL_CALL;
+
+ return EXTERNAL_CALL;
+
+}
+
+enum preload_call_type pc_call_type(struct sspt_ip *ip, void *caller)
+{
+ if (__is_instrumented(caller))
+ return INTERNAL_CALL;
+
+ if (ip->desc->info.pl_i.flags & SWAP_PRELOAD_ALWAYS_RUN)
+ return EXTERNAL_CALL;
+
+ return NOT_INSTRUMENTED;
+}
+
+int pc_add_instrumented_binary(char *filename)
+{
+ struct dentry *dentry = swap_get_dentry(filename);
+ int res = 0;
+
+ if (dentry == NULL)
+ return -EINVAL;
+
+ res = __add_binary(dentry, filename, &target);
+ if (res != 0)
+ swap_put_dentry(dentry);
+
+ return res > 0 ? 0 : res;
+}
+
+int pc_clean_instrumented_bins(void)
+{
+ __free_binaries(&target);
+
+ return 0;
+}
+
+int pc_add_ignored_binary(char *filename)
+{
+ struct dentry *dentry = swap_get_dentry(filename);
+ int res = 0;
+
+ if (dentry == NULL)
+ return -EINVAL;
+
+ res = __add_binary(dentry, filename, &ignored);
+ if (res != 0)
+ swap_put_dentry(dentry);
+
+ return res > 0 ? 0 : res;
+}
+
+int pc_clean_ignored_bins(void)
+{
+ __free_binaries(&ignored);
+
+ return 0;
+}
+
+unsigned int pc_get_target_names(char ***filenames_p)
+{
+ return __get_names(&target, filenames_p);
+}
+
+void pc_release_target_names(char ***filenames_p)
+{
+ kfree(*filenames_p);
+}
+
+unsigned int pc_get_ignored_names(char ***filenames_p)
+{
+ return __get_names(&ignored, filenames_p);
+}
+
+void pc_release_ignored_names(char ***filenames_p)
+{
+ kfree(*filenames_p);
+}
+
+bool pc_check_dentry_is_ignored(struct dentry *dentry)
+{
+ struct bin_desc *p;
+ bool ret = false;
+
+ if (dentry == NULL)
+ return false;
+
+ read_lock(&ignored.lock);
+
+ list_for_each_entry(p, &ignored.list, list) {
+ if (p->dentry == dentry) {
+ ret = true;
+ break;
+ }
+ }
+
+ read_unlock(&ignored.lock);
+
+ return ret;
+}
+
+int pc_init(void)
+{
+ return 0;
+}
+
+void pc_exit(void)
+{
+ __free_binaries(&target);
+ __free_binaries(&ignored);
+}
--- /dev/null
+#ifndef __PRELOAD_CONTROL_H__
+#define __PRELOAD_CONTROL_H__
+
+struct sspt_ip;
+
+enum preload_call_type {
+ NOT_INSTRUMENTED,
+ EXTERNAL_CALL,
+ INTERNAL_CALL
+};
+
+int pc_init(void);
+void pc_exit(void);
+
+enum preload_call_type pc_call_type_always_inst(void *caller);
+enum preload_call_type pc_call_type(struct sspt_ip *ip, void *caller);
+int pc_add_instrumented_binary(char *filename);
+int pc_clean_instrumented_bins(void);
+int pc_add_ignored_binary(char *filename);
+int pc_clean_ignored_bins(void);
+
+unsigned int pc_get_target_names(char ***filenames_p);
+void pc_release_target_names(char ***filenames_p);
+
+unsigned int pc_get_ignored_names(char ***filenames_p);
+void pc_release_ignored_names(char ***filenames_p);
+
+bool pc_check_dentry_is_ignored(struct dentry *dentry);
+
+#endif /* __PRELOAD_HANDLERS_CONTROL_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/limits.h>
+#include <asm/uaccess.h>
+#include <master/swap_debugfs.h>
+#include <master/swap_initializer.h>
+#include "preload.h"
+#include "preload_module.h"
+#include "preload_debugfs.h"
+#include "preload_control.h"
+#include "preload_process.h"
+
+static const char PRELOAD_FOLDER[] = "preload";
+static const char PRELOAD_TARGET[] = "target_binaries";
+static const char PRELOAD_IGNORED[] = "ignored_binaries";
+static const char PRELOAD_BINARIES_LIST[] = "bins_list";
+static const char PRELOAD_BINARIES_ADD[] = "bins_add";
+static const char PRELOAD_BINARIES_REMOVE[] = "bins_remove";
+static const char PRELOAD_ENABLE[] = "enable";
+static const char PRELOAD_BY_PATH[] = "by_path";
+static const char PRELOAD_BY_PID[] = "by_pid";
+static const char PRELOAD_BY_ID[] = "by_id";
+static const char PRELOAD_ADD[] = "add";
+static const char PRELOAD_DEL[] = "del";
+static const char PRELOAD_DEL_ALL[] = "del_all";
+static const char PRELOAD_PTHREAD[] = "pthread";
+static const char PRELOAD_PATH[] = "path";
+static const char PRELOAD_MINIMAL_INIT[] = "minimal_init_off";
+
+static struct dentry *preload_root;
+static struct dentry *target_list = NULL;
+static struct dentry *target_add = NULL;
+static struct dentry *target_remove = NULL;
+static struct dentry *ignored_list = NULL;
+static struct dentry *ignored_add = NULL;
+static struct dentry *ignored_remove = NULL;
+
+
+/* Type for functions that add process by path and by id */
+typedef int (*sh_t)(const char *);
+
+/* Type for functions that add process by pid */
+typedef int (*ph_t)(pid_t);
+
+/* Type for function that handles unsigned long grabbed from userspace */
+typedef int (*ulh_t)(unsigned long);
+
+
+/* remove end-line symbols */
+static void rm_endline_symbols(char *buf, size_t len)
+{
+ char *p, *buf_end;
+
+ buf_end = buf + len;
+ for (p = buf; p != buf_end; ++p)
+ if (*p == '\n' || *p == '\r')
+ *p = '\0';
+}
+
+static ssize_t get_string(const char __user *buf, size_t len, char **kbuf)
+{
+ char *string;
+ ssize_t ret;
+
+ string = kmalloc(len + 1, GFP_KERNEL);
+ if (!string) {
+ pr_warn(PRELOAD_PREFIX "No mem for user string!\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(string, buf, len)) {
+ pr_warn(PRELOAD_PREFIX "Failed to copy data from user!\n");
+ ret = -EINVAL;
+ goto get_string_fail;
+ }
+
+ string[len] = '\0';
+ rm_endline_symbols(string, len);
+ *kbuf = string;
+
+ return len;
+
+get_string_fail:
+ kfree(string);
+
+ return ret;
+}
+
+
+static ssize_t get_ul_and_call(const char __user *buf, size_t len, ulh_t cb)
+{
+ ssize_t ret;
+ char *ulstring;
+ unsigned long ul;
+
+ ret = get_string(buf, len, &ulstring);
+ if (ret != len)
+ return ret;
+
+ ret = kstrtoul(ulstring, 16, &ul);
+ if (ret)
+ goto get_ul_write_out;
+
+ ret = cb(ul);
+
+get_ul_write_out:
+ kfree(ulstring);
+
+ return ret == 0 ? len : ret;
+}
+
+static ssize_t get_string_and_call(const char __user *buf, size_t len, sh_t cb)
+{
+ char *string;
+ ssize_t ret;
+
+ ret = get_string(buf, len, &string);
+ if (ret != len)
+ return ret;
+
+ ret = cb(string);
+ if (ret)
+ pr_warn(PRELOAD_PREFIX "Error adding process by <%s>\n",
+ string);
+
+ kfree(string);
+
+ return ret == 0 ? len : ret;
+}
+
+static ssize_t get_pid_and_call(const char __user *buf, size_t len, ph_t cb)
+{
+ char *string;
+ pid_t pid;
+ ssize_t ret;
+
+ ret = get_string(buf, len, &string);
+ if (ret != len)
+ return ret;
+
+ ret = kstrtoul(string, 10, (unsigned long *)&pid);
+ if (ret) {
+ pr_warn(PRELOAD_PREFIX "Invalid PID!\n");
+ goto get_pid_out;
+ }
+
+ ret = cb(pid);
+ if (ret)
+ pr_warn(PRELOAD_PREFIX "Error adding process by <%s>\n",
+ string);
+
+get_pid_out:
+ kfree(string);
+
+ return ret == 0 ? len : ret;
+}
+
+/* ===========================================================================
+ * = TARGET PROCESSES =
+ * ===========================================================================
+ */
+
+static ssize_t by_path_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, pp_add_by_path);
+}
+
+static ssize_t by_path_del_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, pp_del_by_path);
+}
+
+static ssize_t by_pid_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_pid_and_call(buf, len, pp_add_by_pid);
+}
+
+static ssize_t by_pid_del_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_pid_and_call(buf, len, pp_del_by_pid);
+}
+
+static ssize_t by_id_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, pp_add_by_id);
+}
+
+static ssize_t by_id_del_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, pp_del_by_id);
+}
+
+static ssize_t del_all_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ pp_del_all();
+
+ WARN(pc_clean_instrumented_bins(), PRELOAD_PREFIX
+ "Error while cleaning target bins\n");
+ WARN(pc_clean_ignored_bins(), PRELOAD_PREFIX
+ "Error while cleaning ignored bins\n");
+
+ return len;
+}
+
+static const struct file_operations by_path_add_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = by_path_add_write,
+};
+
+static const struct file_operations by_path_del_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = by_path_del_write,
+};
+
+static const struct file_operations by_pid_add_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = by_pid_add_write,
+};
+
+static const struct file_operations by_pid_del_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = by_pid_del_write,
+};
+
+static const struct file_operations by_id_add_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = by_id_add_write,
+};
+
+static const struct file_operations by_id_del_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = by_id_del_write,
+};
+
+static const struct file_operations del_all_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = del_all_write,
+};
+
+/* ===========================================================================
+ * = ENABLE =
+ * ===========================================================================
+ */
+
+static ssize_t enable_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ char val[2];
+
+ val[0] = (pm_status() == PRELOAD_ON ? '1' : '0');
+ val[1] = '\0';
+
+ return simple_read_from_buffer(buf, len, ppos, val, 2);
+}
+
+static ssize_t enable_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret = 0;
+ char val[2];
+ size_t val_size;
+
+ val_size = min(len, (sizeof(val) - 1));
+ if (copy_from_user(val, buf, val_size))
+ return -EFAULT;
+
+ val[1] = '\0';
+ switch (val[0]) {
+ case '0':
+ ret = pm_switch(PRELOAD_OFF);
+ break;
+ case '1':
+ ret = pm_switch(PRELOAD_ON);
+ break;
+ default:
+ printk(PRELOAD_PREFIX "Invalid state!\n");
+ return -EINVAL;
+ }
+
+ return ret == 0 ? len : ret;
+}
+
+static const struct file_operations enable_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = enable_write,
+ .read = enable_read,
+};
+
+
+/* ===========================================================================
+ * = BIN PATH =
+ * ===========================================================================
+ */
+
+static ssize_t bin_add_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (path == NULL) {
+ ret = -ENOMEM;
+ goto bin_add_write_out;
+ }
+
+ if (copy_from_user(path, buf, len)) {
+ ret = -EINVAL;
+ goto bin_add_write_out;
+ }
+
+ path[len - 1] = '\0';
+
+ if (file->f_path.dentry == target_add)
+ ret = pc_add_instrumented_binary(path);
+ else if (file->f_path.dentry == ignored_add)
+ ret = pc_add_ignored_binary(path);
+ else {
+ /* Should never occur */
+ printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
+ file->f_path.dentry->d_name.name);
+ ret = -EINVAL;
+ goto bin_add_write_out;
+ }
+
+
+ if (ret != 0) {
+ printk(PRELOAD_PREFIX "Cannot add binary %s\n", path);
+ ret = -EINVAL;
+ goto bin_add_write_out;
+ }
+
+ ret = len;
+
+bin_add_write_out:
+ kfree(path);
+
+ return ret;
+}
+
+static ssize_t bin_remove_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+
+ if (file->f_path.dentry == target_remove)
+ ret = pc_clean_instrumented_bins();
+ else if (file->f_path.dentry == ignored_remove)
+ ret = pc_clean_ignored_bins();
+ else {
+ /* Should never occur */
+ printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
+ file->f_path.dentry->d_name.name);
+ ret = -EINVAL;
+ goto bin_remove_write_out;
+ }
+
+ if (ret != 0) {
+ printk(PRELOAD_PREFIX "Error during clean!\n");
+ ret = -EINVAL;
+ goto bin_remove_write_out;
+ }
+
+ ret = len;
+
+bin_remove_write_out:
+ return ret;
+}
+
+static ssize_t bin_list_read(struct file *file, char __user *usr_buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int i;
+ unsigned int files_cnt = 0;
+ ssize_t len = 0, tmp, ret = 0;
+ char **filenames = NULL;
+ char *buf = NULL;
+ char *ptr = NULL;
+
+ if (file->f_path.dentry == target_list) {
+ files_cnt = pc_get_target_names(&filenames);
+ } else if (file->f_path.dentry == ignored_list) {
+ files_cnt = pc_get_ignored_names(&filenames);
+ } else {
+ /* Should never occur */
+ printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
+ file->f_path.dentry->d_name.name);
+ ret = 0;
+ goto bin_list_read_out;
+ }
+
+ if (files_cnt == 0) {
+ printk(PRELOAD_PREFIX "Cannot read binaries names!\n");
+ ret = 0;
+ goto bin_list_read_fail;
+ }
+
+ for (i = 0; i < files_cnt; i++)
+ len += strlen(filenames[i]);
+
+ buf = kmalloc(len + files_cnt, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = 0;
+ goto bin_list_read_fail;
+ }
+
+ ptr = buf;
+
+ for (i = 0; i < files_cnt; i++) {
+ tmp = strlen(filenames[i]);
+ memcpy(ptr, filenames[i], tmp);
+ ptr += tmp;
+ *ptr = '\n';
+ ptr += 1;
+ }
+
+ ret = simple_read_from_buffer(usr_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+bin_list_read_fail:
+ if (file->f_path.dentry == target_list) {
+ pc_release_target_names(&filenames);
+ } else if (file->f_path.dentry == ignored_list) {
+ pc_release_ignored_names(&filenames);
+ } else {
+ /* Should never occur */
+ printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
+ file->f_path.dentry->d_name.name);
+ ret = 0;
+ }
+
+bin_list_read_out:
+ return ret;
+}
+
+static const struct file_operations bin_list_file_ops = {
+ .owner = THIS_MODULE,
+ .read = bin_list_read
+};
+
+static const struct file_operations bin_add_file_ops = {
+ .owner = THIS_MODULE,
+ .write = bin_add_write,
+};
+
+static const struct file_operations bin_remove_file_ops = {
+ .owner = THIS_MODULE,
+ .write = bin_remove_write,
+};
+
+
+
+/* ===========================================================================
+ * = PTHREAD =
+ * ===========================================================================
+ */
+
+static ssize_t pthread_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_string_and_call(buf, len, pp_set_pthread_path);
+}
+
+static ssize_t init_off_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return get_ul_and_call(buf, len, pp_set_init_offset);
+}
+
+static const struct file_operations pthread_path_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = pthread_path_write,
+};
+
+static const struct file_operations pthread_init_off_fops = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = init_off_write,
+};
+
+
+
+
+
+
+int pd_init(void)
+{
+ struct dentry *swap_dentry, *root, *target_path, *ignored_path,
+ *by_path, *by_pid, *by_id, *pthread, *dentry;
+ int ret;
+
+ ret = -ENODEV;
+ if (!debugfs_initialized())
+ goto fail;
+
+ ret = -ENOENT;
+ swap_dentry = swap_debugfs_getdir();
+ if (!swap_dentry)
+ goto fail;
+
+ ret = -ENOMEM;
+ root = swap_debugfs_create_dir(PRELOAD_FOLDER, swap_dentry);
+ if (IS_ERR_OR_NULL(root))
+ goto fail;
+
+ preload_root = root;
+
+ target_path = swap_debugfs_create_dir(PRELOAD_TARGET, root);
+ if (IS_ERR_OR_NULL(target_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ target_list = swap_debugfs_create_file(PRELOAD_BINARIES_LIST,
+ PRELOAD_DEFAULT_PERMS,
+ target_path,
+ NULL, &bin_list_file_ops);
+ if (IS_ERR_OR_NULL(target_list)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ target_add = swap_debugfs_create_file(PRELOAD_BINARIES_ADD,
+ PRELOAD_DEFAULT_PERMS,
+ target_path,
+ NULL, &bin_add_file_ops);
+ if (IS_ERR_OR_NULL(target_add)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ target_remove = swap_debugfs_create_file(PRELOAD_BINARIES_REMOVE,
+ PRELOAD_DEFAULT_PERMS,
+ target_path,
+ NULL, &bin_remove_file_ops);
+ if (IS_ERR_OR_NULL(target_remove)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ ignored_path = swap_debugfs_create_dir(PRELOAD_IGNORED, root);
+ if (IS_ERR_OR_NULL(ignored_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ ignored_list = swap_debugfs_create_file(PRELOAD_BINARIES_LIST,
+ PRELOAD_DEFAULT_PERMS,
+ ignored_path,
+ NULL, &bin_list_file_ops);
+ if (IS_ERR_OR_NULL(ignored_list)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ ignored_add = swap_debugfs_create_file(PRELOAD_BINARIES_ADD,
+ PRELOAD_DEFAULT_PERMS,
+ ignored_path,
+ NULL, &bin_add_file_ops);
+ if (IS_ERR_OR_NULL(ignored_add)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ ignored_remove = swap_debugfs_create_file(PRELOAD_BINARIES_REMOVE,
+ PRELOAD_DEFAULT_PERMS,
+ ignored_path, NULL,
+ &bin_remove_file_ops);
+ if (IS_ERR_OR_NULL(ignored_remove)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ by_path = swap_debugfs_create_dir(PRELOAD_BY_PATH, root);
+ if (IS_ERR_OR_NULL(by_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
+ by_path, NULL, &by_path_add_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
+ by_path, NULL, &by_path_del_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ by_pid = swap_debugfs_create_dir(PRELOAD_BY_PID, root);
+ if (IS_ERR_OR_NULL(by_pid)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
+ by_pid, NULL, &by_pid_add_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
+ by_pid, NULL, &by_pid_del_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ by_id = swap_debugfs_create_dir(PRELOAD_BY_ID, root);
+ if (IS_ERR_OR_NULL(by_id)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
+ by_id, NULL, &by_id_add_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
+ by_id, NULL, &by_id_del_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_DEL_ALL,
+ PRELOAD_DEFAULT_PERMS,
+ root, NULL, &del_all_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_ENABLE, PRELOAD_DEFAULT_PERMS,
+ root, NULL, &enable_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ pthread = swap_debugfs_create_dir(PRELOAD_PTHREAD, root);
+ if (IS_ERR_OR_NULL(pthread)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_PATH, PRELOAD_DEFAULT_PERMS,
+ pthread, NULL, &pthread_path_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ dentry = swap_debugfs_create_file(PRELOAD_MINIMAL_INIT,
+ PRELOAD_DEFAULT_PERMS, pthread, NULL,
+ &pthread_init_off_fops);
+ if (IS_ERR_OR_NULL(dentry)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+
+ debugfs_remove_recursive(root);
+
+fail:
+ printk(PRELOAD_PREFIX "Debugfs initialization failure: %d\n", ret);
+
+ return ret;
+}
+
+void pd_exit(void)
+{
+ if (preload_root)
+ debugfs_remove_recursive(preload_root);
+ target_list = NULL;
+ target_add = NULL;
+ target_remove = NULL;
+ ignored_list = NULL;
+ ignored_add = NULL;
+ ignored_remove = NULL;
+ preload_root = NULL;
+}
--- /dev/null
+#ifndef __PRELOAD_HANDLERS_DEBUGFS_H__
+#define __PRELOAD_HANDLERS_DEBUGFS_H__
+
+struct dentry;
+
+int pd_init(void);
+void pd_exit(void);
+
+#endif /* __PRELOAD_HANDLERS_DEBUGFS_H__ */
--- /dev/null
+#include <linux/bug.h>
+#include <linux/slab.h>
+#include <linux/namei.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <writer/kernel_operations.h>
+#include <writer/swap_msg.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+#include <us_manager/us_common_file.h>
+#include <loader/loader.h>
+#include <master/swap_initializer.h>
+#include "preload.h"
+#include "preload_module.h"
+#include "preload_debugfs.h"
+#include "preload_control.h"
+#include "preload_threads.h"
+#include "preload_process.h"
+
+enum {
+ /* task preload flags */
+ HANDLER_RUNNING = 0x1
+};
+
+struct user_ptrs {
+ char *caller;
+ char *orig;
+ char *call_type;
+};
+
+static enum preload_status _status = PRELOAD_OFF;
+static DEFINE_MUTEX(status_change);
+
+
+static inline bool _is_enable_no_lock(void)
+{
+ return _status;
+}
+
+static inline struct vm_area_struct *__get_vma_by_addr(struct task_struct *task,
+ unsigned long caddr)
+{
+ struct vm_area_struct *vma = NULL;
+
+ if ((task == NULL) || (task->mm == NULL))
+ return NULL;
+ vma = find_vma_intersection(task->mm, caddr, caddr + 1);
+
+ return vma;
+}
+
+static inline bool __is_probe_non_block(struct sspt_ip *ip)
+{
+ if (ip->desc->info.pl_i.flags & SWAP_PRELOAD_NON_BLOCK_PROBE)
+ return true;
+
+ return false;
+}
+
+static inline bool __inverted(struct sspt_ip *ip)
+{
+ unsigned long flags = ip->desc->info.pl_i.flags;
+
+ if (flags & SWAP_PRELOAD_INVERTED_PROBE)
+ return true;
+
+ return false;
+}
+
+static inline bool __check_flag_and_call_type(struct sspt_ip *ip,
+ enum preload_call_type ct)
+{
+ bool inverted = __inverted(ip);
+
+ if (ct != NOT_INSTRUMENTED || inverted)
+ return true;
+
+ return false;
+}
+
+static inline bool __is_handlers_call(struct vm_area_struct *caller,
+ struct pd_t *pd)
+{
+ struct hd_t *hd;
+
+ if (caller == NULL || caller->vm_file == NULL ||
+ caller->vm_file->f_path.dentry == NULL) {
+ return false;
+ }
+
+ hd = lpd_get_hd(pd, caller->vm_file->f_path.dentry);
+ if (hd != NULL)
+ return true;
+
+ return false;
+}
+
+static inline bool __should_drop(struct sspt_ip *ip, enum preload_call_type ct)
+{
+ if (ct == NOT_INSTRUMENTED)
+ return true;
+
+ return false;
+}
+
+static inline int __msg_sanitization(char *user_msg, size_t len,
+ struct user_ptrs *ptrs)
+{
+ if (ptrs->caller &&
+ (ptrs->caller < user_msg || ptrs->caller > user_msg + len))
+ return -EINVAL;
+
+ if (ptrs->orig &&
+ (ptrs->orig < user_msg || ptrs->orig > user_msg + len))
+ return -EINVAL;
+
+ if (ptrs->call_type &&
+ (ptrs->call_type < user_msg || ptrs->call_type > user_msg + len))
+ return -EINVAL;
+
+ return 0;
+}
+
+
+
+
+static unsigned long __do_preload_entry(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ struct hd_t *hd)
+{
+ struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
+ unsigned long offset = ip->desc->info.pl_i.handler;
+ unsigned long vaddr = 0;
+ unsigned long base;
+ struct vm_area_struct *cvma;
+ struct pd_t *pd;
+ struct pt_data_t ptd;
+
+ base = lpd_get_handlers_base(hd);
+ if (base == 0)
+ return 0; /* handlers isn't mapped */
+
+ /* jump to preloaded handler */
+ vaddr = base + offset;
+ ptd.caller = get_regs_ret_func(regs);
+ cvma = __get_vma_by_addr(current, ptd.caller);
+ ptd.call_type = pc_call_type(ip, (void *)ptd.caller);
+ ptd.disable_addr = __is_probe_non_block(ip) ? ip->orig_addr : 0;
+ ptd.orig = ip->orig_addr;
+ pd = lpd_get_parent_pd(hd);
+
+ /* jump only if caller is instumented and it is not a system lib -
+ * this leads to some errors
+ */
+ if (cvma != NULL && cvma->vm_file != NULL &&
+ !pc_check_dentry_is_ignored(cvma->vm_file->f_path.dentry) &&
+ __check_flag_and_call_type(ip, ptd.call_type) &&
+ !__is_handlers_call(cvma, pd)) {
+
+ ptd.drop = __should_drop(ip, ptd.call_type);
+ if (pt_set_data(current, &ptd) != 0)
+ printk(PRELOAD_PREFIX "Error! Failed to store data for %d/%d\n",
+ current->tgid, current->pid);
+ /* args are not changed */
+ loader_module_prepare_ujump(ri, regs, vaddr);
+ if (ptd.disable_addr == 0)
+ pt_set_flags(current, HANDLER_RUNNING);
+ }
+
+ return vaddr;
+}
+
+static int preload_us_entry(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+ struct hd_t *hd;
+ unsigned long old_pc = swap_get_upc(regs);
+ unsigned long flags = pt_get_flags(current);
+ struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
+ unsigned long vaddr = 0;
+ struct dentry *dentry = ip->desc->info.pl_i.dentry;
+
+ if (dentry == NULL)
+ goto out_set_orig;
+
+ if ((flags & HANDLER_RUNNING) ||
+ pt_check_disabled_probe(current, ip->orig_addr))
+ goto out_set_orig;
+
+ hd = lpd_get_hd(pd, dentry);
+ if (hd == NULL)
+ goto out_set_orig;
+
+ if ((lpd_get_state(hd) == NOT_LOADED ||
+ lpd_get_state(hd) == FAILED) && lpd_get_init_state(pd))
+ vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
+ else if (lpd_get_state(hd) == LOADED)
+ vaddr =__do_preload_entry(ri, regs, hd);
+
+out_set_orig:
+ loader_set_priv_origin(ri, vaddr);
+
+ /* PC change check */
+ return old_pc != swap_get_upc(regs);
+}
+
+static void __do_preload_ret(struct uretprobe_instance *ri, struct hd_t *hd)
+{
+ struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
+ unsigned long flags = pt_get_flags(current);
+ unsigned long offset = ip->desc->info.pl_i.handler;
+ unsigned long vaddr = 0;
+
+ if ((flags & HANDLER_RUNNING) ||
+ pt_check_disabled_probe(current, ip->orig_addr)) {
+ bool non_blk_probe = __is_probe_non_block(ip);
+
+ /* drop the flag if the handler has completed */
+ vaddr = lpd_get_handlers_base(hd) + offset;
+ if (vaddr && (loader_get_priv_origin(ri) == vaddr)) {
+ if (pt_put_data(current) != 0)
+ printk(PRELOAD_PREFIX "Error! Failed to put "
+ "caller slot for %d/%d\n", current->tgid,
+ current->pid);
+ if (!non_blk_probe) {
+ flags &= ~HANDLER_RUNNING;
+ pt_set_flags(current, flags);
+ }
+ }
+ }
+}
+
+static int preload_us_ret(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+ struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
+ struct dentry *dentry = ip->desc->info.pl_i.dentry;
+ struct hd_t *hd;
+
+ if (dentry == NULL)
+ return 0;
+
+ hd = lpd_get_hd(pd, dentry);
+ if (hd == NULL)
+ return 0;
+
+ switch (lpd_get_state(hd)) {
+ case NOT_LOADED:
+ /* loader has not yet been mapped... just ignore */
+ break;
+ case LOADING:
+ loader_loading_ret(ri, regs, pd, hd);
+ break;
+ case LOADED:
+ __do_preload_ret(ri, hd);
+ break;
+ case FAILED:
+ loader_failed_ret(ri, regs, pd, hd);
+ break;
+ case ERROR:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+
+
+
+static void __write_data_on_demand(char *user, char *msg, size_t len,
+ struct user_ptrs *ptrs,
+ unsigned long caller_addr)
+{
+ unsigned long caller_ptr = 0, orig_ptr = 0, ct_ptr = 0;
+ unsigned long caller = 0, orig = 0;
+ unsigned char call_type = 0;
+ int ret;
+
+ /* Evaluate addresses whereto write data as:
+ * pointer from user - user buffer beggining + kernel buffer beginning
+ * If incoming pointer is NULL - do not write
+ */
+
+ if (ptrs->orig) {
+ orig_ptr = (unsigned long)(ptrs->orig - user + msg);
+
+ ret = pt_get_orig(current, &orig);
+ if (ret) {
+ orig = 0xbadbeef;
+ printk(PRELOAD_PREFIX "No orig for %d/%d\n",
+ current->tgid, current->pid);
+ }
+
+ /* Types should be the same as in preload lib!!! */
+ *(uint64_t *)orig_ptr = (uint64_t)orig;
+ }
+
+ if (caller_addr && ptrs->caller && ptrs->call_type) {
+ caller_ptr = (unsigned long)(ptrs->caller - user + msg);
+ ct_ptr = (unsigned long)(ptrs->call_type - user + msg);
+
+ caller = caller_addr;
+ call_type =
+ pc_call_type_always_inst((void *)caller);
+
+ /* Types should be the same as in preload lib!!! */
+ *(uint64_t *)caller_ptr = (uint64_t)caller;
+ *(uint32_t *)ct_ptr = (uint32_t)call_type;
+
+ return;
+ }
+
+ if (ptrs->caller) {
+ caller_ptr = (unsigned long)(ptrs->caller - user + msg);
+
+ ret = pt_get_caller(current, &caller);
+ if (ret) {
+ caller = 0xbadbeef;
+ printk(PRELOAD_PREFIX "No caller for %d/%d\n",
+ current->tgid, current->pid);
+ }
+
+ /* Types should be the same as in preload lib!!! */
+ *(uint64_t *)caller_ptr = (uint64_t)caller;
+ }
+
+ if (ptrs->call_type) {
+ ct_ptr = (unsigned long)(ptrs->call_type - user + msg);
+
+ ret = pt_get_call_type(current, &call_type);
+ if (ret) {
+ call_type = 0xff;
+ printk(PRELOAD_PREFIX "No call type for %d/%d\n",
+ current->tgid, current->pid);
+ }
+
+ /* Types should be the same as in preload lib!!! */
+ *(uint32_t *)ct_ptr = (uint32_t)call_type;
+ }
+}
+
+static int write_msg_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct user_ptrs ptrs;
+ char *user_buf, *buf;
+ size_t len;
+ unsigned long caller_addr;
+ int ret;
+
+ /* FIXME: swap_get_uarg uses get_user(), it might sleep */
+ user_buf = (char *)swap_get_uarg(regs, 0);
+ len = swap_get_uarg(regs, 1);
+ ptrs.call_type = (char *)swap_get_uarg(regs, 2);
+ ptrs.caller = (char *)swap_get_uarg(regs, 3);
+ caller_addr = swap_get_uarg(regs, 4);
+ ptrs.orig = (char *)swap_get_uarg(regs, 5);
+
+ if (ptrs.caller || ptrs.call_type || ptrs.orig) {
+ ret = __msg_sanitization(user_buf, len, &ptrs);
+ if (ret != 0) {
+ printk(PRELOAD_PREFIX "Invalid message pointers!\n");
+ return 0;
+ }
+ ret = pt_get_drop(current);
+ if (ret > 0)
+ return 0;
+ }
+
+ buf = kmalloc(len, GFP_ATOMIC);
+ if (buf == NULL) {
+ printk(PRELOAD_PREFIX "No mem for buffer! Size = %zd\n", len);
+ return 0;
+ }
+
+ ret = read_proc_vm_atomic(current, (unsigned long)user_buf, buf, len);
+ if (ret < 0) {
+ printk(PRELOAD_PREFIX "Cannot copy data from userspace! Size = "
+ "%zd ptr 0x%lx ret %d\n", len,
+ (unsigned long)user_buf, ret);
+ goto write_msg_fail;
+ }
+
+ __write_data_on_demand(user_buf, buf, len, &ptrs, caller_addr);
+
+ ret = swap_msg_raw(buf, len);
+ if (ret != len)
+ printk(PRELOAD_PREFIX "Error writing probe lib message\n");
+
+write_msg_fail:
+ kfree(buf);
+
+ return 0;
+}
+
+static int get_caller_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ unsigned long caller;
+ int ret;
+
+ ret = pt_get_caller(current, &caller);
+ if (ret != 0) {
+ caller = 0xbadbeef;
+ printk(PRELOAD_PREFIX "Error! Cannot get caller address for "
+ "%d/%d\n", current->tgid, current->pid);
+ }
+
+ swap_put_uarg(regs, 0, caller);
+
+ return 0;
+}
+
+static int get_call_type_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ unsigned char call_type;
+ int ret;
+
+ ret = pt_get_call_type(current, &call_type);
+ if (ret != 0) {
+ call_type = 0xff;
+ printk(PRELOAD_PREFIX "Error! Cannot get call type for %d/%d\n",
+ current->tgid, current->pid);
+ }
+
+ swap_put_uarg(regs, 0, call_type);
+
+ return 0;
+}
+
+
+
+
+
+int pm_get_caller_init(struct sspt_ip *ip)
+{
+ struct uprobe *up = &ip->uprobe;
+
+ up->pre_handler = get_caller_handler;
+
+ return 0;
+}
+
+void pm_get_caller_exit(struct sspt_ip *ip)
+{
+}
+
+int pm_get_call_type_init(struct sspt_ip *ip)
+{
+ struct uprobe *up = &ip->uprobe;
+
+ up->pre_handler = get_call_type_handler;
+
+ return 0;
+}
+
+void pm_get_call_type_exit(struct sspt_ip *ip)
+{
+}
+
+int pm_write_msg_init(struct sspt_ip *ip)
+{
+ struct uprobe *up = &ip->uprobe;
+
+ up->pre_handler = write_msg_handler;
+
+ return 0;
+}
+
+void pm_write_msg_exit(struct sspt_ip *ip)
+{
+}
+
+
+
+int pm_uprobe_init(struct sspt_ip *ip)
+{
+ struct uretprobe *rp = &ip->retprobe;
+ struct dentry *dentry;
+ const char *path = ip->desc->info.pl_i.path;
+
+ rp->entry_handler = preload_us_entry;
+ rp->handler = preload_us_ret;
+
+ /* Get dentry and set it in probe info struct */
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL) {
+ pr_warn(PRELOAD_PREFIX "Error! Cannot get handler %s\n", path);
+ return -EINVAL;
+ }
+ ip->desc->info.pl_i.dentry = dentry;
+
+ /* Add handler to loader */
+ loader_add_handler(path);
+
+ /* FIXME actually additional data_size is needed only when we jump
+ * to dlopen */
+ loader_set_rp_data_size(rp);
+
+ return 0;
+}
+
+void pm_uprobe_exit(struct sspt_ip *ip)
+{
+ struct dentry *dentry = ip->desc->info.pl_i.dentry;
+
+ WARN_ON(!dentry);
+
+ if (dentry)
+ swap_put_dentry(dentry);
+}
+
+int pm_switch(enum preload_status stat)
+{
+ int ret = 0;
+
+ mutex_lock(&status_change);
+ switch (stat) {
+ case PRELOAD_ON:
+ if (_is_enable_no_lock())
+ goto pm_switch_unlock;
+
+ ret = pp_enable();
+ if (!ret)
+ _status = PRELOAD_ON;
+ break;
+ case PRELOAD_OFF:
+ if (!_is_enable_no_lock())
+ goto pm_switch_unlock;
+
+ pp_disable();
+ _status = PRELOAD_OFF;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+pm_switch_unlock:
+ mutex_unlock(&status_change);
+
+ return ret;
+}
+
+enum preload_status pm_status(void)
+{
+ enum preload_status s;
+
+ mutex_lock(&status_change);
+ s = _status;
+ mutex_unlock(&status_change);
+
+ return s;
+}
+
+static int pm_init(void)
+{
+ int ret;
+
+ ret = pd_init();
+ if (ret)
+ goto out_err;
+
+ ret = pc_init();
+ if (ret)
+ goto control_init_fail;
+
+ ret = pt_init();
+ if (ret)
+ goto threads_init_fail;
+
+ ret = register_preload_probes();
+ if (ret)
+ goto probes_register_fail;
+
+ return 0;
+
+probes_register_fail:
+ pt_exit();
+
+threads_init_fail:
+ pc_exit();
+
+control_init_fail:
+ pd_exit();
+
+out_err:
+ return ret;
+}
+
+static void pm_exit(void)
+{
+ unregister_preload_probes();
+ pt_exit();
+ pc_exit();
+ pd_exit();
+}
+
+SWAP_LIGHT_INIT_MODULE(NULL, pm_init, pm_exit, NULL, NULL);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP Preload Module");
+MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>");
--- /dev/null
+#ifndef __PRELOAD_MODULE_H__
+#define __PRELOAD_MODULE_H__
+
+struct sspt_ip;
+struct dentry;
+
+
+enum preload_status {
+ PRELOAD_ON,
+ PRELOAD_OFF
+};
+
+enum preload_status pm_status(void);
+int pm_switch(enum preload_status stat);
+
+int pm_uprobe_init(struct sspt_ip *ip);
+void pm_uprobe_exit(struct sspt_ip *ip);
+
+int pm_get_caller_init(struct sspt_ip *ip);
+void pm_get_caller_exit(struct sspt_ip *ip);
+int pm_get_call_type_init(struct sspt_ip *ip);
+void pm_get_call_type_exit(struct sspt_ip *ip);
+int pm_write_msg_init(struct sspt_ip *ip);
+void pm_write_msg_exit(struct sspt_ip *ip);
+
+#endif /* __PRELOAD_MODULE_H__ */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/preload_probe.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: Preload implement
+ *
+ */
+
+#include <linux/module.h>
+#include <us_manager/us_manager.h>
+#include <us_manager/probes/register_probes.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <uprobe/swap_uprobes.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include "preload_probe.h"
+#include "preload.h"
+#include "preload_module.h"
+
+static int preload_info_copy(struct probe_info *dest,
+ const struct probe_info *source)
+{
+ memcpy(dest, source, sizeof(*source));
+
+ return 0;
+}
+
+static void preload_info_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *preload_get_uprobe(struct sspt_ip *ip)
+{
+ return &ip->retprobe.up;
+}
+
+/* Registers probe if preload is 'running' or 'ready'.
+ */
+static int preload_register_probe(struct sspt_ip *ip)
+{
+ return swap_register_uretprobe(&ip->retprobe);
+}
+
+static void preload_unregister_probe(struct sspt_ip *ip, int disarm)
+{
+ __swap_unregister_uretprobe(&ip->retprobe, disarm);
+}
+
+static void preload_init(struct sspt_ip *ip)
+{
+ pm_uprobe_init(ip);
+}
+
+static void preload_uninit(struct sspt_ip *ip)
+{
+ pm_uprobe_exit(ip);
+
+ preload_info_cleanup(&ip->desc->info);
+}
+
+static struct probe_iface preload_iface = {
+ .init = preload_init,
+ .uninit = preload_uninit,
+ .reg = preload_register_probe,
+ .unreg = preload_unregister_probe,
+ .get_uprobe = preload_get_uprobe,
+ .copy = preload_info_copy,
+ .cleanup = preload_info_cleanup
+};
+
+static int get_caller_info_copy(struct probe_info *dest,
+ const struct probe_info *source)
+{
+ memcpy(dest, source, sizeof(*source));
+
+ return 0;
+}
+
+static void get_caller_info_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *get_caller_get_uprobe(struct sspt_ip *ip)
+{
+ return &ip->uprobe;
+}
+
+static int get_caller_register_probe(struct sspt_ip *ip)
+{
+ return swap_register_uprobe(&ip->uprobe);
+}
+
+static void get_caller_unregister_probe(struct sspt_ip *ip, int disarm)
+{
+ __swap_unregister_uprobe(&ip->uprobe, disarm);
+}
+
+static void get_caller_init(struct sspt_ip *ip)
+{
+ pm_get_caller_init(ip);
+}
+
+static void get_caller_uninit(struct sspt_ip *ip)
+{
+ pm_get_caller_exit(ip);
+
+ get_caller_info_cleanup(&ip->desc->info);
+}
+
+static struct probe_iface get_caller_iface = {
+ .init = get_caller_init,
+ .uninit = get_caller_uninit,
+ .reg = get_caller_register_probe,
+ .unreg = get_caller_unregister_probe,
+ .get_uprobe = get_caller_get_uprobe,
+ .copy = get_caller_info_copy,
+ .cleanup = get_caller_info_cleanup
+};
+
+static void get_call_type_init(struct sspt_ip *ip)
+{
+ pm_get_call_type_init(ip);
+}
+
+static void get_call_type_uninit(struct sspt_ip *ip)
+{
+ pm_get_call_type_exit(ip);
+
+ get_caller_info_cleanup(&ip->desc->info);
+}
+
+static struct probe_iface get_call_type_iface = {
+ .init = get_call_type_init,
+ .uninit = get_call_type_uninit,
+ .reg = get_caller_register_probe,
+ .unreg = get_caller_unregister_probe,
+ .get_uprobe = get_caller_get_uprobe,
+ .copy = get_caller_info_copy,
+ .cleanup = get_caller_info_cleanup
+};
+
+static void write_msg_init(struct sspt_ip *ip)
+{
+ pm_write_msg_init(ip);
+}
+
+static int write_msg_reg(struct sspt_ip *ip)
+{
+ return get_caller_register_probe(ip);
+}
+
+static void write_msg_uninit(struct sspt_ip *ip)
+{
+ pm_write_msg_exit(ip);
+
+ get_caller_info_cleanup(&ip->desc->info);
+}
+
+static struct probe_iface write_msg_iface = {
+ .init = write_msg_init,
+ .uninit = write_msg_uninit,
+ .reg = write_msg_reg,
+ .unreg = get_caller_unregister_probe,
+ .get_uprobe = get_caller_get_uprobe,
+ .copy = get_caller_info_copy,
+ .cleanup = get_caller_info_cleanup
+};
+
+int register_preload_probes(void)
+{
+ int ret;
+
+ ret = swap_register_probe_type(SWAP_PRELOAD_PROBE, &preload_iface);
+ if (ret != 0)
+ return ret;
+
+ ret = swap_register_probe_type(SWAP_GET_CALLER, &get_caller_iface);
+ if (ret != 0)
+ return ret;
+
+ ret = swap_register_probe_type(SWAP_GET_CALL_TYPE, &get_call_type_iface);
+ if (ret != 0)
+ return ret;
+
+ ret = swap_register_probe_type(SWAP_WRITE_MSG, &write_msg_iface);
+
+ return ret;
+}
+
+void unregister_preload_probes(void)
+{
+ swap_unregister_probe_type(SWAP_PRELOAD_PROBE);
+ swap_unregister_probe_type(SWAP_GET_CALLER);
+ swap_unregister_probe_type(SWAP_GET_CALL_TYPE);
+ swap_unregister_probe_type(SWAP_WRITE_MSG);
+}
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/preload_probe.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: FBI implement
+ *
+ */
+
+#ifndef __PRELOAD_HANDLERS_PROBE_H__
+#define __PRELOAD_HANDLERS_PROBE_H__
+
+/* Probe flags description:
+ *
+ * 0 - handler is ran only when probe has fired from a target binary;
+ * 1 - handler is always ran;
+ *
+ * 00 - probe is disabling internal probes;
+ * 10 - probe is non blocking one;
+ *
+ * 000 - probe is executed for instrumented binaries
+ * 100 - probe is executed for non-instrumented binaries
+ */
+
+enum {
+ SWAP_PRELOAD_ALWAYS_RUN = (1 << 0),
+ SWAP_PRELOAD_NON_BLOCK_PROBE = (1 << 1),
+ SWAP_PRELOAD_INVERTED_PROBE = (1 << 2)
+};
+
+/* Preload probe info. */
+struct preload_info {
+ unsigned long handler; /* Handler offset in probe library. */
+ unsigned char flags; /* Preload probe flags. */
+ const char *path; /* Library with handler */
+ struct dentry *dentry; /* Handler file dentry */
+};
+
+/* Get caller probe info */
+struct get_caller_info {
+};
+
+/* Get call type probe info */
+struct get_call_type_info {
+};
+
+/* Write message probe info */
+struct write_msg_info {
+};
+
+int register_preload_probes(void);
+void unregister_preload_probes(void);
+
+#endif /* __PRELOAD_HANDLERS_PROBE_H__ */
--- /dev/null
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <us_manager/us_common_file.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/probes/probe_info_new.h>
+#include <loader/loader.h>
+#include "preload.h"
+#include "preload_process.h"
+
+
+enum task_id_t {
+ SET_BY_PATH,
+ SET_BY_PID,
+ SET_BY_ID
+};
+
+struct process_t {
+ struct list_head list;
+ struct pf_group *pfg;
+ enum task_id_t idt;
+ union {
+ struct dentry *dentry;
+ pid_t pid;
+ char *id;
+ };
+ struct probe_new p_init;
+};
+
+struct bin_data_t {
+ struct dentry *dentry;
+ unsigned long off;
+};
+
+/* For process list elements checker */
+typedef struct process_t *(*checker_t)(struct process_t *, void *);
+
+static LIST_HEAD(_reg_proc_list);
+static LIST_HEAD(_unreg_proc_list);
+static DEFINE_MUTEX(_proc_list_lock);
+
+static struct bin_data_t _pthread_init;
+
+static inline void _lock_proc_list(void)
+{
+ mutex_lock(&_proc_list_lock);
+}
+
+static inline void _unlock_proc_list(void)
+{
+ mutex_unlock(&_proc_list_lock);
+}
+
+static inline struct process_t *_check_by_dentry(struct process_t *proc,
+ void *data)
+{
+ struct dentry *dentry = data;
+
+ if (proc->idt == SET_BY_PATH && proc->dentry == dentry)
+ return proc;
+
+ return NULL;
+}
+
+static inline struct process_t *_check_by_pid(struct process_t *proc,
+ void *data)
+{
+ pid_t pid = *(pid_t *)data;
+
+ if (proc->idt == SET_BY_PID && proc->pid == pid)
+ return proc;
+
+ return NULL;
+}
+
+static inline struct process_t *_check_by_id(struct process_t *proc, void *data)
+{
+ char *id = data;
+
+ if (proc->idt == SET_BY_ID && proc->id == id)
+ return proc;
+
+ return NULL;
+}
+
+static inline bool _is_pthread_data_available(void)
+{
+ return (_pthread_init.dentry && _pthread_init.off);
+}
+
+
+
+
+static int pthread_init_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+
+ lpd_set_init_state(pd, false);
+
+ return 0;
+}
+
+static int pthread_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+
+ lpd_set_init_state(pd, true);
+
+ return 0;
+}
+
+static struct probe_desc pin_pinit = MAKE_URPROBE(pthread_init_eh,
+ pthread_init_rh, 0);
+
+static int _proc_reg_no_lock(struct process_t *proc)
+{
+ int ret;
+
+ proc->p_init.desc = &pin_pinit;
+ proc->p_init.offset = _pthread_init.off;
+ ret = pin_register(&proc->p_init, proc->pfg, _pthread_init.dentry);
+ if (ret)
+ pr_warn(PRELOAD_PREFIX "Can't register pthread init probe\n");
+
+ return ret;
+}
+
+static void _proc_unreg_no_lock(struct process_t *proc)
+{
+ pin_unregister(&proc->p_init, proc->pfg);
+}
+
+static struct process_t *_proc_create(struct pf_group *pfg)
+{
+ struct process_t *proc;
+
+ proc = kzalloc(sizeof(*proc), GFP_KERNEL);
+ if (!proc) {
+ pr_warn(PRELOAD_PREFIX "No mem to alloc proccess_t struct!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ INIT_LIST_HEAD(&proc->list);
+ proc->pfg = pfg;
+
+ return proc;
+}
+
+static void _proc_destroy(struct process_t *proc)
+{
+ if (proc->pfg)
+ put_pf_group(proc->pfg);
+
+ if (proc->idt == SET_BY_PATH)
+ swap_put_dentry(proc->dentry);
+
+ if (proc->idt == SET_BY_ID)
+ kfree(proc->id);
+
+ kfree(proc);
+}
+
+static int _add_to_list(struct process_t *proc)
+{
+ _lock_proc_list();
+ list_add_tail(&proc->list, &_unreg_proc_list);
+ _unlock_proc_list();
+
+ return 0;
+}
+
+/* checker - used to check process list candidates, chosen by type of
+ * registration (path, pid, id)
+ * data - pointer to target process data, checker compares candidate with
+ */
+
+static struct process_t *_get_from_list_no_lock(checker_t checker, void *data,
+ struct list_head *list)
+{
+ struct process_t *proc;
+
+ list_for_each_entry(proc, list, list) {
+ if (checker(proc, data))
+ return proc;
+ }
+
+ return NULL;
+}
+
+static void _delete_from_list(checker_t checker, void *data)
+{
+ struct process_t *proc;
+
+ _lock_proc_list();
+ proc = _get_from_list_no_lock(checker, data, &_unreg_proc_list);
+ if (proc) {
+ list_del(&proc->list);
+ _proc_destroy(proc);
+ goto delete_from_list_unlock;
+ }
+
+ proc = _get_from_list_no_lock(checker, data, &_reg_proc_list);
+ if (proc) {
+ list_del(&proc->list);
+ _proc_unreg_no_lock(proc);
+ _proc_destroy(proc);
+ }
+
+delete_from_list_unlock:
+ _unlock_proc_list();
+}
+
+
+
+int pp_add_by_path(const char *path)
+{
+ struct dentry *dentry;
+ struct pf_group *pfg;
+ struct process_t *proc;
+ int ret;
+
+ dentry = swap_get_dentry(path);
+ if (!dentry) {
+ pr_warn(PRELOAD_PREFIX "Can't get dentry for <%s>\n", path);
+ return -EINVAL;
+ }
+
+ pfg = get_pf_group_by_dentry(dentry, dentry);
+ if (pfg == NULL) {
+ ret = -ENOMEM;
+ goto by_path_put_dentry;
+ }
+
+ proc = _proc_create(pfg);
+ if (!proc) {
+ ret = -EINVAL;
+ goto by_path_put_pfg;
+ }
+
+ proc->idt = SET_BY_PATH;
+ proc->dentry = dentry;
+
+ ret = _add_to_list(proc);
+ if (ret)
+ goto by_path_free;
+
+ return 0;
+
+by_path_free:
+ kfree(proc);
+
+by_path_put_pfg:
+ put_pf_group(pfg);
+
+by_path_put_dentry:
+ swap_put_dentry(dentry);
+
+ return ret;
+}
+
+int pp_add_by_pid(pid_t pid)
+{
+ struct pf_group *pfg;
+ struct process_t *proc;
+ int ret;
+
+ pfg = get_pf_group_by_tgid(pid, NULL);
+ if (pfg == NULL)
+ return -ENOMEM;
+
+ proc = _proc_create(pfg);
+ if (!proc) {
+ ret = -EINVAL;
+ goto by_pid_put_pfg;
+ }
+
+ proc->idt = SET_BY_PID;
+ proc->pid = pid;
+
+ ret = _add_to_list(proc);
+ if (ret)
+ goto by_pid_free;
+
+ return 0;
+
+by_pid_free:
+ kfree(proc);
+
+by_pid_put_pfg:
+ put_pf_group(pfg);
+
+ return ret;
+}
+
+int pp_add_by_id(const char *id)
+{
+ char *new_id;
+ struct pf_group *pfg;
+ struct process_t *proc;
+ int ret;
+
+ new_id = kstrdup(id, GFP_KERNEL);
+ if (!new_id)
+ return -ENOMEM;
+
+ pfg = get_pf_group_by_comm((char *)id, NULL);
+ if (pfg == NULL) {
+ ret = -ENOMEM;
+ goto by_id_free_new_id;
+ }
+
+ proc = _proc_create(pfg);
+ if (!proc) {
+ ret = -EINVAL;
+ goto by_id_put_pfg;
+ }
+
+ proc->idt = SET_BY_ID;
+ proc->id = new_id;
+
+ ret = _add_to_list(proc);
+ if (ret)
+ goto by_id_free;
+
+ return 0;
+
+by_id_free:
+ kfree(proc);
+
+by_id_put_pfg:
+ put_pf_group(pfg);
+
+by_id_free_new_id:
+ kfree(new_id);
+
+ return ret;
+}
+
+int pp_del_by_path(const char *path)
+{
+ struct dentry *dentry;
+
+ dentry = swap_get_dentry(path);
+ if (!dentry) {
+ pr_warn(PRELOAD_PREFIX "No dentry for <%s>\n", path);
+ return -EINVAL;
+ }
+
+ _delete_from_list(_check_by_dentry, dentry);
+
+ swap_put_dentry(dentry);
+
+ return 0;
+}
+
+int pp_del_by_pid(pid_t pid)
+{
+ _delete_from_list(_check_by_pid, &pid);
+
+ return 0;
+}
+
+int pp_del_by_id(const char *id)
+{
+ _delete_from_list(_check_by_id, (void *)id);
+
+ return 0;
+}
+
+void pp_del_all(void)
+{
+ struct process_t *tmp, *proc;
+
+ _lock_proc_list();
+
+ list_for_each_entry_safe(proc, tmp, &_unreg_proc_list, list) {
+ list_del(&proc->list);
+ _proc_destroy(proc);
+ }
+
+ list_for_each_entry_safe(proc, tmp, &_reg_proc_list, list) {
+ list_del(&proc->list);
+ _proc_unreg_no_lock(proc);
+ _proc_destroy(proc);
+ }
+
+ _unlock_proc_list();
+}
+
+void pp_disable(void)
+{
+ struct process_t *proc;
+
+ _lock_proc_list();
+
+ list_for_each_entry(proc, &_reg_proc_list, list) {
+ _proc_unreg_no_lock(proc);
+ list_move_tail(&proc->list, &_unreg_proc_list);
+ }
+
+ _unlock_proc_list();
+}
+
+int pp_enable(void)
+{
+ struct process_t *proc;
+ int ret;
+
+ if (!_is_pthread_data_available())
+ return -EINVAL;
+
+ _lock_proc_list();
+
+ list_for_each_entry(proc, &_unreg_proc_list, list) {
+ ret = _proc_reg_no_lock(proc);
+ if (ret)
+ goto enable_pp_fail;
+ else
+ list_move_tail(&proc->list, &_reg_proc_list);
+ }
+
+ _unlock_proc_list();
+
+ return 0;
+
+enable_pp_fail:
+ _unlock_proc_list();
+
+ pr_warn(PRELOAD_PREFIX "Error register probes, disabling...\n");
+ pp_disable();
+
+ return ret;
+}
+
+int pp_set_pthread_path(const char *path)
+{
+ struct dentry *dentry;
+
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL)
+ return -EINVAL;
+
+ if (_pthread_init.dentry != NULL)
+ swap_put_dentry(_pthread_init.dentry);
+
+ _pthread_init.dentry = dentry;
+
+ return 0;
+}
+
+int pp_set_init_offset(unsigned long off)
+{
+ _pthread_init.off = off;
+
+ return 0;
+}
--- /dev/null
+#ifndef __PRELOAD_PROCESS_H__
+#define __PRELOAD_PROCESS_H__
+
+int pp_add_by_path(const char *path);
+int pp_add_by_pid(pid_t pid);
+int pp_add_by_id(const char *id);
+
+int pp_del_by_path(const char *path);
+int pp_del_by_pid(pid_t pid);
+int pp_del_by_id(const char *id);
+void pp_del_all(void);
+
+int pp_enable(void);
+void pp_disable(void);
+
+int pp_set_pthread_path(const char *path);
+int pp_set_init_offset(unsigned long off);
+
+#endif /* __PRELOAD_PROCESS_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/list.h>
+#include <kprobe/swap_ktd.h>
+#include "preload.h"
+#include "preload_threads.h"
+
+struct preload_td {
+ struct list_head slots;
+ unsigned long flags;
+};
+
+struct thread_slot {
+ struct list_head list;
+ struct list_head disabled_addrs;
+
+ unsigned long caller;
+ unsigned long orig;
+ unsigned char call_type;
+ bool drop; /* TODO Workaround, remove when will be possible to install
+ * several us probes at the same addr. */
+};
+
+struct disabled_addr {
+ struct list_head list;
+ unsigned long addr;
+};
+
+static void preload_ktd_init(struct task_struct *task, void *data)
+{
+ struct preload_td *td = (struct preload_td *)data;
+
+ INIT_LIST_HEAD(&td->slots);
+ td->flags = 0;
+}
+
+static void preload_ktd_exit(struct task_struct *task, void *data)
+{
+ /* TODO: to be implement */
+}
+
+static struct ktask_data preload_ktd = {
+ .init = preload_ktd_init,
+ .exit = preload_ktd_exit,
+ .size = sizeof(struct preload_td),
+};
+
+
+static inline struct preload_td *get_preload_td(struct task_struct *task)
+{
+ return (struct preload_td *)swap_ktd(&preload_ktd, task);
+}
+
+unsigned long pt_get_flags(struct task_struct *task)
+{
+ return get_preload_td(task)->flags;
+}
+
+void pt_set_flags(struct task_struct *task, unsigned long flags)
+{
+ get_preload_td(task)->flags = flags;
+}
+
+
+static inline bool __is_addr_found(struct disabled_addr *da,
+ unsigned long addr)
+{
+ if (da->addr == addr)
+ return true;
+
+ return false;
+}
+
+static inline void __remove_from_disable_list(struct disabled_addr *da)
+{
+ list_del(&da->list);
+ kfree(da);
+}
+
+static inline void __remove_whole_disable_list(struct thread_slot *slot)
+{
+ struct disabled_addr *da, *n;
+
+ list_for_each_entry_safe(da, n, &slot->disabled_addrs, list)
+ __remove_from_disable_list(da);
+}
+
+static inline void __init_slot(struct thread_slot *slot)
+{
+ slot->caller = 0;
+ slot->orig = 0;
+ slot->call_type = 0;
+ slot->drop = false;
+ INIT_LIST_HEAD(&slot->disabled_addrs);
+}
+
+static inline void __reinit_slot(struct thread_slot *slot)
+{
+ __remove_whole_disable_list(slot);
+ __init_slot(slot);
+}
+
+static inline void __set_slot(struct thread_slot *slot,
+ struct task_struct *task, unsigned long caller,
+ unsigned long orig, unsigned char call_type,
+ bool drop)
+{
+ slot->caller = caller;
+ slot->orig = orig;
+ slot->call_type = call_type;
+ slot->drop = drop;
+}
+
+static inline int __add_to_disable_list(struct thread_slot *slot,
+ unsigned long disable_addr)
+{
+ struct disabled_addr *da = kmalloc(sizeof(*da), GFP_ATOMIC);
+
+ if (da == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&da->list);
+ da->addr = disable_addr;
+ list_add_tail(&da->list, &slot->disabled_addrs);
+
+ return 0;
+}
+
+static inline struct disabled_addr *__find_disabled_addr(struct thread_slot *slot,
+ unsigned long addr)
+{
+ struct disabled_addr *da;
+
+ list_for_each_entry(da, &slot->disabled_addrs, list)
+ if (__is_addr_found(da, addr))
+ return da;
+
+ return NULL;
+}
+
+/* Adds a new slot */
+static inline struct thread_slot *__grow_slot(void)
+{
+ struct thread_slot *tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);
+
+ if (tmp == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&tmp->list);
+ __init_slot(tmp);
+
+ return tmp;
+}
+
+/* Free slot */
+static void __clean_slot(struct thread_slot *slot)
+{
+ list_del(&slot->list);
+ kfree(slot);
+}
+
+/* There is no list_last_entry in Linux 3.10 */
+#ifndef list_last_entry
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+#endif /* list_last_entry */
+
+static inline struct thread_slot *__get_task_slot(struct task_struct *task)
+{
+ struct preload_td *td = get_preload_td(task);
+
+ return list_empty(&td->slots) ? NULL :
+ list_last_entry(&td->slots, struct thread_slot, list);
+}
+
+
+
+
+int pt_set_data(struct task_struct *task, struct pt_data_t *data)
+{
+ struct preload_td *td = get_preload_td(task);
+ struct thread_slot *slot;
+ unsigned long caller, disable_addr, orig;
+ unsigned char call_type;
+ bool drop;
+ int ret = 0;
+
+ caller = data->caller;
+ disable_addr = data->disable_addr;
+ orig = data->orig;
+ call_type = data->call_type;
+ drop = data->drop;
+
+ slot = __grow_slot();
+ if (slot == NULL) {
+ ret = -ENOMEM;
+ goto set_data_done;
+ }
+
+ if ((disable_addr != 0) &&
+ (__add_to_disable_list(slot, disable_addr) != 0)) {
+ printk(KERN_ERR PRELOAD_PREFIX "Cannot alloc memory!\n");
+ ret = -ENOMEM;
+ goto set_data_done;
+ }
+
+ __set_slot(slot, task, caller, orig, call_type, drop);
+ list_add_tail(&slot->list, &td->slots);
+
+set_data_done:
+ return ret;
+}
+
+int pt_get_caller(struct task_struct *task, unsigned long *caller)
+{
+ struct thread_slot *slot;
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ *caller = slot->caller;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int pt_get_orig(struct task_struct *task, unsigned long *orig)
+{
+ struct thread_slot *slot;
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ *orig = slot->caller;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int pt_get_call_type(struct task_struct *task,
+ unsigned char *call_type)
+{
+ struct thread_slot *slot;
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ *call_type = slot->call_type;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int pt_get_drop(struct task_struct *task)
+{
+ struct thread_slot *slot;
+
+ slot = __get_task_slot(task);
+ if (slot != NULL)
+ return (int)slot->drop;
+
+ return -EINVAL;
+}
+
+bool pt_check_disabled_probe(struct task_struct *task, unsigned long addr)
+{
+ struct thread_slot *slot;
+ bool ret = false;
+
+ slot = __get_task_slot(task);
+ if (slot != NULL)
+ ret = __find_disabled_addr(slot, addr) == NULL ? false : true;
+
+ return ret;
+}
+
+void pt_enable_probe(struct task_struct *task, unsigned long addr)
+{
+ struct thread_slot *slot;
+ struct disabled_addr *da;
+
+ slot = __get_task_slot(task);
+ if (slot == NULL) {
+ printk(KERN_ERR PRELOAD_PREFIX "Error! Slot not found!\n");
+ goto enable_probe_failed;
+ }
+
+ da = __find_disabled_addr(slot, addr);
+ if (da != NULL)
+ __remove_from_disable_list(da);
+
+enable_probe_failed:
+ return; /* make gcc happy: cannot place label right before '}' */
+}
+
+int pt_put_data(struct task_struct *task)
+{
+ struct thread_slot *slot;
+ int ret = 0;
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ __reinit_slot(slot);
+ __clean_slot(slot); /* remove from list */
+ }
+
+ return ret;
+}
+
+int pt_init(void)
+{
+ return swap_ktd_reg(&preload_ktd);
+}
+
+void pt_exit(void)
+{
+ swap_ktd_unreg(&preload_ktd);
+}
--- /dev/null
+#ifndef __PRELOAD_HANDLERS_THREADS_H__
+#define __PRELOAD_HANLDERS_THREADS_H__
+
+struct task_struct;
+
+struct pt_data_t {
+ unsigned long caller;
+ unsigned long disable_addr;
+ unsigned long orig;
+ unsigned char call_type;
+ bool drop;
+};
+
+unsigned long pt_get_flags(struct task_struct *task);
+void pt_set_flags(struct task_struct *task, unsigned long flags);
+
+int pt_set_data(struct task_struct *task, struct pt_data_t *data);
+
+int pt_get_caller(struct task_struct *task, unsigned long *caller);
+int pt_get_orig(struct task_struct *task, unsigned long *orig);
+int pt_get_call_type(struct task_struct *task, unsigned char *call_type);
+int pt_get_drop(struct task_struct *task);
+bool pt_check_disabled_probe(struct task_struct *task, unsigned long addr);
+
+void pt_enable_probe(struct task_struct *task, unsigned long addr);
+int pt_put_data(struct task_struct *task);
+int pt_init(void);
+void pt_exit(void);
+
+#endif /* __PRELOAD_HANDLERS_THREADS_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_retprobe.o
+swap_retprobe-y := \
+ retprobe.o \
+ rp_msg.o
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/retprobe/retprobe.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: Probes interface implement
+ *
+ */
+
+#include "retprobe.h"
+#include <us_manager/us_manager.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/probes/register_probes.h>
+#include <uprobe/swap_uprobes.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "rp_msg.h"
+
+
+static int retprobe_copy(struct probe_info *dest,
+ const struct probe_info *source)
+{
+ size_t len;
+
+ memcpy(dest, source, sizeof(*source));
+
+ len = strlen(source->rp_i.args) + 1;
+ dest->rp_i.args = kmalloc(len, GFP_ATOMIC);
+ if (dest->rp_i.args == NULL)
+ return -ENOMEM;
+ memcpy(dest->rp_i.args, source->rp_i.args, len);
+
+ return 0;
+}
+
+
+static void retprobe_cleanup(struct probe_info *probe_i)
+{
+ kfree(probe_i->rp_i.args);
+}
+
+
+
+static struct uprobe *retprobe_get_uprobe(struct sspt_ip *ip)
+{
+ return &ip->retprobe.up;
+}
+
+static int retprobe_register_probe(struct sspt_ip *ip)
+{
+ return swap_register_uretprobe(&ip->retprobe);
+}
+
+static void retprobe_unregister_probe(struct sspt_ip *ip, int disarm)
+{
+ __swap_unregister_uretprobe(&ip->retprobe, disarm);
+}
+
+
+static int retprobe_entry_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct uretprobe *rp = ri->rp;
+
+ if (rp && get_quiet() == QT_OFF) {
+ struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
+ const char *fmt = ip->desc->info.rp_i.args;
+ const unsigned long func_addr = (unsigned long)ip->orig_addr;
+
+ rp_msg_entry(regs, func_addr, fmt);
+ }
+
+ return 0;
+}
+
+static int retprobe_ret_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct uretprobe *rp = ri->rp;
+
+ if (rp && get_quiet() == QT_OFF) {
+ struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
+ const unsigned long func_addr = (unsigned long)ip->orig_addr;
+ const unsigned long ret_addr = (unsigned long)ri->ret_addr;
+ const char ret_type = ip->desc->info.rp_i.ret_type;
+
+ rp_msg_exit(regs, func_addr, ret_type, ret_addr);
+ }
+
+ return 0;
+}
+
+static void retprobe_init(struct sspt_ip *ip)
+{
+ ip->retprobe.entry_handler = retprobe_entry_handler;
+ ip->retprobe.handler = retprobe_ret_handler;
+ ip->retprobe.maxactive = 0;
+}
+
+static void retprobe_uninit(struct sspt_ip *ip)
+{
+ retprobe_cleanup(&ip->desc->info);
+}
+
+
+static struct probe_iface retprobe_iface = {
+ .init = retprobe_init,
+ .uninit = retprobe_uninit,
+ .reg = retprobe_register_probe,
+ .unreg = retprobe_unregister_probe,
+ .get_uprobe = retprobe_get_uprobe,
+ .copy = retprobe_copy,
+ .cleanup = retprobe_cleanup
+};
+
+static int __init retprobe_module_init(void)
+{
+ return swap_register_probe_type(SWAP_RETPROBE, &retprobe_iface);
+}
+
+static void __exit retprobe_module_exit(void)
+{
+ swap_unregister_probe_type(SWAP_RETPROBE);
+}
+
+module_init(retprobe_module_init);
+module_exit(retprobe_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP retprobe");
+MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>");
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/uretprobe.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: Probes interface implement
+ *
+ */
+
+#ifndef __URETPROBE_H__
+#define __URETPROBE_H__
+
+/* Common retprobe info */
+struct retprobe_info {
+ char *args;
+ char ret_type;
+};
+
+#endif /* __URETPROBE_H__ */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <writer/swap_msg.h>
+#include <writer/kernel_operations.h>
+#include "rp_msg.h"
+
+
+#define RP_PREFIX KERN_INFO "[RP] "
+
+
+struct msg_entry {
+ u32 pid;
+ u32 tid;
+ u64 pc_addr;
+ u64 caller_pc_addr;
+ u32 cpu_num;
+ u32 cnt_args;
+ char args[0];
+} __packed;
+
+struct msg_exit {
+ u32 pid;
+ u32 tid;
+ u64 pc_addr;
+ u64 caller_pc_addr;
+ u32 cpu_num;
+ char ret_val[0];
+} __packed;
+
+
+void rp_msg_entry(struct pt_regs *regs, unsigned long func_addr,
+ const char *fmt)
+{
+ int ret;
+ struct task_struct *task = current;
+ struct swap_msg *m;
+ struct msg_entry *ent;
+ void *p;
+ size_t size;
+
+ m = swap_msg_get(MSG_FUNCTION_ENTRY);
+ p = swap_msg_payload(m);
+
+ ent = p;
+ ent->pid = task->tgid;
+ ent->tid = task->pid;
+ ent->pc_addr = func_addr;
+ ent->caller_pc_addr = get_regs_ret_func(regs);
+ ent->cpu_num = raw_smp_processor_id();
+ ent->cnt_args = strlen(fmt);
+
+ size = swap_msg_size(m);
+ ret = swap_msg_pack_args(p + sizeof(*ent), size - sizeof(*ent),
+ fmt, regs);
+ if (ret < 0) {
+ printk(RP_PREFIX "ERROR: arguments packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, sizeof(*ent) + ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+EXPORT_SYMBOL_GPL(rp_msg_entry);
+
+void rp_msg_exit(struct pt_regs *regs, unsigned long func_addr,
+ char ret_type, unsigned long ret_addr)
+{
+ int ret;
+ struct task_struct *task = current;
+ struct swap_msg *m;
+ struct msg_exit *ext;
+ void *p;
+ size_t size;
+
+ m = swap_msg_get(MSG_FUNCTION_EXIT);
+ p = swap_msg_payload(m);
+
+ ext = p;
+ ext->pid = task->tgid;
+ ext->tid = task->pid;
+ ext->pc_addr = func_addr;
+ ext->caller_pc_addr = ret_addr;
+ ext->cpu_num = raw_smp_processor_id();
+
+ size = swap_msg_size(m);
+ ret = swap_msg_pack_ret_val(p + sizeof(*ext), size - sizeof(*ext),
+ ret_type, regs);
+ if (ret < 0) {
+ printk(RP_PREFIX "ERROR: return value packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, sizeof(*ext) + ret);
+
+put_msg:
+ swap_msg_put(m);
+}
+EXPORT_SYMBOL_GPL(rp_msg_exit);
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _RP_MSG_H
+#define _RP_MSG_H
+
+
+struct pt_regs;
+
+
+void rp_msg_entry(struct pt_regs *regs, unsigned long func_addr,
+ const char *fmt);
+void rp_msg_exit(struct pt_regs *regs, unsigned long func_addr,
+ char ret_type, unsigned long ret_addr);
+
+
+#endif /* _RP_MSG_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../writer/Module.symvers
+
+obj-m := swap_sampler.o
+swap_sampler-y := swap_sampler_module.o
+
+ifdef CONFIG_HIGH_RES_TIMERS
+ swap_sampler-y += sampler_hrtimer.o
+else
+ swap_sampler-y += sampler_timer.o
+endif
--- /dev/null
+/**
+ * @file sampler/kernel_operations.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Parser definitions.
+ */
+
+#ifndef __KERNEL_OPERATIONS_H__
+#define __KERNEL_OPERATIONS_H__
+
+#include <linux/kernel.h>
+
+/** Prints debug message.*/
+#define print_debug(msg, args...) \
+ printk(KERN_DEBUG "SWAP_SAMPLER DEBUG : " msg, ##args)
+/** Prints info message.*/
+#define print_msg(msg, args...) \
+ printk(KERN_INFO "SWAP_SAMPLER : " msg, ##args)
+/** Prints warning message.*/
+#define print_warn(msg, args...) \
+ printk(KERN_WARNING "SWAP_SAMPLER WARNING : " msg, ##args)
+/** Prints error message.*/
+#define print_err(msg, args...) \
+ printk(KERN_ERR "SWAP_SAMPLER ERROR : " msg, ##args)
+/** Prints critical error message.*/
+#define print_crit(msg, args...) \
+ printk(KERN_CRIT "SWAP_SAMPLER CRITICAL : " msg, ##args)
+
+#endif /* __KERNEL_OPERATIONS_H__ */
--- /dev/null
+/**
+ * sampler/sampler_hrtimer.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Sampler for high resolution timers.
+ */
+
+
+
+#include <linux/types.h>
+#include <linux/version.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include "sampler_timers.h"
+
+
+static u64 sampler_timer_quantum;
+static DEFINE_PER_CPU(struct hrtimer, swap_hrtimer);
+static int swap_hrtimer_running;
+
+/**
+ * @brief Restarts sampling.
+ *
+ * @param timer Pointer to hrtimer struct.
+ * @return hrtimer_restart flag.
+ */
+restart_ret sampler_timers_restart(swap_timer *timer)
+{
+ restart_ret ret;
+
+ hrtimer_forward_now(timer, ns_to_ktime(sampler_timer_quantum));
+ ret = HRTIMER_RESTART;
+
+ return ret;
+}
+
+/**
+ * @brief Sets running flag true.
+ *
+ * @return Void.
+ */
+void sampler_timers_set_run(void)
+{
+ swap_hrtimer_running = 1;
+}
+
+/**
+ * @brief Sets running flag false.
+ *
+ * @return Void.
+ */
+void sampler_timers_set_stop(void)
+{
+ swap_hrtimer_running = 0;
+}
+
+/**
+ * @brief Starts timer sampling.
+ *
+ * @param restart_func Pointer to restart function.
+ * @return Void.
+ */
+void sampler_timers_start(void *restart_func)
+{
+ struct hrtimer *hrtimer = &__get_cpu_var(swap_hrtimer);
+
+ if (!swap_hrtimer_running)
+ return;
+
+ hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer->function = restart_func;
+ hrtimer_start(hrtimer, ns_to_ktime(sampler_timer_quantum),
+ HRTIMER_MODE_REL_PINNED);
+}
+
+/**
+ * @brief Stops timer sampling.
+ *
+ * @param cpu Online CPUs.
+ * @return Void.
+ */
+void sampler_timers_stop(int cpu)
+{
+ struct hrtimer *hrtimer = &per_cpu(swap_hrtimer, cpu);
+
+ if (!swap_hrtimer_running)
+ return;
+
+ hrtimer_cancel(hrtimer);
+}
+
+/**
+ * @brief Sets timer quantum.
+ *
+ * @param timer_quantum Timer quantum.
+ * @return Void.
+ */
+void sampler_timers_set_quantum(unsigned int timer_quantum)
+{
+ u64 tmp = (u64)timer_quantum;
+ sampler_timer_quantum = tmp * 1000 * 1000;
+}
--- /dev/null
+/**
+ * sampler/sampler_timer.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Sampler based on common timers.
+ */
+
+
+
+#include "sampler_timers.h"
+
+
+
+static unsigned long sampler_timer_quantum;
+static DEFINE_PER_CPU(struct timer_list, swap_timer);
+static int swap_timer_running;
+
+/**
+ * @brief Restarts sampling.
+ *
+ * @param timer Pointer to timer_list struct.
+ * @return 0.
+ */
+restart_ret sampler_timers_restart(swap_timer *timer)
+{
+ restart_ret ret;
+
+ mod_timer_pinned((struct timer_list *)timer,
+ jiffies + sampler_timer_quantum);
+ ret = 0;
+
+ return ret;
+}
+
+/**
+ * @brief Sets running flag true.
+ *
+ * @return Void.
+ */
+void sampler_timers_set_run(void)
+{
+ swap_timer_running = 1;
+}
+
+/**
+ * @brief Sets running flag false.
+ *
+ * @return Void.
+ */
+void sampler_timers_set_stop(void)
+{
+ swap_timer_running = 0;
+}
+
+/**
+ * @brief Starts timer sampling.
+ *
+ * @param restart_func Pointer to restart function.
+ * @return Void.
+ */
+void sampler_timers_start(void *restart_func)
+{
+ struct timer_list *timer = &__get_cpu_var(swap_timer);
+
+ if (!swap_timer_running)
+ return;
+
+ init_timer(timer);
+ timer->data = (unsigned long)timer;
+ timer->function = restart_func;
+
+ mod_timer_pinned(timer, jiffies + sampler_timer_quantum);
+}
+
+/**
+ * @brief Stops timer sampling.
+ *
+ * @param cpu Online CPUs.
+ * @return Void.
+ */
+void sampler_timers_stop(int cpu)
+{
+ struct timer_list *timer = &per_cpu(swap_timer, cpu);
+
+ if (!swap_timer_running)
+ return;
+ del_timer_sync(timer);
+}
+
+/**
+ * @brief Sets timer quantum.
+ *
+ * @param timer_quantum Timer quantum.
+ * @return Void.
+ */
+void sampler_timers_set_quantum(unsigned int timer_quantum)
+{
+ sampler_timer_quantum = timer_quantum;
+}
--- /dev/null
+/*
+ * SWAP sampler
+ * modules/sampler/sampler_timers.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP sampler porting
+ *
+ */
+
+
+
+#ifndef __SAMPLER_TIMERS_H__
+#define __SAMPLER_TIMERS_H__
+
+
+/* ===================== INCLUDE ==================== */
+
+#if defined(CONFIG_HIGH_RES_TIMERS)
+
+#include <linux/hrtimer.h>
+
+#else /* CONFIG_HIGH_RES_TIMERS */
+
+#include <linux/timer.h>
+
+#endif /* CONFIG_HIGH_RES_TIMERS */
+
+/* ==================== TYPE DEFS =================== */
+
+#if defined(CONFIG_HIGH_RES_TIMERS)
+
+typedef struct hrtimer swap_timer;
+typedef enum hrtimer_restart restart_ret;
+
+#else /* CONFIG_HIGH_RES_TIMERS */
+
+typedef struct timer_list swap_timer;
+typedef int restart_ret;
+
+#endif /* CONFIG_HIGH_RES_TIMERS */
+
+
+/* ====================== FUNCS ===================== */
+
+restart_ret sampler_timers_restart(swap_timer *timer);
+void sampler_timers_stop(int cpu);
+void sampler_timers_start(void *unused);
+void sampler_timers_set_quantum(unsigned int timer_quantum);
+void sampler_timers_set_run(void);
+void sampler_timers_set_stop(void);
+
+#endif /* __SAMPLER_TIMERS_H__ */
--- /dev/null
+/**
+ * @file sampler/swap_sampler_errors.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Sampler error codes.
+ */
+
+
+/**
+ * @enum _swap_sampler_errors
+ * @brief Sampler errors.
+ */
+enum _swap_sampler_errors {
+ E_SS_SUCCESS = 0, /**< Success. */
+ E_SS_WRONG_QUANTUM = 1 /**< Wrong timer quantum set. */
+};
--- /dev/null
+/**
+ * sampler/swap_sampler_module.c
+ * @author Andreev S.V.: SWAP Sampler implementation
+ * @author Alexander Aksenov <a.aksenov@samsung.com>: SWAP sampler porting
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Timer-based sampling module.
+ */
+
+#include <linux/ptrace.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+
+#include "swap_sampler_module.h"
+#include "swap_sampler_errors.h"
+#include "kernel_operations.h"
+#include "sampler_timers.h"
+
+
+static BLOCKING_NOTIFIER_HEAD(swap_sampler_notifier_list);
+static swap_sample_cb_t sampler_cb;
+
+static restart_ret swap_timer_restart(swap_timer *timer)
+{
+ sampler_cb(task_pt_regs(current));
+
+ return sampler_timers_restart(timer);
+}
+
+static int swap_timer_start(void)
+{
+ get_online_cpus();
+ sampler_timers_set_run();
+
+ on_each_cpu(sampler_timers_start, swap_timer_restart, 1);
+ put_online_cpus();
+
+ return E_SS_SUCCESS;
+}
+
+static void swap_timer_stop(void)
+{
+ int cpu;
+
+ get_online_cpus();
+
+ for_each_online_cpu(cpu)
+ sampler_timers_stop(cpu);
+ sampler_timers_set_stop();
+ put_online_cpus();
+}
+
+static int swap_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ long cpu = (long) hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ smp_call_function_single(cpu, sampler_timers_start,
+ swap_timer_restart, 1);
+ break;
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ sampler_timers_stop(cpu);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata swap_cpu_notifier = {
+ .notifier_call = swap_cpu_notify,
+};
+
+static int do_swap_sampler_start(unsigned int timer_quantum)
+{
+ if (timer_quantum <= 0)
+ return -EINVAL;
+
+ sampler_timers_set_quantum(timer_quantum);
+ swap_timer_start();
+
+ return 0;
+}
+
+static void do_swap_sampler_stop(void)
+{
+ swap_timer_stop();
+}
+
+static DEFINE_MUTEX(mutex_run);
+static int sampler_run;
+
+
+/**
+ * @brief Starts sampling with specified timer quantum.
+ *
+ * @param timer_quantum Timer quantum for sampling.
+ * @return 0 on success, error code on error.
+ */
+int swap_sampler_start(unsigned int timer_quantum, swap_sample_cb_t cb)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&mutex_run);
+ if (sampler_run) {
+ printk(KERN_INFO "sampler profiling is already run!\n");
+ goto unlock;
+ }
+
+ sampler_cb = cb;
+
+ ret = do_swap_sampler_start(timer_quantum);
+ if (ret == 0)
+ sampler_run = 1;
+
+unlock:
+ mutex_unlock(&mutex_run);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_sampler_start);
+
+
+/**
+ * @brief Stops sampling.
+ *
+ * @return 0 on success, error code on error.
+ */
+int swap_sampler_stop(void)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_run);
+ if (sampler_run == 0) {
+ printk(KERN_INFO "energy profiling is not running!\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ do_swap_sampler_stop();
+
+ sampler_run = 0;
+unlock:
+ mutex_unlock(&mutex_run);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_sampler_stop);
+
+static int __init sampler_init(void)
+{
+ int retval;
+
+ retval = register_hotcpu_notifier(&swap_cpu_notifier);
+ if (retval) {
+ print_err("Error of register_hotcpu_notifier()\n");
+ return retval;
+ }
+
+ print_msg("Sample ininitialization success\n");
+
+ return E_SS_SUCCESS;
+}
+
+static void __exit sampler_exit(void)
+{
+ if (sampler_run)
+ do_swap_sampler_stop();
+
+ unregister_hotcpu_notifier(&swap_cpu_notifier);
+
+ print_msg("Sampler uninitialized\n");
+}
+
+module_init(sampler_init);
+module_exit(sampler_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP sampling module");
+MODULE_AUTHOR("Andreev S.V., Aksenov A.S.");
--- /dev/null
+/**
+ * @file sampler/swap_sampler_module.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Sampling module interface declaration.
+ */
+
+
+/* SWAP Sampler interface */
+
+#ifndef __SWAP_SAMPLER_MODULE_H__
+#define __SWAP_SAMPLER_MODULE_H__
+
+
+typedef void (*swap_sample_cb_t)(struct pt_regs *);
+
+
+/* Starts the SWAP Sampler */
+int swap_sampler_start(unsigned int timer_quantum, swap_sample_cb_t cb);
+
+/* Stops the SWAP Sampler */
+int swap_sampler_stop(void);
+
+#endif /* __SWAP_SAMPLER_MODULE_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
+
+obj-m := swap_taskctx.o
+swap_taskctx-y := task_ctx.o
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kconfig.h>
+#include <linux/completion.h>
+#include <ksyms/ksyms.h>
+#include <kprobe/swap_ktd.h>
+#include <master/swap_initializer.h>
+#include "task_ctx.h"
+
+
+enum { WAIT_TIMEOUT = 1500 }; /* max waiting time the signal delivery */
+
+struct call_task {
+ taskctx_t func;
+ void *data;
+
+ bool is_running;
+ struct completion comp0;
+ struct completion comp1;
+};
+
+
+static void (*swap_signal_wake_up_state)(struct task_struct *t,
+ unsigned int state);
+
+static struct sighand_struct *swap_lock_task_sighand(struct task_struct *tsk,
+ unsigned long *flags)
+{
+ struct sighand_struct *sighand;
+
+ for (;;) {
+ local_irq_save(*flags);
+ rcu_read_lock();
+
+ sighand = rcu_dereference(tsk->sighand);
+ if (unlikely(sighand == NULL)) {
+ rcu_read_unlock();
+ local_irq_restore(*flags);
+ break;
+ }
+
+ spin_lock(&sighand->siglock);
+ if (likely(sighand == tsk->sighand)) {
+ rcu_read_unlock();
+ break;
+ }
+ spin_unlock(&sighand->siglock);
+
+ rcu_read_unlock();
+ local_irq_restore(*flags);
+ }
+
+ return sighand;
+}
+
+static inline void swap_unlock_task_sighand(struct task_struct *tsk,
+ unsigned long *flags)
+{
+ spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
+}
+
+static int fake_signal_wake_up(struct task_struct *p)
+{
+ unsigned long flags;
+
+ if (swap_lock_task_sighand(p, &flags) == NULL)
+ return -ESRCH;
+
+ swap_signal_wake_up_state(p, 0);
+ swap_unlock_task_sighand(p, &flags);
+
+ return 0;
+}
+
+
+static void ktd_init(struct task_struct *task, void *data)
+{
+ struct call_task **call = (struct call_task **)data;
+
+ *call = NULL;
+}
+
+static void ktd_exit(struct task_struct *task, void *data)
+{
+ struct call_task **call = (struct call_task **)data;
+
+ WARN(*call, "call is not NULL");
+}
+
+static struct ktask_data ktd = {
+ .init = ktd_init,
+ .exit = ktd_exit,
+ .size = sizeof(struct call_task *),
+};
+
+static void call_set(struct task_struct *task, struct call_task *call)
+{
+ *(struct call_task **)swap_ktd(&ktd, task) = call;
+}
+
+static struct call_task *call_get(struct task_struct *task)
+{
+ return *(struct call_task **)swap_ktd(&ktd, task);
+}
+
+
+int taskctx_run(struct task_struct *task, taskctx_t func, void *data)
+{
+ if (IS_ENABLED(CONFIG_SWAP_KERNEL_IMMUTABLE))
+ return -ENOSYS;
+
+ if (task == current) {
+ func(data);
+ } else {
+ int ret;
+ unsigned long jiff;
+ struct call_task call = {
+ .func = func,
+ .data = data,
+ .comp0 = COMPLETION_INITIALIZER(call.comp0),
+ .comp1 = COMPLETION_INITIALIZER(call.comp1),
+ .is_running = false,
+ };
+
+ /* check task possibility to receive signals */
+ if (task->flags & (PF_KTHREAD | PF_EXITING | PF_SIGNALED))
+ return -EINVAL;
+
+ /* set call by task */
+ call_set(task, &call);
+
+ ret = fake_signal_wake_up(task);
+ if (ret) {
+ /* reset call by task */
+ call_set(task, NULL);
+ pr_err("cannot send signal to task[%u %u %s] flags=%08x state=%08lx\n",
+ task->tgid, task->pid, task->comm,
+ task->flags, task->state);
+ return ret;
+ }
+
+ jiff = msecs_to_jiffies(WAIT_TIMEOUT);
+ wait_for_completion_timeout(&call.comp0, jiff);
+
+ /* reset call by task */
+ call_set(task, NULL);
+
+ /* wait the return from sig_pre_handler() */
+ synchronize_sched();
+
+ if (call.is_running)
+ wait_for_completion(&call.comp1);
+
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(taskctx_run);
+
+
+static void sig_handler(void)
+{
+ struct call_task *call = call_get(current);
+
+ if (call) {
+ call_set(current, NULL);
+ call->is_running = true;
+
+ complete(&call->comp0);
+ call->func(call->data);
+ complete(&call->comp1);
+ }
+}
+
+
+#ifdef CONFIG_SWAP_HOOK_SIGNAL
+# include <swap/hook_signal.h>
+void hook_handler(struct ksignal *ksig)
+{
+ sig_handler();
+}
+
+struct hook_signal hook_signal = {
+ .owner = THIS_MODULE,
+ .hook = hook_handler,
+};
+
+static int signal_reg(void)
+{
+ int ret = hook_signal_reg(&hook_signal);
+ if (ret)
+ pr_err("Cannot register hook_signal, ret=%d\n", ret);
+
+ return ret;
+}
+
+static void signal_unreg(void)
+{
+ hook_signal_unreg(&hook_signal);
+}
+
+static int signal_once(void)
+{
+ return 0;
+}
+
+#else /* !CONFIG_SWAP_HOOK_SIGNAL */
+# include <kprobe/swap_kprobes.h>
+
+static int sig_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ sig_handler();
+
+ return 0;
+}
+
+static struct kprobe sig_kprobe = {
+ .pre_handler = sig_pre_handler,
+};
+
+static int signal_reg(void)
+{
+ int ret = swap_register_kprobe(&sig_kprobe);
+ if (ret)
+ pr_err("register sig_kprobe ret=%d\n", ret);
+
+ return ret;
+}
+
+static void signal_unreg(void)
+{
+ swap_unregister_kprobe(&sig_kprobe);
+}
+
+static int signal_once(void)
+{
+ const char *sym;
+
+ sym = "signal_wake_up_state";
+ swap_signal_wake_up_state = (void *)swap_ksyms(sym);
+ if (swap_signal_wake_up_state == NULL)
+ goto not_found;
+
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
+ sym = "get_signal";
+# else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) */
+ sym = "get_signal_to_deliver";
+# endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) */
+ sig_kprobe.addr = swap_ksyms(sym);
+ if (sig_kprobe.addr == 0)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ printk("Cannot find address for '%s'!\n", sym);
+ return -ESRCH;
+}
+#endif /* CONFIG_SWAP_HOOK_SIGNAL */
+
+
+static int use_cnt = 0;
+static DEFINE_MUTEX(use_lock);
+
+int taskctx_get(void)
+{
+ int ret = 0;
+
+ mutex_lock(&use_lock);
+ if (use_cnt == 0) {
+ ret = signal_reg();
+ if (ret)
+ goto unlock;
+ }
+
+ ++use_cnt;
+
+unlock:
+ mutex_unlock(&use_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(taskctx_get);
+
+void taskctx_put(void)
+{
+ mutex_lock(&use_lock);
+ if (use_cnt == 0) {
+ WARN_ON("call put_task_context() without get_task_context");
+ goto unlock;
+ }
+
+ --use_cnt;
+ if (use_cnt == 0)
+ signal_unreg();
+
+unlock:
+ mutex_unlock(&use_lock);
+}
+EXPORT_SYMBOL_GPL(taskctx_put);
+
+
+static int taskctx_once(void)
+{
+ return signal_once();
+}
+
+static int taskctx_init(void)
+{
+ return swap_ktd_reg(&ktd);
+}
+
+static void taskctx_uninit(void)
+{
+ WARN(use_cnt, "use_cnt=%d\n", use_cnt);
+ swap_ktd_unreg(&ktd);
+}
+
+SWAP_LIGHT_INIT_MODULE(taskctx_once, taskctx_init, taskctx_uninit, NULL, NULL);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+#ifndef _TASK_CTX_H
+#define _TASK_CTX_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+struct task_struct;
+
+typedef void (*taskctx_t)(void *info);
+
+
+int taskctx_run(struct task_struct *task, taskctx_t func, void *data);
+
+int taskctx_get(void);
+void taskctx_put(void);
+
+
+#endif /* _TASK_CTX_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := kprobe_tests/
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_kp_tests.o
+swap_kp_tests-y := \
+ kp_module.o \
+ kp_tests.o \
+ krp_tests.o \
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/kallsyms.h>
+#include "kp_module.h"
+
+
+static asmlinkage long (*olog_sys_write)(unsigned int, const char __user *, size_t);
+
+static long write_to_stdout(const char *buf, size_t len)
+{
+ long ret;
+
+ mm_segment_t fs = get_fs();
+ set_fs(get_ds());
+ ret = olog_sys_write(1, buf, len);
+ set_fs(fs);
+
+ return ret;
+}
+
+static int olog_init(void)
+{
+ olog_sys_write = (void *)kallsyms_lookup_name("sys_write");
+ if (olog_sys_write == NULL) {
+ pr_err("ERR: not found 'sys_write' symbol\n");
+ return -ESRCH;
+ }
+
+ return 0;
+}
+
+void olog(const char *fmt, ...)
+{
+ char buf[256];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ printk("%s", buf);
+ write_to_stdout(buf, strlen(buf));
+}
+
+
+
+
+static void print_mod_info(void)
+{
+ struct module *mod = THIS_MODULE;
+
+ printk("### MOD_INFO:\n");
+ printk(" core: %p..%p\n", mod->module_init, mod->module_init + mod->init_text_size);
+ printk(" init: %p..%p\n", mod->module_core, mod->module_core + mod->core_text_size);
+ printk("\n");
+}
+
+
+/* TODO: move declare to header */
+int kp_tests_run(void);
+int krp_tests_run(void);
+
+static int __init tests_init(void)
+{
+ int ret;
+
+ ret = olog_init();
+ if (ret)
+ return ret;
+
+ print_mod_info();
+
+ olog("### Begin tests ###\n");
+ kp_tests_run();
+ krp_tests_run();
+ olog("### End tests ###\n");
+
+ return -1;
+}
+
+static void __exit tests_exit(void)
+{
+}
+
+module_init(tests_init);
+module_exit(tests_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _KP_MODULE_H
+#define _KP_MODULE_H
+
+
+void olog(const char *fmt, ...);
+
+
+#endif /* _KP_MODULE_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <kprobe/swap_kprobes.h>
+#include "kp_module.h"
+
+
+static struct task_struct *cur_task;
+
+
+static struct kprobe *kp_create(char *name,
+ int (*pre_h)(struct kprobe *, struct pt_regs *))
+{
+ struct kprobe *p;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p) {
+ p->symbol_name = name;
+ p->pre_handler = pre_h;
+ }
+
+ return p;
+}
+
+static void kp_free(struct kprobe *p)
+{
+ memset(p, 0x10, sizeof(*p));
+}
+
+#define kp_reg(ptr, name, handler) \
+ do { \
+ ptr = kp_create(name, handler); \
+ swap_register_kprobe(ptr); \
+ } while (0)
+
+#define kp_unreg(ptr) \
+ do { \
+ swap_unregister_kprobe(ptr); \
+ kp_free(ptr); \
+ ptr = NULL; \
+ } while (0)
+
+
+noinline char *my_kstrdup(const char *s, gfp_t gfp)
+{
+ return kstrdup(s, gfp);
+}
+
+noinline void my_kfree(const void *data)
+{
+ kfree(data);
+}
+
+
+
+
+/*
+ ******************************************************************************
+ * recursion *
+ ******************************************************************************
+ */
+static int kstrdup_cnt;
+static int kfree_cnt;
+
+static struct kprobe *kp_kstrdup;
+static int kstrdup_h(struct kprobe *kp, struct pt_regs *regs)
+{
+ char *str;
+
+ str = my_kstrdup("from_kfree_h", GFP_ATOMIC);
+ my_kfree(str);
+
+ ++kstrdup_cnt;
+
+ return 0;
+}
+
+static struct kprobe *kp_kfree;
+static int kfree_h(struct kprobe *kp, struct pt_regs *regs)
+{
+ ++kfree_cnt;
+
+ return 0;
+}
+
+static void run_test_recursion(void)
+{
+ char *str;
+
+ str = my_kstrdup("test_string_0", GFP_KERNEL);
+ my_kfree(str);
+
+ str = my_kstrdup("test_string_1", GFP_KERNEL);
+ my_kfree(str);
+}
+
+static void do_test_recursion(void)
+{
+ kp_reg(kp_kfree, "my_kfree", kfree_h);
+ kp_reg(kp_kstrdup, "my_kstrdup", kstrdup_h);
+
+ run_test_recursion();
+
+ kp_unreg(kp_kstrdup);
+ kp_unreg(kp_kfree);
+}
+
+
+static void test_recursion(void)
+{
+ olog("Recursion:\n");
+
+ kstrdup_cnt = 0;
+ kfree_cnt = 0;
+
+ do_test_recursion();
+
+ if (kstrdup_cnt == 2 && kfree_cnt == 2) {
+ olog(" OK\n");
+ } else {
+ olog(" ERROR: kstrdup_cnt=%d kfree_cnt=%d\n",
+ kstrdup_cnt, kfree_cnt);
+ }
+}
+
+
+
+
+/*
+ ******************************************************************************
+ * recursion and multiple handlers (Aggregate probe) *
+ ******************************************************************************
+ */
+static int kfree2_cnt;
+
+static struct kprobe *kp_kfree2;
+static int kfree2_h(struct kprobe *kp, struct pt_regs *regs)
+{
+ if (current != cur_task || in_interrupt())
+ return 0;
+
+ ++kfree2_cnt;
+ return 0;
+}
+
+static void pre_test_recursion_and_mh(void)
+{
+ kstrdup_cnt = 0;
+ kfree_cnt = 0;
+ kfree2_cnt = 0;
+}
+
+static void post_test_recursion_and_mh(void)
+{
+ if (kstrdup_cnt == 2 && kfree_cnt == 2 && kfree2_cnt == 2) {
+ olog(" OK\n");
+ } else {
+ olog(" ERROR: kstrdup_cnt=%d kfree_cnt=%d kfree2_cnt=%d\n",
+ kstrdup_cnt, kfree_cnt, kfree2_cnt);
+ }
+}
+
+static void test_recursion_and_multiple_handlers(void)
+{
+ olog("Recursion and multiple handlers:\n");
+
+ pre_test_recursion_and_mh();
+
+ kp_reg(kp_kfree2, "my_kfree", kfree2_h);
+ do_test_recursion();
+ kp_unreg(kp_kfree2);
+
+ post_test_recursion_and_mh();
+}
+
+static void test_recursion_and_multiple_handlers2(void)
+{
+ olog("Recursion and multiple handlers [II]:\n");
+
+ pre_test_recursion_and_mh();
+
+ kp_reg(kp_kfree, "my_kfree", kfree_h);
+ kp_reg(kp_kstrdup, "my_kstrdup", kstrdup_h);
+ kp_reg(kp_kfree2, "my_kfree", kfree2_h);
+
+ run_test_recursion();
+
+ kp_unreg(kp_kstrdup);
+ kp_unreg(kp_kfree);
+ kp_unreg(kp_kfree2);
+
+ post_test_recursion_and_mh();
+}
+
+
+
+
+/*
+ ******************************************************************************
+ * swap_unregister_kprobe(), sync *
+ ******************************************************************************
+ */
+
+static const char task_name[] = "my_task";
+
+static int is_my_task(void)
+{
+ return !strcmp(task_name, current->comm);
+}
+
+static int find_module_cnt;
+
+static struct kprobe *kp_find_module;
+static int find_module_h(struct kprobe *kp, struct pt_regs *regs)
+{
+ if (is_my_task()) {
+ might_sleep();
+ ++find_module_cnt;
+
+ /* sleep 0.5 sec */
+ msleep(500);
+ schedule();
+
+ ++find_module_cnt;
+ }
+
+ return 0;
+}
+
+static int kthread_my_fn(void *data)
+{
+ find_module("o_lo_lo");
+ find_module("o_lo_lo");
+
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+
+ return 0;
+}
+
+
+static void do_test_sync_unreg(unsigned int ms)
+{
+ struct task_struct *task;
+
+ kp_reg(kp_find_module, "find_module", find_module_h);
+
+ task = kthread_run(kthread_my_fn, NULL, task_name);
+ if (IS_ERR(task)) {
+ olog("ERROR: kthread_run()\n");
+ goto unreg;
+ }
+
+ /* waiting for kthread_my_fn() call */
+ msleep(ms);
+unreg:
+ kp_unreg(kp_find_module);
+ if (!IS_ERR(task))
+ kthread_stop(task);
+}
+
+static void test_sync_unreg(void)
+{
+ olog("Unreg kp:\n");
+
+ find_module_cnt = 0;
+
+ do_test_sync_unreg(200);
+
+ if (find_module_cnt == 2) {
+ olog(" OK\n");
+ } else {
+ olog(" ERROR: find_module_cnt=%d\n", find_module_cnt);
+ }
+}
+
+
+
+/*
+ ******************************************************************************
+ * swap_unregister_kprobe(), sync and multiple handlers *
+ ******************************************************************************
+ */
+static int find_module2_cnt;
+
+static struct kprobe *kp_find_module2;
+static int find_module2_h(struct kprobe *kp, struct pt_regs *regs)
+{
+ if (is_my_task()) {
+ ++find_module2_cnt;
+
+ /* sleep 0.5 sec */
+ msleep(500);
+
+ ++find_module2_cnt;
+ }
+
+ return 0;
+}
+
+static void pre_test_sync_unreg_and_mh(void)
+{
+ find_module_cnt = 0;
+ find_module2_cnt = 0;
+}
+
+static void post_test_sync_unreg_and_mh(int cnt, int cnt2)
+{
+ if (find_module_cnt == cnt && find_module2_cnt == cnt2) {
+ olog(" OK\n");
+ } else {
+ olog(" ERROR: find_module_cnt=%d find_module2_cnt=%d\n",
+ find_module_cnt, find_module2_cnt);
+ }
+}
+
+static void do_test_sync_unreg_and_mh(unsigned int ms)
+{
+ struct task_struct *task;
+
+ kp_reg(kp_find_module, "find_module", find_module_h);
+ kp_reg(kp_find_module2, "find_module", find_module2_h);
+
+ task = kthread_run(kthread_my_fn, NULL, task_name);
+ if (IS_ERR(task)) {
+ olog("ERROR: kthread_run()\n");
+ goto unreg;
+ }
+
+ /* waiting for kthread_my_fn() call */
+ msleep(ms);
+unreg:
+ kp_unreg(kp_find_module2);
+ kp_unreg(kp_find_module);
+ if (!IS_ERR(task))
+ kthread_stop(task);
+}
+
+static void test_sync_unreg_and_multiple_handlers(void)
+{
+ olog("Unreg kp and multiple handlers:\n");
+
+ pre_test_sync_unreg_and_mh();
+
+ do_test_sync_unreg_and_mh(700);
+
+ post_test_sync_unreg_and_mh(2, 2);
+}
+
+static void do_test_sync_unreg_and_mh2(unsigned int ms)
+{
+ struct task_struct *task;
+
+ kp_reg(kp_find_module, "find_module", find_module_h);
+ kp_reg(kp_find_module2, "find_module", find_module2_h);
+
+ task = kthread_run(kthread_my_fn, NULL, task_name);
+ if (IS_ERR(task)) {
+ olog("ERROR: kthread_run()\n");
+ goto unreg;
+ }
+
+ /* waiting for kthread_my_fn() call */
+ msleep(ms);
+unreg:
+ kp_unreg(kp_find_module);
+ kp_unreg(kp_find_module2);
+ if (!IS_ERR(task))
+ kthread_stop(task);
+}
+
+static void test_sync_unreg_and_multiple_handlers2(void)
+{
+ olog("Unreg kp and multiple handlers [II]:\n");
+
+ pre_test_sync_unreg_and_mh();
+
+ do_test_sync_unreg_and_mh2(700);
+
+ post_test_sync_unreg_and_mh(2, 2);
+}
+
+int kp_tests_run(void)
+{
+ cur_task = current;
+
+ test_recursion();
+ test_recursion_and_multiple_handlers();
+ test_recursion_and_multiple_handlers2();
+ // add 3
+
+ test_sync_unreg();
+ test_sync_unreg_and_multiple_handlers();
+ test_sync_unreg_and_multiple_handlers2();
+
+ return 0;
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <kprobe/swap_kprobes.h>
+#include "kp_module.h"
+
+
+static struct kretprobe *krp_create(char *name,
+ int (*eh)(struct kretprobe_instance *, struct pt_regs *),
+ int (*rh)(struct kretprobe_instance *, struct pt_regs *),
+ size_t data_size)
+{
+ struct kretprobe *rp;
+
+ rp = kzalloc(sizeof(*rp), GFP_KERNEL);
+ if (rp) {
+ rp->kp.symbol_name = name;
+ rp->entry_handler = eh;
+ rp->handler = rh;
+ rp->data_size = data_size;
+ }
+
+ return rp;
+}
+
+static void krp_free(struct kretprobe *rp)
+{
+ memset(rp, 0x10, sizeof(*rp));
+}
+
+#define krp_reg(ptr, name, eh, rh, sz) \
+ do { \
+ ptr = krp_create(name, eh, rh, sz); \
+ swap_register_kretprobe(ptr); \
+ } while (0)
+
+#define krp_unreg(ptr) \
+ do { \
+ swap_unregister_kretprobe(ptr); \
+ krp_free(ptr); \
+ ptr = NULL; \
+ } while (0)
+
+
+
+
+
+struct test_func_data {
+ long v0, v1, v2, v3, v4, v5, v6, v7;
+};
+
+static struct test_func_data tf_data_tmp;
+static unsigned long tf_data_tmp_ret;
+
+
+static long do_test_func(long v0, long v1, long v2, long v3,
+ long v4, long v5, long v6, long v7)
+{
+ return v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7;
+}
+
+static noinline long test_func(long v0, long v1, long v2, long v3,
+ long v4, long v5, long v6, long v7)
+{
+ return do_test_func(v0, v1, v2, v3, v4, v5, v6, v7);
+}
+
+static int test_func_eh(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct test_func_data *data = (struct test_func_data *)ri->data;
+
+ data->v0 = swap_get_karg(regs, 0);
+ data->v1 = swap_get_karg(regs, 1);
+ data->v2 = swap_get_karg(regs, 2);
+ data->v3 = swap_get_karg(regs, 3);
+ data->v4 = swap_get_karg(regs, 4);
+ data->v5 = swap_get_karg(regs, 5);
+ data->v6 = swap_get_karg(regs, 6);
+ data->v7 = swap_get_karg(regs, 7);
+
+ pr_info("E data=[%ld %ld %ld %ld %ld %ld %ld %ld]\n",
+ data->v0, data->v1, data->v2, data->v3,
+ data->v4, data->v5, data->v6, data->v7);
+
+ return 0;
+}
+
+static int test_func_rh(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct test_func_data *data = (struct test_func_data *)ri->data;
+
+ tf_data_tmp_ret = regs_return_value(regs);
+ tf_data_tmp = *data;
+
+ pr_info("R data=[%ld %ld %ld %ld %ld %ld %ld %ld] ret=%ld\n",
+ data->v0, data->v1, data->v2, data->v3,
+ data->v4, data->v5, data->v6, data->v7,
+ tf_data_tmp_ret);
+
+ return 0;
+}
+
+struct kretprobe *test_func_krp;
+
+static struct test_func_data tf_data_gage = {
+ .v0 = 0,
+ .v1 = 1,
+ .v2 = 2,
+ .v3 = 3,
+ .v4 = 4,
+ .v5 = 5,
+ .v6 = 6,
+ .v7 = 7,
+};
+
+static void pre_test_krp(void)
+{
+ memset(&tf_data_tmp, 0, sizeof(tf_data_tmp));
+ tf_data_tmp_ret = 0;
+}
+
+static void post_test_krp(void)
+{
+ long ret;
+
+ ret = do_test_func(tf_data_gage.v0, tf_data_gage.v1, tf_data_gage.v2, tf_data_gage.v3,
+ tf_data_gage.v4, tf_data_gage.v5, tf_data_gage.v6, tf_data_gage.v7);
+
+ if (tf_data_tmp_ret == ret &&
+ memcmp(&tf_data_gage, &tf_data_tmp, sizeof(tf_data_gage)) == 0) {
+ olog(" OK\n");
+ } else {
+ olog(" ERROR:\n"
+ " tf_data_gage=[%ld %ld %ld %ld %ld %ld %ld %ld] ret=%ld\n"
+ " tf_data_tmp =[%ld %ld %ld %ld %ld %ld %ld %ld] ret=%ld\n",
+ tf_data_gage.v0, tf_data_gage.v1,
+ tf_data_gage.v2, tf_data_gage.v3,
+ tf_data_gage.v4, tf_data_gage.v5,
+ tf_data_gage.v6, tf_data_gage.v7, ret,
+ tf_data_tmp.v0, tf_data_tmp.v1,
+ tf_data_tmp.v2, tf_data_tmp.v3,
+ tf_data_tmp.v4, tf_data_tmp.v5,
+ tf_data_tmp.v6, tf_data_tmp.v7, tf_data_tmp_ret);
+ }
+}
+
+static void do_test_krp(void)
+{
+ krp_reg(test_func_krp, "test_func",
+ test_func_eh, test_func_rh,
+ sizeof(struct test_func_data));
+
+ test_func(tf_data_gage.v0, tf_data_gage.v1, tf_data_gage.v2, tf_data_gage.v3,
+ tf_data_gage.v4, tf_data_gage.v5, tf_data_gage.v6, tf_data_gage.v7);
+
+ krp_unreg(test_func_krp);
+}
+
+static void test_krp(void)
+{
+ olog("Kretprobe (get_args{8} and ri->data):\n");
+
+ pre_test_krp();
+
+ do_test_krp();
+
+ post_test_krp();
+}
+
+int krp_tests_run(void)
+{
+ test_krp();
+
+ return 0;
+}
--- /dev/null
+
+cd /opt/swap/sdk
+
+insmod swap_master.ko
+echo 1 > /sys/kernel/debug/swap/enable
+insmod swap_kprobe.ko
+
+insmod swap_kp_tests.ko 2>/dev/null
+
+rmmod swap_kprobe
+rmmod swap_master
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_uihv.o
+swap_uihv-y := uihv_module.o \
+ uihv_debugfs.o
--- /dev/null
+#ifndef __UIHV_H__
+#define __UIHV_H__
+
+#define UIHV_PREFIX "SWAP_UIHV: "
+#define UIHV_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
+
+#endif /* __UIHV_H__ */
--- /dev/null
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <master/swap_debugfs.h>
+#include "uihv.h"
+#include "uihv_module.h"
+
+static const char UIHV_FOLDER[] = "uihv";
+static const char UIHV_PATH[] = "path";
+static const char UIHV_APP_INFO[] = "app_info";
+static const char UIHV_ENABLE[] = "enable";
+
+static struct dentry *uihv_root;
+
+
+
+/* ===========================================================================
+ * = UI VIEWER PATH =
+ * ===========================================================================
+ */
+
+
+static ssize_t uihv_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (path == NULL) {
+ ret = -ENOMEM;
+ goto uihv_path_write_out;
+ }
+
+ if (copy_from_user(path, buf, len)) {
+ ret = -EINVAL;
+ goto uihv_path_write_out;
+ }
+
+ path[len - 1] = '\0';
+
+ if (uihv_set_handler(path) != 0) {
+ printk(UIHV_PREFIX "Cannot set ui viewer path %s\n", path);
+ ret = -EINVAL;
+ goto uihv_path_write_out;
+ }
+
+ ret = len;
+
+ printk(UIHV_PREFIX "Set ui viewer path %s\n", path);
+
+uihv_path_write_out:
+ kfree(path);
+
+ return ret;
+}
+
+static const struct file_operations uihv_path_file_ops = {
+ .owner = THIS_MODULE,
+ .write = uihv_path_write,
+};
+
+
+/*
+ * format:
+ * main:app_path
+ *
+ * sample:
+ * 0x00000d60:/bin/app_sample
+ */
+static int uihv_add_app_info(const char *buf, size_t len)
+{
+ int n, ret;
+ char *app_path;
+ unsigned long main_addr;
+ const char fmt[] = "%%lx:/%%%ds";
+ char fmt_buf[64];
+
+ n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2);
+ if (n <= 0)
+ return -EINVAL;
+
+ app_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (app_path == NULL)
+ return -ENOMEM;
+
+ n = sscanf(buf, fmt_buf, &main_addr, app_path + 1);
+ if (n != 2) {
+ ret = -EINVAL;
+ goto free_app_path;
+ }
+ app_path[0] = '/';
+
+ printk(UIHV_PREFIX "Set ui viewer app path %s, main offset 0x%lx\n", app_path, main_addr);
+
+ ret = uihv_data_set(app_path, main_addr);
+
+free_app_path:
+ kfree(app_path);
+ return ret;
+}
+
+static ssize_t write_uihv_app_info(struct file *file,
+ const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret = len;
+ char *buf;
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto free_buf;
+ }
+
+ if (copy_from_user(buf, user_buf, len)) {
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ buf[len - 1] = '\0';
+
+ if (uihv_add_app_info(buf, len))
+ ret = -EINVAL;
+
+free_buf:
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct file_operations uihv_app_info_file_ops = {
+ .owner = THIS_MODULE,
+ .write = write_uihv_app_info,
+};
+
+static ssize_t write_uihv_enable(struct file *file,
+ const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret = len;
+ char *buf;
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(buf, user_buf, len)) {
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ buf[len - 1] = '\0';
+
+ if (buf[0] == '0')
+ ret = uihv_disable();
+ else
+ ret = uihv_enable();
+
+free_buf:
+ kfree(buf);
+
+out:
+ return ret;
+}
+
+static const struct file_operations uihv_enable_file_ops = {
+ .owner = THIS_MODULE,
+ .write = write_uihv_enable,
+};
+
+int uihv_dfs_init(void)
+{
+ struct dentry *swap_dentry, *root, *path, *app_info, *uihv_enable;
+ int ret;
+
+ ret = -ENODEV;
+ if (!debugfs_initialized())
+ goto fail;
+
+ ret = -ENOENT;
+ swap_dentry = swap_debugfs_getdir();
+ if (!swap_dentry)
+ goto fail;
+
+ ret = -ENOMEM;
+ root = swap_debugfs_create_dir(UIHV_FOLDER, swap_dentry);
+ if (IS_ERR_OR_NULL(root))
+ goto fail;
+
+ uihv_root = root;
+
+ path = swap_debugfs_create_file(UIHV_PATH, UIHV_DEFAULT_PERMS, root,
+ NULL, &uihv_path_file_ops);
+ if (IS_ERR_OR_NULL(path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ app_info = swap_debugfs_create_file(UIHV_APP_INFO,
+ UIHV_DEFAULT_PERMS, root, NULL,
+ &uihv_app_info_file_ops);
+ if (IS_ERR_OR_NULL(app_info)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ uihv_enable = swap_debugfs_create_file(UIHV_ENABLE,
+ UIHV_DEFAULT_PERMS, root, NULL,
+ &uihv_enable_file_ops);
+ if (IS_ERR_OR_NULL(uihv_enable)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ debugfs_remove_recursive(root);
+
+fail:
+ printk(UIHV_PREFIX "Debugfs initialization failure: %d\n", ret);
+
+ return ret;
+}
+
+void uihv_dfs_exit(void)
+{
+ if (uihv_root)
+ debugfs_remove_recursive(uihv_root);
+
+ uihv_root = NULL;
+}
--- /dev/null
+#ifndef __UIHV_DEBUGFS_H__
+#define __UIHV_DEBUGFS_H__
+
+int uihv_dfs_init(void);
+void uihv_dfs_exit(void);
+
+#endif /* __UIHV_DEBUGFS_H__ */
--- /dev/null
+#include <linux/namei.h>
+#include <us_manager/us_manager_common.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/callbacks.h>
+#include <us_manager/probes/probe_info_new.h>
+#include <us_manager/us_common_file.h>
+#include <writer/kernel_operations.h>
+#include <master/swap_initializer.h>
+#include <writer/swap_msg.h>
+#include <loader/loader.h>
+#include "uihv.h"
+#include "uihv_module.h"
+#include "uihv_debugfs.h"
+
+#define page_to_proc(page) ((page)->file->proc)
+#define ip_to_proc(ip) page_to_proc((ip)->page)
+#define urp_to_ip(rp) container_of(rp, struct sspt_ip, retprobe)
+
+static DEFINE_MUTEX(mutex_enable);
+
+static struct dentry *uihv_dentry = NULL;
+
+
+/* ============================================================================
+ * = ui_viewer =
+ * ============================================================================
+ */
+
+/* main handler for ui viewer */
+static int uihv_main_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static int uihv_main_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
+static struct probe_desc pin_main = MAKE_URPROBE(uihv_main_eh,
+ uihv_main_rh, 0);
+
+struct ui_viewer_data {
+ struct dentry *app_dentry;
+ struct probe_new p_main;
+ struct pf_group *pfg;
+ bool enable;
+};
+
+static struct ui_viewer_data __ui_data;
+
+static int uihv_data_inst(void)
+{
+ struct pf_group *pfg;
+
+ pfg = get_pf_group_by_dentry(__ui_data.app_dentry,
+ (void *)__ui_data.app_dentry);
+ if (!pfg)
+ return -ENOMEM;
+
+ __ui_data.pfg = pfg;
+
+ return 0;
+}
+
+int uihv_data_set(const char *app_path, unsigned long main_addr)
+{
+ struct dentry *dentry;
+
+ if (__ui_data.enable) {
+ pr_err("UIHV already enabled, can't set data\n");
+ return -EBUSY;
+ }
+
+ dentry = dentry_by_path(app_path);
+ if (dentry == NULL)
+ return -ENOENT;
+
+ __ui_data.app_dentry = dentry;
+ __ui_data.p_main.desc = &pin_main;
+ __ui_data.p_main.offset = main_addr;
+
+ return uihv_data_inst();
+}
+
+int uihv_set_handler(char *path)
+{
+ struct dentry *dentry;
+ int ret;
+
+ if (uihv_dentry != NULL) {
+ swap_put_dentry(uihv_dentry);
+ uihv_dentry = NULL;
+ }
+
+ dentry = swap_get_dentry(path);
+ if (dentry == NULL) {
+ printk(KERN_WARNING UIHV_PREFIX "Error! Cannot get handler %s\n",
+ path);
+ return -EINVAL;
+ }
+
+ ret = loader_add_handler(path);
+ if (ret != 0)
+ return ret;
+
+ uihv_dentry = dentry;
+
+ return 0;
+}
+
+
+
+/* ============================================================================
+ * = ui viewer handlers =
+ * ============================================================================
+ */
+static int uihv_main_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+ struct hd_t *hd;
+ unsigned long old_pc = swap_get_upc(regs);
+ unsigned long vaddr = 0;
+
+ if (uihv_dentry == NULL)
+ return 0;
+
+ hd = lpd_get_hd(pd, uihv_dentry);
+ if (hd == NULL)
+ return 0;
+
+ if (lpd_get_state(hd) == NOT_LOADED)
+ vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
+
+ loader_set_priv_origin(ri, vaddr);
+
+ /* PC change check */
+ return old_pc != swap_get_upc(regs);
+}
+
+static int uihv_main_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pd_t *pd = lpd_get_by_task(current);
+ struct hd_t *hd;
+
+ if (uihv_dentry == NULL)
+ return 0;
+
+ hd = lpd_get_hd(pd, uihv_dentry);
+ if (hd == NULL)
+ return 0;
+
+ if (lpd_get_state(hd) == LOADING)
+ loader_loading_ret(ri, regs, pd, hd);
+
+ return 0;
+}
+
+int uihv_enable(void)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_enable);
+ if (__ui_data.enable) {
+ pr_err("UIHV already enabled\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = pin_register(&__ui_data.p_main, __ui_data.pfg,
+ __ui_data.app_dentry);
+ if (ret)
+ goto out;
+
+ __ui_data.enable = true;
+
+out:
+ mutex_unlock(&mutex_enable);
+ return ret;
+}
+
+int uihv_disable(void)
+{
+ int ret = 0;
+
+ mutex_lock(&mutex_enable);
+ if (!__ui_data.enable) {
+ pr_err("UIHV already disabled\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ pin_unregister(&__ui_data.p_main, __ui_data.pfg);
+ put_pf_group(__ui_data.pfg);
+ __ui_data.pfg = NULL;
+ __ui_data.enable = false;
+
+out:
+ mutex_unlock(&mutex_enable);
+ return ret;
+}
+
+static int uihv_init(void)
+{
+ int ret;
+
+ ret = uihv_dfs_init();
+
+ return ret;
+}
+
+static void uihv_exit(void)
+{
+ if (uihv_dentry != NULL) {
+ swap_put_dentry(uihv_dentry);
+ uihv_dentry = NULL;
+ }
+
+ uihv_dfs_exit();
+}
+
+SWAP_LIGHT_INIT_MODULE(NULL, uihv_init, uihv_exit, NULL, NULL);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP UI Hierarchy Viewer");
+MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>, Anastasia Lypa");
--- /dev/null
+#ifndef __UIHV_MODULE_H__
+#define __UIHV_MODULE_H__
+
+int uihv_data_set(const char *app_path, unsigned long main_addr);
+int uihv_set_handler(char *path);
+int uihv_enable(void);
+int uihv_disable(void);
+
+#endif /* __UIHV_MODULE_H__ */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
+
+obj-m := swap_uprobe.o
+swap_uprobe-y := swap_uprobes.o
+
+### ARM
+swap_uprobe-$(CONFIG_ARM) += \
+ arch/arm/swap-asm/swap_uprobes.o \
+ ../arch/arm/probes/probes_thumb.o \
+ ../arch/arm/probes/decode_thumb.o \
+ ../arch/arm/probes/probes.o \
+ ../arch/arm/uprobe/swap_uprobe.o
+
+
+### ARM64
+swap_uprobe-$(CONFIG_ARM64) += \
+ arch/arm64/swap-asm/swap_uprobes.o \
+ arch/arm64/swap-asm/uprobes-arm64.o \
+ ../arch/arm/probes/probes_arm.o \
+ ../arch/arm/probes/decode_thumb.o \
+ ../arch/arm/probes/probes_thumb.o \
+ ../arch/arm/probes/probes.o \
+ ../arch/arm/uprobe/swap_uprobe.o
+
+
+### X86
+swap_uprobe-$(CONFIG_X86) += arch/x86/swap-asm/swap_uprobes.o \
+ arch/x86/swap-asm/swap_sc_patch.o
--- /dev/null
+/**
+ * uprobe/arch/asm-arm/swap_uprobes.c
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Arch-dependent uprobe interface implementation for ARM.
+ */
+
+
+#include <linux/init.h> /* need for asm/traps.h */
+#include <linux/sched.h> /* need for asm/traps.h */
+
+#include <linux/ptrace.h> /* need for asm/traps.h */
+#include <asm/traps.h>
+
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <uprobe/swap_uprobes.h>
+#include <arch/arm/probes/probes_arm.h>
+#include <arch/arm/probes/probes_thumb.h>
+#include <swap-asm/swap_kprobes.h>
+#include "swap_uprobes.h"
+
+
+/**
+ * @brief Prepares uprobe for ARM.
+ *
+ * @param up Pointer to the uprobe.
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_prepare_uprobe(struct uprobe *p)
+{
+ int ret;
+
+ ret = arch_prepare_uprobe_arm(p);
+ if (!ret) {
+ /* for uretprobe */
+ add_uprobe_table(p);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Analysis opcodes.
+ *
+ * @param rp Pointer to the uretprobe.
+ * @return Void.
+ */
+void arch_opcode_analysis_uretprobe(struct uretprobe *rp)
+{
+ /* Remove retprobe if first insn overwrites lr */
+ rp->thumb_noret = noret_thumb(rp->up.opcode);
+ rp->arm_noret = noret_arm(rp->up.opcode);
+}
+
+/**
+ * @brief Prepates uretprobe for ARM.
+ *
+ * @param ri Pointer to the uretprobe instance.
+ * @param regs Pointer to CPU register data.
+ * @return Error code.
+ */
+int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ unsigned long thumb, bp_offset;
+
+ thumb = ri->preload.use ? ri->preload.thumb : thumb_mode(regs);
+ bp_offset = thumb ? 0x1b : sizeof(long) * PROBES_TRAMP_RET_BREAK_IDX;
+
+ /* save original return address */
+ ri->ret_addr = (uprobe_opcode_t *)regs->ARM_lr;
+
+ /* replace return address with break point adddress */
+ regs->ARM_lr = (unsigned long)(ri->rp->up.insn) + bp_offset;
+
+ /* save stack pointer address */
+ ri->sp = (uprobe_opcode_t *)regs->ARM_sp;
+
+ /* Set flag of current mode */
+ ri->sp = (uprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
+
+ return 0;
+}
+
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+ /* Understand function mode */
+ return ((unsigned long)ri->sp & 1) ?
+ ((unsigned long)ri->rp->up.insn + 0x1b) :
+ (unsigned long)(ri->rp->up.insn +
+ PROBES_TRAMP_RET_BREAK_IDX);
+}
+
+/**
+ * @brief Disarms uretprobe instance.
+ *
+ * @param ri Pointer to the uretprobe instance
+ * @param task Pointer to the task for which the uretprobe instance
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ struct pt_regs *uregs = task_pt_regs(ri->task);
+ unsigned long ra = uregs->ARM_lr;
+ unsigned long *tramp = (unsigned long *)arch_tramp_by_ri(ri);
+ unsigned long *sp = (unsigned long *)((long)ri->sp & ~1);
+ unsigned long *stack = sp - URETPROBE_STACK_DEPTH + 1;
+ unsigned long *found = NULL;
+ unsigned long *buf[URETPROBE_STACK_DEPTH];
+ unsigned long vaddr = (unsigned long)ri->rp->up.addr;
+ int i, retval;
+
+ /* check stack */
+ retval = read_proc_vm_atomic(task, (unsigned long)stack,
+ buf, sizeof(buf));
+ if (retval != sizeof(buf)) {
+ printk(KERN_INFO "---> %s (%d/%d): failed to read "
+ "stack from %08lx\n", task->comm, task->tgid, task->pid,
+ (unsigned long)stack);
+ retval = -EFAULT;
+ goto check_lr;
+ }
+
+ /* search the stack from the bottom */
+ for (i = URETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
+ if (buf[i] == tramp) {
+ found = stack + i;
+ break;
+ }
+ }
+
+ if (!found) {
+ retval = -ESRCH;
+ goto check_lr;
+ }
+
+ printk(KERN_INFO "---> %s (%d/%d): trampoline found at "
+ "%08lx (%08lx /%+d) - %lx, set ret_addr=%p\n",
+ task->comm, task->tgid, task->pid,
+ (unsigned long)found, (unsigned long)sp,
+ found - sp, vaddr, ri->ret_addr);
+ retval = write_proc_vm_atomic(task, (unsigned long)found,
+ &ri->ret_addr,
+ sizeof(ri->ret_addr));
+ if (retval != sizeof(ri->ret_addr)) {
+ printk(KERN_INFO "---> %s (%d/%d): "
+ "failed to write value to %08lx",
+ task->comm, task->tgid, task->pid, (unsigned long)found);
+ retval = -EFAULT;
+ } else {
+ retval = 0;
+ }
+
+check_lr: /* check lr anyway */
+ if (ra == (unsigned long)tramp) {
+ printk(KERN_INFO "---> %s (%d/%d): trampoline found at "
+ "lr = %08lx - %lx, set ret_addr=%p\n",
+ task->comm, task->tgid, task->pid, ra, vaddr, ri->ret_addr);
+
+ swap_set_uret_addr(uregs, (unsigned long)ri->ret_addr);
+ retval = 0;
+ } else if (retval) {
+ printk(KERN_INFO "---> %s (%d/%d): trampoline NOT found at "
+ "sp = %08lx, lr = %08lx - %lx, ret_addr=%p\n",
+ task->comm, task->tgid, task->pid,
+ (unsigned long)sp, ra, vaddr, ri->ret_addr);
+ }
+
+ return retval;
+}
+
+/**
+ * @brief Jump pre-handler.
+ *
+ * @param p Pointer to the uprobe.
+ * @param regs Pointer to CPU register data.
+ * @return 0.
+ */
+int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct ujprobe *jp = container_of(p, struct ujprobe, up);
+ entry_point_t entry = (entry_point_t)jp->entry;
+
+ if (entry) {
+ entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
+ regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
+ } else {
+ arch_ujprobe_return();
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Gets trampoline address.
+ *
+ * @param p Pointer to the uprobe.
+ * @param regs Pointer to CPU register data.
+ * @return Trampoline address.
+ */
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
+{
+ return thumb_mode(regs) ?
+ (unsigned long)(p->insn) + 0x1b :
+ (unsigned long)(p->insn +
+ PROBES_TRAMP_RET_BREAK_IDX);
+}
+
+/**
+ * @brief Restores return address.
+ *
+ * @param orig_ret_addr Original return address.
+ * @param regs Pointer to CPU register data.
+ * @return Void.
+ */
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->ARM_lr = orig_ret_addr;
+ regs->ARM_pc = orig_ret_addr & ~0x1;
+
+ if (regs->ARM_lr & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+ else
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+}
+
+/**
+ * @brief Removes uprobe.
+ *
+ * @param up Pointer to the uprobe.
+ * @return Void.
+ */
+void arch_remove_uprobe(struct uprobe *up)
+{
+ swap_slot_free(up->sm, up->insn);
+}
+
+static int urp_handler(struct pt_regs *regs, pid_t tgid)
+{
+ struct uprobe *p;
+ unsigned long flags;
+ unsigned long vaddr = regs->ARM_pc;
+ unsigned long offset_bp = thumb_mode(regs) ?
+ 0x1a :
+ 4 * PROBES_TRAMP_RET_BREAK_IDX;
+ unsigned long tramp_addr = vaddr - offset_bp;
+
+ local_irq_save(flags);
+ p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
+ if (unlikely(p == NULL)) {
+ local_irq_restore(flags);
+
+ pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n",
+ vaddr);
+ return 1;
+ }
+
+ get_up(p);
+ local_irq_restore(flags);
+ trampoline_uprobe_handler(p, regs);
+ put_up(p);
+
+ return 0;
+}
+/**
+ * @brief Prepares singlestep for current CPU.
+ *
+ * @param p Pointer to kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return Void.
+ */
+static void arch_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
+{
+ if (p->ainsn.insn.handler) {
+ regs->ARM_pc += 4;
+ p->ainsn.insn.handler(p->opcode, &p->ainsn.insn, regs);
+ } else {
+ regs->ARM_pc = (unsigned long)p->insn;
+ }
+}
+
+/**
+ * @brief Breakpoint instruction handler.
+ *
+ * @param regs Pointer to CPU register data.
+ * @param instr Instruction.
+ * @return uprobe_handler results.
+ */
+static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+ int ret = 0;
+ struct uprobe *p;
+ unsigned long flags;
+ unsigned long vaddr = regs->ARM_pc | !!thumb_mode(regs);
+ pid_t tgid = current->tgid;
+
+ local_irq_save(flags);
+ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
+ get_up(p);
+ local_irq_restore(flags);
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ arch_prepare_singlestep(p, regs);
+ put_up(p);
+ } else {
+ local_irq_restore(flags);
+ ret = urp_handler(regs, tgid);
+
+ /* check ARM/THUMB CPU mode matches installed probe mode */
+ if (ret) {
+ vaddr ^= 1;
+
+ local_irq_save(flags);
+ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
+ get_up(p);
+ local_irq_restore(flags);
+ pr_err("invalid mode: thumb=%d addr=%p insn=%08x\n",
+ !!thumb_mode(regs), p->addr, p->opcode);
+ ret = 0;
+
+ disarm_uprobe(p, current);
+ put_up(p);
+ } else {
+ local_irq_restore(flags);
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* userspace probes hook (arm) */
+static struct undef_hook undef_hook_for_us_arm = {
+ .instr_mask = 0xffffffff,
+ .instr_val = BREAK_ARM,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = USR_MODE,
+ .fn = uprobe_trap_handler
+};
+
+/* userspace probes hook (thumb) */
+static struct undef_hook undef_hook_for_us_thumb = {
+ .instr_mask = 0xffffffff,
+ .instr_val = BREAK_THUMB,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = USR_MODE,
+ .fn = uprobe_trap_handler
+};
+
+/**
+ * @brief Installs breakpoint hooks.
+ *
+ * @return 0.
+ */
+int swap_arch_init_uprobes(void)
+{
+ swap_register_undef_hook(&undef_hook_for_us_arm);
+ swap_register_undef_hook(&undef_hook_for_us_thumb);
+
+ return 0;
+}
+
+/**
+ * @brief Uninstalls breakpoint hooks.
+ *
+ * @return Void.
+ */
+void swap_arch_exit_uprobes(void)
+{
+ swap_unregister_undef_hook(&undef_hook_for_us_thumb);
+ swap_unregister_undef_hook(&undef_hook_for_us_arm);
+}
--- /dev/null
+/**
+ * @file uprobe/arch/asm-arm/swap_uprobes.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Arch-dependent uprobe interface declaration.
+ */
+
+
+#ifndef _ARM_SWAP_UPROBES_H
+#define _ARM_SWAP_UPROBES_H
+
+
+#include <linux/uaccess.h>
+#include <arch/arm/probes/probes.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
+
+
+/** Uprobes trampoline length */
+#define UPROBES_TRAMP_LEN PROBES_TRAMP_LEN
+
+
+struct task_struct;
+struct uprobe;
+struct arch_insn;
+struct uretprobe;
+struct uretprobe_instance;
+
+typedef u32 uprobe_opcode_t;
+
+
+/**
+ * @struct arch_insn
+ * @brief Architecture depend copy of original instruction.
+ * @var arch_insn::insn
+ * Copy of the original instruction.
+ */
+struct arch_insn {
+ struct arch_insn_arm insn;
+};
+
+
+static inline u32 swap_get_urp_float(struct pt_regs *regs)
+{
+ return regs->ARM_r0;
+}
+
+static inline u64 swap_get_urp_double(struct pt_regs *regs)
+{
+
+ return regs->ARM_r0 | (u64)regs->ARM_r1 << 32;
+}
+
+static inline void arch_ujprobe_return(void)
+{
+}
+
+int arch_prepare_uprobe(struct uprobe *up);
+int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs);
+static inline int longjmp_break_uhandler(struct uprobe *p, struct pt_regs *regs)
+{
+ return 0;
+}
+
+#define arch_urp_check_opcode NULL /* Arch doesn't requires special callback */
+void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
+int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task);
+
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
+void arch_remove_uprobe(struct uprobe *up);
+
+static inline int arch_arm_uprobe(struct uprobe *p)
+{
+ return arch_arm_uprobe_arm(p);
+}
+
+static inline void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
+{
+ arch_disarm_uprobe_arm(p, task);
+}
+
+static inline unsigned long swap_get_upc(struct pt_regs *regs)
+{
+ return swap_get_upc_arm(regs);
+}
+
+static inline void swap_set_upc(struct pt_regs *regs, unsigned long val)
+{
+ swap_set_upc_arm(regs, val);
+}
+
+static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
+{
+ return swap_get_uarg_arm(regs, n);
+}
+
+static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
+ unsigned long val)
+{
+ swap_put_uarg_arm(regs, n, val);
+}
+
+static inline unsigned long swap_get_uret_addr(struct pt_regs *regs)
+{
+ return swap_get_uret_addr_arm(regs);
+}
+
+static inline void swap_set_uret_addr(struct pt_regs *regs, unsigned long val)
+{
+ swap_set_uret_addr_arm(regs, val);
+}
+
+int swap_arch_init_uprobes(void);
+void swap_arch_exit_uprobes(void);
+
+#endif /* _ARM_SWAP_UPROBES_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/kconfig.h>
+#include <linux/types.h>
+#include <asm/traps.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
+#include <uprobe/swap_uprobes.h>
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_td_raw.h>
+#include <kprobe/swap_kprobes_deps.h> /* FIXME: remove it */
+#include <swap-asm/swap_probes.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
+#include <swap-asm/dbg_interface.h>
+#include "uprobes-arm64.h"
+
+
+#define BRK_BP 0x45
+#define BRK_PSEUDO_SS 0x54
+#define BRK_URP 0x67
+#define BRK64_OPCODE_BP MAKE_BRK(BRK_BP)
+#define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS)
+#define BRK64_OPCODE_URP MAKE_BRK(BRK_URP)
+
+
+enum arch_mode {
+ AM_UNKNOWN,
+ AM_THUMB,
+ AM_ARM,
+ AM_ARM64
+};
+
+#define ARM64_MODE_VADDR_MASK ((unsigned long)1 << 63)
+
+static enum arch_mode get_arch_mode(unsigned long vaddr)
+{
+ if (vaddr & 1)
+ return AM_THUMB;
+
+ if (vaddr & ARM64_MODE_VADDR_MASK)
+ return AM_ARM64;
+
+ return AM_ARM;
+}
+
+static unsigned long get_real_addr(unsigned long vaddr)
+{
+ return vaddr & ~(ARM64_MODE_VADDR_MASK | 1);
+}
+
+static unsigned long get_64bit_addr(unsigned long raddr)
+{
+ return raddr | ARM64_MODE_VADDR_MASK;
+}
+
+struct uprobe_ctlblk {
+ struct uprobe *p;
+};
+
+static struct td_raw td_raw;
+
+static struct uprobe_ctlblk *current_ctlblk(void)
+{
+ return (struct uprobe_ctlblk *)swap_td_raw(&td_raw, current);
+}
+
+static struct uprobe *get_current_uprobe(void)
+{
+ return current_ctlblk()->p;
+}
+
+static void set_current_uprobe(struct uprobe *p)
+{
+ current_ctlblk()->p = p;
+}
+
+static void reset_current_uprobe(void)
+{
+ set_current_uprobe(NULL);
+}
+
+
+static unsigned long trampoline_addr_arm64(struct uprobe *p)
+{
+ return (unsigned long)(p->insn + URP_RET_BREAK_IDX);
+}
+
+static int prepare_uretprobe_arm64(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ unsigned long bp_addr, ret_addr;
+
+ ret_addr = regs->regs[30] | ARM64_MODE_VADDR_MASK;
+ bp_addr = trampoline_addr_arm64(&ri->rp->up);
+
+ ri->sp = (uprobe_opcode_t *)regs->sp;
+ ri->ret_addr = (uprobe_opcode_t *)ret_addr;
+
+ /* replace the return address (regs[30] - lr) */
+ regs->regs[30] = bp_addr;
+
+ return 0;
+}
+
+int arch_prepare_uretprobe(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
+ return prepare_uretprobe_arm64(ri, regs);
+ else
+ return prepare_uretprobe_arm(ri, regs);
+}
+
+static void arch_opcode_analysis_uretprobe_arm64(struct uretprobe *rp)
+{
+ /* FIXME: to implement */
+}
+
+void arch_opcode_analysis_uretprobe(struct uretprobe *rp)
+{
+ if (get_arch_mode((unsigned long)rp->up.addr) == AM_ARM64)
+ arch_opcode_analysis_uretprobe_arm64(rp);
+ else
+ arch_opcode_analysis_uretprobe_arm(rp);
+}
+
+/**
+ * @brief Gets trampoline address.
+ *
+ * @param p Pointer to the kprobe.
+ * @param regs Pointer to CPU register data.
+ * @return Trampoline address.
+ */
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
+{
+ if (get_arch_mode((unsigned long)p->addr) == AM_ARM64)
+ return trampoline_addr_arm64(p);
+ else
+ return arch_get_trampoline_addr_arm(p, regs);
+}
+
+static unsigned long arch_tramp_by_ri_arm64(struct uretprobe_instance *ri)
+{
+ return trampoline_addr_arm64(&ri->rp->up);
+}
+
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+ if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
+ return arch_tramp_by_ri_arm64(ri);
+ else
+ return arch_tramp_by_ri_arm(ri);
+}
+
+static int arch_disarm_urp_inst_arm64(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ struct pt_regs *uregs = task_pt_regs(ri->task);
+ u64 ra = uregs->regs[30];
+ u64 raddr, tramp, found = 0;
+ u64 sp = (u64)ri->sp;
+ u64 ret_addr = (u64)ri->ret_addr;
+ u64 stack = sp - 8 * (URETPROBE_STACK_DEPTH + 1);
+ u64 buf[URETPROBE_STACK_DEPTH];
+ int i, ret;
+
+ raddr = get_real_addr((unsigned long)ri->rp->up.addr);
+ tramp = arch_tramp_by_ri_arm64(ri);
+
+ /* check stack */
+ ret = read_proc_vm_atomic(task, stack, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ pr_info("---> %s (%d/%d): failed to read stack from %016llx\n",
+ task->comm, task->tgid, task->pid, stack);
+ ret = -EFAULT;
+ goto check_lr;
+ }
+
+ /* search the stack from the bottom */
+ for (i = URETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
+ if (buf[i] == tramp) {
+ found = stack + 8 * i;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret = -ESRCH;
+ goto check_lr;
+ }
+
+ pr_info("---> %s (%d/%d): trampoline found at "
+ "%016llx (%016llx /%+lld) - %llx, set ret_addr=%016llx\n",
+ task->comm, task->tgid, task->pid,
+ found, sp,
+ found - sp, raddr, ret_addr);
+ ret = write_proc_vm_atomic(task, found, &ret_addr, 8);
+ if (ret != 8) {
+ pr_info("---> %s (%d/%d): failed to write value to %016llx",
+ task->comm, task->tgid, task->pid, found);
+ ret = -EFAULT;
+ } else {
+ ret = 0;
+ }
+
+check_lr: /* check lr anyway */
+ if (ra == tramp) {
+ pr_info("---> %s (%d/%d): trampoline found at "
+ "lr = %016llx - %llx, set ret_addr=%016llx\n",
+ task->comm, task->tgid, task->pid, ra, raddr,
+ ret_addr);
+
+ /* set ret_addr */
+ uregs->regs[30] = ret_addr;
+ ret = 0;
+ } else if (ret) {
+ pr_info("---> %s (%d/%d): trampoline NOT found at "
+ "sp=%016llx, lr=%016llx - %llx, ret_addr=%016llx\n",
+ task->comm, task->tgid, task->pid,
+ sp, ra, raddr, ret_addr);
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Disarms uretprobe instance.
+ *
+ * @param ri Pointer to the uretprobe instance
+ * @param task Pointer to the task for which the uretprobe instance
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
+ return arch_disarm_urp_inst_arm64(ri, task);
+ else
+ return arch_disarm_urp_inst_arm(ri, task);
+}
+
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ if (get_arch_mode(orig_ret_addr) == AM_ARM64) {
+ regs->pc = get_real_addr(orig_ret_addr);
+ } else {
+ set_orig_ret_addr_arm(orig_ret_addr, regs);
+ }
+}
+
+static enum dbg_code urp_handler(struct pt_regs *regs, unsigned int esr)
+{
+ struct uprobe *p;
+ unsigned long pc = regs->pc;
+ unsigned long insn_addr = pc - sizeof(u32) * URP_RET_BREAK_IDX;
+
+ p = get_uprobe_by_insn_slot((void *)insn_addr, current->tgid, regs);
+ if (p == NULL) {
+ pr_err("no_uretprobe: Not one of ours: let "
+ "kernel handle it %lx\n", pc);
+ return DBG_ERROR;
+ }
+
+ local_irq_enable();
+ trampoline_uprobe_handler(p, regs);
+
+ return DBG_HANDLED;
+}
+
+static int arch_arm_uprobe_arm64(struct uprobe *p)
+{
+ int ret;
+ unsigned long vaddr = (unsigned long)p->addr;
+ unsigned long raddr = get_real_addr(vaddr);
+ u32 insn = BRK64_OPCODE_BP;
+
+ ret = write_proc_vm_atomic(p->task, raddr, &insn, sizeof(insn));
+ if (!ret) {
+ pr_err("failed to write memory addr=%lx\n", vaddr);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static void arch_disarm_uprobe_arm64(struct uprobe *p,
+ struct task_struct *task)
+{
+ unsigned long vaddr = (unsigned long)p->addr;
+ unsigned long raddr = get_real_addr(vaddr);
+ int ret;
+
+ ret = write_proc_vm_atomic(task, raddr, &p->opcode, sizeof(p->opcode));
+ if (!ret)
+ pr_err("failed to write memory vaddr=%lx\n", vaddr);
+}
+
+int arch_arm_uprobe(struct uprobe *p)
+{
+ int ret;
+ unsigned long vaddr = (unsigned long)p->addr;
+
+ switch (get_arch_mode(vaddr)) {
+ case AM_THUMB:
+ case AM_ARM:
+ ret = arch_arm_uprobe_arm(p);
+ break;
+ case AM_ARM64:
+ ret = arch_arm_uprobe_arm64(p);
+ break;
+ default:
+ pr_err("Error: unknown mode vaddr=%lx\n", vaddr);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
+{
+ unsigned long vaddr = (unsigned long)p->addr;
+
+ switch (get_arch_mode(vaddr)) {
+ case AM_THUMB:
+ case AM_ARM:
+ arch_disarm_uprobe_arm(p, task);
+ break;
+ case AM_ARM64:
+ arch_disarm_uprobe_arm64(p, task);
+ break;
+ default:
+ pr_err("Error: unknown mode vaddr=%lx\n", vaddr);
+ break;
+ }
+}
+
+
+static void arch_prepare_simulate_arm64(struct uprobe *p)
+{
+ if (p->ainsn.prepare)
+ p->ainsn.prepare(p, &p->ainsn);
+}
+
+static int arch_prepare_ss_arm64(struct uprobe *p, u32 insn, u32 __user *utramp)
+{
+ u32 tramp[2];
+
+ /* prepare insn slot */
+ tramp[0] = insn;
+ tramp[1] = BRK64_OPCODE_PSEUDO_SS;
+
+ if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp, 8)) {
+ pr_err("%s: failed to write memory %p\n", __func__, utramp);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static int arch_uretprobe_set_break_arm64(struct uprobe *p, u32 __user *utramp)
+{
+ int ret;
+ u32 urp_brk = BRK64_OPCODE_URP;
+ unsigned long brk_addr = (unsigned long)(utramp + URP_RET_BREAK_IDX);
+
+ ret = write_proc_vm_atomic(p->task, brk_addr, &urp_brk, sizeof(urp_brk));
+ if (ret != sizeof(urp_brk)) {
+ pr_err("%s failed to write memory %016lx\n",
+ __func__, brk_addr);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static int arch_prepare_uprobe_arm64(struct uprobe *p)
+{
+ int ret = 0;
+ struct task_struct *task = p->task;
+ unsigned long vaddr = (unsigned long)p->addr;
+ unsigned long raddr = get_real_addr(vaddr);
+ u32 insn, __user *utramp;
+
+ ret = read_proc_vm_atomic(task, raddr, &insn, sizeof(insn));
+ if (ret != sizeof(insn)) {
+ pr_err("failed to read memory %lx!\n", raddr);
+ return -EINVAL;
+ }
+
+ utramp = swap_slot_alloc(p->sm);
+ if (utramp == NULL) {
+ pr_err("ERROR: cannot allocate trampoline (%016lx)\n", vaddr);
+ return -ENOMEM;
+ }
+
+ switch (arm64_uprobe_decode_insn(insn, &p->ainsn)) {
+ case INSN_REJECTED: /* insn not supported */
+ ret = -EINVAL;
+ break;
+
+ case INSN_GOOD_NO_SLOT: /* insn need simulation */
+ arch_prepare_simulate_arm64(p);
+ ret = 0;
+ break;
+
+ case INSN_GOOD: /* instruction uses slot */
+ ret = arch_prepare_ss_arm64(p, insn, utramp);
+ break;
+ };
+
+ if (ret)
+ goto fail;
+
+ ret = arch_uretprobe_set_break_arm64(p, utramp);
+ if (ret)
+ goto fail;
+
+ p->insn = utramp;
+ p->opcode = insn;
+
+ return 0;
+
+fail:
+ swap_slot_free(p->sm, utramp);
+ return ret;
+}
+
+int arch_prepare_uprobe(struct uprobe *p)
+{
+ int ret;
+
+ if (get_arch_mode((unsigned long)p->addr) == AM_ARM64) {
+ ret = arch_prepare_uprobe_arm64(p);
+ } else {
+ ret = arch_prepare_uprobe_arm(p);
+ }
+
+ if (!ret) {
+ /* for uretprobe */
+ add_uprobe_table(p);
+ }
+
+ return ret;
+}
+
+void arch_remove_uprobe(struct uprobe *p)
+{
+ swap_slot_free(p->sm, p->insn);
+}
+
+static void simulate_insn_arm64(struct uprobe *p, struct pt_regs *regs)
+{
+ if (p->ainsn.handler)
+ p->ainsn.handler(p->opcode, (long)p->addr, regs);
+
+ reset_current_uprobe();
+}
+
+static void setup_ss_arm64(struct uprobe *p, struct pt_regs *regs)
+{
+ /* set trampoline */
+ regs->pc = (u64)p->insn;
+
+ set_current_uprobe(p);
+}
+
+static void setup_singlestep(struct uprobe *p, struct pt_regs *regs)
+{
+ if (p->ainsn.handler)
+ simulate_insn_arm64(p, regs);
+ else
+ setup_ss_arm64(p, regs);
+}
+
+static enum dbg_code uprobe_handler_compat(struct pt_regs *regs)
+{
+ pr_err("ARM and THUMB modes not supported\n");
+ return DBG_ERROR;
+}
+
+static enum dbg_code uprobe_handler_arm64(struct pt_regs *regs)
+{
+ struct uprobe *p;
+
+ p = get_uprobe((void *)get_64bit_addr(regs->pc), current->tgid);
+ if (p) {
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ setup_singlestep(p, regs);
+ } else {
+ return DBG_ERROR;
+ }
+
+ return DBG_HANDLED;
+}
+
+static enum dbg_code uprobe_handler(struct pt_regs *regs, unsigned int esr)
+{
+ local_irq_enable();
+
+ return compat_user_mode(regs) ?
+ uprobe_handler_compat(regs) :
+ uprobe_handler_arm64(regs);
+}
+
+static enum dbg_code uprobe_ss_handler(struct pt_regs *regs, unsigned int esr)
+{
+ struct uprobe *p;
+
+ p = get_current_uprobe();
+ if (p) {
+ regs->pc = (u64)p->addr + 4;
+ reset_current_uprobe();
+ }
+
+ return DBG_HANDLED;
+}
+
+
+static struct brk_hook dbg_up_bp = {
+ .spsr_mask = PSR_MODE_MASK,
+ .spsr_val = PSR_MODE_EL0t,
+ .esr_mask = DBG_BRK_ESR_MASK,
+ .esr_val = DBG_BRK_ESR(BRK_BP),
+ .fn = uprobe_handler,
+};
+
+static struct brk_hook dbg_up_ss = {
+ .spsr_mask = PSR_MODE_MASK,
+ .spsr_val = PSR_MODE_EL0t,
+ .esr_mask = DBG_BRK_ESR_MASK,
+ .esr_val = DBG_BRK_ESR(BRK_PSEUDO_SS),
+ .fn = uprobe_ss_handler,
+};
+
+static struct brk_hook dbg_urp_bp = {
+ .spsr_mask = PSR_MODE_MASK,
+ .spsr_val = PSR_MODE_EL0t,
+ .esr_mask = DBG_BRK_ESR_MASK,
+ .esr_val = DBG_BRK_ESR(BRK_URP),
+ .fn = urp_handler,
+};
+
+
+static void arch_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
+{
+ if (p->ainsn.insn.handler) {
+ regs->pc += 4;
+ p->ainsn.insn.handler(p->opcode, &p->ainsn.insn, regs);
+ } else {
+ regs->pc = (unsigned long)p->insn;
+ }
+}
+
+static int urp_handler_aarch32(struct pt_regs *regs, pid_t tgid)
+{
+ struct uprobe *p;
+ unsigned long vaddr = regs->pc;
+ unsigned long offset_bp = compat_thumb_mode(regs) ?
+ 0x1a :
+ 4 * PROBES_TRAMP_RET_BREAK_IDX;
+ unsigned long tramp_addr = vaddr - offset_bp;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
+ if (unlikely(p == NULL)) {
+ local_irq_restore(flags);
+
+ pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n",
+ vaddr);
+ return 1;
+ }
+
+ get_up(p);
+ local_irq_restore(flags);
+ trampoline_uprobe_handler(p, regs);
+ put_up(p);
+
+ return 0;
+}
+
+static int uprobe_handler_aarch32(struct pt_regs *regs, u32 instr)
+{
+ int ret = 0;
+ struct uprobe *p;
+ unsigned long flags;
+ unsigned long vaddr = regs->pc | !!compat_thumb_mode(regs);
+ pid_t tgid = current->tgid;
+
+ local_irq_enable();
+
+ local_irq_save(flags);
+ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
+ get_up(p);
+ local_irq_restore(flags);
+
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ arch_prepare_singlestep(p, regs);
+
+ put_up(p);
+ } else {
+ local_irq_restore(flags);
+ ret = urp_handler_aarch32(regs, tgid);
+
+ /* check ARM/THUMB CPU mode matches installed probe mode */
+ if (ret == 1) {
+ vaddr ^= 1;
+
+ local_irq_save(flags);
+ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
+ get_up(p);
+ local_irq_restore(flags);
+ pr_err("invalid mode: thumb=%d addr=%p insn=%08x\n",
+ !!compat_thumb_mode(regs), p->addr, p->opcode);
+ ret = 0;
+
+ disarm_uprobe(p, current);
+ put_up(p);
+ } else {
+ local_irq_restore(flags);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+static void (*__register_undef_hook)(struct undef_hook *hook);
+static void (*__unregister_undef_hook)(struct undef_hook *hook);
+
+static int undef_hook_once(void)
+{
+ const char *sym;
+
+ sym = "register_undef_hook";
+ __register_undef_hook = (void *)swap_ksyms(sym);
+ if (__register_undef_hook == NULL)
+ goto not_found;
+
+ sym = "unregister_undef_hook";
+ __unregister_undef_hook = (void *)swap_ksyms(sym);
+ if (__unregister_undef_hook == NULL)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ pr_err("ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+
+}
+
+static struct undef_hook undef_hook_arm = {
+ .instr_mask = 0xffffffff,
+ .instr_val = BREAK_ARM,
+ .pstate_mask = COMPAT_PSR_MODE_MASK,
+ .pstate_val = COMPAT_PSR_MODE_USR,
+ .fn = uprobe_handler_aarch32,
+};
+
+static struct undef_hook undef_hook_thumb = {
+ .instr_mask = 0xffff,
+ .instr_val = BREAK_THUMB,
+ .pstate_mask = COMPAT_PSR_MODE_MASK,
+ .pstate_val = COMPAT_PSR_MODE_USR,
+ .fn = uprobe_handler_aarch32,
+};
+
+int swap_arch_init_uprobes(void)
+{
+ int ret;
+
+ ret = undef_hook_once();
+ if (ret)
+ return ret;
+
+ ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk));
+ if (ret)
+ return ret;
+
+ if (!IS_ENABLED(CONFIG_SWAP_KERNEL_IMMUTABLE)) {
+ /* for aarch64 */
+ dbg_brk_hook_reg(&dbg_up_ss);
+ dbg_brk_hook_reg(&dbg_up_bp);
+ dbg_brk_hook_reg(&dbg_urp_bp);
+ } else {
+ pr_err("64-bit uprobes doesn't supported in case of Immutable kernel\n");
+ }
+
+ /* for aarch32 */
+ __register_undef_hook(&undef_hook_arm);
+ __register_undef_hook(&undef_hook_thumb);
+
+ return 0;
+}
+
+void swap_arch_exit_uprobes(void)
+{
+ /* for aarch32 */
+ __unregister_undef_hook(&undef_hook_thumb);
+ __unregister_undef_hook(&undef_hook_arm);
+
+ if (!IS_ENABLED(CONFIG_SWAP_KERNEL_IMMUTABLE)) {
+ /* for aarch64 */
+ dbg_brk_hook_unreg(&dbg_urp_bp);
+ dbg_brk_hook_unreg(&dbg_up_bp);
+ dbg_brk_hook_unreg(&dbg_up_ss);
+ }
+
+ swap_td_raw_unreg(&td_raw);
+}
--- /dev/null
+#ifndef _ASM_ARM64_UPROBES_H
+#define _ASM_ARM64_UPROBES_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <arch/arm/probes/probes.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
+
+
+#define UP_TRAMP_INSN_CNT 3 /* | opcode | ss_bp | urp_bp | */
+#define UPROBES_TRAMP_LEN (UP_TRAMP_INSN_CNT * 4) /* 4 - instruction size */
+#define URP_RET_BREAK_IDX 2
+
+
+struct uprobe;
+struct arch_insn;
+struct uretprobe;
+struct task_struct;
+struct uretprobe_instance;
+
+
+typedef unsigned long (uprobes_pstate_check_t)(unsigned long pstate);
+typedef unsigned long (uprobes_condition_check_t)(struct uprobe *p,
+ struct pt_regs *regs);
+typedef void (uprobes_prepare_t)(struct uprobe *p, struct arch_insn *asi);
+typedef void (uprobes_handler_t)(u32 opcode, long addr, struct pt_regs *regs);
+
+
+struct arch_insn {
+ /* arm */
+ struct arch_insn_arm insn;
+
+ /* arm64 */
+ uprobes_pstate_check_t *pstate_cc;
+ uprobes_condition_check_t *check_condn;
+ uprobes_prepare_t *prepare;
+ uprobes_handler_t *handler;
+};
+
+
+typedef u32 uprobe_opcode_t;
+
+static inline u32 swap_get_float(struct pt_regs *regs, unsigned long n)
+{
+ u32 *ptr;
+ register unsigned long w0 asm ("w0");
+
+ switch (n) {
+ case 0:
+ asm volatile("fmov w0, s0");
+ break;
+ case 1:
+ asm volatile("fmov w0, s1");
+ break;
+ case 2:
+ asm volatile("fmov w0, s2");
+ break;
+ case 3:
+ asm volatile("fmov w0, s3");
+ break;
+ case 4:
+ asm volatile("fmov w0, s4");
+ break;
+ case 5:
+ asm volatile("fmov w0, s5");
+ break;
+ case 6:
+ asm volatile("fmov w0, s6");
+ break;
+ case 7:
+ asm volatile("fmov w0, s7");
+ break;
+ default:
+ w0 = 0;
+ ptr = (u32 *)((u64 *)regs->sp + n - 8);
+ if (get_user(w0, ptr))
+ pr_err("failed to dereference a pointer\n");
+
+ break;
+ }
+
+ return w0;
+}
+
+static inline u64 swap_get_double(struct pt_regs *regs, unsigned long n)
+{
+ u64 *ptr;
+ register unsigned long x0 asm ("x0");
+
+ switch (n) {
+ case 0:
+ asm volatile("fmov x0, d0");
+ break;
+ case 1:
+ asm volatile("fmov x0, d1");
+ break;
+ case 2:
+ asm volatile("fmov x0, d2");
+ break;
+ case 3:
+ asm volatile("fmov x0, d3");
+ break;
+ case 4:
+ asm volatile("fmov x0, d4");
+ break;
+ case 5:
+ asm volatile("fmov x0, d5");
+ break;
+ case 6:
+ asm volatile("fmov x0, d6");
+ break;
+ case 7:
+ asm volatile("fmov x0, d7");
+ break;
+ default:
+ x0 = 0;
+ ptr = (u64 *)regs->sp + n - 8;
+ if (get_user(x0, ptr))
+ pr_err("failed to dereference a pointer\n");
+
+ break;
+ }
+
+ return x0;
+}
+
+static inline u32 swap_get_urp_float(struct pt_regs *regs)
+{
+ return swap_get_float(regs, 0);
+}
+
+static inline u64 swap_get_urp_double(struct pt_regs *regs)
+{
+ return swap_get_double(regs, 0);
+}
+
+static inline unsigned long swap_get_upc_arm64(struct pt_regs *regs)
+{
+ return regs->pc;
+}
+
+static inline void swap_set_upc_arm64(struct pt_regs *regs, unsigned long val)
+{
+ regs->pc = val;
+}
+
+static inline unsigned long swap_get_uarg_arm64(struct pt_regs *regs,
+ unsigned long n)
+{
+ u64 *ptr, val;
+
+ if (n < 8)
+ return regs->regs[n];
+
+ ptr = (u64 *)regs->sp + n - 8;
+ if (get_user(val, ptr))
+ pr_err("failed to dereference a pointer, ptr=%p\n", ptr);
+
+ return val;
+}
+
+static inline void swap_put_uarg_arm64(struct pt_regs *regs, unsigned long n,
+ unsigned long val)
+{
+ if (n < 8) {
+ regs->regs[n] = val;
+ } else {
+ u64 *ptr = (u64 *)regs->sp + n - 8;
+ if (put_user(val, ptr))
+ pr_err("Failed to dereference a pointer, ptr=%p\n", ptr);
+ }
+}
+
+static inline unsigned long swap_get_uret_addr_arm64(struct pt_regs *regs)
+{
+ return regs->regs[30];
+}
+
+static inline void swap_set_uret_addr_arm64(struct pt_regs *regs,
+ unsigned long val)
+{
+ regs->regs[30] = val;
+}
+
+static inline unsigned long swap_get_upc(struct pt_regs *regs)
+{
+ if (compat_user_mode(regs))
+ return swap_get_upc_arm(regs);
+ else
+ return swap_get_upc_arm64(regs);
+}
+
+static inline void swap_set_upc(struct pt_regs *regs, unsigned long val)
+{
+ if (compat_user_mode(regs))
+ swap_set_upc_arm(regs, val);
+ else
+ swap_set_upc_arm64(regs, val);
+}
+
+static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
+{
+ if (compat_user_mode(regs))
+ return swap_get_uarg_arm(regs, n);
+ else
+ return swap_get_uarg_arm64(regs, n);
+}
+
+static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
+ unsigned long val)
+{
+ if (compat_user_mode(regs))
+ return swap_put_uarg_arm(regs, n, val);
+ else
+ return swap_put_uarg_arm64(regs, n, val);
+}
+
+static inline unsigned long swap_get_uret_addr(struct pt_regs *regs)
+{
+ if (compat_user_mode(regs))
+ return swap_get_uret_addr_arm(regs);
+ else
+ return swap_get_uret_addr_arm64(regs);
+}
+
+static inline void swap_set_uret_addr(struct pt_regs *regs, unsigned long val)
+{
+ if (compat_user_mode(regs))
+ swap_set_uret_addr_arm(regs, val);
+ else
+ swap_set_uret_addr_arm64(regs, val);
+}
+
+int arch_prepare_uprobe(struct uprobe *p);
+void arch_remove_uprobe(struct uprobe *p);
+
+static inline int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0;
+}
+
+static inline int longjmp_break_uhandler(struct uprobe *p,
+ struct pt_regs *regs)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0;
+}
+
+
+int arch_arm_uprobe(struct uprobe *p);
+void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
+
+
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
+int arch_prepare_uretprobe(struct uretprobe_instance *ri,
+ struct pt_regs *regs);
+
+#define arch_urp_check_opcode NULL /* Arch doesn't requires special callback */
+void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task);
+
+static inline void arch_ujprobe_return(void)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+}
+
+int swap_arch_init_uprobes(void);
+void swap_arch_exit_uprobes(void);
+
+
+#endif /* _ASM_ARM64_UPROBES_H */
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/kprobes-arm64.c
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+
+#include <asm/ptrace.h>
+#include <uprobe/swap_uprobes.h>
+#include <swap-asm/simulate-insn.h>
+#include <swap-asm/condn-helpers.h>
+
+#include "uprobes-decode.h"
+#include "uprobes-arm64.h"
+
+
+/*
+ * condition check functions for uprobes simulation
+ */
+static unsigned long __check_pstate(struct uprobe *p, struct pt_regs *regs)
+{
+ struct arch_insn *asi = &p->ainsn;
+ unsigned long pstate = regs->pstate & 0xffffffff;
+
+ return asi->pstate_cc(pstate);
+}
+
+static unsigned long __check_cbz(struct uprobe *p, struct pt_regs *regs)
+{
+ return check_cbz((u32)p->opcode, regs);
+}
+
+static unsigned long __check_cbnz(struct uprobe *p, struct pt_regs *regs)
+{
+ return check_cbnz((u32)p->opcode, regs);
+}
+
+static unsigned long __check_tbz(struct uprobe *p, struct pt_regs *regs)
+{
+ return check_tbz((u32)p->opcode, regs);
+}
+
+static unsigned long __check_tbnz(struct uprobe *p, struct pt_regs *regs)
+{
+ return check_tbnz((u32)p->opcode, regs);
+}
+
+/*
+ * prepare functions for instruction simulation
+ */
+static void prepare_none(struct uprobe *p, struct arch_insn *asi)
+{
+}
+
+static void prepare_bcond(struct uprobe *p, struct arch_insn *asi)
+{
+ uprobe_opcode_t insn = p->opcode;
+
+ asi->check_condn = __check_pstate;
+ asi->pstate_cc = probe_condition_checks[insn & 0xf];
+}
+
+static void prepare_cbz_cbnz(struct uprobe *p, struct arch_insn *asi)
+{
+ uprobe_opcode_t insn = p->opcode;
+
+ asi->check_condn = (insn & (1 << 24)) ? __check_cbnz : __check_cbz;
+}
+
+static void prepare_tbz_tbnz(struct uprobe *p, struct arch_insn *asi)
+{
+ uprobe_opcode_t insn = p->opcode;
+
+ asi->check_condn = (insn & (1 << 24)) ? __check_tbnz : __check_tbz;
+}
+
+
+/* Load literal (PC-relative) instructions
+ * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
+ *
+ * opcode[26]: V=0, Load GP registers, simulate them.
+ * Encoding: xx01 1000 xxxx xxxx xxxx xxxx xxxx xxxx
+ * opcode[31:30]: op = 00, 01 - LDR literal
+ * opcode[31:30]: op = 10, - LDRSW literal
+ *
+ * 1. V=1 -Load FP/AdvSIMD registers
+ * Encoding: xx01 1100 xxxx xxxx xxxx xxxx xxxx xxxx
+ * 2. V=0,opc=11 -PRFM(Prefetch literal)
+ * Encoding: 1101 1000 xxxx xxxx xxxx xxxx xxxx xxxx
+ * Reject FP/AdvSIMD literal load & PRFM literal.
+ */
+static const struct aarch64_decode_item load_literal_subtable[] = {
+ DECODE_REJECT(0x1C000000, 0x3F000000),
+ DECODE_REJECT(0xD8000000, 0xFF000000),
+ DECODE_LITERAL(0x18000000, 0xBF000000, prepare_none,
+ simulate_ldr_literal),
+ DECODE_LITERAL(0x98000000, 0xFF000000, prepare_none,
+ simulate_ldrsw_literal),
+ DECODE_END,
+};
+
+/* AArch64 instruction decode table for kprobes:
+ * The instruction will fall into one of the 3 groups:
+ * 1. Single stepped out-of-the-line slot.
+ * -Most instructions fall in this group, those does not
+ * depend on PC address.
+ *
+ * 2. Should be simulated because of PC-relative/literal access.
+ * -All branching and PC-relative insrtcutions are simulated
+ * in C code, making use of saved pt_regs
+ * Catch: SIMD/NEON register context are not saved while
+ * entering debug exception, so are rejected for now.
+ *
+ * 3. Cannot be probed(not safe) so are rejected.
+ * - Exception generation and exception return instructions
+ * - Exclusive monitor(LDREX/STREX family)
+ *
+ */
+static const struct aarch64_decode_item aarch64_decode_table[] = {
+ /*
+ * Data processing - PC relative(literal) addressing:
+ * Encoding: xxx1 0000 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_LITERAL(0x10000000, 0x1F000000, prepare_none,
+ simulate_adr_adrp),
+
+ /*
+ * Data processing - Add/Substract Immediate:
+ * Encoding: xxx1 0001 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x11000000, 0x1F000000),
+
+ /*
+ * Data processing
+ * Encoding:
+ * xxx1 0010 0xxx xxxx xxxx xxxx xxxx xxxx (Logical)
+ * xxx1 0010 1xxx xxxx xxxx xxxx xxxx xxxx (Move wide)
+ * xxx1 0011 0xxx xxxx xxxx xxxx xxxx xxxx (Bitfield)
+ * xxx1 0011 1xxx xxxx xxxx xxxx xxxx xxxx (Extract)
+ */
+ DECODE_SINGLESTEP(0x12000000, 0x1E000000),
+
+ /*
+ * Data processing - SIMD/FP/AdvSIMD/Crypto-AES/SHA
+ * Encoding: xxx0 111x xxxx xxxx xxxx xxxx xxxx xxxx
+ * Encoding: xxx1 111x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x0E000000, 0x0E000000),
+
+ /*
+ * Data processing - Register
+ * Encoding: xxxx 101x xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x0A000000, 0x0E000000),
+
+ /* Branching Instructions
+ *
+ * Encoding:
+ * x001 01xx xxxx xxxx xxxx xxxx xxxx xxxx (uncondtional Branch)
+ * x011 010x xxxx xxxx xxxx xxxx xxxx xxxx (compare & branch)
+ * x011 011x xxxx xxxx xxxx xxxx xxxx xxxx (Test & Branch)
+ * 0101 010x xxxx xxxx xxxx xxxx xxxx xxxx (Conditional, immediate)
+ * 1101 011x xxxx xxxx xxxx xxxx xxxx xxxx (Unconditional,register)
+ */
+ DECODE_BRANCH(0x14000000, 0x7C000000, prepare_none,
+ simulate_b_bl),
+ DECODE_BRANCH(0x34000000, 0x7E000000, prepare_cbz_cbnz,
+ simulate_cbz_cbnz),
+ DECODE_BRANCH(0x36000000, 0x7E000000, prepare_tbz_tbnz,
+ simulate_tbz_tbnz),
+ DECODE_BRANCH(0x54000000, 0xFE000000, prepare_bcond,
+ simulate_b_cond),
+ DECODE_BRANCH(0xD6000000, 0xFE000000, prepare_none,
+ simulate_br_blr_ret),
+
+ /* System insn:
+ * Encoding: 1101 0101 00xx xxxx xxxx xxxx xxxx xxxx
+ *
+ * Note: MSR immediate (update PSTATE daif) is not safe handling
+ * within kprobes, rejected.
+ *
+ * Don't re-arrange these decode table entries.
+ */
+ DECODE_REJECT(0xD500401F, 0xFFF8F01F),
+ DECODE_SINGLESTEP(0xD5000000, 0xFFC00000),
+
+ /* Exception Generation:
+ * Encoding: 1101 0100 xxxx xxxx xxxx xxxx xxxx xxxx
+ * Instructions: SVC, HVC, SMC, BRK, HLT, DCPS1, DCPS2, DCPS3
+ */
+ DECODE_REJECT(0xD4000000, 0xFF000000),
+
+ /*
+ * Load/Store - Exclusive monitor
+ * Encoding: xx00 1000 xxxx xxxx xxxx xxxx xxxx xxxx
+ *
+ * Reject exlusive monitor'ed instructions
+ */
+ DECODE_REJECT(0x08000000, 0x3F000000),
+
+ /*
+ * Load/Store - PC relative(literal):
+ * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_TABLE(0x18000000, 0x3B000000, load_literal_subtable),
+
+ /*
+ * Load/Store - Register Pair
+ * Encoding:
+ * xx10 1x00 0xxx xxxx xxxx xxxx xxxx xxxx
+ * xx10 1x00 1xxx xxxx xxxx xxxx xxxx xxxx
+ * xx10 1x01 0xxx xxxx xxxx xxxx xxxx xxxx
+ * xx10 1x01 1xxx xxxx xxxx xxxx xxxx xxxx
+ */
+ DECODE_SINGLESTEP(0x28000000, 0x3A000000),
+
+ /*
+ * Load/Store - Register
+ * Encoding:
+ * xx11 1x00 xx0x xxxx xxxx 00xx xxxx xxxx (unscaled imm)
+ * xx11 1x00 xx0x xxxx xxxx 01xx xxxx xxxx (imm post-indexed)
+ * xx11 1x00 xx0x xxxx xxxx 10xx xxxx xxxx (unpriviledged)
+ * xx11 1x00 xx0x xxxx xxxx 11xx xxxx xxxx (imm pre-indexed)
+ *
+ * xx11 1x00 xx10 xxxx xxxx xx10 xxxx xxxx (register offset)
+ *
+ * xx11 1x01 xxxx xxxx xxxx xxxx xxxx xxxx (unsigned imm)
+ */
+ DECODE_SINGLESTEP(0x38000000, 0x3B200000),
+ DECODE_SINGLESTEP(0x38200200, 0x38300300),
+ DECODE_SINGLESTEP(0x39000000, 0x3B000000),
+
+ /*
+ * Load/Store - AdvSIMD
+ * Encoding:
+ * 0x00 1100 0x00 0000 xxxx xxxx xxxx xxxx (Multiple-structure)
+ * 0x00 1100 1x0x xxxx xxxx xxxx xxxx xxxx (Multi-struct post-indexed)
+ * 0x00 1101 0xx0 0000 xxxx xxxx xxxx xxxx (Single-structure))
+ * 0x00 1101 1xxx xxxx xxxx xxxx xxxx xxxx (Single-struct post-index)
+ */
+ DECODE_SINGLESTEP(0x0C000000, 0xBFBF0000),
+ DECODE_SINGLESTEP(0x0C800000, 0xBFA00000),
+ DECODE_SINGLESTEP(0x0D000000, 0xBF9F0000),
+ DECODE_SINGLESTEP(0x0D800000, 0xBF800000),
+
+ /* Unallocated: xxx0 0xxx xxxx xxxx xxxx xxxx xxxx xxxx */
+ DECODE_REJECT(0x00000000, 0x18000000),
+ DECODE_END,
+};
+
+static int uprobe_decode_insn(u32 insn, struct arch_insn *asi,
+ const struct aarch64_decode_item *tbl)
+{
+ unsigned int entry, ret = INSN_REJECTED;
+
+ for (entry = 0; !decode_table_end(tbl[entry]); entry++) {
+ if (decode_table_hit(tbl[entry], insn))
+ break;
+ }
+
+ switch (decode_get_type(tbl[entry])) {
+ case DECODE_TYPE_END:
+ case DECODE_TYPE_REJECT:
+ default:
+ ret = INSN_REJECTED;
+ break;
+
+ case DECODE_TYPE_SINGLESTEP:
+ ret = INSN_GOOD;
+ break;
+
+ case DECODE_TYPE_SIMULATE:
+ asi->prepare = decode_prepare_fn(tbl[entry]);
+ asi->handler = decode_handler_fn(tbl[entry]);
+ ret = INSN_GOOD_NO_SLOT;
+ break;
+
+ case DECODE_TYPE_TABLE:
+ /* recurse with next level decode table */
+ ret = uprobe_decode_insn(insn, asi,
+ decode_sub_table(tbl[entry]));
+ };
+
+ return ret;
+}
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD If instruction is supported and uses instruction slot,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ */
+enum uprobe_insn arm64_uprobe_decode_insn(u32 insn, struct arch_insn *asi)
+{
+ return uprobe_decode_insn(insn, asi, aarch64_decode_table);
+}
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/kprobes-arm64.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _ARM_UPROBES_ARM64_H
+#define _ARM_UPROBES_ARM64_H
+
+
+enum uprobe_insn {
+ INSN_REJECTED,
+ INSN_GOOD_NO_SLOT,
+ INSN_GOOD,
+};
+
+
+enum uprobe_insn arm64_uprobe_decode_insn(u32 insn, struct arch_insn *asi);
+
+
+#endif /* _ARM_UPROBES_ARM64_H */
--- /dev/null
+/*
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * Copied from: arch/arm64/kernel/probes-decode.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _ARM64_KERNEL_UPROBES_DECODE_H
+#define _ARM64_KERNEL_UPROBES_DECODE_H
+
+
+#include "swap_uprobes.h"
+
+
+/*
+ * The following definitions and macros are used to build instruction
+ * decoding tables.
+ */
+enum decode_type {
+ DECODE_TYPE_END,
+ DECODE_TYPE_SINGLESTEP,
+ DECODE_TYPE_SIMULATE,
+ DECODE_TYPE_TABLE,
+ DECODE_TYPE_REJECT,
+};
+
+struct aarch64_decode_item;
+
+struct aarch64_decode_header {
+ enum decode_type type;
+ u32 mask;
+ u32 val;
+};
+
+struct aarch64_decode_actions {
+ uprobes_prepare_t *prepare;
+ uprobes_handler_t *handler;
+};
+
+struct aarch64_decode_table {
+ const struct aarch64_decode_item *tbl;
+};
+
+union aarch64_decode_handler {
+ struct aarch64_decode_actions actions;
+ struct aarch64_decode_table table;
+};
+
+struct aarch64_decode_item {
+ struct aarch64_decode_header header;
+ union aarch64_decode_handler decode;
+};
+
+#define decode_get_type(_entry) ((_entry).header.type)
+
+#define decode_table_end(_entry) \
+ ((_entry).header.type == DECODE_TYPE_END)
+
+#define decode_table_hit(_entry, insn) \
+ ((insn & (_entry).header.mask) == (_entry).header.val)
+
+#define decode_prepare_fn(_entry) ((_entry).decode.actions.prepare)
+#define decode_handler_fn(_entry) ((_entry).decode.actions.handler)
+#define decode_sub_table(_entry) ((_entry).decode.table.tbl)
+
+#define DECODE_ADD_HEADER(_type, _val, _mask) \
+ .header = { \
+ .type = _type, \
+ .mask = _mask, \
+ .val = _val, \
+ }
+
+#define DECODE_ADD_ACTION(_prepare, _handler) \
+ .decode = { \
+ .actions = { \
+ .prepare = _prepare, \
+ .handler = _handler, \
+ } \
+ }
+
+#define DECODE_ADD_TABLE(_table) \
+ .decode = { \
+ .table = {.tbl = _table} \
+ }
+
+#define DECODE_REJECT(_v, _m) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_REJECT, _v, _m) }
+
+#define DECODE_SINGLESTEP(_v, _m) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_SINGLESTEP, _v, _m) }
+
+#define DECODE_SIMULATE(_v, _m, _p, _h) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_SIMULATE, _v, _m), \
+ DECODE_ADD_ACTION(_p, _h) }
+
+#define DECODE_TABLE(_v, _m, _table) \
+ { DECODE_ADD_HEADER(DECODE_TYPE_TABLE, _v, _m), \
+ DECODE_ADD_TABLE(_table) }
+
+#define DECODE_LITERAL(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
+#define DECODE_BRANCH(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
+
+/* should be the last element in decode structure */
+#define DECODE_END { .header = {.type = DECODE_TYPE_END, } }
+
+#endif /* _ARM64_KERNEL_UPROBES_DECODE_H */
--- /dev/null
+/**
+ * swap_sc_patch.c
+ * @author Dmitry Kovalenko <d.kovalenko@samsung.com>
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Patching of sys_call_table
+ */
+
+#include <ksyms/ksyms.h>
+#include "swap_sc_patch.h"
+
+static unsigned long original_syscall;
+static int patched_syscall = -1;
+
+/* disable write protection */
+#define swap_disable_wprot() \
+ asm("pushl %eax \n" \
+ "movl %cr0, %eax \n" \
+ "andl $0xfffeffff, %eax \n" \
+ "movl %eax, %cr0 \n" \
+ "popl %eax");
+
+/* enable write protection */
+#define swap_enable_wprot() \
+ asm("push %eax \n" \
+ "movl %cr0, %eax \n" \
+ "orl $0x00010000, %eax \n" \
+ "movl %eax, %cr0 \n" \
+ "popl %eax");
+
+void patch_syscall(int syscall_n, unsigned long new_syscall_addr)
+{
+ unsigned long tmp;
+ unsigned long *sc_table;
+
+ /*
+ * Search for sys_call_table (4 bytes before sysenter_after_call)
+ * sysenter_do_call function which locates before sysenter_after_call
+ * has sys_call_table address in call instruction (latest instruction)
+ */
+ tmp = swap_ksyms("sysenter_after_call");
+ sc_table = *(unsigned long **)(tmp - 4);
+
+ swap_disable_wprot();
+ original_syscall = sc_table[syscall_n];
+ sc_table[syscall_n] = new_syscall_addr;
+ patched_syscall = syscall_n;
+ swap_enable_wprot();
+}
+
+void swap_depatch_syscall(void)
+{
+ if (patched_syscall == -1) {
+ printk(KERN_WARNING
+ "SWAP SC_PATCH: there is no patched syscalls");
+ return;
+ }
+
+ patch_syscall(patched_syscall, original_syscall);
+ patched_syscall = -1;
+}
+
+asmlinkage long sys_swap_func(void)
+{
+ /* Your code here */
+
+ return -ENOSYS;
+}
+
+#define NI_SYSCALL4SWAP 31
+void swap_patch_syscall(void)
+{
+ patch_syscall(NI_SYSCALL4SWAP, (unsigned long)&sys_swap_func);
+}
--- /dev/null
+void swap_depatch_syscall(void);
+void swap_patch_syscall(void);
--- /dev/null
+/**
+ * uprobe/arch/asm-x86/swap_uprobes.c
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Arch-dependent uprobe interface implementation for x86.
+ */
+
+
+#include <linux/kdebug.h>
+
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_td_raw.h>
+#include <kprobe/swap_kprobes.h>
+#include <uprobe/swap_uprobes.h>
+
+#include "swap_uprobes.h"
+
+
+struct save_context {
+ struct pt_regs save_regs;
+ struct pt_regs *ptr_regs;
+ unsigned long val;
+ int (*handler)(struct uprobe *, struct pt_regs *);
+};
+
+/**
+ * @struct uprobe_ctlblk
+ * @brief Uprobe control block
+ */
+struct uprobe_ctlblk {
+ unsigned long flags; /**< Flags */
+ struct uprobe *p; /**< Pointer to the uprobe */
+
+ struct save_context ctx;
+};
+
+
+static struct td_raw td_raw;
+
+
+static unsigned long trampoline_addr(struct uprobe *up)
+{
+ return (unsigned long)(up->insn + UPROBES_TRAMP_RET_BREAK_IDX);
+}
+
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+ return trampoline_addr(&ri->rp->up);
+}
+
+static struct uprobe_ctlblk *current_ucb(void)
+{
+ return (struct uprobe_ctlblk *)swap_td_raw(&td_raw, current);
+}
+
+static struct save_context *current_ctx(void)
+{
+ return ¤t_ucb()->ctx;
+}
+
+static struct uprobe *get_current_probe(void)
+{
+ return current_ucb()->p;
+}
+
+static void set_current_probe(struct uprobe *p)
+{
+ current_ucb()->p = p;
+}
+
+static void save_current_flags(struct pt_regs *regs)
+{
+ current_ucb()->flags = regs->flags;
+}
+
+static void restore_current_flags(struct pt_regs *regs, unsigned long flags)
+{
+ regs->flags &= ~IF_MASK;
+ regs->flags |= flags & IF_MASK;
+}
+
+/**
+ * @brief Prepares uprobe for x86.
+ *
+ * @param up Pointer to the uprobe.
+ * @return 0 on success,\n
+ * -1 on error.
+ */
+int arch_prepare_uprobe(struct uprobe *p)
+{
+ struct task_struct *task = p->task;
+ u8 tramp[UPROBES_TRAMP_LEN + BP_INSN_SIZE]; /* BP for uretprobe */
+
+ if (!read_proc_vm_atomic(task, (unsigned long)p->addr,
+ tramp, MAX_INSN_SIZE)) {
+ printk(KERN_ERR "failed to read memory %p!\n", p->addr);
+ return -EINVAL;
+ }
+
+ if (p->check_opcode_cb) {
+ int ret;
+ ret = p->check_opcode_cb((uprobe_opcode_t *)tramp);
+ if (ret)
+ return ret;
+ }
+
+ tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+
+ p->opcode = tramp[0];
+ p->ainsn.boostable = swap_can_boost(tramp) ? 0 : -1;
+
+ p->insn = swap_slot_alloc(p->sm);
+ if (p->insn == NULL) {
+ printk(KERN_ERR "trampoline out of memory\n");
+ return -ENOMEM;
+ }
+
+ if (!write_proc_vm_atomic(task, (unsigned long)p->insn,
+ tramp, sizeof(tramp))) {
+ swap_slot_free(p->sm, p->insn);
+ printk(KERN_INFO "failed to write memory %p!\n", tramp);
+ return -EINVAL;
+ }
+
+ /* for uretprobe */
+ add_uprobe_table(p);
+
+ return 0;
+}
+
+/**
+ * @brief Jump pre-handler.
+ *
+ * @param p Pointer to the uprobe.
+ * @param regs Pointer to CPU register data.
+ * @return 0.
+ */
+int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct ujprobe *jp = container_of(p, struct ujprobe, up);
+ entry_point_t entry = (entry_point_t)jp->entry;
+ unsigned long args[6];
+
+ /* FIXME some user space apps crash if we clean interrupt bit */
+ /* regs->EREG(flags) &= ~IF_MASK; */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ trace_hardirqs_off();
+#endif
+
+ /* read first 6 args from stack */
+ if (!read_proc_vm_atomic(current, regs->EREG(sp) + 4,
+ args, sizeof(args)))
+ printk(KERN_WARNING
+ "failed to read user space func arguments %lx!\n",
+ regs->sp + 4);
+
+ if (entry)
+ entry(args[0], args[1], args[2], args[3], args[4], args[5]);
+ else
+ arch_ujprobe_return();
+
+ return 0;
+}
+
+/**
+ * @brief Check opcode is applicable for retprobe
+ *
+ * @param opcode Pointer to opcode
+ * @return result of checking
+ */
+int arch_urp_check_opcode(uprobe_opcode_t *opcode)
+{
+ enum { call_relative_opcode = 0xe8 };
+
+ /* do not set uprobe for retprobe if insn is relative call */
+ if (opcode[0] == call_relative_opcode) {
+ pr_err("failed to set retprobe to relative call\n");
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Prepares uretprobe for x86.
+ *
+ * @param ri Pointer to the uretprobe instance.
+ * @param regs Pointer to CPU register data.
+ * @return Void.
+ */
+int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ /* Replace the return addr with trampoline addr */
+ unsigned long ra = trampoline_addr(&ri->rp->up);
+ unsigned long ret_addr;
+ ri->sp = (kprobe_opcode_t *)regs->sp;
+
+ if (get_user(ret_addr, (unsigned long *)regs->sp)) {
+ pr_err("failed to read user space func ra %lx addr=%p!\n",
+ regs->sp, ri->rp->up.addr);
+ return -EINVAL;
+ }
+
+ if (put_user(ra, (unsigned long *)regs->sp)) {
+ pr_err("failed to write user space func ra %lx!\n", regs->sp);
+ return -EINVAL;
+ }
+
+ ri->ret_addr = (uprobe_opcode_t *)ret_addr;
+
+ return 0;
+}
+
+static bool get_long(struct task_struct *task,
+ unsigned long vaddr, unsigned long *val)
+{
+ return sizeof(*val) != read_proc_vm_atomic(task, vaddr,
+ val, sizeof(*val));
+}
+
+static bool put_long(struct task_struct *task,
+ unsigned long vaddr, unsigned long *val)
+{
+ return sizeof(*val) != write_proc_vm_atomic(task, vaddr,
+ val, sizeof(*val));
+}
+
+/**
+ * @brief Disarms uretprobe on x86 arch.
+ *
+ * @param ri Pointer to the uretprobe instance.
+ * @param task Pointer to the task for which the probe.
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ unsigned long ret_addr;
+ unsigned long sp = (unsigned long)ri->sp;
+ unsigned long tramp_addr = trampoline_addr(&ri->rp->up);
+
+ if (get_long(task, sp, &ret_addr)) {
+ printk(KERN_INFO "---> %s (%d/%d): failed to read stack from %08lx\n",
+ task->comm, task->tgid, task->pid, sp);
+ return -EFAULT;
+ }
+
+ if (tramp_addr == ret_addr) {
+ if (put_long(task, sp, (unsigned long *)&ri->ret_addr)) {
+ printk(KERN_INFO "---> %s (%d/%d): failed to write "
+ "orig_ret_addr to %08lx",
+ task->comm, task->tgid, task->pid, sp);
+ return -EFAULT;
+ }
+ } else {
+ printk(KERN_INFO "---> %s (%d/%d): trampoline NOT found at sp = %08lx\n",
+ task->comm, task->tgid, task->pid, sp);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Gets trampoline address.
+ *
+ * @param p Pointer to the uprobe.
+ * @param regs Pointer to CPU register data.
+ * @return Trampoline address.
+ */
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
+{
+ return trampoline_addr(p);
+}
+
+/**
+ * @brief Restores return address.
+ *
+ * @param orig_ret_addr Original return address.
+ * @param regs Pointer to CPU register data.
+ * @return Void.
+ */
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->EREG(ip) = orig_ret_addr;
+}
+
+/**
+ * @brief Removes uprobe.
+ *
+ * @param up Pointer to the target uprobe.
+ * @return Void.
+ */
+void arch_remove_uprobe(struct uprobe *p)
+{
+ swap_slot_free(p->sm, p->insn);
+}
+
+int arch_arm_uprobe(struct uprobe *p)
+{
+ int ret;
+ uprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
+ unsigned long vaddr = (unsigned long)p->addr;
+
+ ret = write_proc_vm_atomic(p->task, vaddr, &insn, sizeof(insn));
+ if (!ret) {
+ pr_err("arch_arm_uprobe: failed to write memory tgid=%u vaddr=%08lx\n",
+ p->task->tgid, vaddr);
+
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
+{
+ int ret;
+ unsigned long vaddr = (unsigned long)p->addr;
+
+ ret = write_proc_vm_atomic(task, vaddr, &p->opcode, sizeof(p->opcode));
+ if (!ret) {
+ pr_err("arch_disarm_uprobe: failed to write memory tgid=%u, vaddr=%08lx\n",
+ task->tgid, vaddr);
+ }
+}
+
+static void set_user_jmp_op(void *from, void *to)
+{
+ struct __arch_jmp_op {
+ char op;
+ long raddr;
+ } __packed jop;
+
+ jop.raddr = (long)(to) - ((long)(from) + 5);
+ jop.op = RELATIVEJUMP_INSTRUCTION;
+
+ if (put_user(jop.op, (char *)from) ||
+ put_user(jop.raddr, (long *)(from + 1)))
+ pr_err("failed to write jump opcode to user space %p\n", from);
+}
+
+static void resume_execution(struct uprobe *p,
+ struct pt_regs *regs,
+ unsigned long flags)
+{
+ unsigned long *tos, tos_dword = 0;
+ unsigned long copy_eip = (unsigned long)p->insn;
+ unsigned long orig_eip = (unsigned long)p->addr;
+ uprobe_opcode_t insns[2];
+
+ regs->EREG(flags) &= ~TF_MASK;
+
+ tos = (unsigned long *)&tos_dword;
+ if (get_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to read from user space sp=%lx!\n", regs->sp);
+ return;
+ }
+
+ if (get_user(*(unsigned short *)insns, (unsigned short *)p->insn)) {
+ pr_err("failed to read first 2 opcodes %p!\n", p->insn);
+ return;
+ }
+
+ switch (insns[0]) {
+ case 0x9c: /* pushfl */
+ *tos &= ~(TF_MASK | IF_MASK);
+ *tos |= flags & (TF_MASK | IF_MASK);
+ break;
+ case 0xc2: /* iret/ret/lret */
+ case 0xc3:
+ case 0xca:
+ case 0xcb:
+ case 0xcf:
+ case 0xea: /* jmp absolute -- eip is correct */
+ /* eip is already adjusted, no more changes required */
+ p->ainsn.boostable = 1;
+ goto no_change;
+ case 0xe8: /* call relative - Fix return addr */
+ *tos = orig_eip + (*tos - copy_eip);
+ break;
+ case 0x9a: /* call absolute -- same as call absolute, indirect */
+ *tos = orig_eip + (*tos - copy_eip);
+
+ if (put_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to write dword to sp=%lx\n", regs->sp);
+ return;
+ }
+
+ goto no_change;
+ case 0xff:
+ if ((insns[1] & 0x30) == 0x10) {
+ /*
+ * call absolute, indirect
+ * Fix return addr; eip is correct.
+ * But this is not boostable
+ */
+ *tos = orig_eip + (*tos - copy_eip);
+
+ if (put_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to write dword to sp=%lx\n", regs->sp);
+ return;
+ }
+
+ goto no_change;
+ } else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute
+ * indirect */
+ ((insns[1] & 0x31) == 0x21)) {
+ /* jmp far, absolute indirect */
+ /* eip is correct. And this is boostable */
+ p->ainsn.boostable = 1;
+ goto no_change;
+ }
+ case 0xf3:
+ if (insns[1] == 0xc3)
+ /* repz ret special handling: no more changes */
+ goto no_change;
+ break;
+ default:
+ break;
+ }
+
+ if (put_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to write dword to sp=%lx\n", regs->sp);
+ return;
+ }
+
+ if (p->ainsn.boostable == 0) {
+ if ((regs->EREG(ip) > copy_eip) && (regs->EREG(ip) - copy_eip) +
+ 5 < MAX_INSN_SIZE) {
+ /*
+ * These instructions can be executed directly if it
+ * jumps back to correct address.
+ */
+ set_user_jmp_op((void *) regs->EREG(ip),
+ (void *)orig_eip +
+ (regs->EREG(ip) - copy_eip));
+ p->ainsn.boostable = 1;
+ } else {
+ p->ainsn.boostable = -1;
+ }
+ }
+
+ regs->EREG(ip) = orig_eip + (regs->EREG(ip) - copy_eip);
+
+no_change:
+ return;
+}
+
+static void prepare_tramp(struct uprobe *p, struct pt_regs *regs)
+{
+ regs->ip = (unsigned long)p->insn;
+}
+
+static void prepare_ss(struct pt_regs *regs)
+{
+ /* set single step mode */
+ regs->flags |= TF_MASK;
+ regs->flags &= ~IF_MASK;
+}
+
+
+static unsigned long resume_userspace_addr;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+static void __rcu_nmi_enter(void) {}
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
+#error "This kernel is not support"
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) */
+static void (*__rcu_nmi_enter)(void);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) */
+
+static void __used __up_handler(void)
+{
+ struct pt_regs *regs = current_ctx()->ptr_regs;
+ struct thread_info *tinfo = current_thread_info();
+ struct uprobe *p = current_ucb()->p;
+
+ /* restore KS regs */
+ *regs = current_ctx()->save_regs;
+
+ /* call handler */
+ current_ctx()->handler(p, regs);
+
+ /* resume_userspace */
+ asm volatile (
+ "movl %0, %%esp\n"
+ "movl %1, %%ebp\n"
+ "jmpl *%2\n"
+ : /* No outputs. */
+ : "r" (regs), "r" (tinfo) , "r" (resume_userspace_addr)
+ );
+}
+
+void up_handler(void);
+__asm(
+ "up_handler:\n"
+ /* skip hex tractor-driver bytes to make some free space (skip regs) */
+ "sub $0x300, %esp\n"
+ "jmp __up_handler\n"
+);
+
+static int exceptions_handler(struct pt_regs *regs,
+ int (*handler)(struct uprobe *, struct pt_regs *))
+{
+ /* save regs */
+ current_ctx()->save_regs = *regs;
+ current_ctx()->ptr_regs = regs;
+
+ /* set handler */
+ current_ctx()->handler = handler;
+
+ /* setup regs to return to KS */
+ regs->ip = (unsigned long)up_handler;
+ regs->ds = __USER_DS;
+ regs->es = __USER_DS;
+ regs->fs = __KERNEL_PERCPU;
+ regs->cs = __KERNEL_CS | get_kernel_rpl();
+ regs->gs = 0;
+ regs->flags = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
+
+ /*
+ * Here rcu_nmi_enter() call is needed, because we change
+ * US context to KS context as a result rcu_nmi_exit() will
+ * be called on exiting exception and rcu_nmi_enter() and
+ * rcu_nmi_exit() calls must be consistent
+ */
+ __rcu_nmi_enter();
+
+ return 1;
+}
+
+static int uprobe_handler_retprobe(struct uprobe *p, struct pt_regs *regs)
+{
+ int ret;
+
+ ret = trampoline_uprobe_handler(p, regs);
+ set_current_probe(NULL);
+ put_up(p);
+
+ return ret;
+}
+
+static int uprobe_handler_part2(struct uprobe *p, struct pt_regs *regs)
+{
+ if (p->pre_handler && !p->pre_handler(p, regs)) {
+ prepare_tramp(p, regs);
+ if (p->ainsn.boostable == 1 && !p->post_handler)
+ goto exit_and_put_up;
+
+ save_current_flags(regs);
+ set_current_probe(p);
+ prepare_ss(regs);
+
+ return 1;
+ }
+
+exit_and_put_up:
+ set_current_probe(NULL);
+ put_up(p);
+ return 1;
+}
+
+static int uprobe_handler_atomic(struct pt_regs *regs)
+{
+ pid_t tgid = current->tgid;
+ unsigned long vaddr = regs->ip - 1;
+ struct uprobe *p = get_uprobe((void *)vaddr, tgid);
+
+ if (p) {
+ get_up(p);
+ if (p->pre_handler) {
+ set_current_probe(p);
+ exceptions_handler(regs, uprobe_handler_part2);
+ } else {
+ uprobe_handler_part2(p, regs);
+ }
+ } else {
+ unsigned long tramp_vaddr;
+
+ tramp_vaddr = vaddr - UPROBES_TRAMP_RET_BREAK_IDX;
+ p = get_uprobe_by_insn_slot((void *)tramp_vaddr, tgid, regs);
+ if (p == NULL) {
+ pr_info("no_uprobe\n");
+ return 0;
+ }
+
+ set_current_probe(p);
+ get_up(p);
+ exceptions_handler(regs, uprobe_handler_retprobe);
+ }
+
+ return 1;
+}
+
+static int post_uprobe_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ unsigned long flags = current_ucb()->flags;
+
+ resume_execution(p, regs, flags);
+ restore_current_flags(regs, flags);
+
+ /* reset current probe */
+ set_current_probe(NULL);
+ put_up(p);
+
+ return 1;
+}
+
+static int post_uprobe_handler_atomic(struct pt_regs *regs)
+{
+ struct uprobe *p = get_current_probe();
+
+ if (p) {
+ exceptions_handler(regs, post_uprobe_handler);
+ } else {
+ pr_info("task[%u %u %s] current uprobe is not found\n",
+ current->tgid, current->pid, current->comm);
+ }
+
+ return !!p;
+}
+
+static int uprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = (struct die_args *)data;
+ int ret = NOTIFY_DONE;
+
+ if (args->regs == NULL || !swap_user_mode(args->regs))
+ return ret;
+
+ switch (val) {
+#ifdef CONFIG_KPROBES
+ case DIE_INT3:
+#else
+ case DIE_TRAP:
+#endif
+ if (uprobe_handler_atomic(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_DEBUG:
+ if (post_uprobe_handler_atomic(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static struct notifier_block uprobe_exceptions_nb = {
+ .notifier_call = uprobe_exceptions_notify,
+ .priority = INT_MAX
+};
+
+struct up_valid_struct {
+ struct uprobe *p;
+ bool found;
+};
+
+static int __uprobe_is_valid(struct uprobe *p, void *data)
+{
+ struct up_valid_struct *valid = (struct up_valid_struct *)data;
+
+ if (valid->p == p) {
+ valid->found = true;
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool uprobe_is_valid(struct uprobe *p)
+{
+ struct up_valid_struct valid = {
+ .p = p,
+ .found = false,
+ };
+
+ for_each_uprobe(__uprobe_is_valid, (void *)&valid);
+
+ return valid.found;
+}
+
+static int do_exit_handler(struct kprobe *kp, struct pt_regs *regs)
+{
+ struct uprobe *p;
+
+ p = get_current_probe();
+ if (p && uprobe_is_valid(p)) {
+ set_current_probe(NULL);
+ put_up(p);
+ }
+
+ return 0;
+}
+
+static struct kprobe kp_do_exit = {
+ .pre_handler = do_exit_handler
+};
+
+/**
+ * @brief Registers notify.
+ *
+ * @return register_die_notifier result.
+ */
+int swap_arch_init_uprobes(void)
+{
+ int ret;
+ const char *sym;
+
+ sym = "resume_userspace";
+ resume_userspace_addr = swap_ksyms(sym);
+ if (resume_userspace_addr == 0)
+ goto not_found;
+
+ sym = "do_exit";
+ kp_do_exit.addr = swap_ksyms(sym);
+ if (kp_do_exit.addr == 0)
+ goto not_found;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+ sym = "rcu_nmi_enter";
+ __rcu_nmi_enter = (void *)swap_ksyms(sym);
+ if (__rcu_nmi_enter == NULL)
+ goto not_found;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) */
+
+ ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk));
+ if (ret)
+ return ret;
+
+ ret = register_die_notifier(&uprobe_exceptions_nb);
+ if (ret)
+ goto unreg_td;
+
+ ret = swap_register_kprobe(&kp_do_exit);
+ if (ret)
+ goto unreg_exeption;
+
+ return 0;
+
+unreg_exeption:
+ unregister_die_notifier(&uprobe_exceptions_nb);
+unreg_td:
+ swap_td_raw_unreg(&td_raw);
+ return ret;
+
+not_found:
+ pr_err("symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
+/**
+ * @brief Unregisters notify.
+ *
+ * @return Void.
+ */
+void swap_arch_exit_uprobes(void)
+{
+ swap_unregister_kprobe(&kp_do_exit);
+ unregister_die_notifier(&uprobe_exceptions_nb);
+ swap_td_raw_unreg(&td_raw);
+}
+
--- /dev/null
+/**
+ * @file uprobe/arch/asm-x86/swap_uprobes.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Arch-dependent uprobe interface declaration.
+ */
+
+#ifndef _X86_SWAP_UPROBES_H
+#define _X86_SWAP_UPROBES_H
+
+
+#include <swap-asm/swap_kprobes.h> /* FIXME: for UPROBES_TRAMP_LEN */
+
+
+struct uprobe;
+struct uretprobe;
+struct uretprobe_instance;
+
+typedef u8 uprobe_opcode_t;
+
+/**
+ * @struct arch_insn
+ * @brief Architecture depend copy of original instruction.
+ * @var arch_insn::insn
+ * Copy of the original instruction.
+ * @var arch_insn::boostable
+ * If this flag is not 0, this kprobe can be boost when its
+ * post_handler and break_handler is not set.
+ */
+struct arch_insn {
+ int boostable;
+};
+
+
+static inline u32 swap_get_urp_float(struct pt_regs *regs)
+{
+ u32 st0;
+
+ asm volatile ("fstps %0" : "=m" (st0));
+
+ return st0;
+}
+
+static inline u64 swap_get_urp_double(struct pt_regs *regs)
+{
+ u64 st1;
+
+ asm volatile ("fstpl %0" : "=m" (st1));
+
+ return st1;
+}
+
+static inline void arch_ujprobe_return(void)
+{
+}
+
+int arch_prepare_uprobe(struct uprobe *up);
+int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs);
+static inline int longjmp_break_uhandler(struct uprobe *p, struct pt_regs *regs)
+{
+ return 0;
+}
+
+static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp)
+{
+ return 0;
+}
+
+int arch_urp_check_opcode(uprobe_opcode_t *opcode);
+int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task);
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
+void arch_remove_uprobe(struct uprobe *up);
+int arch_arm_uprobe(struct uprobe *p);
+void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
+
+static inline unsigned long swap_get_upc(struct pt_regs *regs)
+{
+ return regs->ip;
+}
+
+static inline void swap_set_upc(struct pt_regs *regs, unsigned long val)
+{
+ regs->ip = val;
+}
+
+static inline unsigned long swap_get_ustack_val(struct pt_regs *regs,
+ unsigned long n)
+{
+ u32 *ptr, addr = 0;
+
+ ptr = (u32 *)regs->sp + n;
+ if (get_user(addr, ptr))
+ pr_err("Failed to dereference a pointer, ptr=%p\n", ptr);
+
+ return addr;
+}
+
+static inline void swap_set_ustack_val(struct pt_regs *regs, unsigned long n,
+ unsigned long val)
+{
+ u32 *ptr;
+
+ ptr = (u32 *)regs->sp + n;
+ if (put_user(val, ptr))
+ pr_err("Failed to dereference a pointer, ptr=%p\n", ptr);
+}
+
+static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
+{
+ /* 1 - return address saved on top of the stack */
+ return swap_get_ustack_val(regs, n + 1);
+}
+
+static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
+ unsigned long val)
+{
+ /* 1 - return address saved on top of the stack */
+ swap_set_ustack_val(regs, n + 1, val);
+}
+
+static inline unsigned long swap_get_uret_addr(struct pt_regs *regs)
+{
+ return swap_get_ustack_val(regs, 0);
+}
+
+static inline void swap_set_uret_addr(struct pt_regs *regs, unsigned long val)
+{
+ swap_set_ustack_val(regs, 0, val);
+}
+
+
+int swap_arch_init_uprobes(void);
+void swap_arch_exit_uprobes(void);
+
+#endif /* _X86_SWAP_UPROBES_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+
+static const char *strdup_from_user(const char __user *user_s, gfp_t gfp)
+{
+ enum { max_str_len = 1024 };
+ char *str;
+ int len_s, ret;
+
+ len_s = strnlen_user(user_s, max_str_len - 1);
+ str = kmalloc(len_s + 1, gfp);
+ if (str == NULL)
+ return NULL;
+
+ ret = copy_from_user(str, user_s, len_s);
+ if (ret < 0) {
+ kfree(str);
+ return NULL;
+ }
+
+ str[len_s] = '\0';
+
+ return str;
+}
--- /dev/null
+/**
+ * uprobe/swap_uprobes.c
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Uprobes implementation.
+ */
+
+
+#include <linux/hash.h>
+#include <linux/sched.h>
+#include <linux/mempolicy.h>
+#include <linux/module.h>
+
+#include <master/swap_initializer.h>
+#include <kprobe/swap_slots.h>
+#include <kprobe/swap_kdebug.h>
+#include <kprobe/swap_kprobes_deps.h>
+
+#include <swap-asm/swap_uprobes.h>
+
+#include "swap_uprobes.h"
+
+
+enum {
+ UPROBE_HASH_BITS = 10,
+ UPROBE_TABLE_SIZE = (1 << UPROBE_HASH_BITS)
+};
+
+static DEFINE_RWLOCK(st_lock);
+static struct hlist_head slot_table[UPROBE_TABLE_SIZE];
+static DEFINE_MUTEX(up_mtx); /* Protects uprobe_table */
+struct hlist_head uprobe_table[UPROBE_TABLE_SIZE];
+
+static DEFINE_MUTEX(urp_mtx); /* Protects uretprobe_inst_table */
+static struct hlist_head uretprobe_inst_table[UPROBE_TABLE_SIZE];
+
+#define DEBUG_PRINT_HASH_TABLE 0
+
+#if DEBUG_PRINT_HASH_TABLE
+void print_uprobe_hash_table(void)
+{
+ int i;
+ struct hlist_head *head;
+ struct uprobe *p;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ /* print uprobe table */
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
+ head = &uprobe_insn_slot_table[i];
+ swap_hlist_for_each_entry_rcu(p, node, head, is_hlist) {
+ printk(KERN_INFO "####### find U tgid=%u, addr=0x%lx\n",
+ p->task->tgid, (unsigned long)p->addr);
+ }
+ }
+}
+#endif
+
+/*
+ * Keep all fields in the uprobe consistent
+ */
+static inline void copy_uprobe(struct uprobe *old_p, struct uprobe *p)
+{
+ memcpy(&p->opcode, &old_p->opcode, sizeof(uprobe_opcode_t));
+ memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_insn));
+ memcpy(&p->insn, &old_p->insn, sizeof(uprobe_opcode_t *));
+}
+
+/*
+ * Aggregate handlers for multiple uprobes support - these handlers
+ * take care of invoking the individual uprobe handlers on p->list
+ */
+static int aggr_pre_uhandler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct uprobe *up;
+ int ret;
+
+ list_for_each_entry_rcu(up, &p->list, list) {
+ if (up->pre_handler) {
+ ret = up->pre_handler(up, regs);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void aggr_post_uhandler(struct uprobe *p, struct pt_regs *regs,
+ unsigned long flags)
+{
+ struct uprobe *up;
+
+ list_for_each_entry_rcu(up, &p->list, list) {
+ if (up->post_handler)
+ up->post_handler(up, regs, flags);
+ }
+}
+
+static int aggr_fault_uhandler(struct uprobe *p,
+ struct pt_regs *regs,
+ int trapnr)
+{
+ return 0;
+}
+
+static int aggr_break_uhandler(struct uprobe *p, struct pt_regs *regs)
+{
+ return 0;
+}
+
+/*
+ * Add the new probe to old_p->list. Fail if this is the
+ * second ujprobe at the address - two ujprobes can't coexist
+ */
+static int add_new_uprobe(struct uprobe *old_p, struct uprobe *p)
+{
+ if (p->break_handler) {
+ if (old_p->break_handler)
+ return -EEXIST;
+
+ list_add_tail_rcu(&p->list, &old_p->list);
+ old_p->break_handler = aggr_break_uhandler;
+ } else {
+ list_add_rcu(&p->list, &old_p->list);
+ }
+
+ if (p->post_handler && !old_p->post_handler)
+ old_p->post_handler = aggr_post_uhandler;
+
+ return 0;
+}
+
+/*
+ * Fill in the required fields of the "manager uprobe". Replace the
+ * earlier uprobe in the hlist with the manager uprobe
+ */
+static inline void add_aggr_uprobe(struct uprobe *ap, struct uprobe *p)
+{
+ copy_uprobe(p, ap);
+
+ ap->addr = p->addr;
+ ap->pre_handler = aggr_pre_uhandler;
+ ap->fault_handler = aggr_fault_uhandler;
+
+ if (p->post_handler)
+ ap->post_handler = aggr_post_uhandler;
+
+ if (p->break_handler)
+ ap->break_handler = aggr_break_uhandler;
+
+ INIT_LIST_HEAD(&ap->list);
+ list_add_rcu(&p->list, &ap->list);
+
+ hlist_replace_rcu(&p->hlist, &ap->hlist);
+}
+
+/*
+ * This is the second or subsequent uprobe at the address - handle
+ * the intricacies
+ */
+static int register_aggr_uprobe(struct uprobe *old_p, struct uprobe *p)
+{
+ int ret = 0;
+
+ if (old_p->pre_handler == aggr_pre_uhandler) {
+ copy_uprobe(old_p, p);
+ ret = add_new_uprobe(old_p, p);
+ } else {
+ struct uprobe *uap = kzalloc(sizeof(*uap), GFP_KERNEL);
+ if (!uap)
+ return -ENOMEM;
+
+ uap->task = p->task;
+ add_aggr_uprobe(uap, old_p);
+ copy_uprobe(uap, p);
+ ret = add_new_uprobe(uap, p);
+ }
+
+ return ret;
+}
+
+static int arm_uprobe(struct uprobe *p)
+{
+ return arch_arm_uprobe(p);
+}
+
+/**
+ * @brief Disarms uprobe.
+ *
+ * @param p Pointer to the uprobe.
+ * @param task Pointer to the target task.
+ * @return Void.
+ */
+void disarm_uprobe(struct uprobe *p, struct task_struct *task)
+{
+ arch_disarm_uprobe(p, task);
+}
+EXPORT_SYMBOL_GPL(disarm_uprobe);
+
+static void init_uprobes_insn_slots(void)
+{
+ int i;
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i)
+ INIT_HLIST_HEAD(&slot_table[i]);
+}
+
+static void init_uprobe_table(void)
+{
+ int i;
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i)
+ INIT_HLIST_HEAD(&uprobe_table[i]);
+}
+
+static void init_uretprobe_inst_table(void)
+{
+ int i;
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i)
+ INIT_HLIST_HEAD(&uretprobe_inst_table[i]);
+}
+
+/**
+ * @brief Gets uprobe.
+ *
+ * @param addr Probe's address.
+ * @param tgid Probes's thread group ID.
+ * @return Pointer to the uprobe on success,\n
+ * NULL otherwise.
+ */
+struct uprobe *get_uprobe(void *addr, pid_t tgid)
+{
+ struct hlist_head *head;
+ struct uprobe *p;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ head = &uprobe_table[hash_ptr(addr, UPROBE_HASH_BITS)];
+ swap_hlist_for_each_entry_rcu(p, node, head, hlist) {
+ if (p->addr == addr && p->task->tgid == tgid)
+ return p;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Adds uprobe to hlist when trampoline have been made.
+ *
+ * @param p Pointer to the uprobe.
+ * @return Void.
+ */
+void add_uprobe_table(struct uprobe *p)
+{
+ write_lock(&st_lock);
+ hlist_add_head(&p->is_hlist,
+ &slot_table[hash_ptr(p->insn, UPROBE_HASH_BITS)]);
+ write_unlock(&st_lock);
+}
+
+static void del_uprobe_table(struct uprobe *p)
+{
+ write_lock(&st_lock);
+ if (!hlist_unhashed(&p->is_hlist))
+ hlist_del(&p->is_hlist);
+ write_unlock(&st_lock);
+}
+
+/**
+ * @brief Gets uprobe by insn slot.
+ *
+ * @param addr Probe's address.
+ * @param tgit Probe's thread group ID.
+ * @param regs Pointer to CPU registers data.
+ * @return Pointer to the uprobe on success,\n
+ * NULL otherwise.
+ */
+struct uprobe *get_uprobe_by_insn_slot(void *addr,
+ pid_t tgid,
+ struct pt_regs *regs)
+{
+ struct hlist_head *head;
+ struct uprobe *p;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ read_lock(&st_lock);
+ head = &slot_table[hash_ptr(addr, UPROBE_HASH_BITS)];
+ swap_hlist_for_each_entry(p, node, head, is_hlist) {
+ if (p->insn == addr && p->task->tgid == tgid) {
+ read_unlock(&st_lock);
+ return p;
+ }
+ }
+ read_unlock(&st_lock);
+
+ return NULL;
+}
+
+
+static void remove_uprobe(struct uprobe *up)
+{
+ del_uprobe_table(up);
+ arch_remove_uprobe(up);
+}
+
+static struct hlist_head *uretprobe_inst_table_head(void *hash_key)
+{
+ return &uretprobe_inst_table[hash_ptr(hash_key, UPROBE_HASH_BITS)];
+}
+
+/* Called with urp_mtx held */
+static void add_urp_inst(struct uretprobe_instance *ri)
+{
+ /*
+ * Remove rp inst off the free list -
+ * Add it back when probed function returns
+ */
+ hlist_del(&ri->uflist);
+
+ /* Add rp inst onto table */
+ INIT_HLIST_NODE(&ri->hlist);
+ hlist_add_head(&ri->hlist, uretprobe_inst_table_head(ri->task->mm));
+
+ /* Also add this rp inst to the used list. */
+ INIT_HLIST_NODE(&ri->uflist);
+ hlist_add_head(&ri->uflist, &ri->rp->used_instances);
+}
+
+/* Called with urp_mtx held */
+static void recycle_urp_inst(struct uretprobe_instance *ri)
+{
+ if (ri->rp) {
+ hlist_del(&ri->hlist);
+ /* remove rp inst off the used list */
+ hlist_del(&ri->uflist);
+ /* put rp inst back onto the free list */
+ INIT_HLIST_NODE(&ri->uflist);
+ hlist_add_head(&ri->uflist, &ri->rp->free_instances);
+ }
+}
+
+/* Called with urp_mtx held */
+static struct uretprobe_instance *get_used_urp_inst(struct uretprobe *rp)
+{
+ struct uretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->used_instances, uflist) {
+ return ri;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Gets free uretprobe instanse for the specified uretprobe without
+ * allocation. Called with urp_mtx held.
+ *
+ * @param rp Pointer to the uretprobe.
+ * @return Pointer to the uretprobe_instance on success,\n
+ * NULL otherwise.
+ */
+struct uretprobe_instance *get_free_urp_inst_no_alloc(struct uretprobe *rp)
+{
+ struct uretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
+ return ri;
+ }
+
+ return NULL;
+}
+
+/* Called with urp_mtx held */
+static void free_urp_inst(struct uretprobe *rp)
+{
+ struct uretprobe_instance *ri;
+ while ((ri = get_free_urp_inst_no_alloc(rp)) != NULL) {
+ hlist_del(&ri->uflist);
+ kfree(ri);
+ }
+}
+
+#define COMMON_URP_NR 10
+
+static int alloc_nodes_uretprobe(struct uretprobe *rp)
+{
+ int alloc_nodes;
+ struct uretprobe_instance *inst;
+ int i;
+
+#if 1 /* def CONFIG_PREEMPT */
+ rp->maxactive += max(COMMON_URP_NR, 2 * NR_CPUS);
+#else
+ rp->maxacpptive += NR_CPUS;
+#endif
+ alloc_nodes = COMMON_URP_NR;
+
+ for (i = 0; i < alloc_nodes; ++i) {
+ inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_ATOMIC);
+ if (inst == NULL) {
+ free_urp_inst(rp);
+ return -ENOMEM;
+ }
+ INIT_HLIST_NODE(&inst->uflist);
+ hlist_add_head(&inst->uflist, &rp->free_instances);
+ }
+
+ return 0;
+}
+
+/* Called with urp_mtx held */
+static struct uretprobe_instance *get_free_urp_inst(struct uretprobe *rp)
+{
+ struct uretprobe_instance *ri;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
+ return ri;
+ }
+
+ if (!alloc_nodes_uretprobe(rp)) {
+ swap_hlist_for_each_entry(ri, node,
+ &rp->free_instances, uflist) {
+ return ri;
+ }
+ }
+
+ return NULL;
+}
+/* =================================================================== */
+
+
+void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data)
+{
+ int i;
+ struct uprobe *p;
+ struct hlist_head *head;
+ struct hlist_node *tnode;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
+ head = &uprobe_table[i];
+ swap_hlist_for_each_entry_safe(p, node, tnode, head, hlist) {
+ if (func(p, data))
+ return;
+ }
+ }
+}
+
+static int wait_up_action(atomic_t *val)
+{
+ BUG_ON(atomic_read(val));
+ schedule();
+ return 0;
+}
+
+static void wait_up(struct uprobe *p)
+{
+ wait_on_atomic_t(&p->usage, wait_up_action, TASK_UNINTERRUPTIBLE);
+}
+
+/**
+ * @brief Registers uprobe.
+ *
+ * @param up Pointer to the uprobe to register.
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int swap_register_uprobe(struct uprobe *p)
+{
+ int ret = 0;
+ struct uprobe *old_p;
+
+ if (!p->addr)
+ return -EINVAL;
+
+ p->insn = NULL;
+ INIT_LIST_HEAD(&p->list);
+ atomic_set(&p->usage, 1);
+
+ /* get the first item */
+ old_p = get_uprobe(p->addr, p->task->tgid);
+ if (old_p) {
+ ret = register_aggr_uprobe(old_p, p);
+ goto out;
+ }
+
+ INIT_HLIST_NODE(&p->is_hlist);
+
+ ret = arch_prepare_uprobe(p);
+ if (ret) {
+ DBPRINTF("goto out\n", ret);
+ goto out;
+ }
+
+ DBPRINTF("before out ret = 0x%x\n", ret);
+
+ /* TODO: add uprobe (must be in function) */
+ INIT_HLIST_NODE(&p->hlist);
+ hlist_add_head_rcu(&p->hlist,
+ &uprobe_table[hash_ptr(p->addr, UPROBE_HASH_BITS)]);
+
+ ret = arm_uprobe(p);
+ if (ret) {
+ hlist_del_rcu(&p->hlist);
+ synchronize_rcu();
+ remove_uprobe(p);
+ }
+
+out:
+ DBPRINTF("out ret = 0x%x\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_register_uprobe);
+
+/**
+ * @brief Unregisters uprobe.
+ *
+ * @param up Pointer to the uprobe.
+ * @param disarm Disarm flag. When true uprobe is disarmed.
+ * @return Void.
+ */
+void __swap_unregister_uprobe(struct uprobe *p, int disarm)
+{
+ struct uprobe *old_p, *list_p;
+ int cleanup_p;
+
+ /* we MUST check probe for uncreated process */
+ if (!p->task)
+ return;
+
+ mutex_lock(&up_mtx);
+ rcu_read_lock();
+ old_p = get_uprobe(p->addr, p->task->tgid);
+ rcu_read_unlock();
+ if (unlikely(!old_p))
+ goto out_unlock;
+
+ if (p != old_p) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(list_p, &old_p->list, list) {
+ if (list_p == p) {
+ /* uprobe p is a valid probe */
+ rcu_read_unlock();
+ goto valid_p;
+ }
+ }
+ rcu_read_unlock();
+ goto out_unlock;
+ }
+
+valid_p:
+ if ((old_p == p) || ((old_p->pre_handler == aggr_pre_uhandler) &&
+ (p->list.next == &old_p->list) && (p->list.prev == &old_p->list))) {
+ /* Only probe on the hash list */
+ if (disarm)
+ disarm_uprobe(p, p->task);
+
+ hlist_del_rcu(&old_p->hlist);
+ cleanup_p = 1;
+ } else {
+ list_del_rcu(&p->list);
+ cleanup_p = 0;
+ }
+
+ if (cleanup_p) {
+ if (p != old_p) {
+ list_del_rcu(&p->list);
+ kfree(old_p);
+ }
+
+ if (!in_atomic()) {
+ synchronize_sched();
+
+ atomic_dec(&p->usage);
+ wait_up(p);
+ }
+
+ remove_uprobe(p);
+ } else {
+ if (p->break_handler)
+ old_p->break_handler = NULL;
+
+ if (p->post_handler) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(list_p, &old_p->list, list) {
+ if (list_p->post_handler) {
+ cleanup_p = 2;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (cleanup_p == 0)
+ old_p->post_handler = NULL;
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&up_mtx);
+}
+EXPORT_SYMBOL_GPL(__swap_unregister_uprobe);
+
+/**
+ * @brief Unregisters uprobe. Main interface function, wrapper for
+ * __swap_unregister_uprobe.
+ *
+ * @param up Pointer to the uprobe.
+ * @return Void.
+ */
+void swap_unregister_uprobe(struct uprobe *up)
+{
+ __swap_unregister_uprobe(up, 1);
+}
+
+/**
+ * @brief Registers ujprobe.
+ *
+ * @param uj Pointer to the ujprobe function.
+ * @return 0 on success,\n
+ * error code on error.
+ */
+int swap_register_ujprobe(struct ujprobe *jp)
+{
+ int ret = 0;
+
+ /* Todo: Verify probepoint is a function entry point */
+ jp->up.pre_handler = setjmp_upre_handler;
+ jp->up.break_handler = longjmp_break_uhandler;
+
+ ret = swap_register_uprobe(&jp->up);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_register_ujprobe);
+
+/**
+ * @brief Unregisters ujprobe.
+ *
+ * @param jp Pointer to the ujprobe.
+ * @param disarm Disarm flag, passed to __swap_unregister_uprobe.
+ * @return Void.
+ */
+void __swap_unregister_ujprobe(struct ujprobe *jp, int disarm)
+{
+ __swap_unregister_uprobe(&jp->up, disarm);
+}
+EXPORT_SYMBOL_GPL(__swap_unregister_ujprobe);
+
+/**
+ * @brief Unregisters ujprobe. Main interface function, wrapper for
+ * __swap_unregister_ujprobe.
+ *
+ * @param jp Pointer to the jprobe.
+ * @return Void.
+ */
+void swap_unregister_ujprobe(struct ujprobe *jp)
+{
+ __swap_unregister_ujprobe(jp, 1);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_ujprobe);
+
+/**
+ * @brief Trampoline uprobe handler.
+ *
+ * @param p Pointer to the uprobe.
+ * @param regs Pointer to CPU register data.
+ * @return 1
+ */
+int trampoline_uprobe_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct uretprobe_instance *ri = NULL;
+ struct uprobe *up;
+ struct hlist_head *head = uretprobe_inst_table_head(current->mm);
+ unsigned long tramp_addr = arch_get_trampoline_addr(p, regs);
+ unsigned long orig_ret_addr = 0;
+ struct hlist_node *tmp;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because an multiple functions in the call path
+ * have a return probe installed on them, and/or more then one
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * uretprobe_trampoline
+ */
+ mutex_lock(&urp_mtx);
+ swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current) {
+ /* another task is sharing our hash bucket */
+ continue;
+ }
+
+ up = NULL;
+ if (ri->rp) {
+ up = &ri->rp->up;
+
+ if (ri->rp->handler)
+ ri->rp->handler(ri, regs);
+ }
+
+ orig_ret_addr = (unsigned long)ri->ret_addr;
+ recycle_urp_inst(ri);
+
+ if (orig_ret_addr != tramp_addr || up == NULL) {
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+ }
+ mutex_unlock(&urp_mtx);
+
+ /* orig_ret_addr is NULL when there is no need to restore anything
+ * (all the magic is performed inside handler) */
+ if (likely(orig_ret_addr))
+ arch_set_orig_ret_addr(orig_ret_addr, regs);
+
+ return 1;
+}
+
+static int pre_handler_uretprobe(struct uprobe *p, struct pt_regs *regs)
+{
+ struct uretprobe *rp = container_of(p, struct uretprobe, up);
+ struct uretprobe_instance *ri;
+ int ret = 0;
+
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+# if defined(CONFIG_ARM64)
+# define thumb_mode(regs) compat_thumb_mode(regs)
+# endif /* defined(CONFIG_ARM64) */
+ int noret = thumb_mode(regs) ? rp->thumb_noret : rp->arm_noret;
+
+ if (noret)
+ return 0;
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+
+ /* TODO: consider to only swap the
+ * RA after the last pre_handler fired */
+
+ /* TODO: test - remove retprobe after func entry but before its exit */
+ mutex_lock(&urp_mtx);
+ ri = get_free_urp_inst(rp);
+ if (ri != NULL) {
+ int err;
+
+ ri->rp = rp;
+ ri->task = current;
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ ri->preload.use = false;
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+ if (rp->entry_handler)
+ ret = rp->entry_handler(ri, regs);
+
+ add_urp_inst(ri);
+
+ err = arch_prepare_uretprobe(ri, regs);
+ if (err) {
+ recycle_urp_inst(ri);
+ ++rp->nmissed;
+ }
+ } else {
+ ++rp->nmissed;
+ }
+ mutex_unlock(&urp_mtx);
+
+ return ret;
+}
+
+/**
+ * @brief Registers uretprobe.
+ *
+ * @param rp Pointer to the uretprobe.
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int swap_register_uretprobe(struct uretprobe *rp)
+{
+ int i, ret = 0;
+ struct uretprobe_instance *inst;
+
+ DBPRINTF("START\n");
+
+ rp->up.pre_handler = pre_handler_uretprobe;
+ rp->up.post_handler = NULL;
+ rp->up.fault_handler = NULL;
+ rp->up.break_handler = NULL;
+ /* Set callback to check for unsupported insns */
+ rp->up.check_opcode_cb = arch_urp_check_opcode;
+
+ /* Pre-allocate memory for max kretprobe instances */
+ if (rp->maxactive <= 0) {
+#if 1 /* def CONFIG_PREEMPT */
+ rp->maxactive = max(10, 2 * NR_CPUS);
+#else
+ rp->maxactive = NR_CPUS;
+#endif
+ }
+
+ INIT_HLIST_HEAD(&rp->used_instances);
+ INIT_HLIST_HEAD(&rp->free_instances);
+
+ for (i = 0; i < rp->maxactive; i++) {
+ inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_KERNEL);
+ if (inst == NULL) {
+ ret = -ENOMEM;
+ goto register_err;
+ }
+
+ INIT_HLIST_NODE(&inst->uflist);
+ hlist_add_head(&inst->uflist, &rp->free_instances);
+ }
+
+ rp->nmissed = 0;
+
+ /* Establish function entry probe point */
+ ret = swap_register_uprobe(&rp->up);
+ if (ret)
+ goto register_err;
+
+ arch_opcode_analysis_uretprobe(rp);
+
+ return 0;
+
+register_err:
+ free_urp_inst(rp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(swap_register_uretprobe);
+
+/**
+ * @brief Unregisters uretprobe.
+ *
+ * @param rp Pointer to the ureprobe.
+ * @param disarm Disarm flag, passed to __swap_unregister_uprobe
+ * @return Void.
+ */
+void __swap_unregister_uretprobe(struct uretprobe *rp, int disarm)
+{
+ struct uretprobe_instance *ri;
+
+ __swap_unregister_uprobe(&rp->up, disarm);
+
+ mutex_lock(&urp_mtx);
+ while ((ri = get_used_urp_inst(rp)) != NULL) {
+ /* FIXME: arch_disarm_urp_inst() for no current context */
+ if (arch_disarm_urp_inst(ri, ri->task) != 0)
+ printk(KERN_INFO "%s (%d/%d): "
+ "cannot disarm urp instance (%08lx)\n",
+ ri->task->comm, ri->task->tgid, ri->task->pid,
+ (unsigned long)rp->up.addr);
+
+ recycle_urp_inst(ri);
+ }
+
+ while ((ri = get_used_urp_inst(rp)) != NULL) {
+ ri->rp = NULL;
+ hlist_del(&ri->uflist);
+ }
+ mutex_unlock(&urp_mtx);
+
+ free_urp_inst(rp);
+}
+EXPORT_SYMBOL_GPL(__swap_unregister_uretprobe);
+
+/**
+ * @brief Unregistets uretprobe. Main interface function, wrapper for
+ * __swap_unregister_uretprobe.
+ *
+ * @param rp Pointer to the uretprobe.
+ * @return Void.
+ */
+void swap_unregister_uretprobe(struct uretprobe *rp)
+{
+ __swap_unregister_uretprobe(rp, 1);
+}
+EXPORT_SYMBOL_GPL(swap_unregister_uretprobe);
+
+/**
+ * @brief Unregisters all uprobes for task's thread group ID.
+ *
+ * @param task Pointer to the task_struct
+ * @return Void.
+ */
+void swap_unregister_all_uprobes(struct task_struct *task)
+{
+ struct hlist_head *head;
+ struct uprobe *p;
+ int i;
+ struct hlist_node *tnode;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
+ head = &uprobe_table[i];
+ swap_hlist_for_each_entry_safe(p, node, tnode, head, hlist) {
+ if (p->task->tgid == task->tgid) {
+ printk(KERN_INFO "%s: delete uprobe at %p[%lx]"
+ " for %s/%d\n", __func__, p->addr,
+ (unsigned long)p->opcode,
+ task->comm, task->pid);
+ swap_unregister_uprobe(p);
+ }
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(swap_unregister_all_uprobes);
+
+/**
+ * @brief Arch-independent wrapper for arch_ujprobe_return.
+ *
+ * @return Void.
+ */
+void swap_ujprobe_return(void)
+{
+ arch_ujprobe_return();
+}
+EXPORT_SYMBOL_GPL(swap_ujprobe_return);
+
+void swap_uretprobe_free_task(struct task_struct *armed,
+ struct task_struct *will_disarm, bool recycle)
+{
+ struct uretprobe_instance *ri;
+ struct hlist_head *hhead = uretprobe_inst_table_head(armed->mm);
+ struct hlist_node *n;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ mutex_lock(&urp_mtx);
+ swap_hlist_for_each_entry_safe(ri, node, n, hhead, hlist) {
+ if (armed != ri->task)
+ continue;
+
+ if (will_disarm)
+ arch_disarm_urp_inst(ri, will_disarm);
+
+ if (recycle)
+ recycle_urp_inst(ri);
+ }
+ mutex_unlock(&urp_mtx);
+}
+EXPORT_SYMBOL_GPL(swap_uretprobe_free_task);
+
+
+static int once(void)
+{
+ init_uprobe_table();
+ init_uprobes_insn_slots();
+ init_uretprobe_inst_table();
+
+ return 0;
+}
+
+SWAP_LIGHT_INIT_MODULE(once, swap_arch_init_uprobes, swap_arch_exit_uprobes,
+ NULL, NULL);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/**
+ * @file uprobe/swap_uprobes.h
+ * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
+ * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
+ * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
+ * separating core and arch parts
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2006-2010
+ *
+ * @section DESCRIPTION
+ *
+ * Uprobes interface declaration.
+ */
+
+#ifndef _SWAP_UPROBES_H
+#define _SWAP_UPROBES_H
+
+
+#include <master/wait.h>
+#include <swap-asm/swap_uprobes.h>
+
+
+#define URETPROBE_STACK_DEPTH 64
+
+
+/**
+ * @brief Uprobe pre-handler pointer.
+ */
+typedef int (*uprobe_pre_handler_t) (struct uprobe *, struct pt_regs *);
+
+/**
+ * @brief Uprobe break handler pointer.
+ */
+typedef int (*uprobe_break_handler_t) (struct uprobe *, struct pt_regs *);
+
+/**
+ * @brief Uprobe post handler pointer.
+ */
+typedef void (*uprobe_post_handler_t) (struct uprobe *,
+ struct pt_regs *,
+ unsigned long flags);
+
+/**
+ * @brief Uprobe fault handler pointer.
+ */
+typedef int (*uprobe_fault_handler_t) (struct uprobe *,
+ struct pt_regs *,
+ int trapnr);
+
+/**
+ * @struct uprobe
+ * @brief Stores uprobe data.
+ */
+struct uprobe {
+ struct hlist_node hlist; /**< Hash list.*/
+ /** List of probes to search by instruction slot.*/
+ struct hlist_node is_hlist;
+ /** List of uprobes for multi-handler support.*/
+ struct list_head list;
+ /** Location of the probe point. */
+ uprobe_opcode_t *addr;
+ /** Called before addr is executed.*/
+ uprobe_pre_handler_t pre_handler;
+ /** Called after addr is executed, unless...*/
+ uprobe_post_handler_t post_handler;
+ /** ... called if executing addr causes a fault (eg. page fault).*/
+ uprobe_fault_handler_t fault_handler;
+ /** Return 1 if it handled fault, otherwise kernel will see it.*/
+ uprobe_break_handler_t break_handler;
+ /** Saved opcode (which has been replaced with breakpoint).*/
+ uprobe_opcode_t opcode;
+ atomic_t usage;
+#ifdef CONFIG_ARM
+ /** Safe/unsafe to use probe on ARM.*/
+ unsigned safe_arm:1;
+ /** Safe/unsafe to use probe on Thumb.*/
+ unsigned safe_thumb:1;
+#endif
+ uprobe_opcode_t __user *insn;
+ struct arch_insn ainsn; /**< Copy of the original instruction.*/
+ struct task_struct *task; /**< Pointer to the task struct */
+ struct slot_manager *sm; /**< Pointer to slot manager */
+ int (*check_opcode_cb)(uprobe_opcode_t *opcode); /**< Callback check opcode */
+};
+
+
+void swap_uretprobe_free_task(struct task_struct *task,
+ struct task_struct *dtask, bool recycle);
+
+
+/**
+ * @brief Uprobe pre-entry handler.
+ */
+typedef unsigned long (*uprobe_pre_entry_handler_t)(void *priv_arg,
+ struct pt_regs *regs);
+
+/**
+ * @struct ujprobe
+ * @brief Stores ujprobe data, based on uprobe.
+ */
+struct ujprobe {
+ struct uprobe up; /**< Uprobe for this ujprobe */
+ void *entry; /**< Probe handling code to jump to */
+ /** Handler which will be called before 'entry' */
+ uprobe_pre_entry_handler_t pre_entry;
+ void *priv_arg; /**< Private args for handler */
+ char *args; /**< Function args format string */
+};
+
+struct uretprobe_instance;
+
+/**
+ * @brief Uretprobe handler.
+ */
+typedef int (*uretprobe_handler_t)(struct uretprobe_instance *,
+ struct pt_regs *);
+
+/**
+ * @strict uretprobe
+ * @brief Function-return probe.
+ *
+ * Note:
+ * User needs to provide a handler function, and initialize maxactive.
+ */
+struct uretprobe {
+ struct uprobe up; /**< Uprobe for this uretprobe */
+ uretprobe_handler_t handler; /**< Uretprobe handler */
+ uretprobe_handler_t entry_handler; /**< Uretprobe entry handler */
+ /** Maximum number of instances of the probed function that can be
+ * active concurrently. */
+ int maxactive;
+ /** Tracks the number of times the probed function's return was
+ * ignored, due to maxactive being too low. */
+ int nmissed;
+ size_t data_size; /**< Instance data size */
+ struct hlist_head free_instances; /**< Free instances list */
+ struct hlist_head used_instances; /**< Used instances list */
+
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ unsigned arm_noret:1; /**< No-return flag for ARM */
+ unsigned thumb_noret:1; /**< No-return flag for Thumb */
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
+};
+
+/**
+ * @struct uretprobe_instance
+ * @brief Structure for each uretprobe instance.
+ */
+struct uretprobe_instance {
+ /* either on free list or used list */
+ struct hlist_node uflist; /**< Free list */
+ struct hlist_node hlist; /**< Used list */
+ struct uretprobe *rp; /**< Pointer to the parent uretprobe */
+ uprobe_opcode_t *ret_addr; /**< Return address */
+ uprobe_opcode_t *sp; /**< Pointer to stack */
+ struct task_struct *task; /**< Pointer to the task struct */
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+ /* FIXME Preload: if this flag is set then ignore the thumb_mode(regs)
+ * check in arch_prepare_uretprobe and use thumb trampoline. For the
+ * moment we have to explicitly force arm mode when jumping to preload
+ * handlers but we need the correct (i.e. original) retprobe tramp set
+ * anyway. */
+ struct {
+ unsigned use:1;
+ unsigned thumb:1;
+ } preload;
+#endif
+ char data[0]; /**< Custom data */
+};
+
+
+static void inline get_up(struct uprobe *p)
+{
+ atomic_inc(&p->usage);
+}
+
+static void inline put_up(struct uprobe *p)
+{
+ if (atomic_dec_and_test(&p->usage))
+ wake_up_atomic_t(&p->usage);
+}
+
+void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data);
+int swap_register_uprobe(struct uprobe *p);
+void swap_unregister_uprobe(struct uprobe *p);
+void __swap_unregister_uprobe(struct uprobe *up, int disarm);
+
+int swap_register_ujprobe(struct ujprobe *jp);
+void swap_unregister_ujprobe(struct ujprobe *jp);
+void __swap_unregister_ujprobe(struct ujprobe *jp, int disarm);
+
+int swap_register_uretprobe(struct uretprobe *rp);
+void swap_unregister_uretprobe(struct uretprobe *rp);
+void __swap_unregister_uretprobe(struct uretprobe *rp, int disarm);
+
+void swap_unregister_all_uprobes(struct task_struct *task);
+
+void swap_ujprobe_return(void);
+struct uprobe *get_uprobe(void *addr, pid_t tgid);
+struct uprobe *get_uprobe_by_insn_slot(void *addr,
+ pid_t tgid,
+ struct pt_regs *regs);
+
+void disarm_uprobe(struct uprobe *p, struct task_struct *task);
+
+int trampoline_uprobe_handler(struct uprobe *p, struct pt_regs *regs);
+
+void add_uprobe_table(struct uprobe *p);
+
+#endif /* _SWAP_UPROBES_H */
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../writer/Module.symvers \
+ $(src)/../kprobe/Module.symvers \
+ $(src)/../uprobe/Module.symvers
+
+obj-m := swap_us_manager.o
+swap_us_manager-y := \
+ helper.o \
+ us_manager.o \
+ us_slot_manager.o \
+ debugfs_us_manager.o \
+ sspt/sspt_ip.o \
+ sspt/sspt_page.o \
+ sspt/sspt_file.o \
+ sspt/sspt_proc.o \
+ sspt/sspt_feature.o \
+ sspt/sspt_filter.o \
+ pf/pf_group.o \
+ pf/proc_filters.o \
+ img/img_ip.o \
+ img/img_file.o \
+ img/img_proc.o \
+ probes/probes.o \
+ probes/probe_info_new.o \
+ callbacks.o \
+ usm_hook.o
--- /dev/null
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "callbacks.h"
+
+static LIST_HEAD(cbs_list);
+static DEFINE_MUTEX(cbs_mutex);
+static int cur_handle = 0;
+
+struct cb_item {
+ struct list_head list;
+ enum callback_t type;
+ int handle;
+ void (*func)(void);
+};
+
+static inline void __lock_cbs_list(void)
+{
+ mutex_lock(&cbs_mutex);
+}
+
+static inline void __unlock_cbs_list(void)
+{
+ mutex_unlock(&cbs_mutex);
+}
+
+static inline int __get_new_handle(void)
+{
+ return cur_handle++;
+}
+
+static inline void __free_cb(struct cb_item *cb)
+{
+ list_del(&cb->list);
+ kfree(cb);
+}
+
+static struct cb_item *__get_cb_by_handle(int handle)
+{
+ struct cb_item *cb;
+
+ list_for_each_entry(cb, &cbs_list, list)
+ if (cb->handle == handle)
+ return cb;
+
+ return NULL;
+}
+
+
+/**
+ * @brief Executes callbacks on start/stop
+ *
+ * @param cbt Callback type
+ * @return Void
+ */
+void exec_cbs(enum callback_t cbt)
+{
+ struct cb_item *cb;
+
+ __lock_cbs_list();
+
+ list_for_each_entry(cb, &cbs_list, list)
+ if (cb->type == cbt)
+ cb->func();
+
+ __unlock_cbs_list();
+}
+
+/**
+ * @brief Removes all callbacks from list
+ *
+ * @return Void
+ */
+void remove_all_cbs(void)
+{
+ struct cb_item *cb, *n;
+
+ __lock_cbs_list();
+
+ list_for_each_entry_safe(cb, n, &cbs_list, list)
+ __free_cb(cb);
+
+ __unlock_cbs_list();
+}
+
+/**
+ * @brief Registers callback on event
+ *
+ * @param cbt Callback type
+ * @param func Callback function
+ * @return Handle on succes, error code on error
+ */
+int us_manager_reg_cb(enum callback_t cbt, void (*func)(void))
+{
+ struct cb_item *cb;
+ int handle;
+
+ cb = kmalloc(sizeof(*cb), GFP_KERNEL);
+ if (cb == NULL)
+ return -ENOMEM;
+
+ handle = __get_new_handle();
+
+ INIT_LIST_HEAD(&cb->list);
+ cb->type = cbt;
+ cb->handle = handle;
+ cb->func = func;
+
+ __lock_cbs_list();
+ list_add_tail(&cb->list, &cbs_list);
+ __unlock_cbs_list();
+
+ return handle;
+}
+EXPORT_SYMBOL_GPL(us_manager_reg_cb);
+
+/**
+ * @brief Unegisters callback by handle
+ *
+ * @param handle Callback handle
+ * @return Void
+ */
+void us_manager_unreg_cb(int handle)
+{
+ struct cb_item *cb;
+
+ __lock_cbs_list();
+
+ cb = __get_cb_by_handle(handle);
+ if (cb == NULL)
+ goto handle_not_found;
+
+ __free_cb(cb);
+
+handle_not_found:
+ __unlock_cbs_list();
+}
+EXPORT_SYMBOL_GPL(us_manager_unreg_cb);
--- /dev/null
+#ifndef __CALLBACKS_H__
+#define __CALLBACKS_H__
+
+enum callback_t {
+ START_CB = 0,
+ STOP_CB,
+};
+
+/* Gets callback type (on start or on stop) and function pointer.
+ * Returns positive callback's handle that is used to unregister on success,
+ * negative error code otherwise.
+ * Exported function. */
+int us_manager_reg_cb(enum callback_t cbt, void (*func)(void));
+
+/* Gets handle and unregisters function with this handle.
+ * Exported function. */
+void us_manager_unreg_cb(int handle);
+
+/* Used to execute callbacks when start/stop is occuring. */
+void exec_cbs(enum callback_t cbt);
+
+/* Removes all callbacks */
+void remove_all_cbs(void);
+
+#endif /* __CALLBACKS_H__ */
--- /dev/null
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#include <master/swap_debugfs.h>
+#include <master/swap_initializer.h>
+#include <us_manager/sspt/sspt_proc.h>
+
+#include "debugfs_us_manager.h"
+
+#define MAX_APPS_COUNT 8 /* According to daemon defenitions */
+#define PID_STRING 21 /* Maximum pid string = 20 (max digits count in
+ * unsigned int on 64-bit arch) + 1 (for \n) */
+
+/* ============================================================================
+ * = FOPS_TASKS =
+ * ============================================================================
+ */
+
+struct read_buf {
+ char *begin;
+ char *ptr;
+ char *end;
+};
+
+static void on_each_proc_callback(struct sspt_proc *proc, void *data)
+{
+ struct read_buf *rbuf = (struct read_buf *)data;
+ char pid_str[PID_STRING];
+ int len;
+
+ /* skip process */
+ if (!sspt_proc_is_send_event(proc))
+ return;
+
+ snprintf(pid_str, sizeof(pid_str), "%d", proc->tgid);
+
+ len = strlen(pid_str);
+
+ if (rbuf->end - rbuf->ptr < len + 2)
+ return;
+
+ memcpy(rbuf->ptr, pid_str, len);
+ rbuf->ptr += len;
+
+ *rbuf->ptr = ' ';
+ ++rbuf->ptr;
+}
+
+static ssize_t read_tasks(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[PID_STRING * MAX_APPS_COUNT];
+ struct read_buf rbuf = {
+ .begin = buf,
+ .ptr = buf,
+ .end = buf + sizeof(buf)
+ };
+
+ on_each_proc_no_lock(on_each_proc_callback, (void *)&rbuf);
+
+ if (rbuf.ptr != rbuf.begin)
+ rbuf.ptr--;
+
+ *rbuf.ptr = '\n';
+
+ return simple_read_from_buffer(user_buf, count, ppos, rbuf.begin,
+ rbuf.ptr - rbuf.begin);
+}
+
+static const struct file_operations fops_tasks = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .read = read_tasks,
+ .llseek = default_llseek
+};
+
+/* ============================================================================
+ * = INIT/EXIT =
+ * ============================================================================
+ */
+
+static struct dentry *us_manager_dir;
+
+/**
+ * @brief Destroy debugfs for us_manager
+ *
+ * @return Void
+ */
+void exit_debugfs_us_manager(void)
+{
+ if (us_manager_dir)
+ debugfs_remove_recursive(us_manager_dir);
+
+ us_manager_dir = NULL;
+}
+
+/**
+ * @brief Create debugfs for us_manager
+ *
+ * @return Error code
+ */
+int init_debugfs_us_manager(void)
+{
+ struct dentry *swap_dir, *dentry;
+
+ swap_dir = swap_debugfs_getdir();
+ if (swap_dir == NULL)
+ return -ENOENT;
+
+ us_manager_dir = swap_debugfs_create_dir(US_MANAGER_DFS_DIR, swap_dir);
+ if (us_manager_dir == NULL)
+ return -ENOMEM;
+
+ dentry = swap_debugfs_create_file(US_MANAGER_TASKS, 0600,
+ us_manager_dir, NULL, &fops_tasks);
+ if (dentry == NULL)
+ goto fail;
+
+ return 0;
+
+fail:
+ exit_debugfs_us_manager();
+ return -ENOMEM;
+}
--- /dev/null
+#ifndef __DEBUGFS_US_MANAGER_H__
+#define __DEBUGFS_US_MANAGER_H__
+
+/**
+ * @file us_manager/debugfs_us_manager.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2014
+ */
+
+/**
+ * @def US_MANAGER_DFS_DIR @hideinitializer
+ * Name in debugfs
+ */
+#define US_MANAGER_DFS_DIR "us_manager"
+
+/**
+ * @def US_MANAGER_DFS_DIR @hideinitializer
+ * Name in debugfs
+ */
+#define US_MANAGER_TASKS "tasks"
+
+int init_debugfs_us_manager(void);
+void exit_debugfs_us_manager(void);
+
+#endif /* __DEBUGFS_US_MANAGER_H__ */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/helper.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include "sspt/sspt.h"
+#include "sspt/sspt_filter.h"
+#include "helper.h"
+#include "usm_hook.h"
+
+
+/* do_page_fault() */
+static void hh_page_fault(unsigned long addr)
+{
+ unsigned long page_addr = addr & PAGE_MASK;
+
+ call_page_fault(current, page_addr);
+}
+
+
+/* copy_process() */
+static void disarm_ip(struct sspt_ip *ip, void *data)
+{
+ struct task_struct *child = (struct task_struct *)data;
+ struct uprobe *up;
+
+ up = probe_info_get_uprobe(ip->desc->type, ip);
+ if (up)
+ disarm_uprobe(up, child);
+}
+
+static void hh_clean_task(struct task_struct *parent, struct task_struct *child)
+{
+ struct sspt_proc *proc;
+
+ proc = sspt_proc_get_by_task(parent);
+ if (proc) {
+ /* disarm up for child */
+ sspt_proc_on_each_ip(proc, disarm_ip, (void *)child);
+
+ /* disarm urp for child */
+ swap_uretprobe_free_task(parent, child, false);
+
+ sspt_proc_put(proc);
+ }
+}
+
+
+/* mm_release() */
+static void hh_mm_release(struct task_struct *task)
+{
+ struct mm_struct *mm = task->mm;
+
+ if (mm == NULL) {
+ pr_err("mm is NULL\n");
+ return;
+ }
+
+ /* TODO: this lock for synchronizing to disarm urp */
+ down_write(&mm->mmap_sem);
+ if (task != task->group_leader) {
+ struct sspt_proc *proc;
+
+ if (task != current) {
+ pr_err("call mm_release in isn't current context\n");
+ goto up_mmsem;
+ }
+
+ /* if the thread is killed we need to discard pending
+ * uretprobe instances which have not triggered yet */
+ proc = sspt_proc_by_task(task);
+ if (proc)
+ swap_uretprobe_free_task(task, task, true);
+ } else {
+ call_mm_release(task);
+ }
+
+up_mmsem:
+ up_write(&mm->mmap_sem);
+}
+
+
+/* do_munmap() */
+struct msg_unmap_data {
+ unsigned long start;
+ unsigned long end;
+};
+
+static void msg_unmap(struct sspt_filter *f, void *data)
+{
+ if (f->pfg_is_inst) {
+ struct pfg_msg_cb *cb = pfg_msg_cb_get(f->pfg);
+
+ if (cb && cb->msg_unmap) {
+ struct msg_unmap_data *msg_data;
+
+ msg_data = (struct msg_unmap_data *)data;
+ cb->msg_unmap(msg_data->start, msg_data->end);
+ }
+ }
+}
+
+static void __remove_unmap_probes(struct sspt_proc *proc,
+ unsigned long start, unsigned long end)
+{
+ LIST_HEAD(head);
+
+ if (sspt_proc_get_files_by_region(proc, &head, start, end)) {
+ struct sspt_file *file, *n;
+ struct task_struct *task = proc->leader;
+
+ list_for_each_entry_safe(file, n, &head, list) {
+ if (file->vm_start >= end)
+ continue;
+
+ if (file->vm_start >= start)
+ sspt_file_uninstall(file, task, US_UNINSTALL);
+ /* TODO: else: uninstall pages: * start..file->vm_end */
+ }
+
+ sspt_proc_insert_files(proc, &head);
+ }
+}
+
+static void hh_munmap(unsigned long start, unsigned long end)
+{
+ struct sspt_proc *proc;
+
+ proc = sspt_proc_get_by_task(current);
+ if (proc) {
+ struct msg_unmap_data msg_data = {
+ .start = start,
+ .end = end,
+ };
+
+ __remove_unmap_probes(proc, start, end);
+
+ /* send unmap region */
+ sspt_proc_on_each_filter(proc, msg_unmap, (void *)&msg_data);
+
+ sspt_proc_put(proc);
+ }
+}
+
+
+/* do_mmap_pgoff() */
+static void msg_map(struct sspt_filter *f, void *data)
+{
+ if (f->pfg_is_inst) {
+ struct pfg_msg_cb *cb = pfg_msg_cb_get(f->pfg);
+
+ if (cb && cb->msg_map)
+ cb->msg_map((struct vm_area_struct *)data);
+ }
+}
+
+static void hh_mmap(struct file *file, unsigned long addr)
+{
+ struct sspt_proc *proc;
+ struct task_struct *task;
+ struct vm_area_struct *vma;
+
+ task = current->group_leader;
+ if (is_kthread(task))
+ return;
+
+ if (IS_ERR_VALUE(addr))
+ return;
+
+ proc = sspt_proc_get_by_task(task);
+ if (proc == NULL)
+ return;
+
+ vma = find_vma_intersection(task->mm, addr, addr + 1);
+ if (vma && check_vma(vma)) {
+ usm_hook_mmap(proc, vma);
+ sspt_proc_on_each_filter(proc, msg_map, (void *)vma);
+ }
+
+ sspt_proc_put(proc);
+}
+
+
+/* set_task_comm() */
+static void hh_set_comm(struct task_struct *task)
+{
+ if (task == current)
+ check_task_and_install(current);
+}
+
+
+/* release_task() */
+static void hh_change_leader(struct task_struct *prev,
+ struct task_struct *next)
+{
+ sspt_change_leader(prev, next);
+}
+
+
+#ifdef CONFIG_SWAP_HOOK_USAUX
+# include "helper_hook.c"
+#else /* CONFIG_SWAP_HOOK_USAUX */
+# include "helper_kprobe.c"
+#endif /* CONFIG_SWAP_HOOK_USAUX */
--- /dev/null
+/**
+ * @file us_manager/helper.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+#ifndef _HELPER_H
+#define _HELPER_H
+
+#include <linux/sched.h>
+
+static inline int is_kthread(struct task_struct *task)
+{
+ return !task->mm;
+}
+
+int helper_once(void);
+int helper_init(void);
+void helper_uninit(void);
+
+int helper_reg(void);
+void helper_unreg_top(void);
+void helper_unreg_bottom(void);
+
+#endif /* _HELPER_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ */
+
+
+#include <linux/rwsem.h>
+#include <swap/hook_usaux.h>
+#include <kprobe/swap_ktd.h>
+
+
+struct hooks_td {
+ bool in_copy_process;
+};
+
+static void hooks_td_init(struct task_struct *task, void *data)
+{
+ struct hooks_td *td = (struct hooks_td *)data;
+
+ td->in_copy_process = false;
+}
+
+static void hooks_td_exit(struct task_struct *task, void *data)
+{
+ struct hooks_td *td = (struct hooks_td *)data;
+
+ WARN(td->in_copy_process, "in_copy_process=%d", td->in_copy_process);
+}
+
+static struct ktask_data hooks_ktd = {
+ .init = hooks_td_init,
+ .exit = hooks_td_exit,
+ .size = sizeof(struct hooks_td),
+};
+
+static struct hooks_td *current_hooks_td(void)
+{
+ return (struct hooks_td *)swap_ktd(&hooks_ktd, current);
+}
+
+
+static atomic_t run_flag = ATOMIC_INIT(0);
+
+static void hook_start(void)
+{
+ atomic_set(&run_flag, 1);
+}
+
+static void hook_stop(void)
+{
+ atomic_set(&run_flag, 0);
+}
+
+static bool hook_is_running(void)
+{
+ return atomic_read(&run_flag);
+}
+
+
+static void hook_page_fault(unsigned long addr)
+{
+ if (hook_is_running())
+ hh_page_fault(addr);
+}
+
+static DECLARE_RWSEM(copy_process_sem);
+
+static void hook_copy_process_pre(void)
+{
+ if (hook_is_running()) {
+ down_read(©_process_sem);
+ if (hook_is_running())
+ current_hooks_td()->in_copy_process = true;
+ else
+ up_read(©_process_sem);
+ }
+}
+
+static void hook_copy_process_post(struct task_struct *task)
+{
+ struct task_struct *parent = current;
+ struct hooks_td *td = current_hooks_td();
+
+ if (!td->in_copy_process)
+ return;
+
+ if (IS_ERR(task))
+ goto out;
+
+ /* check flags CLONE_VM */
+ if (task->mm != parent->mm)
+ hh_clean_task(current, task);
+
+out:
+ up_read(©_process_sem);
+ td->in_copy_process = false;
+}
+
+static void hook_mm_release(struct task_struct *task)
+{
+ hh_mm_release(task);
+}
+
+static void hook_munmap(unsigned long start, unsigned long end)
+{
+ hh_munmap(start, end);
+}
+
+static void hook_mmap(struct file *file, unsigned long addr)
+{
+ hh_mmap(file, addr);
+}
+
+static void hook_set_comm(struct task_struct *task)
+{
+ if (hook_is_running())
+ hh_set_comm(task);
+}
+
+static void hook_change_leader(struct task_struct *prev,
+ struct task_struct *next)
+{
+ hh_change_leader(prev, next);
+}
+
+static struct hook_usaux hook_usaux = {
+ .owner = THIS_MODULE,
+ .page_fault = hook_page_fault,
+ .copy_process_pre = hook_copy_process_pre,
+ .copy_process_post = hook_copy_process_post,
+ .mm_release = hook_mm_release,
+ .munmap = hook_munmap,
+ .mmap = hook_mmap,
+ .set_comm = hook_set_comm,
+ .change_leader = hook_change_leader,
+};
+
+int helper_once(void)
+{
+ return 0;
+}
+
+int helper_init(void)
+{
+ return swap_ktd_reg(&hooks_ktd);
+}
+
+void helper_uninit(void)
+{
+ swap_ktd_unreg(&hooks_ktd);
+}
+
+int helper_reg(void)
+{
+ int ret;
+
+ ret = hook_usaux_set(&hook_usaux);
+ if (ret)
+ return ret;
+
+ hook_start();
+ return ret;
+}
+
+void helper_unreg_top(void)
+{
+ hook_stop();
+}
+
+void helper_unreg_bottom(void)
+{
+ /* waiting for copy_process() finishing */
+ down_write(©_process_sem);
+ up_write(©_process_sem);
+
+ hook_usaux_reset();
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2016
+ *
+ * 2016 Vyacheslav Cherkashin
+ */
+
+
+#include <linux/kconfig.h>
+#include <kprobe/swap_kprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <ksyms/ksyms.h>
+#include <writer/kernel_operations.h>
+#include "us_slot_manager.h"
+#include "sspt/sspt.h"
+#include "sspt/sspt_filter.h"
+#include "helper.h"
+
+
+static atomic_t stop_flag = ATOMIC_INIT(0);
+
+
+/*
+ ******************************************************************************
+ * do_page_fault() *
+ ******************************************************************************
+ */
+
+struct pf_data {
+ unsigned long addr;
+
+ struct pt_regs *pf_regs;
+ unsigned long save_pc;
+};
+
+static int entry_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct pf_data *data = (struct pf_data *)ri->data;
+
+#if defined(CONFIG_ARM)
+ data->addr = swap_get_karg(regs, 0);
+ data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 2);
+ data->save_pc = data->pf_regs->ARM_pc;
+#elif defined(CONFIG_X86_32)
+ data->addr = read_cr2();
+ data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 0);
+ data->save_pc = data->pf_regs->ip;
+#elif defined(CONFIG_ARM64)
+ data->addr = swap_get_karg(regs, 0);
+ data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 2);
+ data->save_pc = data->pf_regs->pc;
+ /* FIXME: to implement */
+#else
+ #error "this architecture is not supported"
+#endif /* CONFIG_arch */
+
+ if (data->addr) {
+ int ret = 0;
+ struct sspt_proc *proc;
+
+ proc = sspt_proc_get_by_task(current);
+ if (proc) {
+ if (proc->r_state_addr == data->addr) {
+ /* skip ret_handler_pf() for current task */
+ ret = 1;
+ }
+
+ sspt_proc_put(proc);
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static unsigned long cb_pf(void *data)
+{
+ unsigned long addr = *(unsigned long *)data;
+
+ hh_page_fault(addr);
+
+ return 0;
+}
+
+/* Detects when IPs are really loaded into phy mem and installs probes. */
+static int ret_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct task_struct *task = current;
+ struct pf_data *data = (struct pf_data *)ri->data;
+ unsigned long addr;
+ int ret;
+
+ if (is_kthread(task))
+ return 0;
+
+ /* skip fixup page_fault */
+#if defined(CONFIG_ARM)
+ if (data->save_pc != data->pf_regs->ARM_pc)
+ return 0;
+#elif defined(CONFIG_X86_32)
+ if (data->save_pc != data->pf_regs->ip)
+ return 0;
+#elif defined(CONFIG_ARM64)
+ if (data->save_pc != data->pf_regs->pc)
+ return 0;
+#endif /* CONFIG_arch */
+
+ /* TODO: check return value */
+ addr = data->addr;
+ ret = set_jump_cb((unsigned long)ri->ret_addr, regs, cb_pf,
+ &addr, sizeof(addr));
+
+ if (ret == 0)
+ ri->ret_addr = (unsigned long *)get_jump_addr();
+
+ return 0;
+}
+
+static struct kretprobe mf_kretprobe = {
+ .entry_handler = entry_handler_pf,
+ .handler = ret_handler_pf,
+ .data_size = sizeof(struct pf_data)
+};
+
+static int register_mf(void)
+{
+ int ret;
+
+ ret = swap_register_kretprobe(&mf_kretprobe);
+ if (ret)
+ pr_err("swap_register_kretprobe(handle_mm_fault) ret=%d!\n", ret);
+
+ return ret;
+}
+
+static void unregister_mf(void)
+{
+ swap_unregister_kretprobe(&mf_kretprobe);
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * copy_process() *
+ ******************************************************************************
+ */
+static atomic_t rm_uprobes_child_cnt = ATOMIC_INIT(0);
+
+static unsigned long cb_clean_child(void *data)
+{
+ struct task_struct *parent = current;
+ struct task_struct *child = *(struct task_struct **)data;
+
+ hh_clean_task(parent, child);
+
+ atomic_dec(&rm_uprobes_child_cnt);
+ return 0;
+}
+
+static void rm_uprobes_child(struct kretprobe_instance *ri,
+ struct pt_regs *regs, struct task_struct *child)
+{
+ int ret;
+
+ if (!sspt_proc_by_task(current))
+ return;
+
+ /* set jumper */
+ ret = set_jump_cb((unsigned long)ri->ret_addr, regs,
+ cb_clean_child, &child, sizeof(child));
+ if (ret == 0) {
+ atomic_inc(&rm_uprobes_child_cnt);
+ ri->ret_addr = (unsigned long *)get_jump_addr();
+ } else {
+ WARN_ON(1);
+ }
+}
+
+
+static int pre_handler_cp(struct kprobe *p, struct pt_regs *regs)
+{
+ if (is_kthread(current))
+ goto out;
+
+ if (atomic_read(&stop_flag))
+ call_mm_release(current);
+
+out:
+ return 0;
+}
+
+static atomic_t copy_process_cnt = ATOMIC_INIT(0);
+
+static int entry_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ atomic_inc(©_process_cnt);
+
+ return 0;
+}
+
+/* Delete uprobs in children at fork */
+static int ret_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct task_struct *task;
+
+ task = (struct task_struct *)regs_return_value(regs);
+ if (IS_ERR(task))
+ goto out;
+
+ if (task->mm != current->mm) /* check flags CLONE_VM */
+ rm_uprobes_child(ri, regs, task);
+out:
+ atomic_dec(©_process_cnt);
+ return 0;
+}
+
+static struct kretprobe cp_kretprobe = {
+ .entry_handler = entry_handler_cp,
+ .handler = ret_handler_cp,
+};
+
+static struct kprobe cp_kprobe = {
+ .pre_handler = pre_handler_cp
+};
+
+static int register_cp(void)
+{
+ int ret;
+
+
+ ret = swap_register_kprobe(&cp_kprobe);
+ if (ret)
+ pr_err("swap_register_kprobe(copy_process) ret=%d!\n", ret);
+
+ ret = swap_register_kretprobe(&cp_kretprobe);
+ if (ret) {
+ pr_err("swap_register_kretprobe(copy_process) ret=%d!\n", ret);
+ swap_unregister_kprobe(&cp_kprobe);
+ }
+
+ return ret;
+}
+
+static void unregister_cp(void)
+{
+ swap_unregister_kretprobe_top(&cp_kretprobe, 0);
+ do {
+ synchronize_sched();
+ } while (atomic_read(©_process_cnt));
+ swap_unregister_kretprobe_bottom(&cp_kretprobe);
+ swap_unregister_kprobe(&cp_kprobe);
+
+ do {
+ synchronize_sched();
+ } while (atomic_read(&rm_uprobes_child_cnt));
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * mm_release() *
+ ******************************************************************************
+ */
+
+/* Detects when target process removes IPs. */
+static int mr_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct task_struct *task = (struct task_struct *)swap_get_karg(regs, 0);
+
+ hh_mm_release(task);
+
+ return 0;
+}
+
+static struct kprobe mr_kprobe = {
+ .pre_handler = mr_pre_handler
+};
+
+static int register_mr(void)
+{
+ int ret;
+
+ ret = swap_register_kprobe(&mr_kprobe);
+ if (ret)
+ pr_err("swap_register_kprobe(mm_release) ret=%d!\n", ret);
+
+ return ret;
+}
+
+static void unregister_mr(void)
+{
+ swap_unregister_kprobe(&mr_kprobe);
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * do_munmap() *
+ ******************************************************************************
+ */
+struct unmap_data {
+ unsigned long start;
+ size_t len;
+};
+
+static atomic_t unmap_cnt = ATOMIC_INIT(0);
+
+static unsigned long cb_munmap(void *data)
+{
+ struct unmap_data *umd = (struct unmap_data *)data;
+
+ hh_munmap(umd->start, umd->start + umd->len);
+
+ atomic_dec(&unmap_cnt);
+ return 0;
+}
+
+static int entry_handler_unmap(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct unmap_data *data = (struct unmap_data *)ri->data;
+
+ data->start = swap_get_karg(regs, 1);
+ data->len = (size_t)PAGE_ALIGN(swap_get_karg(regs, 2));
+
+ atomic_inc(&unmap_cnt);
+ return 0;
+}
+
+static int ret_handler_unmap(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int ret;
+
+ if (regs_return_value(regs)) {
+ atomic_dec(&unmap_cnt);
+ return 0;
+ }
+
+ ret = set_jump_cb((unsigned long)ri->ret_addr, regs, cb_munmap,
+ (struct unmap_data *)ri->data,
+ sizeof(struct unmap_data));
+ if (ret == 0) {
+ ri->ret_addr = (unsigned long *)get_jump_addr();
+ } else {
+ WARN_ON(1);
+ atomic_dec(&unmap_cnt);
+ }
+
+ return 0;
+}
+
+static struct kretprobe unmap_kretprobe = {
+ .entry_handler = entry_handler_unmap,
+ .handler = ret_handler_unmap,
+ .data_size = sizeof(struct unmap_data)
+};
+
+static int register_unmap(void)
+{
+ int ret;
+
+ ret = swap_register_kretprobe(&unmap_kretprobe);
+ if (ret)
+ pr_err("swap_register_kprobe(do_munmap) ret=%d!\n", ret);
+
+ return ret;
+}
+
+static void unregister_unmap(void)
+{
+ swap_unregister_kretprobe_top(&unmap_kretprobe, 0);
+ do {
+ synchronize_sched();
+ } while (atomic_read(&unmap_cnt));
+ swap_unregister_kretprobe_bottom(&unmap_kretprobe);
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * do_mmap_pgoff() *
+ ******************************************************************************
+ */
+static int ret_handler_mmap(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ unsigned long addr = regs_return_value(regs);
+
+ hh_mmap(NULL, addr);
+
+ return 0;
+}
+
+static struct kretprobe mmap_kretprobe = {
+ .handler = ret_handler_mmap
+};
+
+static int register_mmap(void)
+{
+ int ret;
+
+ ret = swap_register_kretprobe(&mmap_kretprobe);
+ if (ret)
+ pr_err("swap_register_kretprobe(do_mmap_pgoff) ret=%d!\n", ret);
+
+ return ret;
+}
+
+static void unregister_mmap(void)
+{
+ swap_unregister_kretprobe(&mmap_kretprobe);
+}
+
+
+
+
+
+/*
+ ******************************************************************************
+ * set_task_comm() *
+ ******************************************************************************
+ */
+struct comm_data {
+ struct task_struct *task;
+};
+
+static unsigned long cb_check_and_install(void *data)
+{
+ hh_set_comm(current);
+
+ return 0;
+}
+
+static int entry_handler_comm(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct comm_data *data = (struct comm_data *)ri->data;
+
+ data->task = (struct task_struct *)swap_get_karg(regs, 0);
+
+ return 0;
+}
+
+static int ret_handler_comm(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct task_struct *task;
+ int ret;
+
+ if (is_kthread(current))
+ return 0;
+
+ task = ((struct comm_data *)ri->data)->task;
+ if (task != current)
+ return 0;
+
+ ret = set_jump_cb((unsigned long)ri->ret_addr, regs,
+ cb_check_and_install, NULL, 0);
+ if (ret == 0)
+ ri->ret_addr = (unsigned long *)get_jump_addr();
+
+ return 0;
+}
+
+static struct kretprobe comm_kretprobe = {
+ .entry_handler = entry_handler_comm,
+ .handler = ret_handler_comm,
+ .data_size = sizeof(struct comm_data)
+};
+
+static int register_comm(void)
+{
+ int ret;
+
+ ret = swap_register_kretprobe(&comm_kretprobe);
+ if (ret)
+ pr_err("swap_register_kretprobe(set_task_comm) ret=%d!\n", ret);
+
+ return ret;
+}
+
+static void unregister_comm(void)
+{
+ swap_unregister_kretprobe(&comm_kretprobe);
+}
+
+
+
+
+/*
+ ******************************************************************************
+ * release_task() *
+ ******************************************************************************
+ */
+static int release_task_h(struct kprobe *p, struct pt_regs *regs)
+{
+ struct task_struct *task = (struct task_struct *)swap_get_karg(regs, 0);
+ struct task_struct *cur = current;
+
+ if (cur->flags & PF_KTHREAD)
+ return 0;
+
+ /* EXEC: change group leader */
+ if (cur != task && task->pid == cur->pid)
+ hh_change_leader(task, cur);
+
+ return 0;
+}
+
+struct kprobe release_task_kp = {
+ .pre_handler = release_task_h,
+};
+
+static int reg_release_task(void)
+{
+ return swap_register_kprobe(&release_task_kp);
+}
+
+static void unreg_release_task(void)
+{
+ swap_unregister_kprobe(&release_task_kp);
+}
+
+
+
+
+
+/**
+ * @brief Registration of helper
+ *
+ * @return Error code
+ */
+int helper_reg(void)
+{
+ int ret = 0;
+
+ atomic_set(&stop_flag, 0);
+
+ /* tracking group leader changing */
+ ret = reg_release_task();
+ if (ret)
+ return ret;
+
+ /*
+ * install probe on 'set_task_comm' to detect when field comm struct
+ * task_struct changes
+ */
+ ret = register_comm();
+ if (ret)
+ goto unreg_rel_task;
+
+ /* install probe on 'do_munmap' to detect when for remove US probes */
+ ret = register_unmap();
+ if (ret)
+ goto unreg_comm;
+
+ /* install probe on 'mm_release' to detect when for remove US probes */
+ ret = register_mr();
+ if (ret)
+ goto unreg_unmap;
+
+ /* install probe on 'copy_process' to disarm children process */
+ ret = register_cp();
+ if (ret)
+ goto unreg_mr;
+
+ /* install probe on 'do_mmap_pgoff' to detect when mapping file */
+ ret = register_mmap();
+ if (ret)
+ goto unreg_cp;
+
+ /*
+ * install probe on 'handle_mm_fault' to detect when US pages will be
+ * loaded
+ */
+ ret = register_mf();
+ if (ret)
+ goto unreg_mmap;
+
+ return ret;
+
+unreg_mmap:
+ unregister_mmap();
+
+unreg_cp:
+ unregister_cp();
+
+unreg_mr:
+ unregister_mr();
+
+unreg_unmap:
+ unregister_unmap();
+
+unreg_comm:
+ unregister_comm();
+
+unreg_rel_task:
+ unreg_release_task();
+
+ return ret;
+}
+
+/**
+ * @brief Unegistration of helper bottom
+ *
+ * @return Void
+ */
+void helper_unreg_top(void)
+{
+ unregister_mf();
+ atomic_set(&stop_flag, 1);
+}
+
+/**
+ * @brief Unegistration of helper top
+ *
+ * @return Void
+ */
+void helper_unreg_bottom(void)
+{
+ unregister_mmap();
+ unregister_cp();
+ unregister_mr();
+ unregister_unmap();
+ unregister_comm();
+ unreg_release_task();
+}
+
+/**
+ * @brief Initialization of helper
+ *
+ * @return Error code
+ */
+int helper_once(void)
+{
+ const char *sym;
+
+ sym = "do_page_fault";
+ mf_kretprobe.kp.addr = swap_ksyms(sym);
+ if (mf_kretprobe.kp.addr == 0)
+ goto not_found;
+
+ sym = "copy_process";
+ cp_kretprobe.kp.addr = swap_ksyms_substr(sym);
+ if (cp_kretprobe.kp.addr == 0)
+ goto not_found;
+ cp_kprobe.addr = cp_kretprobe.kp.addr;
+
+ sym = "mm_release";
+ mr_kprobe.addr = swap_ksyms(sym);
+ if (mr_kprobe.addr == 0)
+ goto not_found;
+
+ sym = "do_munmap";
+ unmap_kretprobe.kp.addr = swap_ksyms(sym);
+ if (unmap_kretprobe.kp.addr == 0)
+ goto not_found;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+ sym = "do_mmap";
+#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)) */
+ sym = "do_mmap_pgoff";
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)) */
+ mmap_kretprobe.kp.addr = swap_ksyms(sym);
+ if (mmap_kretprobe.kp.addr == 0)
+ goto not_found;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ sym = "__set_task_comm";
+#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) */
+ sym = "set_task_comm";
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) */
+ comm_kretprobe.kp.addr = swap_ksyms(sym);
+ if (comm_kretprobe.kp.addr == 0)
+ goto not_found;
+
+ sym = "release_task";
+ release_task_kp.addr = swap_ksyms(sym);
+ if (release_task_kp.addr == 0)
+ goto not_found;
+
+ return 0;
+
+not_found:
+ pr_err("ERROR: symbol '%s' not found\n", sym);
+ return -ESRCH;
+}
+
+int helper_init(void)
+{
+ return 0;
+}
+
+void helper_uninit(void)
+{
+}
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/img/img_file.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include "img_file.h"
+#include "img_ip.h"
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/atomic.h>
+
+
+static atomic_t file_counter = ATOMIC_INIT(0);
+
+
+static void img_del_ip_by_list(struct img_ip *ip);
+
+/**
+ * @brief Create img_file struct
+ *
+ * @param dentry Dentry of file
+ * @return Pointer to the created img_file struct
+ */
+struct img_file *img_file_create(struct dentry *dentry)
+{
+ struct img_file *file;
+
+ file = kmalloc(sizeof(*file), GFP_ATOMIC);
+ if (file == NULL) {
+ pr_err("%s: failed to allocate memory\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+ atomic_inc(&file_counter);
+
+ file->dentry = dentry;
+ INIT_LIST_HEAD(&file->list);
+ INIT_LIST_HEAD(&file->ips.head);
+ mutex_init(&file->ips.mtx);
+ atomic_set(&file->use, 1);
+
+ return file;
+}
+
+/**
+ * @brief Remove img_file struct
+ *
+ * @param file remove object
+ * @return Void
+ */
+static void img_file_free(struct img_file *file)
+{
+ struct img_ip *ip, *tmp;
+
+ list_for_each_entry_safe(ip, tmp, &file->ips.head, list) {
+ img_del_ip_by_list(ip);
+ img_ip_clean(ip);
+ img_ip_put(ip);
+ }
+
+ atomic_dec(&file_counter);
+ kfree(file);
+}
+
+/* called with mutex_[lock/unlock](&file->ips.mtx) */
+static void img_add_ip_by_list(struct img_file *file, struct img_ip *ip)
+{
+ list_add(&ip->list, &file->ips.head);
+}
+
+/* called with mutex_[lock/unlock](&file->ips.mtx) */
+static void img_del_ip_by_list(struct img_ip *ip)
+{
+ list_del(&ip->list);
+}
+
+void img_file_get(struct img_file *file)
+{
+ WARN_ON(!atomic_read(&file->use));
+ atomic_inc(&file->use);
+}
+
+void img_file_put(struct img_file *file)
+{
+ if (atomic_dec_and_test(&file->use))
+ img_file_free(file);
+}
+
+
+/**
+ * @brief Add instrumentation pointer
+ *
+ * @param file Pointer to the img_file struct
+ * @param addr Function address
+ * @param probe_Pointer to a probe_info structure with an information about
+ * the probe.
+ * @return Error code
+ */
+struct img_ip *img_file_add_ip(struct img_file *file, unsigned long addr,
+ struct probe_desc *pd)
+{
+ struct img_ip *ip;
+
+ ip = img_ip_create(addr, pd, file);
+ if (IS_ERR(ip))
+ return ip;
+
+ mutex_lock(&file->ips.mtx);
+ img_add_ip_by_list(file, ip);
+ mutex_unlock(&file->ips.mtx);
+
+ return ip;
+}
+
+/**
+ * @brief Delete img_ip struct from img_file struct
+ *
+ * @param file Pointer to the img_file struct
+ * @param addr Function address
+ * @return Error code
+ */
+void img_file_del_ip(struct img_file *file, struct img_ip *ip)
+{
+ mutex_lock(&file->ips.mtx);
+ img_del_ip_by_list(ip);
+ mutex_unlock(&file->ips.mtx);
+
+ img_ip_clean(ip);
+ img_ip_put(ip);
+}
+
+/**
+ * @brief Check on absence img_ip structs in img_file struct
+ *
+ * @param file Pointer to the img_file struct
+ * @return
+ * - 0 - not empty
+ * - 1 - empty
+ */
+int img_file_empty(struct img_file *file)
+{
+ return list_empty(&file->ips.head);
+}
+
+bool img_file_is_unloadable(void)
+{
+ return !(atomic_read(&file_counter) + !img_ip_is_unloadable());
+}
+
+/**
+ * @brief For debug
+ *
+ * @param file Pointer to the img_file struct
+ * @return Void
+ */
+
+/* debug */
+void img_file_print(struct img_file *file)
+{
+ struct img_ip *ip;
+
+ printk(KERN_INFO "### d_iname=%s\n", file->dentry->d_iname);
+
+ mutex_lock(&file->ips.mtx);
+ list_for_each_entry(ip, &file->ips.head, list) {
+ img_ip_print(ip);
+ }
+ mutex_unlock(&file->ips.mtx);
+}
+/* debug */
--- /dev/null
+/**
+ * @file us_manager/img/img_file.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ */
+
+
+#ifndef _IMG_FILE_H
+#define _IMG_FILE_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+struct probe_desc;
+
+/**
+ * @struct img_file
+ * @breaf Image of file
+ */
+struct img_file {
+ /* img_proc */
+ struct list_head list; /**< List for img_proc */
+
+ /* img_ip */
+ struct {
+ struct mutex mtx;
+ struct list_head head; /**< Head for img_ip */
+ } ips;
+
+ struct dentry *dentry; /**< Dentry of file */
+ atomic_t use;
+};
+
+struct img_file *img_file_create(struct dentry *dentry);
+void img_file_get(struct img_file *file);
+void img_file_put(struct img_file *file);
+
+struct img_ip *img_file_add_ip(struct img_file *file, unsigned long addr,
+ struct probe_desc *pd);
+void img_file_del_ip(struct img_file *file, struct img_ip *ip);
+
+int img_file_empty(struct img_file *file);
+bool img_file_is_unloadable(void);
+
+/* debug */
+void img_file_print(struct img_file *file);
+/* debug */
+
+#endif /* _IMG_FILE_H */
+
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/img/img_ip.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include "img_ip.h"
+#include "img_file.h"
+#include <us_manager/probes/use_probes.h>
+#include <us_manager/sspt/sspt.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+
+
+static atomic_t ip_counter = ATOMIC_INIT(0);
+
+/**
+ * @brief Create img_ip struct
+ *
+ * @param addr Function address
+ * @param probe_i Pointer to the probe info data.
+ * @return Pointer to the created img_ip struct
+ */
+struct img_ip *img_ip_create(unsigned long addr, struct probe_desc *pd,
+ struct img_file *file)
+{
+ struct img_ip *ip;
+
+ ip = kmalloc(sizeof(*ip), GFP_KERNEL);
+ if (!ip)
+ return ERR_PTR(-ENOMEM);
+ atomic_inc(&ip_counter);
+
+ INIT_LIST_HEAD(&ip->list);
+ kref_init(&ip->ref);
+ mutex_init(&ip->sspt.mtx);
+ INIT_LIST_HEAD(&ip->sspt.head);
+ ip->addr = addr;
+ ip->desc = pd;
+ ip->file = file;
+
+ return ip;
+}
+
+static void img_ip_release(struct kref *ref)
+{
+ struct img_ip *ip = container_of(ref, struct img_ip, ref);
+
+ WARN_ON(!list_empty(&ip->sspt.head));
+
+ atomic_dec(&ip_counter);
+ kfree(ip);
+}
+
+void img_ip_clean(struct img_ip *ip)
+{
+ struct sspt_ip *p;
+
+ img_ip_lock(ip);
+ while(!list_empty(&ip->sspt.head)) {
+ p = list_first_entry(&ip->sspt.head, struct sspt_ip ,img_list);
+ sspt_ip_get(p);
+ img_ip_unlock(ip);
+
+ if (sspt_page_is_installed_ip(p->page, p))
+ sspt_unregister_usprobe(NULL, p, US_UNREGS_PROBE);
+
+ sspt_ip_clean(p);
+
+ img_ip_lock(ip);
+ sspt_ip_put(p);
+ }
+ img_ip_unlock(ip);
+}
+
+void img_ip_get(struct img_ip *ip)
+{
+ kref_get(&ip->ref);
+}
+
+void img_ip_put(struct img_ip *ip)
+{
+ kref_put(&ip->ref, img_ip_release);
+}
+
+void img_ip_add_ip(struct img_ip *ip, struct sspt_ip *sspt_ip)
+{
+ sspt_ip->img_ip = ip;
+ list_add(&sspt_ip->img_list, &ip->sspt.head);
+}
+
+void img_ip_lock(struct img_ip *ip)
+{
+ mutex_lock(&ip->sspt.mtx);
+}
+
+void img_ip_unlock(struct img_ip *ip)
+{
+ mutex_unlock(&ip->sspt.mtx);
+}
+
+bool img_ip_is_unloadable(void)
+{
+ return !atomic_read(&ip_counter);
+}
+
+/**
+ * @brief For debug
+ *
+ * @param ip Pointer to the img_ip struct
+ * @return Void
+ */
+
+/* debug */
+void img_ip_print(struct img_ip *ip)
+{
+ if (ip->desc->type == SWAP_RETPROBE)
+ printk(KERN_INFO "### addr=8%lx, args=%s\n",
+ ip->addr, ip->desc->info.rp_i.args);
+}
+/* debug */
--- /dev/null
+/**
+ * @file us_manager/img/img_ip.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ */
+
+
+#ifndef _IMG_IP_H
+#define _IMG_IP_H
+
+#include <linux/types.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+
+
+struct sspt_ip;
+struct img_file;
+struct probe_desc;
+
+/**
+ * @struct img_ip
+ * @breaf Image of instrumentation pointer
+ */
+struct img_ip {
+ /* img_file */
+ struct list_head list; /**< List for img_file */
+ struct img_file *file; /**< Pointer on the file (parent) */
+
+ struct kref ref;
+
+ /* sspt_ip */
+ struct {
+ struct mutex mtx;
+ struct list_head head;
+ } sspt;
+
+ unsigned long addr; /**< Function address */
+ struct probe_desc *desc; /**< Probe info */
+};
+
+struct img_ip *img_ip_create(unsigned long addr, struct probe_desc *info,
+ struct img_file *file);
+void img_ip_clean(struct img_ip *ip);
+void img_ip_get(struct img_ip *ip);
+void img_ip_put(struct img_ip *ip);
+
+void img_ip_add_ip(struct img_ip *ip, struct sspt_ip *sspt_ip);
+void img_ip_lock(struct img_ip *ip);
+void img_ip_unlock(struct img_ip *ip);
+
+bool img_ip_is_unloadable(void);
+
+/* debug */
+void img_ip_print(struct img_ip *ip);
+/* debug */
+
+#endif /* _IMG_IP_H */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/img/img_proc.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/sspt/sspt_file.h>
+#include "img_ip.h"
+#include "img_proc.h"
+#include "img_file.h"
+
+
+struct img_proc {
+ /* img_file */
+ struct {
+ struct list_head head;
+ struct mutex mtx;
+ } files;
+};
+
+
+static atomic_t proc_counter = ATOMIC_INIT(0);
+
+static void img_del_file_by_list(struct img_file *file);
+
+/**
+ * @brief Create img_proc struct
+ *
+ * @return Pointer to the created img_proc struct
+ */
+struct img_proc *img_proc_create(void)
+{
+ struct img_proc *proc;
+
+ proc = kmalloc(sizeof(*proc), GFP_ATOMIC);
+ if (proc) {
+ atomic_inc(&proc_counter);
+ INIT_LIST_HEAD(&proc->files.head);
+ mutex_init(&proc->files.mtx);
+ }
+
+ return proc;
+}
+
+/**
+ * @brief Remove img_proc struct
+ *
+ * @param file remove object
+ * @return Void
+ */
+void img_proc_free(struct img_proc *proc)
+{
+ struct img_file *file, *tmp;
+
+ mutex_lock(&proc->files.mtx);
+ list_for_each_entry_safe(file, tmp, &proc->files.head, list) {
+ img_del_file_by_list(file);
+ img_file_put(file);
+ }
+ mutex_unlock(&proc->files.mtx);
+
+ atomic_dec(&proc_counter);
+ kfree(proc);
+}
+
+/* called with mutex_[lock/unlock](&proc->files.mtx) */
+static void img_add_file_by_list(struct img_proc *proc, struct img_file *file)
+{
+ list_add(&file->list, &proc->files.head);
+}
+
+/* called with mutex_[lock/unlock](&proc->files.mtx) */
+static void img_del_file_by_list(struct img_file *file)
+{
+ list_del(&file->list);
+}
+
+/* called with mutex_[lock/unlock](&proc->files.mtx) */
+static struct img_file *img_file_find(struct img_proc *proc,
+ struct dentry *dentry)
+{
+ struct img_file *file;
+
+ list_for_each_entry(file, &proc->files.head, list) {
+ if (file->dentry == dentry)
+ return file;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Add instrumentation pointer
+ *
+ * @param proc Pointer to the img_proc struct
+ * @param dentry Dentry of file
+ * @param addr Function address
+ * @param probe_i Pointer to a probe_info struct related with the probe
+ * @return Error code
+ */
+struct img_ip *img_proc_add_ip(struct img_proc *proc, struct dentry *dentry,
+ unsigned long addr, struct probe_desc *pd)
+{
+ struct img_file *file;
+
+ mutex_lock(&proc->files.mtx);
+ file = img_file_find(proc, dentry);
+ if (!file) {
+ file = img_file_create(dentry);
+ if (IS_ERR(file)) {
+ mutex_unlock(&proc->files.mtx);
+
+ /* handle type cast */
+ return ERR_PTR(PTR_ERR(file));
+ }
+
+ img_add_file_by_list(proc, file);
+ }
+ mutex_unlock(&proc->files.mtx);
+
+ return img_file_add_ip(file, addr, pd);
+}
+
+/**
+ * @brief Remove instrumentation pointer
+ *
+ * @param proc Pointer to the img_proc struct
+ * @param dentry Dentry of file
+ * @param args Function address
+ * @return Error code
+ */
+void img_proc_del_ip(struct img_proc *proc, struct img_ip *ip)
+{
+ struct img_file *file = ip->file;
+
+ mutex_lock(&proc->files.mtx);
+ img_file_del_ip(file, ip);
+ if (img_file_empty(file)) {
+ img_del_file_by_list(file);
+ img_file_put(file);
+ }
+ mutex_unlock(&proc->files.mtx);
+}
+
+void img_proc_copy_to_sspt(struct img_proc *i_proc, struct sspt_proc *proc)
+{
+ struct sspt_file *file;
+ struct img_file *i_file;
+
+ mutex_lock(&i_proc->files.mtx);
+ list_for_each_entry(i_file, &i_proc->files.head, list) {
+ file = sspt_proc_find_file_or_new(proc, i_file->dentry);
+ if (file) {
+ struct img_ip *i_ip;
+
+ mutex_lock(&i_file->ips.mtx);
+ list_for_each_entry(i_ip, &i_file->ips.head, list)
+ sspt_file_add_ip(file, i_ip);
+ mutex_unlock(&i_file->ips.mtx);
+ }
+ }
+ mutex_unlock(&i_proc->files.mtx);
+}
+
+bool img_proc_is_unloadable(void)
+{
+ return !(atomic_read(&proc_counter) + !img_file_is_unloadable());
+}
+
+/**
+ * @brief For debug
+ *
+ * @param proc Pointer to the img_proc struct
+ * @return Void
+ */
+
+/* debug */
+void img_proc_print(struct img_proc *proc)
+{
+ struct img_file *file;
+
+ printk(KERN_INFO "### img_proc_print:\n");
+
+ mutex_lock(&proc->files.mtx);
+ list_for_each_entry(file, &proc->files.head, list) {
+ img_file_print(file);
+ }
+ mutex_unlock(&proc->files.mtx);
+}
+/* debug */
--- /dev/null
+/**
+ * @file us_manager/img/img_proc.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENCE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+
+#ifndef _IMG_PROC_H
+#define _IMG_PROC_H
+
+#include <linux/types.h>
+
+struct dentry;
+struct sspt_proc;
+struct probe_desc;
+
+
+struct img_proc *img_proc_create(void);
+void img_proc_free(struct img_proc *proc);
+
+struct img_ip *img_proc_add_ip(struct img_proc *proc, struct dentry *dentry,
+ unsigned long addr, struct probe_desc *pd);
+void img_proc_del_ip(struct img_proc *proc, struct img_ip *ip);
+
+void img_proc_copy_to_sspt(struct img_proc *i_proc, struct sspt_proc *proc);
+bool img_proc_is_unloadable(void);
+
+/* debug */
+void img_proc_print(struct img_proc *proc);
+/* debug */
+
+#endif /* _IMG_PROC_H */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/pf/pf_group.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/namei.h>
+#include <linux/mman.h>
+#include <linux/atomic.h>
+#include <linux/spinlock.h>
+#include "pf_group.h"
+#include "proc_filters.h"
+#include "../sspt/sspt_filter.h"
+#include "../us_manager_common.h"
+#include <us_manager/img/img_proc.h>
+#include <us_manager/img/img_file.h>
+#include <us_manager/img/img_ip.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/helper.h>
+#include <us_manager/us_common_file.h>
+#include <task_ctx/task_ctx.h>
+
+
+struct pf_group {
+ struct list_head list;
+ struct img_proc *i_proc;
+ struct proc_filter filter;
+ struct pfg_msg_cb *msg_cb;
+ atomic_t usage;
+
+ spinlock_t pl_lock; /* for proc_list */
+ struct list_head proc_list;
+};
+
+struct pl_struct {
+ struct list_head list;
+ struct sspt_proc *proc;
+};
+
+
+static atomic_t pfg_counter = ATOMIC_INIT(0);
+
+static LIST_HEAD(pfg_list);
+static DECLARE_RWSEM(pfg_list_sem);
+static DECLARE_RWSEM(uninstall_sem);
+
+static void pfg_list_rlock(void)
+{
+ down_read(&pfg_list_sem);
+}
+
+static void pfg_list_runlock(void)
+{
+ up_read(&pfg_list_sem);
+}
+
+static void pfg_list_wlock(void)
+{
+ down_write(&pfg_list_sem);
+}
+
+static void pfg_list_wunlock(void)
+{
+ up_write(&pfg_list_sem);
+}
+
+
+/* struct pl_struct */
+static struct pl_struct *create_pl_struct(struct sspt_proc *proc)
+{
+ struct pl_struct *pls = kmalloc(sizeof(*pls), GFP_ATOMIC);
+
+ if (pls) {
+ INIT_LIST_HEAD(&pls->list);
+ pls->proc = sspt_proc_get(proc);
+ }
+
+ return pls;
+}
+
+static void free_pl_struct(struct pl_struct *pls)
+{
+ sspt_proc_put(pls->proc);
+ kfree(pls);
+}
+/* struct pl_struct */
+
+static struct pf_group *pfg_create(void)
+{
+ struct pf_group *pfg = kmalloc(sizeof(*pfg), GFP_ATOMIC);
+
+ if (pfg == NULL)
+ return NULL;
+
+ pfg->i_proc = img_proc_create();
+ if (pfg->i_proc == NULL)
+ goto create_pfg_fail;
+
+ INIT_LIST_HEAD(&pfg->list);
+ memset(&pfg->filter, 0, sizeof(pfg->filter));
+ spin_lock_init(&pfg->pl_lock);
+ INIT_LIST_HEAD(&pfg->proc_list);
+ pfg->msg_cb = NULL;
+ atomic_set(&pfg->usage, 1);
+
+ atomic_inc(&pfg_counter);
+ return pfg;
+
+create_pfg_fail:
+
+ kfree(pfg);
+
+ return NULL;
+}
+
+static void pfg_free(struct pf_group *pfg)
+{
+ struct pl_struct *pl, *n;
+
+ img_proc_free(pfg->i_proc);
+ free_pf(&pfg->filter);
+ list_for_each_entry_safe(pl, n, &pfg->proc_list, list) {
+ sspt_proc_del_filter(pl->proc, pfg);
+ free_pl_struct(pl);
+ }
+
+ atomic_dec(&pfg_counter);
+ kfree(pfg);
+}
+
+bool pfg_is_unloadable(void)
+{
+ return !(atomic_read(&pfg_counter) + !img_proc_is_unloadable());
+}
+
+static int pfg_add_proc(struct pf_group *pfg, struct sspt_proc *proc)
+{
+ struct pl_struct *pls;
+
+ pls = create_pl_struct(proc);
+ if (pls == NULL)
+ return -ENOMEM;
+
+ spin_lock(&pfg->pl_lock);
+ list_add(&pls->list, &pfg->proc_list);
+ spin_unlock(&pfg->pl_lock);
+
+ return 0;
+}
+
+static int pfg_del_proc(struct pf_group *pfg, struct sspt_proc *proc)
+{
+ struct pl_struct *pls, *pls_free = NULL;
+
+ spin_lock(&pfg->pl_lock);
+ list_for_each_entry(pls, &pfg->proc_list, list) {
+ if (pls->proc == proc) {
+ list_del(&pls->list);
+ pls_free = pls;
+ break;
+ }
+ }
+ spin_unlock(&pfg->pl_lock);
+
+ if (pls_free)
+ free_pl_struct(pls_free);
+
+ return !!pls_free;
+}
+
+
+/* called with pfg_list_lock held */
+static void pfg_add_to_list(struct pf_group *pfg)
+{
+ list_add(&pfg->list, &pfg_list);
+}
+
+/* called with pfg_list_lock held */
+static void pfg_del_from_list(struct pf_group *pfg)
+{
+ list_del(&pfg->list);
+}
+
+
+static void msg_info(struct sspt_filter *f, void *data)
+{
+ if (f->pfg_is_inst == false) {
+ struct pfg_msg_cb *cb;
+
+ f->pfg_is_inst = true;
+
+ cb = pfg_msg_cb_get(f->pfg);
+ if (cb) {
+ struct dentry *dentry;
+
+ dentry = (struct dentry *)f->pfg->filter.priv;
+
+ if (cb->msg_info)
+ cb->msg_info(f->proc->leader, dentry);
+
+ if (cb->msg_status_info)
+ cb->msg_status_info(f->proc->leader);
+ }
+ }
+}
+
+static void first_install(struct task_struct *task, struct sspt_proc *proc)
+{
+ down_write(&task->mm->mmap_sem);
+ sspt_proc_on_each_filter(proc, msg_info, NULL);
+ sspt_proc_install(proc);
+ up_write(&task->mm->mmap_sem);
+}
+
+static void subsequent_install(struct task_struct *task,
+ struct sspt_proc *proc, unsigned long page_addr)
+{
+ down_write(&task->mm->mmap_sem);
+ sspt_proc_install_page(proc, page_addr);
+ up_write(&task->mm->mmap_sem);
+}
+
+
+/**
+ * @brief Get dentry struct by path
+ *
+ * @param path Path to file
+ * @return Pointer on dentry struct on NULL
+ */
+struct dentry *dentry_by_path(const char *path)
+{
+ struct dentry *d;
+
+ d = swap_get_dentry(path);
+ if (d)
+ dput(d);
+
+ return d;
+}
+EXPORT_SYMBOL_GPL(dentry_by_path);
+
+
+int pfg_msg_cb_set(struct pf_group *pfg, struct pfg_msg_cb *msg_cb)
+{
+ if (pfg->msg_cb)
+ return -EBUSY;
+
+ pfg->msg_cb = msg_cb;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pfg_msg_cb_set);
+
+void pfg_msg_cb_reset(struct pf_group *pfg)
+{
+ pfg->msg_cb = NULL;
+}
+EXPORT_SYMBOL_GPL(pfg_msg_cb_reset);
+
+struct pfg_msg_cb *pfg_msg_cb_get(struct pf_group *pfg)
+{
+ return pfg->msg_cb;
+}
+
+/**
+ * @brief Get pf_group struct by dentry
+ *
+ * @param dentry Dentry of file
+ * @param priv Private data
+ * @return Pointer on pf_group struct
+ */
+struct pf_group *get_pf_group_by_dentry(struct dentry *dentry, void *priv)
+{
+ struct pf_group *pfg;
+
+ pfg_list_wlock();
+ list_for_each_entry(pfg, &pfg_list, list) {
+ if (check_pf_by_dentry(&pfg->filter, dentry)) {
+ atomic_inc(&pfg->usage);
+ goto unlock;
+ }
+ }
+
+ pfg = pfg_create();
+ if (pfg == NULL)
+ goto unlock;
+
+ set_pf_by_dentry(&pfg->filter, dentry, priv);
+
+ pfg_add_to_list(pfg);
+
+unlock:
+ pfg_list_wunlock();
+ return pfg;
+}
+EXPORT_SYMBOL_GPL(get_pf_group_by_dentry);
+
+/**
+ * @brief Get pf_group struct by TGID
+ *
+ * @param tgid Thread group ID
+ * @param priv Private data
+ * @return Pointer on pf_group struct
+ */
+struct pf_group *get_pf_group_by_tgid(pid_t tgid, void *priv)
+{
+ struct pf_group *pfg;
+
+ pfg_list_wlock();
+ list_for_each_entry(pfg, &pfg_list, list) {
+ if (check_pf_by_tgid(&pfg->filter, tgid)) {
+ atomic_inc(&pfg->usage);
+ goto unlock;
+ }
+ }
+
+ pfg = pfg_create();
+ if (pfg == NULL)
+ goto unlock;
+
+ set_pf_by_tgid(&pfg->filter, tgid, priv);
+
+ pfg_add_to_list(pfg);
+
+unlock:
+ pfg_list_wunlock();
+ return pfg;
+}
+EXPORT_SYMBOL_GPL(get_pf_group_by_tgid);
+
+/**
+ * @brief Get pf_group struct by comm
+ *
+ * @param comm Task comm
+ * @param priv Private data
+ * @return Pointer on pf_group struct
+ */
+struct pf_group *get_pf_group_by_comm(char *comm, void *priv)
+{
+ int ret;
+ struct pf_group *pfg;
+
+ pfg_list_wlock();
+ list_for_each_entry(pfg, &pfg_list, list) {
+ if (check_pf_by_comm(&pfg->filter, comm)) {
+ atomic_inc(&pfg->usage);
+ goto unlock;
+ }
+ }
+
+ pfg = pfg_create();
+ if (pfg == NULL)
+ goto unlock;
+
+ ret = set_pf_by_comm(&pfg->filter, comm, priv);
+ if (ret) {
+ printk(KERN_ERR "ERROR: set_pf_by_comm, ret=%d\n", ret);
+ pfg_free(pfg);
+ pfg = NULL;
+ goto unlock;
+ }
+
+ pfg_add_to_list(pfg);
+unlock:
+ pfg_list_wunlock();
+ return pfg;
+}
+EXPORT_SYMBOL_GPL(get_pf_group_by_comm);
+
+/**
+ * @brief Get pf_group struct for each process
+ *
+ * @param priv Private data
+ * @return Pointer on pf_group struct
+ */
+struct pf_group *get_pf_group_dumb(void *priv)
+{
+ struct pf_group *pfg;
+
+ pfg_list_wlock();
+ list_for_each_entry(pfg, &pfg_list, list) {
+ if (check_pf_dumb(&pfg->filter)) {
+ atomic_inc(&pfg->usage);
+ goto unlock;
+ }
+ }
+
+ pfg = pfg_create();
+ if (pfg == NULL)
+ goto unlock;
+
+ set_pf_dumb(&pfg->filter, priv);
+
+ pfg_add_to_list(pfg);
+
+unlock:
+ pfg_list_wunlock();
+ return pfg;
+}
+EXPORT_SYMBOL_GPL(get_pf_group_dumb);
+
+/**
+ * @brief Put pf_group struct
+ *
+ * @param pfg Pointer to the pf_group struct
+ * @return Void
+ */
+void put_pf_group(struct pf_group *pfg)
+{
+ if (atomic_dec_and_test(&pfg->usage)) {
+ pfg_list_wlock();
+ pfg_del_from_list(pfg);
+ pfg_list_wunlock();
+
+ pfg_free(pfg);
+ }
+}
+EXPORT_SYMBOL_GPL(put_pf_group);
+
+/**
+ * @brief Register prober for pf_grpup struct
+ *
+ * @param pfg Pointer to the pf_group struct
+ * @param dentry Dentry of file
+ * @param offset Function offset
+ * @param probe_info Pointer to the related probe_info struct
+ * @return pointer to the img_ip struct or error
+ */
+struct img_ip *pf_register_probe(struct pf_group *pfg, struct dentry *dentry,
+ unsigned long offset, struct probe_desc *pd)
+{
+ return img_proc_add_ip(pfg->i_proc, dentry, offset, pd);
+}
+EXPORT_SYMBOL_GPL(pf_register_probe);
+
+/**
+ * @brief Unregister prober from pf_grpup struct
+ *
+ * @param pfg Pointer to the pf_group struct
+ * @param ip Pointer to the img_ip struct
+ * @return Void
+ */
+void pf_unregister_probe(struct pf_group *pfg, struct img_ip *ip)
+{
+ WARN(IS_ERR_OR_NULL(ip), "invalid img_ip");
+ img_proc_del_ip(pfg->i_proc, ip);
+}
+EXPORT_SYMBOL_GPL(pf_unregister_probe);
+
+static int check_task_on_filters(struct task_struct *task)
+{
+ int ret = 0;
+ struct pf_group *pfg;
+
+ pfg_list_rlock();
+ list_for_each_entry(pfg, &pfg_list, list) {
+ if (check_task_f(&pfg->filter, task)) {
+ ret = 1;
+ goto unlock;
+ }
+ }
+
+unlock:
+ pfg_list_runlock();
+ return ret;
+}
+
+enum pf_inst_flag {
+ PIF_NONE,
+ PIF_FIRST,
+ PIF_SECOND,
+ PIF_ADD_PFG
+};
+
+static enum pf_inst_flag pfg_check_task(struct task_struct *task)
+{
+ struct pf_group *pfg;
+ struct sspt_proc *proc = NULL;
+ enum pf_inst_flag flag = PIF_NONE;
+
+ pfg_list_rlock();
+ list_for_each_entry(pfg, &pfg_list, list) {
+ bool put_flag = false;
+
+ if (check_task_f(&pfg->filter, task) == NULL)
+ continue;
+
+ if (proc == NULL) {
+ proc = sspt_proc_get_by_task(task);
+ put_flag = !!proc;
+ }
+
+ if (proc) {
+ flag = flag == PIF_NONE ? PIF_SECOND : flag;
+ } else if (task->tgid == task->pid) {
+ proc = sspt_proc_get_by_task_or_new(task);
+ if (proc == NULL) {
+ printk(KERN_ERR "cannot create sspt_proc\n");
+ break;
+ }
+ put_flag = true;
+ flag = PIF_FIRST;
+ }
+
+ if (proc) {
+ mutex_lock(&proc->filters.mtx);
+ if (sspt_proc_is_filter_new(proc, pfg)) {
+ img_proc_copy_to_sspt(pfg->i_proc, proc);
+ sspt_proc_add_filter(proc, pfg);
+ pfg_add_proc(pfg, proc);
+ flag = flag == PIF_FIRST ? flag : PIF_ADD_PFG;
+ }
+ mutex_unlock(&proc->filters.mtx);
+ if (put_flag)
+ sspt_proc_put(proc);
+ }
+ }
+ pfg_list_runlock();
+
+ return flag;
+}
+
+static void pfg_all_del_proc(struct sspt_proc *proc)
+{
+ struct pf_group *pfg;
+
+ pfg_list_rlock();
+ list_for_each_entry(pfg, &pfg_list, list)
+ pfg_del_proc(pfg, proc);
+ pfg_list_runlock();
+}
+
+/**
+ * @brief Check task and install probes on demand
+ *
+ * @prarm task Pointer on the task_struct struct
+ * @return Void
+ */
+void check_task_and_install(struct task_struct *task)
+{
+ struct sspt_proc *proc;
+ enum pf_inst_flag flag;
+
+ flag = pfg_check_task(task);
+ switch (flag) {
+ case PIF_FIRST:
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ sspt_proc_priv_create(proc);
+ first_install(task, proc);
+ sspt_proc_put(proc);
+ }
+ break;
+ case PIF_ADD_PFG:
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ first_install(task, proc);
+ sspt_proc_put(proc);
+ }
+ break;
+
+ case PIF_NONE:
+ case PIF_SECOND:
+ break;
+ }
+}
+
+/**
+ * @brief Check task and install probes on demand
+ *
+ * @prarm task Pointer on the task_struct struct
+ * @param page_addr Page fault address
+ * @return Void
+ */
+void call_page_fault(struct task_struct *task, unsigned long page_addr)
+{
+ struct sspt_proc *proc;
+ enum pf_inst_flag flag;
+
+ flag = pfg_check_task(task);
+ switch (flag) {
+ case PIF_FIRST:
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ sspt_proc_priv_create(proc);
+ first_install(task, proc);
+ sspt_proc_put(proc);
+ }
+ break;
+ case PIF_ADD_PFG:
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ first_install(task, proc);
+ sspt_proc_put(proc);
+ }
+ break;
+
+ case PIF_SECOND:
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ subsequent_install(task, proc, page_addr);
+ sspt_proc_put(proc);
+ }
+ break;
+
+ case PIF_NONE:
+ break;
+ }
+}
+
+/**
+ * @brief Uninstall probes from the sspt_proc struct
+ *
+ * @prarm proc Pointer on the sspt_proc struct
+ * @return Void
+ */
+
+/* called with sspt_proc_write_lock() */
+void uninstall_proc(struct sspt_proc *proc)
+{
+ struct task_struct *task = proc->leader;
+
+ sspt_proc_uninstall(proc, task, US_UNREGS_PROBE);
+ sspt_proc_cleanup(proc);
+}
+
+
+static void mmr_from_exit(struct sspt_proc *proc)
+{
+ BUG_ON(proc->leader != current);
+
+ sspt_proc_write_lock();
+ list_del(&proc->list);
+ sspt_proc_write_unlock();
+
+ uninstall_proc(proc);
+
+ pfg_all_del_proc(proc);
+}
+
+static void mmr_from_exec(struct sspt_proc *proc)
+{
+ BUG_ON(proc->leader != current);
+
+ if (proc->suspect.after_exec) {
+ sspt_proc_uninstall(proc, proc->leader, US_UNREGS_PROBE);
+ } else {
+ mmr_from_exit(proc);
+ }
+}
+
+/**
+ * @brief Remove probes from the task on demand
+ *
+ * @prarm task Pointer on the task_struct struct
+ * @return Void
+ */
+void call_mm_release(struct task_struct *task)
+{
+ struct sspt_proc *proc;
+
+ down_read(&uninstall_sem);
+ proc = sspt_proc_get_by_task(task);
+ if (proc) {
+ if (task->flags & PF_EXITING)
+ mmr_from_exit(proc);
+ else
+ mmr_from_exec(proc);
+ sspt_proc_put(proc);
+ }
+ up_read(&uninstall_sem);
+}
+
+/**
+ * @brief Legacy code, it is need remove
+ *
+ * @param addr Page address
+ * @return Void
+ */
+void uninstall_page(unsigned long addr)
+{
+
+}
+
+
+static void install_cb(void *unused)
+{
+ check_task_and_install(current);
+}
+
+
+
+
+struct task_item {
+ struct list_head list;
+ struct task_struct *task;
+};
+
+static void tasks_get(struct list_head *head)
+{
+ struct task_item *item;
+ struct task_struct *task;
+
+ rcu_read_lock();
+ for_each_process(task) {
+ if (task->flags & PF_KTHREAD)
+ continue;
+
+ if (sspt_proc_by_task(task))
+ continue;
+
+ /* TODO: get rid of GFP_ATOMIC */
+ item = kmalloc(sizeof(*item), GFP_ATOMIC);
+ if (item == NULL) {
+ WARN(1, "out of memory\n");
+ goto unlock;
+ }
+
+ get_task_struct(task);
+ item->task = task;
+ list_add(&item->list, head);
+ }
+
+unlock:
+ rcu_read_unlock();
+}
+
+static void tasks_install_and_put(struct list_head *head)
+{
+ struct task_item *item, *n;
+
+ list_for_each_entry_safe(item, n, head, list) {
+ int ret;
+ struct task_struct *task;
+
+ task = item->task;
+ if (!check_task_on_filters(task))
+ goto put_task;
+
+ ret = taskctx_run(task, install_cb, NULL);
+ if (ret) {
+ pr_err("cannot tracking task[%u %u %s] ret=%d\n",
+ task->tgid, task->pid, task->comm, ret);
+ }
+
+put_task:
+ put_task_struct(task);
+ list_del(&item->list);
+ kfree(item);
+ }
+}
+
+static void do_install_all(void)
+{
+ LIST_HEAD(head);
+
+ tasks_get(&head);
+ tasks_install_and_put(&head);
+}
+
+/**
+ * @brief Install probes on running processes
+ *
+ * @return Void
+ */
+void install_all(void)
+{
+ int ret;
+
+ ret = taskctx_get();
+ if (!ret) {
+ do_install_all();
+ taskctx_put();
+ } else {
+ pr_err("taskctx_get ret=%d\n", ret);
+ }
+}
+
+/**
+ * @brief Uninstall probes from all processes
+ *
+ * @return Void
+ */
+void uninstall_all(void)
+{
+ struct list_head *proc_list = sspt_proc_list();
+
+ down_write(&uninstall_sem);
+ sspt_proc_write_lock();
+ while (!list_empty(proc_list)) {
+ struct sspt_proc *proc;
+ proc = list_first_entry(proc_list, struct sspt_proc, list);
+
+ list_del(&proc->list);
+
+ sspt_proc_write_unlock();
+ uninstall_proc(proc);
+ sspt_proc_write_lock();
+ }
+ sspt_proc_write_unlock();
+ up_write(&uninstall_sem);
+}
+
+static void __do_get_proc(struct sspt_proc *proc, void *data)
+{
+ struct task_struct *task = proc->leader;
+
+ get_task_struct(task);
+ proc->__task = task;
+ proc->__mm = get_task_mm(task);
+}
+
+static void __do_put_proc(struct sspt_proc *proc, void *data)
+{
+ if (proc->__mm) {
+ mmput(proc->__mm);
+ proc->__mm = NULL;
+ }
+
+ if (proc->__task) {
+ put_task_struct(proc->__task);
+ proc->__task = NULL;
+ }
+}
+
+void get_all_procs(void)
+{
+ sspt_proc_read_lock();
+ on_each_proc_no_lock(__do_get_proc, NULL);
+ sspt_proc_read_unlock();
+}
+
+void put_all_procs(void)
+{
+ sspt_proc_read_lock();
+ on_each_proc_no_lock(__do_put_proc, NULL);
+ sspt_proc_read_unlock();
+}
+
+/**
+ * @brief For debug
+ *
+ * @param pfg Pointer to the pf_group struct
+ * @return Void
+ */
+
+/* debug */
+void pfg_print(struct pf_group *pfg)
+{
+ img_proc_print(pfg->i_proc);
+}
+EXPORT_SYMBOL_GPL(pfg_print);
+/* debug */
--- /dev/null
+/**
+ * @file us_manager/pf/pf_group.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+
+#ifndef _PF_GROUP_H
+#define _PF_GROUP_H
+
+#include <linux/types.h>
+
+struct img_ip;
+struct dentry;
+struct pf_group;
+struct sspt_proc;
+struct probe_desc;
+
+
+struct pfg_msg_cb {
+ void (*msg_info)(struct task_struct *task, struct dentry *dentry);
+ void (*msg_status_info)(struct task_struct *task);
+ void (*msg_term)(struct task_struct *task);
+ void (*msg_map)(struct vm_area_struct *vma);
+ void (*msg_unmap)(unsigned long start, unsigned long end);
+};
+
+
+/* FIXME: use swap_get_dentry() and swap_put_dentry() */
+struct dentry *dentry_by_path(const char *path);
+
+struct pf_group *get_pf_group_by_dentry(struct dentry *dentry, void *priv);
+struct pf_group *get_pf_group_by_tgid(pid_t tgid, void *priv);
+struct pf_group *get_pf_group_by_comm(char *comm, void *priv);
+struct pf_group *get_pf_group_dumb(void *priv);
+void put_pf_group(struct pf_group *pfg);
+bool pfg_is_unloadable(void);
+
+int pfg_msg_cb_set(struct pf_group *pfg, struct pfg_msg_cb *msg_cb);
+void pfg_msg_cb_reset(struct pf_group *pfg);
+struct pfg_msg_cb *pfg_msg_cb_get(struct pf_group *pfg);
+
+struct img_ip *pf_register_probe(struct pf_group *pfg, struct dentry *dentry,
+ unsigned long offset, struct probe_desc *pd);
+void pf_unregister_probe(struct pf_group *pfg, struct img_ip *ip);
+
+void install_all(void);
+void uninstall_all(void);
+
+void get_all_procs(void);
+void put_all_procs(void);
+
+void call_page_fault(struct task_struct *task, unsigned long page_addr);
+void call_mm_release(struct task_struct *task);
+void check_task_and_install(struct task_struct *task);
+void uninstall_proc(struct sspt_proc *proc);
+
+void uninstall_page(unsigned long addr);
+
+/* debug */
+void pfg_print(struct pf_group *pfg);
+/* debug */
+
+#endif /* _PF_GROUP_H */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/pf/proc_filters.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mm_types.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include "proc_filters.h"
+#include <us_manager/sspt/sspt.h>
+
+
+#define VOIDP2PID(x) ((pid_t)(unsigned long)(x))
+#define PID2VOIDP(x) ((void *)(unsigned long)(x))
+
+static int check_dentry(struct task_struct *task, struct dentry *dentry)
+{
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = task->mm;
+
+ if (mm == NULL)
+ return 0;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (check_vma(vma) && vma->vm_file->f_path.dentry == dentry)
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct task_struct *call_by_dentry(struct proc_filter *self,
+ struct task_struct *task)
+{
+ struct dentry *dentry = (struct dentry *)self->data;
+
+ if (!dentry || check_dentry(task, dentry))
+ return task;
+
+ return NULL;
+}
+
+static inline void free_by_dentry(struct proc_filter *self)
+{
+ return;
+}
+
+static struct task_struct *call_by_tgid(struct proc_filter *self,
+ struct task_struct *task)
+{
+ pid_t tgid = VOIDP2PID(self->data);
+
+ if (task->tgid == tgid)
+ return task;
+
+ return NULL;
+}
+
+static inline void free_by_tgid(struct proc_filter *self)
+{
+ return;
+}
+
+static struct task_struct *call_by_comm(struct proc_filter *self,
+ struct task_struct *task)
+{
+ struct task_struct *parent;
+ char *comm = (char *)self->data;
+ size_t len = strnlen(comm, TASK_COMM_LEN);
+
+ if (!strncmp(comm, task->group_leader->comm, len))
+ return task;
+
+ parent = task->parent;
+ if (parent && !strncmp(comm, parent->comm, len))
+ return task;
+
+ return NULL;
+}
+
+static inline void free_by_comm(struct proc_filter *self)
+{
+ kfree(self->data);
+}
+
+/* Dumb call. Each task is exactly what we are looking for :) */
+static struct task_struct *call_dumb(struct proc_filter *self,
+ struct task_struct *task)
+{
+ return task;
+}
+
+/**
+ * @brief Filling pf_group struct by dentry
+ *
+ * @param pf Pointer to the proc_filter struct
+ * @param dentry Dentry
+ * @param priv Private data
+ * @return Void
+ */
+void set_pf_by_dentry(struct proc_filter *pf, struct dentry *dentry, void *priv)
+{
+ pf->call = &call_by_dentry;
+ pf->data = (void *)dentry;
+ pf->priv = priv;
+}
+
+/**
+ * @brief Filling pf_group struct by TGID
+ *
+ * @param pf Pointer to the proc_filter struct
+ * @param tgid Thread group ID
+ * @param priv Private data
+ * @return Void
+ */
+void set_pf_by_tgid(struct proc_filter *pf, pid_t tgid, void *priv)
+{
+ pf->call = &call_by_tgid;
+ pf->data = PID2VOIDP(tgid);
+ pf->priv = priv;
+}
+
+/**
+ * @brief Fill proc_filter struct for given comm
+ *
+ * @param pf Pointer to the proc_filter struct
+ * @param comm Task comm
+ * @param priv Private data
+ * @return 0 on suceess, error code on error.
+ */
+int set_pf_by_comm(struct proc_filter *pf, char *comm, void *priv)
+{
+ size_t len = strnlen(comm, TASK_COMM_LEN);
+ char *new_comm = kmalloc(len, GFP_KERNEL);
+
+ if (new_comm == NULL)
+ return -ENOMEM;
+
+ /* copy comm */
+ memcpy(new_comm, comm, len - 1);
+ new_comm[len - 1] = '\0';
+
+ pf->call = &call_by_comm;
+ pf->data = new_comm;
+ pf->priv = priv;
+
+ return 0;
+}
+
+/**
+ * @brief Filling pf_group struct for each process
+ *
+ * @param pf Pointer to the proc_filter struct
+ * @param priv Private data
+ * @return Void
+ */
+void set_pf_dumb(struct proc_filter *pf, void *priv)
+{
+ pf->call = &call_dumb;
+ pf->data = NULL;
+ pf->priv = priv;
+}
+
+/**
+ * @brief Free proc_filter struct
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @return Void
+ */
+void free_pf(struct proc_filter *filter)
+{
+ if (filter->call == &call_by_dentry)
+ free_by_dentry(filter);
+ else if (filter->call == &call_by_tgid)
+ free_by_tgid(filter);
+ else if (filter->call == &call_by_comm)
+ free_by_comm(filter);
+}
+
+/**
+ * @brief Check pf_group struct by dentry
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @param dentry Dentry
+ * @return
+ * - 0 - false
+ * - 1 - true
+ */
+int check_pf_by_dentry(struct proc_filter *filter, struct dentry *dentry)
+{
+ return filter->data == (void *)dentry &&
+ filter->call == &call_by_dentry;
+}
+
+/**
+ * @brief Check pf_group struct by TGID
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @param tgid Thread group ID
+ * @return
+ * - 0 - false
+ * - 1 - true
+ */
+int check_pf_by_tgid(struct proc_filter *filter, pid_t tgid)
+{
+ return filter->data == PID2VOIDP(tgid)
+ && filter->call == &call_by_tgid;
+}
+
+/**
+ * @brief Check proc_filter struct by comm
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @param comm Task comm
+ * @return
+ * - 0 - false
+ * - 1 - true
+ */
+int check_pf_by_comm(struct proc_filter *filter, char *comm)
+{
+ return ((filter->call == &call_by_comm) && (filter->data != NULL) &&
+ (!strncmp(filter->data, comm, TASK_COMM_LEN - 1)));
+}
+
+/**
+ * @brief Dumb check always true if filter is a dumb one
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @return
+ * - 0 - false
+ * - 1 - true
+ */
+int check_pf_dumb(struct proc_filter *filter)
+{
+ return filter->call == &call_dumb;
+}
+
+/**
+ * @brief Get priv from pf_group struct
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @return Pointer to the priv
+ */
+void *get_pf_priv(struct proc_filter *filter)
+{
+ return filter->priv;
+}
+
+/* Check function for call_page_fault() and other frequently called
+filter-check functions. It is used to call event-oriented and long-term filters
+only on specified events, but not every time memory map is changed. When
+iteraiting over the filters list, call this function on each step passing here
+pointer on filter. If it returns 1 then the filter should not be called. */
+int ignore_pf(struct proc_filter *filter)
+{
+ return filter->call == &call_by_comm;
+}
--- /dev/null
+/**
+ * @file us_manager/pf/proc_filters.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+
+#ifndef _PROC_FILTERS_H
+#define _PROC_FILTERS_H
+
+#include <linux/types.h>
+
+struct dentry;
+struct task_struct;
+
+/**
+ * @struct proc_filter
+ * @breaf Filter for process
+ */
+struct proc_filter {
+ /** Callback for filtering */
+ struct task_struct *(*call)(struct proc_filter *self,
+ struct task_struct *task);
+ void *data; /**< Data of callback */
+ void *priv; /**< Private data */
+};
+
+/**
+ * @def check_task_f @hideinitializer
+ * Call filter on the task
+ *
+ * @param filter Pointer to the proc_filter struct
+ * @param task Pointer to the task_struct struct
+ */
+#define check_task_f(filter, task) ((filter)->call(filter, task))
+
+void set_pf_by_dentry(struct proc_filter *pf, struct dentry *dentry,
+ void *priv);
+void set_pf_by_tgid(struct proc_filter *pf, pid_t tgid, void *priv);
+int set_pf_by_comm(struct proc_filter *pf, char *comm, void *priv);
+void set_pf_dumb(struct proc_filter *pf, void *priv);
+
+
+int check_pf_by_dentry(struct proc_filter *filter, struct dentry *dentry);
+int check_pf_by_tgid(struct proc_filter *filter, pid_t tgid);
+int check_pf_by_comm(struct proc_filter *filter, char *comm);
+int check_pf_dumb(struct proc_filter *filter);
+void *get_pf_priv(struct proc_filter *filter);
+
+void free_pf(struct proc_filter *filter);
+
+int ignore_pf(struct proc_filter *filter);
+
+#endif /* _PROC_FILTERS_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include "probes.h"
+#include "probe_info_new.h"
+#include "register_probes.h"
+
+
+/*
+ * handlers
+ */
+static int urp_entry_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct uretprobe *rp = ri->rp;
+
+ if (rp) {
+ struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
+ struct probe_desc *pd = NULL;
+
+ pd = ip->desc;
+ if (pd && pd->u.rp.entry_handler)
+ return pd->u.rp.entry_handler(ri, regs);
+
+ }
+
+ return 0;
+}
+
+static int urp_ret_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct uretprobe *rp = ri->rp;
+
+ if (rp) {
+ struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
+ struct probe_desc *pd = NULL;
+
+ pd = ip->desc;
+ if (pd && pd->u.rp.ret_handler)
+ return pd->u.rp.ret_handler(ri, regs);
+ }
+
+ return 0;
+}
+
+static int uprobe_handler(struct uprobe *p, struct pt_regs *regs)
+{
+ struct sspt_ip *ip = container_of(p, struct sspt_ip, uprobe);
+ struct probe_desc *pd = NULL;
+
+ pd = ip->desc;
+ if (pd && pd->u.p.handler)
+ return pd->u.p.handler(p, regs);
+
+ return 0;
+}
+
+/*
+ * register/unregister interface
+ */
+int pin_register(struct probe_new *probe, struct pf_group *pfg,
+ struct dentry *dentry)
+{
+ struct img_ip *ip;
+
+ ip = pf_register_probe(pfg, dentry, probe->offset, probe->desc);
+ if (IS_ERR(ip)) {
+ pr_err("%s: register probe failed\n", __func__);
+ return PTR_ERR(ip);
+ }
+
+ probe->priv = ip;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pin_register);
+
+void pin_unregister(struct probe_new *probe, struct pf_group *pfg)
+{
+ struct img_ip *ip = probe->priv;
+
+ pf_unregister_probe(pfg, ip);
+}
+EXPORT_SYMBOL_GPL(pin_unregister);
+
+
+
+
+
+/*
+ * SWAP_NEW_UP
+ */
+static int up_copy(struct probe_info *dst, const struct probe_info *src)
+{
+ return 0;
+}
+
+static void up_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *up_get_uprobe(struct sspt_ip *ip)
+{
+ return &ip->uprobe;
+}
+
+static int up_register_probe(struct sspt_ip *ip)
+{
+ return swap_register_uprobe(&ip->uprobe);
+}
+
+static void up_unregister_probe(struct sspt_ip *ip, int disarm)
+{
+ __swap_unregister_uprobe(&ip->uprobe, disarm);
+}
+
+static void up_init(struct sspt_ip *ip)
+{
+ ip->uprobe.pre_handler = uprobe_handler;
+}
+
+static void up_uninit(struct sspt_ip *ip)
+{
+}
+
+static struct probe_iface up_iface = {
+ .init = up_init,
+ .uninit = up_uninit,
+ .reg = up_register_probe,
+ .unreg = up_unregister_probe,
+ .get_uprobe = up_get_uprobe,
+ .copy = up_copy,
+ .cleanup = up_cleanup
+};
+
+
+
+
+
+/*
+ * SWAP_NEW_URP
+ */
+static int urp_copy(struct probe_info *dst, const struct probe_info *src)
+{
+ return 0;
+}
+
+static void urp_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *urp_get_uprobe(struct sspt_ip *ip)
+{
+ return &ip->retprobe.up;
+}
+
+static int urp_register_probe(struct sspt_ip *ip)
+{
+ return swap_register_uretprobe(&ip->retprobe);
+}
+
+static void urp_unregister_probe(struct sspt_ip *ip, int disarm)
+{
+ __swap_unregister_uretprobe(&ip->retprobe, disarm);
+}
+
+static void urp_init(struct sspt_ip *ip)
+{
+ ip->retprobe.entry_handler = urp_entry_handler;
+ ip->retprobe.handler = urp_ret_handler;
+ ip->retprobe.maxactive = 0;
+ /* FIXME: make dynamic size field 'data_size' */
+#ifdef CONFIG_ARM64
+ /*
+ * Loader module use field uretprobe_instance.data for storing
+ * 'struct us_priv'. For ARM64 it requires much more space.
+ */
+ ip->retprobe.data_size = 512 - sizeof(struct uretprobe_instance);
+#else /* CONFIG_ARM64 */
+ ip->retprobe.data_size = 128;
+#endif /* CONFIG_ARM64 */
+}
+
+static void urp_uninit(struct sspt_ip *ip)
+{
+}
+
+static struct probe_iface urp_iface = {
+ .init = urp_init,
+ .uninit = urp_uninit,
+ .reg = urp_register_probe,
+ .unreg = urp_unregister_probe,
+ .get_uprobe = urp_get_uprobe,
+ .copy = urp_copy,
+ .cleanup = urp_cleanup
+};
+
+
+
+
+/*
+ * init/exit()
+ */
+int pin_init(void)
+{
+ int ret;
+
+ ret = swap_register_probe_type(SWAP_NEW_UP, &up_iface);
+ if (ret)
+ return ret;
+
+ ret = swap_register_probe_type(SWAP_NEW_URP, &urp_iface);
+ if (ret)
+ swap_unregister_probe_type(SWAP_NEW_UP);
+
+ return ret;
+}
+
+void pin_exit(void)
+{
+ swap_unregister_probe_type(SWAP_NEW_URP);
+ swap_unregister_probe_type(SWAP_NEW_UP);
+}
--- /dev/null
+#ifndef _PROBE_INFO_NEW_H
+#define _PROBE_INFO_NEW_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <uprobe/swap_uprobes.h>
+#include "probes.h"
+
+
+struct dentry;
+struct pf_group;
+
+
+struct probe_info_new {
+ enum probe_t type;
+ union {
+ struct {
+ uprobe_pre_handler_t handler;
+ } p;
+
+ struct {
+ uretprobe_handler_t entry_handler;
+ uretprobe_handler_t ret_handler;
+ /*
+ * FIXME: make dynamic size,
+ * currently data_size = sizeof(void *)
+ */
+ size_t data_size;
+ } rp;
+ } u;
+
+ /* private */
+ struct probe_info info;
+};
+
+struct probe_new {
+ /* reg data */
+ unsigned long offset;
+ struct probe_desc *desc;
+
+ /* unreg data */
+ void *priv;
+};
+
+
+#define MAKE_UPROBE(_handler) \
+ { \
+ .type = SWAP_NEW_UP, \
+ .u.p.handler = _handler \
+ }
+
+#define MAKE_URPROBE(_entry, _ret, _size) \
+ { \
+ .type = SWAP_NEW_URP, \
+ .u.rp.entry_handler = _entry, \
+ .u.rp.ret_handler = _ret, \
+ .u.rp.data_size = _size \
+ }
+
+struct probe_info_otg {
+ struct probe_info info;
+ struct probe_info_new *data; /* field 'data[0]' in probe_info struct */
+};
+
+int pin_register(struct probe_new *probe, struct pf_group *pfg,
+ struct dentry *dentry);
+void pin_unregister(struct probe_new *probe, struct pf_group *pfg);
+
+
+int pin_init(void);
+void pin_exit(void);
+
+
+#endif /* _PROBE_INFO_NEW_H */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/probes.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: Probes interface implement
+ *
+ */
+
+#include "probes.h"
+#include "register_probes.h"
+#include "use_probes.h"
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+
+static struct probe_iface *probes_methods[SWAP_PROBE_MAX_VAL] = { NULL };
+
+/* 1 - correct probe type
+ 0 - wrong probe type
+*/
+static inline int correct_probe_type(enum probe_t probe_type)
+{
+ if (probe_type >= SWAP_PROBE_MAX_VAL)
+ return 0;
+
+ return 1;
+}
+
+static inline int methods_exist(enum probe_t probe_type)
+{
+ if (!correct_probe_type(probe_type) ||
+ (probes_methods[probe_type] == NULL)) {
+ printk(KERN_WARNING "SWAP US_MANAGER: Wrong probe type!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Calls specified probe type init method.
+ *
+ * @param pi Pointer to the probe_info.
+ * @param ip Pointer to the probe us_ip struct.
+ * @return Void.
+ */
+void probe_info_init(enum probe_t type, struct sspt_ip *ip)
+{
+ if (!methods_exist(type)) {
+ return;
+ }
+
+ probes_methods[type]->init(ip);
+}
+
+/**
+ * @brief Calls specified probe type uninit method.
+ *
+ * @param pi Pointer to the probe_info.
+ * @param ip Pointer to the probe us_ip struct.
+ * @return Void.
+ */
+void probe_info_uninit(enum probe_t type, struct sspt_ip *ip)
+{
+ if (!methods_exist(type)) {
+ return;
+ }
+
+ probes_methods[type]->uninit(ip);
+}
+
+/**
+ * @brief Calls specified probe type register method.
+ *
+ * @param pi Pointer to the probe_info.
+ * @param ip Pointer to the probe us_ip struct.
+ * @return -EINVAL on wrong probe type, method result otherwise.
+ */
+int probe_info_register(enum probe_t type, struct sspt_ip *ip)
+{
+ if (!methods_exist(type)) {
+ return -EINVAL;
+ }
+
+ return probes_methods[type]->reg(ip);
+}
+
+/**
+ * @brief Calls specified probe type unregister method.
+ *
+ * @param pi Pointer to the probe_info.
+ * @param ip Pointer to the probe us_ip struct.
+ * @param disarm Disarm flag.
+ * @return Void.
+ */
+void probe_info_unregister(enum probe_t type, struct sspt_ip *ip, int disarm)
+{
+ if (!methods_exist(type)) {
+ return;
+ }
+
+ probes_methods[type]->unreg(ip, disarm);
+}
+
+/**
+ * @brief Calls specified probe type get underlying uprobe method.
+ *
+ * @param pi Pointer to the probe_info.
+ * @param ip Pointer to the probe us_ip struct.
+ * @return Pointer to the uprobe struct, NULL on error.
+ */
+struct uprobe *probe_info_get_uprobe(enum probe_t type, struct sspt_ip *ip)
+{
+ if (!methods_exist(type)) {
+ return NULL;
+ }
+
+ return probes_methods[type]->get_uprobe(ip);
+}
+
+/**
+ * @brief Registers probe type.
+ *
+ * @param probe_type Number, associated with this probe type.
+ * @param pi Pointer to the probe interface structure
+ * @return 0 on succes, error code on error.
+ */
+int swap_register_probe_type(enum probe_t probe_type, struct probe_iface *pi)
+{
+ if (!correct_probe_type(probe_type)) {
+ printk(KERN_ERR "SWAP US_MANAGER: incorrect probe type!\n");
+ return -EINVAL;
+ }
+
+ if (probes_methods[probe_type] != NULL)
+ printk(KERN_WARNING "SWAP US_MANAGER: Re-registering probe %d\n",
+ probe_type);
+
+ probes_methods[probe_type] = pi;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swap_register_probe_type);
+
+/**
+ * @brief Unregisters probe type.
+ *
+ * @param probe_type Probe type that should be unregistered.
+ * @return Void.
+ */
+void swap_unregister_probe_type(enum probe_t probe_type)
+{
+ if (!correct_probe_type(probe_type)) {
+ printk(KERN_ERR "SWAP US_MANAGER: incorrect probe type!\n");
+ return;
+ }
+
+ probes_methods[probe_type] = NULL;
+}
+EXPORT_SYMBOL_GPL(swap_unregister_probe_type);
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/probes.h
+ *
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov : Probes interface implement
+ * 2014 Vitaliy Cherepanov: Portage
+ *
+ */
+
+
+#ifndef __PROBES_H__
+#define __PROBES_H__
+
+#include <linux/types.h>
+#include <uprobe/swap_uprobes.h>
+
+#include <preload/preload_probe.h> /* TODO Remove */
+#include <retprobe/retprobe.h> /* TODO Remove */
+#include <fbiprobe/fbiprobe.h> /* TODO Remove */
+
+
+
+/* All probe types. Only us_manager should know about them - it is its own
+ * business to install proper probes on proper places.
+ */
+enum probe_t {
+ SWAP_RETPROBE = 0, /* Retprobe */
+ SWAP_FBIPROBE = 1, /* FBI probe */
+ SWAP_PRELOAD_PROBE = 2, /* Preload probe */
+ SWAP_GET_CALLER = 4, /* Get caller probe. Supports preload */
+ SWAP_GET_CALL_TYPE = 5, /* Get call type probe. Supports preload */
+ SWAP_WRITE_MSG = 6, /* Write messages from user space directly to
+ * kernel. Supports preload */
+ SWAP_NEW_UP,
+ SWAP_NEW_URP,
+ SWAP_PROBE_MAX_VAL /* Probes max value. */
+};
+
+/* Probe info stuct. It contains the whole information about probe. */
+struct probe_info {
+ /* Union of all SWAP supported probe types */
+ union {
+ struct retprobe_info rp_i;
+ struct fbi_info fbi_i;
+ struct preload_info pl_i;
+ struct get_caller_info gc_i;
+ struct get_call_type_info gct_i;
+ struct write_msg_info wm_i;
+ };
+};
+
+struct probe_desc {
+ enum probe_t type;
+
+ union {
+ struct {
+ uprobe_pre_handler_t handler;
+ } p;
+
+ struct {
+ uretprobe_handler_t entry_handler;
+ uretprobe_handler_t ret_handler;
+ /*
+ * FIXME: make dynamic size,
+ * currently data_size = sizeof(void *)
+ */
+ size_t data_size;
+ } rp;
+ } u;
+
+ struct probe_info info;
+};
+
+#endif /* __PROBES_H__ */
--- /dev/null
+#ifndef __REGISTER_PROBES_H__
+#define __REGISTER_PROBES_H__
+
+#include "probes.h"
+
+struct sspt_ip;
+
+struct probe_iface {
+ void (*init)(struct sspt_ip *);
+ void (*uninit)(struct sspt_ip *);
+ int (*reg)(struct sspt_ip *);
+ void (*unreg)(struct sspt_ip *, int);
+ struct uprobe *(*get_uprobe)(struct sspt_ip *);
+ int (*copy)(struct probe_info *, const struct probe_info *);
+ void (*cleanup)(struct probe_info *);
+};
+
+int swap_register_probe_type(enum probe_t probe_type, struct probe_iface *pi);
+void swap_unregister_probe_type(enum probe_t probe_type);
+
+#endif /* __REGISTER_PROBES_H__ */
--- /dev/null
+#ifndef __USE_PROBES_H__
+#define __USE_PROBES_H__
+
+#include "probes.h"
+
+struct sspt_ip;
+
+void probe_info_init(enum probe_t type, struct sspt_ip *ip);
+void probe_info_uninit(enum probe_t type, struct sspt_ip *ip);
+int probe_info_register(enum probe_t type, struct sspt_ip *ip);
+void probe_info_unregister(enum probe_t type, struct sspt_ip *ip, int disarm);
+struct uprobe *probe_info_get_uprobe(enum probe_t type, struct sspt_ip *ip);
+int probe_info_copy(const struct probe_info *pi, struct probe_info *dest);
+void probe_info_cleanup(struct probe_info *pi);
+
+#endif /* __USE_PROBES_H__ */
--- /dev/null
+#ifndef __SSPT__
+#define __SSPT__
+
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/driver/sspt/sspt.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include "sspt_ip.h"
+#include "sspt_page.h"
+#include "sspt_file.h"
+#include "sspt_proc.h"
+#include "sspt_debug.h"
+#include <uprobe/swap_uprobes.h>
+
+
+#include <us_manager/us_manager.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/probes/use_probes.h>
+
+
+static inline int check_vma(struct vm_area_struct *vma)
+{
+ return vma->vm_file &&
+ !(vma->vm_pgoff != 0 ||
+ !(vma->vm_flags & VM_EXEC) ||
+ !(vma->vm_flags & (VM_READ | VM_MAYREAD)));
+}
+
+static inline int sspt_register_usprobe(struct sspt_ip *ip)
+{
+ int ret;
+ struct uprobe *up = NULL;
+
+ up = probe_info_get_uprobe(ip->desc->type, ip);
+
+ if (!up) {
+ printk(KERN_INFO "SWAP US_MANAGER: failed getting uprobe!\n");
+ return -EINVAL;
+ }
+
+ up->addr = (uprobe_opcode_t *)ip->orig_addr;
+ up->task = ip->page->file->proc->leader;
+ up->sm = ip->page->file->proc->sm;
+
+ ret = probe_info_register(ip->desc->type, ip);
+ if (ret) {
+ struct sspt_file *file = ip->page->file;
+ char *name = file->dentry->d_iname;
+ unsigned long addr = (unsigned long)up->addr;
+ unsigned long offset = addr - file->vm_start;
+
+ printk(KERN_ERR "probe_info_register failed %d (%s:%lx|%lx)\n",
+ ret, name, offset,
+ (unsigned long)ip->retprobe.up.opcode);
+ }
+
+ return ret;
+}
+
+static inline int sspt_unregister_usprobe(struct task_struct *task,
+ struct sspt_ip *ip,
+ enum US_FLAGS flag)
+{
+ struct uprobe *up = NULL;
+
+ switch (flag) {
+ case US_UNREGS_PROBE:
+ probe_info_unregister(ip->desc->type, ip, 1);
+ break;
+ case US_DISARM:
+ up = probe_info_get_uprobe(ip->desc->type, ip);
+ if (up)
+ disarm_uprobe(up, task);
+ break;
+ case US_UNINSTALL:
+ probe_info_unregister(ip->desc->type, ip, 0);
+ break;
+ default:
+ panic("incorrect value flag=%d", flag);
+ }
+
+ return 0;
+}
+
+#endif /* __SSPT__ */
--- /dev/null
+#ifndef __SSPT_DEBUG__
+#define __SSPT_DEBUG__
+
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/driver/sspt/sspt_debug.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include <kprobe/swap_kprobes_deps.h>
+#include <us_manager/probes/probes.h>
+
+
+static inline void print_retprobe(struct uretprobe *rp)
+{
+ printk(KERN_INFO "### RP: handler=%lx\n",
+ (unsigned long)rp->handler);
+}
+
+static inline void print_ip(struct sspt_ip *ip, int i)
+{
+ if (ip->desc->type == SWAP_RETPROBE) {
+ struct uretprobe *rp = &ip->retprobe;
+
+ printk(KERN_INFO "### addr[%2d]=%lx, R_addr=%lx\n",
+ i, (unsigned long)ip->offset,
+ (unsigned long)rp->up.addr);
+ print_retprobe(rp);
+ }
+}
+
+static inline void print_page_probes(const struct sspt_page *page)
+{
+ int i = 0;
+ struct sspt_ip *ip;
+
+ printk(KERN_INFO "### offset=%lx\n", page->offset);
+ printk(KERN_INFO "### no install:\n");
+ list_for_each_entry(ip, &page->ip_list.not_inst, list) {
+ print_ip(ip, i);
+ ++i;
+ }
+
+ printk(KERN_INFO "### install:\n");
+ list_for_each_entry(ip, &page->ip_list.inst, list) {
+ print_ip(ip, i);
+ ++i;
+ }
+}
+
+static inline void print_file_probes(struct sspt_file *file)
+{
+ int i;
+ unsigned long table_size;
+ struct sspt_page *page = NULL;
+ struct hlist_head *head = NULL;
+ static unsigned char *NA = "N/A";
+ unsigned char *name;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ if (file == NULL) {
+ printk(KERN_INFO "### file_p == NULL\n");
+ return;
+ }
+
+ table_size = (1 << file->htable.bits);
+ name = (file->dentry) ? file->dentry->d_iname : NA;
+
+ printk(KERN_INFO "### print_file_probes: path=%s, d_iname=%s, "
+ "table_size=%lu, vm_start=%lx\n",
+ file->dentry->d_iname, name, table_size, file->vm_start);
+
+ down_read(&file->htable.sem);
+ for (i = 0; i < table_size; ++i) {
+ head = &file->htable.heads[i];
+ swap_hlist_for_each_entry_rcu(page, node, head, hlist) {
+ print_page_probes(page);
+ }
+ }
+ up_read(&file->htable.sem);
+}
+
+static inline void print_proc_probes(struct sspt_proc *proc)
+{
+ struct sspt_file *file;
+
+ printk(KERN_INFO "### print_proc_probes\n");
+ down_read(&proc->files.sem);
+ list_for_each_entry(file, &proc->files.head, list) {
+ print_file_probes(file);
+ }
+ up_read(&proc->files.sem);
+ printk(KERN_INFO "### print_proc_probes\n");
+}
+
+
+#endif /* __SSPT_DEBUG__ */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/us_manager/sspt/sspt_feature.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include "sspt_feature.h"
+#include "sspt_proc.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+
+struct sspt_feature {
+ struct list_head feature_list;
+};
+
+struct sspt_feature_img {
+ struct list_head list;
+
+ void *(*alloc)(void);
+ void (*free)(void *data);
+};
+
+struct sspt_feature_data {
+ struct list_head list;
+
+ struct sspt_feature_img *img;
+ void *data;
+};
+
+static DEFINE_SPINLOCK(feature_img_lock);
+static LIST_HEAD(feature_img_list);
+
+static struct sspt_feature_data *create_feature_data(
+ struct sspt_feature_img *img)
+{
+ struct sspt_feature_data *fd;
+
+ fd = kmalloc(sizeof(*fd), GFP_ATOMIC);
+ if (fd) {
+ INIT_LIST_HEAD(&fd->list);
+ fd->img = img;
+ fd->data = img->alloc();
+ }
+
+ return fd;
+}
+
+static void destroy_feature_data(struct sspt_feature_data *fd)
+{
+ fd->img->free(fd->data);
+ kfree(fd);
+}
+
+/**
+ * @brief Create sspt_feature struct
+ *
+ * @return Pointer to the created sspt_feature struct
+ */
+struct sspt_feature *sspt_create_feature(void)
+{
+ struct sspt_feature *f;
+
+ f = kmalloc(sizeof(*f), GFP_ATOMIC);
+ if (f) {
+ struct sspt_feature_data *fd;
+ struct sspt_feature_img *fi;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&f->feature_list);
+
+ spin_lock_irqsave(&feature_img_lock, flags);
+ list_for_each_entry(fi, &feature_img_list, list) {
+ fd = create_feature_data(fi);
+ if (fd) /* add to list */
+ list_add(&fd->list, &f->feature_list);
+ }
+ spin_unlock_irqrestore(&feature_img_lock, flags);
+ }
+
+ return f;
+}
+
+/**
+ * @brief Destroy sspt_feature struct
+ *
+ * @param f remove object
+ * @return Void
+ */
+void sspt_destroy_feature(struct sspt_feature *f)
+{
+ struct sspt_feature_data *fd, *n;
+
+ list_for_each_entry_safe(fd, n, &f->feature_list, list) {
+ /* delete from list */
+ list_del(&fd->list);
+ destroy_feature_data(fd);
+ }
+
+ kfree(f);
+}
+
+static void add_feature_img_to_list(struct sspt_feature_img *fi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&feature_img_lock, flags);
+ list_add(&fi->list, &feature_img_list);
+ spin_unlock_irqrestore(&feature_img_lock, flags);
+}
+
+static void del_feature_img_from_list(struct sspt_feature_img *fi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&feature_img_lock, flags);
+ list_del(&fi->list);
+ spin_unlock_irqrestore(&feature_img_lock, flags);
+}
+
+static struct sspt_feature_img *create_feature_img(void *(*alloc)(void),
+ void (*free)(void *data))
+{
+ struct sspt_feature_img *fi;
+
+ fi = kmalloc(sizeof(*fi), GFP_ATOMIC);
+ if (fi) {
+ INIT_LIST_HEAD(&fi->list);
+ fi->alloc = alloc;
+ fi->free = free;
+
+ add_feature_img_to_list(fi);
+ }
+
+ return fi;
+}
+
+static void destroy_feature_img(struct sspt_feature_img *fi)
+{
+ del_feature_img_from_list(fi);
+
+ kfree(fi);
+}
+
+static void del_feature_by_img(struct sspt_feature *f,
+ struct sspt_feature_img *img)
+{
+ struct sspt_feature_data *fd;
+
+ list_for_each_entry(fd, &f->feature_list, list) {
+ if (img == fd->img) {
+ /* delete from list */
+ list_del(&fd->list);
+ destroy_feature_data(fd);
+ break;
+ }
+ }
+}
+
+static void del_feature_from_proc(struct sspt_proc *proc, void *data)
+{
+ del_feature_by_img(proc->feature, (struct sspt_feature_img *)data);
+}
+
+/**
+ * @brief Get data for feature
+ *
+ * @param f Pointer to the sspt_feature struct
+ * @param id Feature ID
+ * @return Pointer to the data
+ */
+void *sspt_get_feature_data(struct sspt_feature *f, sspt_feature_id_t id)
+{
+ struct sspt_feature_img *img = (struct sspt_feature_img *)id;
+ struct sspt_feature_data *fd;
+
+ list_for_each_entry(fd, &f->feature_list, list) {
+ if (img == fd->img)
+ return fd->data;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sspt_get_feature_data);
+
+/**
+ * @brief Register sspt feature
+ *
+ * @param alloc Callback for allocating data
+ * @param free Callback to release data
+ * @return Feature ID
+ */
+sspt_feature_id_t sspt_register_feature(void *(*alloc)(void),
+ void (*free)(void *data))
+{
+ struct sspt_feature_img *fi;
+
+ fi = create_feature_img(alloc, free);
+
+ /* TODO: add to already instrumentation process */
+
+ return (sspt_feature_id_t)fi;
+}
+EXPORT_SYMBOL_GPL(sspt_register_feature);
+
+/**
+ * @brief Unregister sspt feature
+ *
+ * @param id Feature ID
+ * @return Void
+ */
+void sspt_unregister_feature(sspt_feature_id_t id)
+{
+ struct sspt_feature_img *fi = (struct sspt_feature_img *)id;
+
+ on_each_proc(del_feature_from_proc, (void *)fi);
+ destroy_feature_img(fi);
+}
+EXPORT_SYMBOL_GPL(sspt_unregister_feature);
--- /dev/null
+#ifndef _SSPT_FEATUER_H
+#define _SSPT_FEATUER_H
+
+/**
+ * @file us_manager/sspt/sspt_feature.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+struct sspt_feature;
+
+typedef void *sspt_feature_id_t; /**< @brief sspt feature ID type */
+#define SSPT_FEATURE_ID_BAD NULL /**< @def SSPT_FEATURE_ID_BAD */
+
+struct sspt_feature *sspt_create_feature(void);
+void sspt_destroy_feature(struct sspt_feature *f);
+
+void *sspt_get_feature_data(struct sspt_feature *f, sspt_feature_id_t id);
+sspt_feature_id_t sspt_register_feature(void *(*alloc)(void),
+ void (*free)(void *data));
+void sspt_unregister_feature(sspt_feature_id_t id);
+
+#endif /* _SSPT_FEATUER_H */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/driver/sspt/sspt_file.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include "sspt.h"
+#include "sspt_file.h"
+#include "sspt_page.h"
+#include "sspt_proc.h"
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/hash.h>
+#include <linux/sched.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <us_manager/probes/probes.h>
+#include <us_manager/img/img_ip.h>
+
+static int calculation_hash_bits(int cnt)
+{
+ int bits;
+ for (bits = 1; cnt >>= 1; ++bits)
+ ;
+
+ return bits;
+}
+
+static unsigned long htable_size(const struct sspt_file *file)
+{
+ return 1 << file->htable.bits;
+}
+
+static struct hlist_head *htable_head_by_idx(const struct sspt_file *file,
+ unsigned long idx)
+{
+ return &file->htable.heads[idx];
+}
+
+static struct hlist_head *htable_head_by_key(const struct sspt_file *file,
+ unsigned long offset)
+{
+ unsigned long idx = hash_ptr((void *)offset, file->htable.bits);
+
+ return htable_head_by_idx(file, idx);
+}
+
+/**
+ * @brief Create sspt_file struct
+ *
+ * @param dentry Dentry of file
+ * @param page_cnt Size of hash-table
+ * @return Pointer to the created sspt_file struct
+ */
+struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt)
+{
+ int i, table_size;
+ struct hlist_head *heads;
+ struct sspt_file *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
+
+ if (obj == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&obj->list);
+ obj->proc = NULL;
+ obj->dentry = dentry;
+ obj->loaded = 0;
+ obj->vm_start = 0;
+ obj->vm_end = 0;
+
+ obj->htable.bits = calculation_hash_bits(page_cnt);
+ table_size = htable_size(obj);
+
+ heads = kmalloc(sizeof(*obj->htable.heads) * table_size, GFP_ATOMIC);
+ if (heads == NULL)
+ goto err;
+
+ for (i = 0; i < table_size; ++i)
+ INIT_HLIST_HEAD(&heads[i]);
+
+ obj->htable.heads = heads;
+ init_rwsem(&obj->htable.sem);
+
+ return obj;
+
+err:
+ kfree(obj);
+ return NULL;
+}
+
+/**
+ * @brief Remove sspt_file struct
+ *
+ * @param file remove object
+ * @return Void
+ */
+void sspt_file_free(struct sspt_file *file)
+{
+ struct hlist_head *head;
+ struct sspt_page *page;
+ int i, table_size = htable_size(file);
+ struct hlist_node *n;
+ DECLARE_NODE_PTR_FOR_HLIST(p);
+
+ down_write(&file->htable.sem);
+ for (i = 0; i < table_size; ++i) {
+ head = htable_head_by_idx(file, i);
+ swap_hlist_for_each_entry_safe(page, p, n, head, hlist) {
+ hlist_del(&page->hlist);
+ sspt_page_clean(page);
+ sspt_page_put(page);
+ }
+ }
+ up_write(&file->htable.sem);
+
+ kfree(file->htable.heads);
+ kfree(file);
+}
+
+static void sspt_add_page(struct sspt_file *file, struct sspt_page *page)
+{
+ page->file = file;
+ hlist_add_head(&page->hlist, htable_head_by_key(file, page->offset));
+}
+
+static struct sspt_page *sspt_find_page(struct sspt_file *file,
+ unsigned long offset)
+{
+ struct hlist_head *head = htable_head_by_key(file, offset);
+ struct sspt_page *page;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry(page, node, head, hlist) {
+ if (page->offset == offset)
+ return page;
+ }
+
+ return NULL;
+}
+
+static struct sspt_page *sspt_find_page_or_new(struct sspt_file *file,
+ unsigned long offset)
+{
+ struct sspt_page *page;
+
+ down_write(&file->htable.sem);
+ page = sspt_find_page(file, offset);
+ if (page == NULL) {
+ page = sspt_page_create(offset);
+ if (page)
+ sspt_add_page(file, page);
+ }
+ up_write(&file->htable.sem);
+
+ return page;
+}
+
+/**
+ * @brief Get sspt_page from sspt_file
+ *
+ * @param file Pointer to the sspt_file struct
+ * @param page Page address
+ * @return Pointer to the sspt_page struct
+ */
+struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
+ unsigned long page)
+{
+ unsigned long offset;
+ struct sspt_page *p;
+
+ if (file->vm_start > page || file->vm_end < page) {
+ /* TODO: or panic?! */
+ printk(KERN_INFO "ERROR: file_p[vm_start..vm_end] <> page: "
+ "file_p[vm_start=%lx, vm_end=%lx, "
+ "d_iname=%s] page=%lx\n",
+ file->vm_start, file->vm_end,
+ file->dentry->d_iname, page);
+ return NULL;
+ }
+
+ offset = page - file->vm_start;
+
+ down_read(&file->htable.sem);
+ p = sspt_find_page(file, offset);
+ up_read(&file->htable.sem);
+
+ return p;
+}
+
+/**
+ * @brief Add instruction pointer to sspt_file
+ *
+ * @param file Pointer to the sspt_file struct
+ * @param offset File offset
+ * @param args Function arguments
+ * @param ret_type Return type
+ * @return Void
+ */
+void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip)
+{
+ unsigned long offset = 0;
+ struct sspt_page *page = NULL;
+ struct sspt_ip *ip = NULL;
+
+ offset = img_ip->addr & PAGE_MASK;
+ page = sspt_find_page_or_new(file, offset);
+ if (!page)
+ return;
+
+ ip = sspt_ip_create(img_ip, page);
+ if (!ip)
+ return;
+
+ probe_info_init(ip->desc->type, ip);
+}
+
+void sspt_file_on_each_ip(struct sspt_file *file,
+ void (*func)(struct sspt_ip *, void *), void *data)
+{
+ int i;
+ const int table_size = htable_size(file);
+ struct sspt_page *page;
+ struct hlist_head *head;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ down_read(&file->htable.sem);
+ for (i = 0; i < table_size; ++i) {
+ head = htable_head_by_idx(file, i);
+ swap_hlist_for_each_entry(page, node, head, hlist)
+ sspt_page_on_each_ip(page, func, data);
+ }
+ up_read(&file->htable.sem);
+}
+
+/**
+ * @brief Check install sspt_file (legacy code, it is need remove)
+ *
+ * @param file Pointer to the sspt_file struct
+ * @return
+ * - 0 - false
+ * - 1 - true
+ */
+int sspt_file_check_install_pages(struct sspt_file *file)
+{
+ int ret = 0;
+ int i, table_size;
+ struct sspt_page *page;
+ struct hlist_head *head;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ table_size = htable_size(file);
+
+ down_read(&file->htable.sem);
+ for (i = 0; i < table_size; ++i) {
+ head = htable_head_by_idx(file, i);
+ swap_hlist_for_each_entry(page, node, head, hlist) {
+ if (sspt_page_is_installed(page)) {
+ ret = 1;
+ goto unlock;
+ }
+ }
+ }
+
+unlock:
+ up_read(&file->htable.sem);
+ return ret;
+}
+
+/**
+ * @brief Install sspt_file
+ *
+ * @param file Pointer to the sspt_file struct
+ * @return Void
+ */
+void sspt_file_install(struct sspt_file *file)
+{
+ struct sspt_page *page = NULL;
+ struct hlist_head *head = NULL;
+ int i, table_size = htable_size(file);
+ unsigned long page_addr;
+ struct mm_struct *mm;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ down_read(&file->htable.sem);
+ for (i = 0; i < table_size; ++i) {
+ head = htable_head_by_idx(file, i);
+ swap_hlist_for_each_entry(page, node, head, hlist) {
+ unsigned long offset = page->offset;
+
+#ifdef CONFIG_64BIT
+ /* Reset most significant bit for only 64-bit */
+ offset &= (~(1UL << 63));
+#endif /* CONFIG_64BIT */
+
+ page_addr = file->vm_start + offset;
+ if (page_addr < file->vm_start ||
+ page_addr >= file->vm_end)
+ continue;
+
+ mm = page->file->proc->leader->mm;
+ if (page_present(mm, page_addr))
+ sspt_register_page(page, file);
+ }
+ }
+ up_read(&file->htable.sem);
+}
+
+/**
+ * @brief Uninstall sspt_file
+ *
+ * @param file Pointer to the sspt_file struct
+ * @param task Pointer to the task_stract struct
+ * @param flag Action for probes
+ * @return Void
+ */
+int sspt_file_uninstall(struct sspt_file *file,
+ struct task_struct *task,
+ enum US_FLAGS flag)
+{
+ int i, err = 0;
+ int table_size = htable_size(file);
+ struct sspt_page *page;
+ struct hlist_head *head;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ down_read(&file->htable.sem);
+ for (i = 0; i < table_size; ++i) {
+ head = htable_head_by_idx(file, i);
+ swap_hlist_for_each_entry(page, node, head, hlist) {
+ err = sspt_unregister_page(page, flag, task);
+ if (err != 0) {
+ printk(KERN_INFO "ERROR sspt_file_uninstall: "
+ "err=%d\n", err);
+ up_read(&file->htable.sem);
+ return err;
+ }
+ }
+ }
+ up_read(&file->htable.sem);
+
+ if (flag != US_DISARM) {
+ file->loaded = 0;
+ file->vm_start = 0;
+ file->vm_end = 0;
+ }
+
+ return err;
+}
+
+/**
+ * @brief Set mapping for sspt_file
+ *
+ * @param file Pointer to the sspt_file struct
+ * @param vma Pointer to the vm_area_struct struct
+ * @return Void
+ */
+void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma)
+{
+ if (file->loaded == 0) {
+ file->loaded = 1;
+ file->vm_start = vma->vm_start;
+ file->vm_end = vma->vm_end;
+ }
+}
--- /dev/null
+#ifndef __SSPT_FILE__
+#define __SSPT_FILE__
+
+/**
+ * @file us_manager/sspt/sspt_file.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+#include "sspt_ip.h"
+#include <linux/types.h>
+#include <linux/rwsem.h>
+
+enum US_FLAGS;
+struct vm_area_struct;
+
+/**
+ * @struct sspt_file
+ * @breaf Image of file for specified process
+ */
+struct sspt_file {
+ /* sspt_proc */
+ struct list_head list; /**< For sspt_proc */
+ struct sspt_proc *proc; /**< Pointer to the proc (parent) */
+
+ /* sspt_page */
+ struct {
+ struct rw_semaphore sem; /**< Semaphore for hash-table */
+ unsigned long bits; /**< Hash-table size */
+ struct hlist_head *heads; /**< Heads for pages */
+ } htable;
+
+ struct dentry *dentry; /**< Dentry of file */
+ unsigned long vm_start; /**< VM start */
+ unsigned long vm_end; /**< VM end */
+ unsigned loaded:1; /**< Flag of loading */
+};
+
+
+struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt);
+void sspt_file_free(struct sspt_file *file);
+
+struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
+ unsigned long page);
+void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip);
+
+void sspt_file_on_each_ip(struct sspt_file *file,
+ void (*func)(struct sspt_ip *, void *), void *data);
+
+int sspt_file_check_install_pages(struct sspt_file *file);
+void sspt_file_install(struct sspt_file *file);
+int sspt_file_uninstall(struct sspt_file *file,
+ struct task_struct *task,
+ enum US_FLAGS flag);
+void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma);
+
+#endif /* __SSPT_FILE__ */
--- /dev/null
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "sspt_filter.h"
+#include "sspt_proc.h"
+#include "../pf/pf_group.h"
+
+
+struct sspt_filter *sspt_filter_create(struct sspt_proc *proc,
+ struct pf_group *pfg)
+{
+ struct sspt_filter *fl;
+
+ fl = kmalloc(sizeof(*fl), GFP_ATOMIC);
+ if (fl == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&fl->list);
+
+ fl->proc = proc;
+ fl->pfg = pfg;
+ fl->pfg_is_inst = false;
+
+ return fl;
+}
+
+void sspt_filter_free(struct sspt_filter *fl)
+{
+ if (fl->pfg_is_inst) {
+ struct pfg_msg_cb *cb = pfg_msg_cb_get(fl->pfg);
+
+ if (cb && cb->msg_term)
+ cb->msg_term(fl->proc->leader);
+ }
+
+ kfree(fl);
+}
--- /dev/null
+#ifndef __SSPT_FILTER_H__
+#define __SSPT_FILTER_H__
+
+#include <linux/types.h>
+
+struct pf_group;
+struct sspt_proc;
+
+struct sspt_filter {
+ struct list_head list;
+ struct sspt_proc *proc;
+ struct pf_group *pfg;
+ bool pfg_is_inst;
+};
+
+struct sspt_filter *sspt_filter_create(struct sspt_proc *proc,
+ struct pf_group *pfg);
+void sspt_filter_free(struct sspt_filter *fl);
+
+#endif /* __SSPT_FILTER_H__ */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/driver/sspt/ip.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include "sspt_ip.h"
+#include "sspt_page.h"
+#include "sspt_file.h"
+#include <us_manager/probes/use_probes.h>
+#include <us_manager/img/img_ip.h>
+
+/**
+ * @brief Create us_ip struct
+ *
+ * @param page User page
+ * @param offset Function offset from the beginning of the page
+ * @param probe_i Pointer to the probe data.
+ * @param page Pointer to the parent sspt_page struct
+ * @return Pointer to the created us_ip struct
+ */
+struct sspt_ip *sspt_ip_create(struct img_ip *img_ip, struct sspt_page *page)
+{
+ struct sspt_ip *ip;
+
+ ip = kmalloc(sizeof(*ip), GFP_ATOMIC);
+ if (!ip)
+ return NULL;
+
+ memset(ip, 0, sizeof(*ip));
+ INIT_LIST_HEAD(&ip->list);
+ INIT_LIST_HEAD(&ip->img_list);
+ ip->offset = img_ip->addr & ~PAGE_MASK;
+ ip->desc = img_ip->desc;
+ atomic_set(&ip->usage, 2); /* for 'img_ip' and 'page' */
+
+ /* add to img_ip list */
+ img_ip_get(img_ip);
+ img_ip_lock(img_ip);
+ img_ip_add_ip(img_ip, ip);
+ img_ip_unlock(img_ip);
+
+ /* add to page list */
+ sspt_page_get(page);
+ sspt_page_lock(page);
+ sspt_page_add_ip(page, ip);
+ sspt_page_unlock(page);
+
+ return ip;
+}
+
+static void sspt_ip_free(struct sspt_ip *ip)
+{
+ WARN_ON(!list_empty(&ip->list) || !list_empty(&ip->img_list));
+
+ kfree(ip);
+}
+
+void sspt_ip_get(struct sspt_ip *ip)
+{
+ atomic_inc(&ip->usage);
+}
+
+void sspt_ip_put(struct sspt_ip *ip)
+{
+ if (atomic_dec_and_test(&ip->usage))
+ sspt_ip_free(ip);
+}
+
+void sspt_ip_clean(struct sspt_ip *ip)
+{
+ bool put_page = false;
+ bool put_ip = false;
+
+ /* remove from page */
+ sspt_page_lock(ip->page);
+ if (!list_empty(&ip->list)) {
+ list_del_init(&ip->list);
+ put_page = true;
+ }
+ sspt_page_unlock(ip->page);
+ if (put_page) {
+ sspt_page_put(ip->page);
+ sspt_ip_put(ip);
+ }
+
+ /* remove from img_ip */
+ img_ip_lock(ip->img_ip);
+ if (!list_empty(&ip->img_list)) {
+ list_del_init(&ip->img_list);
+ put_ip = true;
+ }
+ img_ip_unlock(ip->img_ip);
+ if (put_ip) {
+ img_ip_put(ip->img_ip);
+ sspt_ip_put(ip);
+ }
+}
--- /dev/null
+#ifndef _SSPT_IP
+#define _SSPT_IP
+
+/**
+ * @file us_manager/sspt/ip.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+#include <linux/list.h>
+#include <linux/atomic.h>
+#include <uprobe/swap_uprobes.h>
+#include <us_manager/probes/probes.h>
+
+struct sspt_page;
+
+/**
+ * @struct sspt_ip
+ * @breaf Image of instrumentation pointer for specified process
+ */
+struct sspt_ip {
+ /* sspt_page */
+ struct list_head list; /**< For sspt_page */
+ struct sspt_page *page; /**< Pointer on the page (parent) */
+
+ /* img_ip */
+ struct img_ip *img_ip; /**< Pointer on the img_ip (parent) */
+ struct list_head img_list; /**< For img_ip */
+
+ atomic_t usage;
+
+ unsigned long orig_addr; /**< Function address */
+ unsigned long offset; /**< Page offset */
+
+ struct probe_desc *desc; /**< Probe's data */
+
+ union {
+ struct uretprobe retprobe;
+ struct uprobe uprobe;
+ };
+};
+
+
+struct sspt_ip *sspt_ip_create(struct img_ip *img_ip, struct sspt_page *page);
+void sspt_ip_clean(struct sspt_ip *ip);
+void sspt_ip_get(struct sspt_ip *ip);
+void sspt_ip_put(struct sspt_ip *ip);
+
+
+#endif /* _SSPT_IP */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/driver/sspt/sspt_page.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include "sspt.h"
+#include "sspt_page.h"
+#include "sspt_file.h"
+#include "sspt_ip.h"
+#include <us_manager/probes/use_probes.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+/**
+ * @brief Create sspt_page struct
+ *
+ * @param offset File ofset
+ * @return Pointer to the created sspt_page struct
+ */
+struct sspt_page *sspt_page_create(unsigned long offset)
+{
+ struct sspt_page *obj = kmalloc(sizeof(*obj), GFP_KERNEL);
+ if (obj) {
+ INIT_HLIST_NODE(&obj->hlist);
+ mutex_init(&obj->ip_list.mtx);
+ INIT_LIST_HEAD(&obj->ip_list.inst);
+ INIT_LIST_HEAD(&obj->ip_list.not_inst);
+ obj->offset = offset;
+ obj->file = NULL;
+ kref_init(&obj->ref);
+ }
+
+ return obj;
+}
+
+void sspt_page_clean(struct sspt_page *page)
+{
+ struct sspt_ip *ip, *n;
+ LIST_HEAD(head);
+
+ sspt_page_lock(page);
+ WARN_ON(!list_empty(&page->ip_list.inst));
+ list_for_each_entry_safe(ip, n, &page->ip_list.inst, list) {
+ sspt_ip_get(ip);
+ list_move(&ip->list, &head);
+ }
+
+ list_for_each_entry_safe(ip, n, &page->ip_list.not_inst, list) {
+ sspt_ip_get(ip);
+ list_move(&ip->list, &head);
+ }
+
+ while (!list_empty(&head)) {
+ ip = list_first_entry(&head, struct sspt_ip ,list);
+ sspt_page_unlock(page);
+
+ sspt_ip_clean(ip);
+
+ sspt_page_lock(page);
+ sspt_ip_put(ip);
+ }
+ sspt_page_unlock(page);
+}
+
+static void sspt_page_release(struct kref *ref)
+{
+ struct sspt_page *page = container_of(ref, struct sspt_page, ref);
+
+ WARN_ON(!list_empty(&page->ip_list.inst) ||
+ !list_empty(&page->ip_list.not_inst));
+
+ kfree(page);
+}
+
+void sspt_page_get(struct sspt_page *page)
+{
+ kref_get(&page->ref);
+}
+
+void sspt_page_put(struct sspt_page *page)
+{
+ kref_put(&page->ref, sspt_page_release);
+}
+
+/**
+ * @brief Add instruction pointer to sspt_page
+ *
+ * @param page Pointer to the sspt_page struct
+ * @param ip Pointer to the us_ip struct
+ * @return Void
+ */
+void sspt_page_add_ip(struct sspt_page *page, struct sspt_ip *ip)
+{
+ ip->page = page;
+ list_add(&ip->list, &page->ip_list.not_inst);
+}
+
+void sspt_page_lock(struct sspt_page *page)
+{
+ mutex_lock(&page->ip_list.mtx);
+}
+
+void sspt_page_unlock(struct sspt_page *page)
+{
+ mutex_unlock(&page->ip_list.mtx);
+}
+
+/**
+ * @brief Check if probes are set on the page
+ *
+ * @param page Pointer to the sspt_page struct
+ * @return Boolean
+ */
+bool sspt_page_is_installed(struct sspt_page *page)
+{
+ return !list_empty(&page->ip_list.inst);
+}
+
+bool sspt_page_is_installed_ip(struct sspt_page *page, struct sspt_ip *ip)
+{
+ bool result = false; /* not installed by default */
+ struct sspt_ip *p;
+
+ sspt_page_lock(page);
+ list_for_each_entry(p, &page->ip_list.inst, list) {
+ if (p == ip) {
+ result = true;
+ goto unlock;
+ }
+ }
+
+unlock:
+ sspt_page_unlock(page);
+
+ return result;
+}
+
+/**
+ * @brief Install probes on the page
+ *
+ * @param page Pointer to the sspt_page struct
+ * @param file Pointer to the sspt_file struct
+ * @return Error code
+ */
+int sspt_register_page(struct sspt_page *page, struct sspt_file *file)
+{
+ int err = 0;
+ struct sspt_ip *ip, *n;
+ LIST_HEAD(not_inst_head);
+
+ mutex_lock(&page->ip_list.mtx);
+ if (list_empty(&page->ip_list.not_inst))
+ goto unlock;
+
+ list_for_each_entry_safe(ip, n, &page->ip_list.not_inst, list) {
+ /* set virtual address */
+ ip->orig_addr = file->vm_start + page->offset + ip->offset;
+
+ err = sspt_register_usprobe(ip);
+ if (err) {
+ sspt_ip_get(ip);
+ list_move(&ip->list, ¬_inst_head);
+ continue;
+ }
+ }
+
+ list_splice_init(&page->ip_list.not_inst, &page->ip_list.inst);
+
+unlock:
+ mutex_unlock(&page->ip_list.mtx);
+
+ list_for_each_entry_safe(ip, n, ¬_inst_head, list) {
+ sspt_ip_clean(ip);
+ sspt_ip_put(ip);
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Uninstall probes on the page
+ *
+ * @param page Pointer to the sspt_page struct
+ * @param flag Action for probes
+ * @param task Pointer to the task_struct struct
+ * @return Error code
+ */
+int sspt_unregister_page(struct sspt_page *page,
+ enum US_FLAGS flag,
+ struct task_struct *task)
+{
+ int err = 0;
+ struct sspt_ip *ip;
+
+ mutex_lock(&page->ip_list.mtx);
+ if (list_empty(&page->ip_list.inst))
+ goto unlock;
+
+ list_for_each_entry(ip, &page->ip_list.inst, list) {
+ err = sspt_unregister_usprobe(task, ip, flag);
+ if (err != 0) {
+ WARN_ON(1);
+ break;
+ }
+ }
+
+ if (flag != US_DISARM)
+ list_splice_init(&page->ip_list.inst, &page->ip_list.not_inst);
+
+unlock:
+ mutex_unlock(&page->ip_list.mtx);
+ return err;
+}
+
+void sspt_page_on_each_ip(struct sspt_page *page,
+ void (*func)(struct sspt_ip *, void *), void *data)
+{
+ struct sspt_ip *ip;
+
+ mutex_lock(&page->ip_list.mtx);
+ list_for_each_entry(ip, &page->ip_list.inst, list)
+ func(ip, data);
+ mutex_unlock(&page->ip_list.mtx);
+}
--- /dev/null
+#ifndef __SSPT_PAGE__
+#define __SSPT_PAGE__
+
+/**
+ * @file us_manager/sspt/sspt_page.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+
+struct sspt_ip;
+struct sspt_file;
+struct task_struct;
+enum US_FLAGS;
+
+/**
+ * @struct sspt_page
+ * @breaf Image of page for specified process
+ */
+struct sspt_page {
+ /* sspt_file */
+ struct hlist_node hlist; /**< For sspt_file */
+ struct sspt_file *file; /**< Ptr to the file (parent) */
+
+ /* sspt_ip */
+ struct {
+ struct mutex mtx; /**< Lock page */
+ struct list_head inst; /**< For installed ip */
+ struct list_head not_inst; /**< For don'tinstalled ip */
+ } ip_list;
+
+ unsigned long offset; /**< File offset */
+
+ struct kref ref;
+};
+
+struct sspt_page *sspt_page_create(unsigned long offset);
+void sspt_page_clean(struct sspt_page *page);
+void sspt_page_get(struct sspt_page *page);
+void sspt_page_put(struct sspt_page *page);
+
+bool sspt_page_is_installed_ip(struct sspt_page *page, struct sspt_ip *ip);
+void sspt_page_add_ip(struct sspt_page *page, struct sspt_ip *ip);
+void sspt_page_lock(struct sspt_page *page);
+void sspt_page_unlock(struct sspt_page *page);
+
+bool sspt_page_is_installed(struct sspt_page *page);
+
+int sspt_register_page(struct sspt_page *page, struct sspt_file *file);
+
+int sspt_unregister_page(struct sspt_page *page,
+ enum US_FLAGS flag,
+ struct task_struct *task);
+
+void sspt_page_on_each_ip(struct sspt_page *page,
+ void (*func)(struct sspt_ip *, void *), void *data);
+
+#endif /* __SSPT_PAGE__ */
--- /dev/null
+/*
+ * Dynamic Binary Instrumentation Module based on KProbes
+ * modules/driver/sspt/sspt_proc.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+#include "sspt.h"
+#include "sspt_proc.h"
+#include "sspt_page.h"
+#include "sspt_feature.h"
+#include "sspt_filter.h"
+#include "../pf/proc_filters.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <kprobe/swap_ktd.h>
+#include <us_manager/us_slot_manager.h>
+
+static LIST_HEAD(proc_probes_list);
+static DEFINE_RWLOCK(sspt_proc_rwlock);
+
+
+struct list_head *sspt_proc_list()
+{
+ return &proc_probes_list;
+}
+
+/**
+ * @brief Global read lock for sspt_proc
+ *
+ * @return Void
+ */
+void sspt_proc_read_lock(void)
+{
+ read_lock(&sspt_proc_rwlock);
+}
+
+/**
+ * @brief Global read unlock for sspt_proc
+ *
+ * @return Void
+ */
+void sspt_proc_read_unlock(void)
+{
+ read_unlock(&sspt_proc_rwlock);
+}
+
+/**
+ * @brief Global write lock for sspt_proc
+ *
+ * @return Void
+ */
+void sspt_proc_write_lock(void)
+{
+ write_lock(&sspt_proc_rwlock);
+}
+
+/**
+ * @brief Global write unlock for sspt_proc
+ *
+ * @return Void
+ */
+void sspt_proc_write_unlock(void)
+{
+ write_unlock(&sspt_proc_rwlock);
+}
+
+struct ktd_proc {
+ struct sspt_proc *proc;
+ spinlock_t lock;
+};
+
+static void ktd_init(struct task_struct *task, void *data)
+{
+ struct ktd_proc *kproc = (struct ktd_proc *)data;
+
+ kproc->proc = NULL;
+ spin_lock_init(&kproc->lock);
+}
+
+static void ktd_exit(struct task_struct *task, void *data)
+{
+ struct ktd_proc *kproc = (struct ktd_proc *)data;
+
+ WARN_ON(kproc->proc);
+}
+
+static struct ktask_data ktd = {
+ .init = ktd_init,
+ .exit = ktd_exit,
+ .size = sizeof(struct ktd_proc),
+};
+
+static struct ktd_proc *kproc_by_task(struct task_struct *task)
+{
+ return (struct ktd_proc *)swap_ktd(&ktd, task);
+}
+
+int sspt_proc_init(void)
+{
+ return swap_ktd_reg(&ktd);
+}
+
+void sspt_proc_uninit(void)
+{
+ swap_ktd_unreg(&ktd);
+}
+
+void sspt_change_leader(struct task_struct *prev, struct task_struct *next)
+{
+ struct ktd_proc *prev_kproc;
+
+ prev_kproc = kproc_by_task(prev);
+ spin_lock(&prev_kproc->lock);
+ if (prev_kproc->proc) {
+ struct ktd_proc *next_kproc;
+
+ next_kproc = kproc_by_task(next);
+ get_task_struct(next);
+
+ /* Change the keeper sspt_proc */
+ BUG_ON(next_kproc->proc);
+
+ spin_lock(&next_kproc->lock);
+ next_kproc->proc = prev_kproc->proc;
+ prev_kproc->proc = NULL;
+ spin_unlock(&next_kproc->lock);
+
+ /* Set new the task leader to sspt_proc */
+ next_kproc->proc->leader = next;
+
+ put_task_struct(prev);
+ }
+ spin_unlock(&prev_kproc->lock);
+}
+
+static void sspt_reset_proc(struct task_struct *task)
+{
+ struct ktd_proc *kproc;
+
+ kproc = kproc_by_task(task->group_leader);
+ spin_lock(&kproc->lock);
+ kproc->proc = NULL;
+ spin_unlock(&kproc->lock);
+}
+
+
+
+
+
+static struct sspt_proc *sspt_proc_create(struct task_struct *leader)
+{
+ struct sspt_proc *proc = kzalloc(sizeof(*proc), GFP_KERNEL);
+
+ if (proc) {
+ proc->feature = sspt_create_feature();
+ if (proc->feature == NULL) {
+ kfree(proc);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&proc->list);
+ INIT_LIST_HEAD(&proc->files.head);
+ init_rwsem(&proc->files.sem);
+ proc->tgid = leader->tgid;
+ proc->leader = leader;
+ /* FIXME: change the task leader */
+ proc->sm = create_sm_us(leader);
+ mutex_init(&proc->filters.mtx);
+ INIT_LIST_HEAD(&proc->filters.head);
+ atomic_set(&proc->usage, 1);
+
+ get_task_struct(proc->leader);
+
+ proc->suspect.after_exec = 1;
+ proc->suspect.after_fork = 0;
+ }
+
+ return proc;
+}
+
+static void sspt_proc_free(struct sspt_proc *proc)
+{
+ put_task_struct(proc->leader);
+ free_sm_us(proc->sm);
+ sspt_destroy_feature(proc->feature);
+ kfree(proc);
+}
+
+/**
+ * @brief Remove sspt_proc struct
+ *
+ * @param proc remove object
+ * @return Void
+ */
+
+/* called with sspt_proc_write_lock() */
+void sspt_proc_cleanup(struct sspt_proc *proc)
+{
+ struct sspt_file *file, *n;
+
+ sspt_proc_del_all_filters(proc);
+
+ down_write(&proc->files.sem);
+ list_for_each_entry_safe(file, n, &proc->files.head, list) {
+ list_del(&file->list);
+ sspt_file_free(file);
+ }
+ up_write(&proc->files.sem);
+
+ sspt_destroy_feature(proc->feature);
+
+ free_sm_us(proc->sm);
+ sspt_reset_proc(proc->leader);
+ sspt_proc_put(proc);
+}
+
+struct sspt_proc *sspt_proc_get(struct sspt_proc *proc)
+{
+ atomic_inc(&proc->usage);
+
+ return proc;
+}
+
+void sspt_proc_put(struct sspt_proc *proc)
+{
+ if (atomic_dec_and_test(&proc->usage)) {
+ if (proc->__mm) {
+ mmput(proc->__mm);
+ proc->__mm = NULL;
+ }
+ if (proc->__task) {
+ put_task_struct(proc->__task);
+ proc->__task = NULL;
+ }
+
+ WARN_ON(kproc_by_task(proc->leader)->proc);
+
+ put_task_struct(proc->leader);
+ kfree(proc);
+ }
+}
+EXPORT_SYMBOL_GPL(sspt_proc_put);
+
+struct sspt_proc *sspt_proc_by_task(struct task_struct *task)
+{
+ return kproc_by_task(task->group_leader)->proc;
+}
+EXPORT_SYMBOL_GPL(sspt_proc_by_task);
+
+struct sspt_proc *sspt_proc_get_by_task(struct task_struct *task)
+{
+ struct ktd_proc *kproc = kproc_by_task(task->group_leader);
+ struct sspt_proc *proc;
+
+ spin_lock(&kproc->lock);
+ proc = kproc->proc;
+ if (proc)
+ sspt_proc_get(proc);
+ spin_unlock(&kproc->lock);
+
+ return proc;
+}
+EXPORT_SYMBOL_GPL(sspt_proc_get_by_task);
+
+/**
+ * @brief Call func() on each proc (no lock)
+ *
+ * @param func Callback
+ * @param data Data for callback
+ * @return Void
+ */
+void on_each_proc_no_lock(void (*func)(struct sspt_proc *, void *), void *data)
+{
+ struct sspt_proc *proc, *tmp;
+
+ list_for_each_entry_safe(proc, tmp, &proc_probes_list, list) {
+ func(proc, data);
+ }
+}
+
+/**
+ * @brief Call func() on each proc
+ *
+ * @param func Callback
+ * @param data Data for callback
+ * @return Void
+ */
+void on_each_proc(void (*func)(struct sspt_proc *, void *), void *data)
+{
+ sspt_proc_read_lock();
+ on_each_proc_no_lock(func, data);
+ sspt_proc_read_unlock();
+}
+EXPORT_SYMBOL_GPL(on_each_proc);
+
+/**
+ * @brief Get sspt_proc by task or create sspt_proc
+ *
+ * @param task Pointer on the task_struct struct
+ * @param priv Private data
+ * @return Pointer on the sspt_proc struct
+ */
+struct sspt_proc *sspt_proc_get_by_task_or_new(struct task_struct *task)
+{
+ static DEFINE_MUTEX(local_mutex);
+ struct ktd_proc *kproc;
+ struct sspt_proc *proc;
+ struct task_struct *leader = task->group_leader;
+
+ kproc = kproc_by_task(leader);
+ if (kproc->proc)
+ goto out;
+
+ proc = sspt_proc_create(leader);
+
+ spin_lock(&kproc->lock);
+ if (kproc->proc == NULL) {
+ sspt_proc_get(proc);
+ kproc->proc = proc;
+ proc = NULL;
+
+ sspt_proc_write_lock();
+ list_add(&kproc->proc->list, &proc_probes_list);
+ sspt_proc_write_unlock();
+ }
+ spin_unlock(&kproc->lock);
+
+ if (proc)
+ sspt_proc_free(proc);
+
+out:
+ return kproc->proc;
+}
+
+/**
+ * @brief Check sspt_proc on empty
+ *
+ * @return Pointer on the sspt_proc struct
+ */
+void sspt_proc_check_empty(void)
+{
+ WARN_ON(!list_empty(&proc_probes_list));
+}
+
+static void sspt_proc_add_file(struct sspt_proc *proc, struct sspt_file *file)
+{
+ down_write(&proc->files.sem);
+ list_add(&file->list, &proc->files.head);
+ file->proc = proc;
+ up_write(&proc->files.sem);
+}
+
+/**
+ * @brief Get sspt_file from sspt_proc by dentry or new
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @param dentry Dentry of file
+ * @return Pointer on the sspt_file struct
+ */
+struct sspt_file *sspt_proc_find_file_or_new(struct sspt_proc *proc,
+ struct dentry *dentry)
+{
+ struct sspt_file *file;
+
+ file = sspt_proc_find_file(proc, dentry);
+ if (file == NULL) {
+ file = sspt_file_create(dentry, 10);
+ if (file)
+ sspt_proc_add_file(proc, file);
+ }
+
+ return file;
+}
+
+/**
+ * @brief Get sspt_file from sspt_proc by dentry
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @param dentry Dentry of file
+ * @return Pointer on the sspt_file struct
+ */
+struct sspt_file *sspt_proc_find_file(struct sspt_proc *proc,
+ struct dentry *dentry)
+{
+ struct sspt_file *file;
+
+ down_read(&proc->files.sem);
+ list_for_each_entry(file, &proc->files.head, list) {
+ if (dentry == file->dentry)
+ goto unlock;
+ }
+ file = NULL;
+
+unlock:
+ up_read(&proc->files.sem);
+
+ return file;
+}
+
+/**
+ * @brief Install probes on the page to monitored process
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @param page_addr Page address
+ * @return Void
+ */
+void sspt_proc_install_page(struct sspt_proc *proc, unsigned long page_addr)
+{
+ struct mm_struct *mm = proc->leader->mm;
+ struct vm_area_struct *vma;
+
+ vma = find_vma_intersection(mm, page_addr, page_addr + 1);
+ if (vma && check_vma(vma)) {
+ struct dentry *dentry = vma->vm_file->f_path.dentry;
+ struct sspt_file *file = sspt_proc_find_file(proc, dentry);
+ if (file) {
+ sspt_file_set_mapping(file, vma);
+ sspt_file_install(file);
+ }
+ }
+}
+
+/**
+ * @brief Install probes to monitored process
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @return Void
+ */
+void sspt_proc_install(struct sspt_proc *proc)
+{
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = proc->leader->mm;
+
+ proc->first_install = 1;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (check_vma(vma)) {
+ struct dentry *dentry = vma->vm_file->f_path.dentry;
+ struct sspt_file *file =
+ sspt_proc_find_file(proc, dentry);
+ if (file) {
+ sspt_file_set_mapping(file, vma);
+ sspt_file_install(file);
+ }
+ }
+ }
+}
+
+/**
+ * @brief Uninstall probes to monitored process
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @param task Pointer on the task_struct struct
+ * @param flag Action for probes
+ * @return Error code
+ */
+int sspt_proc_uninstall(struct sspt_proc *proc,
+ struct task_struct *task,
+ enum US_FLAGS flag)
+{
+ int err = 0;
+ struct sspt_file *file;
+
+ down_read(&proc->files.sem);
+ list_for_each_entry(file, &proc->files.head, list) {
+ err = sspt_file_uninstall(file, task, flag);
+ if (err != 0) {
+ printk(KERN_INFO "ERROR sspt_proc_uninstall: err=%d\n",
+ err);
+ break;
+ }
+ }
+ up_read(&proc->files.sem);
+
+ return err;
+}
+
+static int intersection(unsigned long start_a, unsigned long end_a,
+ unsigned long start_b, unsigned long end_b)
+{
+ return start_a < start_b ?
+ end_a > start_b :
+ start_a < end_b;
+}
+
+/**
+ * @brief Get sspt_file list by region (remove sspt_file from sspt_proc list)
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @param head[out] Pointer on the head list
+ * @param start Region start
+ * @param len Region length
+ * @return Error code
+ */
+int sspt_proc_get_files_by_region(struct sspt_proc *proc,
+ struct list_head *head,
+ unsigned long start, unsigned long end)
+{
+ int ret = 0;
+ struct sspt_file *file, *n;
+
+ down_write(&proc->files.sem);
+ list_for_each_entry_safe(file, n, &proc->files.head, list) {
+ if (intersection(file->vm_start, file->vm_end, start, end)) {
+ ret = 1;
+ list_move(&file->list, head);
+ }
+ }
+ up_write(&proc->files.sem);
+
+ return ret;
+}
+
+/**
+ * @brief Insert sspt_file to sspt_proc list
+ *
+ * @param proc Pointer on the sspt_proc struct
+ * @param head Pointer on the head list
+ * @return Void
+ */
+void sspt_proc_insert_files(struct sspt_proc *proc, struct list_head *head)
+{
+ down_write(&proc->files.sem);
+ list_splice(head, &proc->files.head);
+ up_write(&proc->files.sem);
+}
+
+/**
+ * @brief Add sspt_filter to sspt_proc list
+ *
+ * @param proc Pointer to sspt_proc struct
+ * @param pfg Pointer to pf_group struct
+ * @return Void
+ */
+void sspt_proc_add_filter(struct sspt_proc *proc, struct pf_group *pfg)
+{
+ struct sspt_filter *f;
+
+ f = sspt_filter_create(proc, pfg);
+ if (f)
+ list_add(&f->list, &proc->filters.head);
+}
+
+/**
+ * @brief Remove sspt_filter from sspt_proc list
+ *
+ * @param proc Pointer to sspt_proc struct
+ * @param pfg Pointer to pf_group struct
+ * @return Void
+ */
+void sspt_proc_del_filter(struct sspt_proc *proc, struct pf_group *pfg)
+{
+ struct sspt_filter *fl, *tmp;
+
+ mutex_lock(&proc->filters.mtx);
+ list_for_each_entry_safe(fl, tmp, &proc->filters.head, list) {
+ if (fl->pfg == pfg) {
+ list_del(&fl->list);
+ sspt_filter_free(fl);
+ }
+ }
+ mutex_unlock(&proc->filters.mtx);
+}
+
+/**
+ * @brief Remove all sspt_filters from sspt_proc list
+ *
+ * @param proc Pointer to sspt_proc struct
+ * @return Void
+ */
+void sspt_proc_del_all_filters(struct sspt_proc *proc)
+{
+ struct sspt_filter *fl, *tmp;
+
+ mutex_lock(&proc->filters.mtx);
+ list_for_each_entry_safe(fl, tmp, &proc->filters.head, list) {
+ list_del(&fl->list);
+ sspt_filter_free(fl);
+ }
+ mutex_unlock(&proc->filters.mtx);
+}
+
+/**
+ * @brief Check if sspt_filter is already in sspt_proc list
+ *
+ * @param proc Pointer to sspt_proc struct
+ * @param pfg Pointer to pf_group struct
+ * @return Boolean
+ */
+bool sspt_proc_is_filter_new(struct sspt_proc *proc, struct pf_group *pfg)
+{
+ struct sspt_filter *fl;
+
+ list_for_each_entry(fl, &proc->filters.head, list)
+ if (fl->pfg == pfg)
+ return false;
+
+ return true;
+}
+
+void sspt_proc_on_each_filter(struct sspt_proc *proc,
+ void (*func)(struct sspt_filter *, void *),
+ void *data)
+{
+ struct sspt_filter *fl;
+
+ list_for_each_entry(fl, &proc->filters.head, list)
+ func(fl, data);
+}
+
+void sspt_proc_on_each_ip(struct sspt_proc *proc,
+ void (*func)(struct sspt_ip *, void *), void *data)
+{
+ struct sspt_file *file;
+
+ down_read(&proc->files.sem);
+ list_for_each_entry(file, &proc->files.head, list)
+ sspt_file_on_each_ip(file, func, data);
+ up_read(&proc->files.sem);
+}
+
+static void is_send_event(struct sspt_filter *f, void *data)
+{
+ bool *is_send = (bool *)data;
+
+ if (!*is_send && f->pfg_is_inst)
+ *is_send = !!pfg_msg_cb_get(f->pfg);
+}
+
+bool sspt_proc_is_send_event(struct sspt_proc *proc)
+{
+ bool is_send = false;
+
+ /* FIXME: add read lock (deadlock in sampler) */
+ sspt_proc_on_each_filter(proc, is_send_event, (void *)&is_send);
+
+ return is_send;
+}
+
+
+static struct sspt_proc_cb *proc_cb;
+
+int sspt_proc_cb_set(struct sspt_proc_cb *cb)
+{
+ if (cb && proc_cb)
+ return -EBUSY;
+
+ proc_cb = cb;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sspt_proc_cb_set);
+
+void sspt_proc_priv_create(struct sspt_proc *proc)
+{
+ if (proc_cb && proc_cb->priv_create)
+ proc->private_data = proc_cb->priv_create(proc);
+}
+
+void sspt_proc_priv_destroy(struct sspt_proc *proc)
+{
+ if (proc->first_install && proc_cb && proc_cb->priv_destroy)
+ proc_cb->priv_destroy(proc, proc->private_data);
+}
--- /dev/null
+#ifndef __SSPT_PROC__
+#define __SSPT_PROC__
+
+/**
+ * @file us_manager/sspt/sspt_proc.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include "sspt_file.h"
+
+struct slot_manager;
+struct task_struct;
+struct pf_group;
+struct sspt_filter;
+struct sspt_ip;
+
+/** Flags for sspt_*_uninstall() */
+enum US_FLAGS {
+ US_UNREGS_PROBE, /**< probes remove and disarm */
+ US_DISARM, /**< probes disarm */
+ US_UNINSTALL /**< probes remove from list install */
+};
+
+/**
+ * @struct sspt_proc
+ * @breaf Image of process for specified process
+ */
+struct sspt_proc {
+ struct list_head list; /**< For global process list */
+
+ /* sspt_file */
+ struct {
+ struct rw_semaphore sem;/**< Semaphore for files list */
+ struct list_head head; /**< For sspt_file */
+ } files;
+
+ pid_t tgid; /**< Thread group ID */
+ struct task_struct *leader; /**< Ptr to the task leader */
+ struct mm_struct *__mm;
+ struct task_struct *__task;
+ struct slot_manager *sm; /**< Ptr to the manager slot */
+
+ struct {
+ unsigned after_exec:1;
+ unsigned after_fork:1;
+ } suspect;
+
+ struct {
+ struct mutex mtx; /**< Mutex for filter list */
+ struct list_head head; /**< Filter head */
+ } filters;
+
+ unsigned first_install:1; /**< Install flag */
+ struct sspt_feature *feature; /**< Ptr to the feature */
+ atomic_t usage;
+
+ /* FIXME: for preload (remove those fields) */
+ unsigned long r_state_addr; /**< address of r_state */
+ void *private_data; /**< Process private data */
+};
+
+struct sspt_proc_cb {
+ void *(*priv_create)(struct sspt_proc *);
+ void (*priv_destroy)(struct sspt_proc *, void *);
+};
+
+
+struct list_head *sspt_proc_list(void);
+
+struct sspt_proc *sspt_proc_by_task(struct task_struct *task);
+struct sspt_proc *sspt_proc_get_by_task(struct task_struct *task);
+struct sspt_proc *sspt_proc_get_by_task_or_new(struct task_struct *task);
+struct sspt_proc *sspt_proc_get(struct sspt_proc *proc);
+void sspt_proc_put(struct sspt_proc *proc);
+void sspt_proc_cleanup(struct sspt_proc *proc);
+
+void on_each_proc_no_lock(void (*func)(struct sspt_proc *, void *),
+ void *data);
+void on_each_proc(void (*func)(struct sspt_proc *, void *), void *data);
+
+void sspt_proc_check_empty(void);
+
+struct sspt_file *sspt_proc_find_file(struct sspt_proc *proc,
+ struct dentry *dentry);
+struct sspt_file *sspt_proc_find_file_or_new(struct sspt_proc *proc,
+ struct dentry *dentry);
+void sspt_proc_install_page(struct sspt_proc *proc, unsigned long page_addr);
+void sspt_proc_install(struct sspt_proc *proc);
+int sspt_proc_uninstall(struct sspt_proc *proc,
+ struct task_struct *task,
+ enum US_FLAGS flag);
+
+int sspt_proc_get_files_by_region(struct sspt_proc *proc,
+ struct list_head *head,
+ unsigned long start, unsigned long end);
+void sspt_proc_insert_files(struct sspt_proc *proc, struct list_head *head);
+
+void sspt_proc_read_lock(void);
+void sspt_proc_read_unlock(void);
+void sspt_proc_write_lock(void);
+void sspt_proc_write_unlock(void);
+
+void sspt_proc_add_filter(struct sspt_proc *proc, struct pf_group *pfg);
+void sspt_proc_del_filter(struct sspt_proc *proc, struct pf_group *pfg);
+void sspt_proc_del_all_filters(struct sspt_proc *proc);
+bool sspt_proc_is_filter_new(struct sspt_proc *proc, struct pf_group *pfg);
+
+void sspt_proc_on_each_filter(struct sspt_proc *proc,
+ void (*func)(struct sspt_filter *, void *),
+ void *data);
+
+void sspt_proc_on_each_ip(struct sspt_proc *proc,
+ void (*func)(struct sspt_ip *, void *), void *data);
+
+bool sspt_proc_is_send_event(struct sspt_proc *proc);
+
+int sspt_proc_cb_set(struct sspt_proc_cb *cb);
+void sspt_proc_priv_create(struct sspt_proc *proc);
+void sspt_proc_priv_destroy(struct sspt_proc *proc);
+
+void sspt_change_leader(struct task_struct *prev, struct task_struct *next);
+int sspt_proc_init(void);
+void sspt_proc_uninit(void);
+
+
+#endif /* __SSPT_PROC__ */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/us_manager_common_file.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2017
+ *
+ * 2017 Alexander Aksenov: SWAP us_manager implement
+ *
+ */
+
+#ifndef __US_MANAGER_COMMON_FILE_H__
+#define __US_MANAGER_COMMON_FILE_H__
+
+#include <linux/dcache.h>
+#include <linux/namei.h>
+
+static inline struct dentry *swap_get_dentry(const char *filepath)
+{
+ struct path path;
+ struct dentry *dentry = NULL;
+
+ if (kern_path(filepath, LOOKUP_FOLLOW, &path) == 0) {
+ dentry = dget(path.dentry);
+ path_put(&path);
+ }
+
+ return dentry;
+}
+
+static inline void swap_put_dentry(struct dentry *dentry)
+{
+ dput(dentry);
+}
+
+#endif /* __US_MANAGER_COMMON_FILE_H__ */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/us_manager.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/stop_machine.h>
+#include "pf/pf_group.h"
+#include "sspt/sspt_proc.h"
+#include "probes/probe_info_new.h"
+#include "helper.h"
+#include "us_manager.h"
+#include "debugfs_us_manager.h"
+#include "callbacks.h"
+#include <writer/event_filter.h>
+#include <master/swap_initializer.h>
+
+
+static DEFINE_MUTEX(mutex_inst);
+static enum status_type status = ST_OFF;
+
+
+static int __do_usm_stop(void *data)
+{
+ get_all_procs();
+
+ return 0;
+}
+
+static void do_usm_stop(void)
+{
+ int ret;
+
+ exec_cbs(STOP_CB);
+ helper_unreg_top();
+
+ ret = stop_machine(__do_usm_stop, NULL, NULL);
+ if (ret)
+ printk("do_usm_stop failed: %d\n", ret);
+
+ uninstall_all();
+ helper_unreg_bottom();
+ sspt_proc_check_empty();
+}
+
+static int do_usm_start(void)
+{
+ int ret;
+
+ ret = helper_reg();
+ if (ret)
+ return ret;
+
+ install_all();
+
+ exec_cbs(START_CB);
+
+ return 0;
+}
+
+/**
+ * @brief Get instrumentation status
+ *
+ * @return Instrumentation status
+ */
+enum status_type usm_get_status(void)
+{
+ mutex_lock(&mutex_inst);
+ return status;
+}
+EXPORT_SYMBOL_GPL(usm_get_status);
+
+/**
+ * @brief Put instrumentation status
+ *
+ * @param st Instrumentation status
+ * @return Void
+ */
+void usm_put_status(enum status_type st)
+{
+ status = st;
+ mutex_unlock(&mutex_inst);
+}
+EXPORT_SYMBOL_GPL(usm_put_status);
+
+/**
+ * @brief Stop instrumentation
+ *
+ * @return Error code
+ */
+int usm_stop(void)
+{
+ int ret = 0;
+
+ if (usm_get_status() == ST_OFF) {
+ printk(KERN_INFO "US instrumentation is not running!\n");
+ ret = -EINVAL;
+ goto put;
+ }
+
+ do_usm_stop();
+
+put:
+ usm_put_status(ST_OFF);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usm_stop);
+
+/**
+ * @brief Start instrumentation
+ *
+ * @return Error code
+ */
+int usm_start(void)
+{
+ int ret = -EINVAL;
+ enum status_type st;
+
+ st = usm_get_status();
+ if (st == ST_ON) {
+ printk(KERN_INFO "US instrumentation is already run!\n");
+ goto put;
+ }
+
+ ret = do_usm_start();
+ if (ret == 0)
+ st = ST_ON;
+
+put:
+ usm_put_status(st);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usm_start);
+
+
+
+
+
+/* ============================================================================
+ * === QUIET ===
+ * ============================================================================
+ */
+static enum quiet_type quiet = QT_ON;
+
+/**
+ * @brief Set quiet mode
+ *
+ * @param q Quiet mode
+ * @return Void
+ */
+void set_quiet(enum quiet_type q)
+{
+ quiet = q;
+}
+EXPORT_SYMBOL_GPL(set_quiet);
+
+/**
+ * @brief Get quiet mode
+ *
+ * @return Quiet mode
+ */
+enum quiet_type get_quiet(void)
+{
+ return quiet;
+}
+EXPORT_SYMBOL_GPL(get_quiet);
+
+
+
+
+
+/* ============================================================================
+ * === US_FILTER ===
+ * ============================================================================
+ */
+static int us_filter(struct task_struct *task)
+{
+ struct sspt_proc *proc;
+
+ /* FIXME: add read lock (deadlock in sampler) */
+ proc = sspt_proc_by_task(task);
+ if (proc)
+ return sspt_proc_is_send_event(proc);
+
+ return 0;
+}
+
+static struct ev_filter ev_us_filter = {
+ .name = "traced_process_only",
+ .filter = us_filter
+};
+
+static int init_us_filter(void)
+{
+ int ret;
+
+ ret = event_filter_register(&ev_us_filter);
+ if (ret)
+ return ret;
+
+ return event_filter_set(ev_us_filter.name);
+}
+
+static void exit_us_filter(void)
+{
+ event_filter_unregister(&ev_us_filter);
+}
+
+
+
+
+
+static int usm_once(void)
+{
+ int ret;
+
+ ret = helper_once();
+
+ return ret;
+}
+
+static int init_us_manager(void)
+{
+ int ret;
+
+ ret = helper_init();
+ if (ret)
+ return ret;
+
+ ret = sspt_proc_init();
+ if (ret)
+ goto uninit_helper;
+
+ ret = pin_init();
+ if (ret)
+ goto uninit_proc;
+
+ ret = init_us_filter();
+ if (ret)
+ goto uninit_pin;
+
+ return 0;
+
+uninit_pin:
+ pin_exit();
+uninit_proc:
+ sspt_proc_uninit();
+uninit_helper:
+ helper_uninit();
+
+ return ret;
+}
+
+static void exit_us_manager(void)
+{
+ if (status == ST_ON)
+ BUG_ON(usm_stop());
+
+ remove_all_cbs();
+
+ exit_us_filter();
+ pin_exit();
+ sspt_proc_uninit();
+ helper_uninit();
+
+ WARN_ON(!pfg_is_unloadable());
+}
+
+SWAP_LIGHT_INIT_MODULE(usm_once, init_us_manager, exit_us_manager,
+ init_debugfs_us_manager, exit_debugfs_us_manager);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/**
+ * @file us_manager/us_manager.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+#ifndef _US_MANAGER_H
+#define _US_MANAGER_H
+
+
+/** Quiet mode */
+enum quiet_type {
+ QT_ON, /**< Quiet mode - on */
+ QT_OFF /**< Quiet mode - off */
+};
+
+/** Instrumentation status */
+enum status_type {
+ ST_OFF, /**< Instrumentation status - off */
+ ST_ON /**< Instrumentation status - on */
+};
+
+void set_quiet(enum quiet_type q);
+enum quiet_type get_quiet(void);
+
+enum status_type usm_get_status(void);
+void usm_put_status(enum status_type st);
+
+int usm_start(void);
+int usm_stop(void);
+
+#endif /* _US_MANAGER_H */
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/us_slot_manager.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Alexander Aksenov: SWAP us_manager implement
+ *
+ */
+
+
+#include <linux/mm.h>
+#include <linux/version.h>
+
+/*
+ * TODO: move declaration and definition swap_do_mmap_pgoff()
+ * from swap_kprobe.ko to swap_us_manager.ko
+ */
+#include <kprobe/swap_kprobes_deps.h>
+
+
+static inline unsigned long __swap_do_mmap(struct file *filp,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long prot,
+ unsigned long flag,
+ unsigned long offset)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)
+ unsigned long populate;
+
+ return swap_do_mmap(filp, addr, len, prot,
+ flag, 0, offset, &populate);
+
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ unsigned long populate;
+
+ return swap_do_mmap_pgoff(filp, addr, len, prot,
+ flag, offset, &populate);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ return swap_do_mmap_pgoff(filp, addr, len, prot, flag, offset);
+#else
+ return do_mmap(filp, addr, len, prot, flag, offset);
+#endif
+}
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/us_slot_manager.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/hardirq.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/list.h>
+
+#include <kprobe/swap_slots.h>
+#include <swap-asm/swap_kprobes.h>
+#include <swap-asm/swap_uprobes.h>
+#include "us_manager_common.h"
+
+
+static void *sm_alloc_us(struct slot_manager *sm)
+{
+ unsigned long addr;
+
+ addr = __swap_do_mmap(NULL, 0, PAGE_SIZE,
+ PROT_EXEC | PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+ return (void *)addr;
+}
+
+static void sm_free_us(struct slot_manager *sm, void *ptr)
+{
+ /*
+ * E. G.: This code provides kernel dump because of rescheduling while
+ * atomic. As workaround, this code was commented. In this case we will
+ * have memory leaks for instrumented process, but instrumentation
+ * process should functionate correctly. Planned that good solution for
+ * this problem will be done during redesigning KProbe for improving
+ * supportability and performance.
+ */
+#if 0
+ struct task_struct *task = sm->data;
+
+ mm = get_task_mm(task);
+ if (mm) {
+ down_write(&mm->mmap_sem);
+ do_munmap(mm, (unsigned long)(ptr), PAGE_SIZE);
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+#endif
+ /* FIXME: implement the removal of memory for task */
+}
+
+/**
+ * @brief Create slot_manager struct for US
+ *
+ * @param task Pointer to the task_struct struct
+ * @return Pointer to the created slot_manager struct
+ */
+struct slot_manager *create_sm_us(struct task_struct *task)
+{
+ struct slot_manager *sm = kmalloc(sizeof(*sm), GFP_ATOMIC);
+
+ if (sm == NULL)
+ return NULL;
+
+ sm->slot_size = UPROBES_TRAMP_LEN;
+ sm->alloc = sm_alloc_us;
+ sm->free = sm_free_us;
+ INIT_HLIST_HEAD(&sm->page_list);
+ sm->data = task;
+
+ return sm;
+}
+
+/**
+ * @brief Remove slot_manager struct for US
+ *
+ * @param sm remove object
+ * @return Void
+ */
+void free_sm_us(struct slot_manager *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (!hlist_empty(&sm->page_list)) {
+ printk(KERN_WARNING "SWAP US_MANAGER: Error! Slot manager is "
+ "not empty!\n");
+ return;
+ }
+
+ kfree(sm);
+}
--- /dev/null
+/**
+ * @file us_manager/us_slot_manager.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ * Copyright (C) Samsung Electronics, 2013
+ */
+
+
+#ifndef _US_SLOT_MANAGER_H
+#define _US_SLOT_MANAGER_H
+
+struct task_struct;
+struct slot_manager;
+
+struct slot_manager *create_sm_us(struct task_struct *task);
+void free_sm_us(struct slot_manager *sm);
+
+#endif /* _US_SLOT_MANAGER_H */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2017
+ *
+ * 2017 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/rwsem.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <kprobe/swap_kprobes_deps.h> // for swap_hlist_for_each_entry
+#include "usm_hook.h"
+
+
+static HLIST_HEAD(hook_head);
+static DECLARE_RWSEM(hook_sem);
+
+
+int usm_hook_reg(struct usm_hook *hook)
+{
+ if (!try_module_get(hook->owner))
+ return -ENODEV;
+
+ INIT_HLIST_NODE(&hook->node);
+
+ down_write(&hook_sem);
+ hlist_add_head(&hook->node, &hook_head);
+ up_write(&hook_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usm_hook_reg);
+
+void usm_hook_unreg(struct usm_hook *hook)
+{
+ down_write(&hook_sem);
+ hlist_del(&hook->node);
+ up_write(&hook_sem);
+
+ module_put(hook->owner);
+}
+EXPORT_SYMBOL_GPL(usm_hook_unreg);
+
+void usm_hook_mmap(struct sspt_proc *proc, struct vm_area_struct *vma)
+{
+ struct usm_hook *hook;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ down_read(&hook_sem);
+ swap_hlist_for_each_entry(hook, node, &hook_head, node) {
+ if (hook->mmap)
+ hook->mmap(proc, vma);
+ }
+ up_read(&hook_sem);
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2017
+ *
+ * 2017 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_USM_HOOK_H
+#define _SWAP_USM_HOOK_H
+
+
+#include <linux/list.h>
+
+
+struct module;
+struct sspt_proc;
+struct vm_area_struct;
+
+
+struct usm_hook {
+ struct hlist_node node;
+ struct module *owner;
+
+ /*
+ * mmap hook called only for vma which we can instrument
+ * (e.g. vma->vm_file is already validate)
+ */
+ void (*mmap)(struct sspt_proc *proc, struct vm_area_struct *vma);
+};
+
+
+int usm_hook_reg(struct usm_hook *hook);
+void usm_hook_unreg(struct usm_hook *hook);
+
+
+/* private interface */
+void usm_hook_mmap(struct sspt_proc *proc, struct vm_area_struct *vma);
+
+
+#endif /* _SWAP_USM_HOOK_H */
+
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../buffer/Module.symvers \
+ $(src)/../driver/Module.symvers
+
+obj-m := swap_writer.o
+swap_writer-y := swap_writer_module.o \
+ debugfs_writer.o \
+ event_filter.o \
+ swap_msg.o
+
--- /dev/null
+/**
+ * writer/debugfs_writer.c
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Writer debugfs implementation.
+ */
+
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <master/swap_debugfs.h>
+#include <master/swap_initializer.h>
+#include "swap_msg.h"
+#include "event_filter.h"
+
+
+/* ============================================================================
+ * === BUFFER ===
+ * ============================================================================
+ */
+static char *common_buf;
+enum { subbuf_size = 8*1024 };
+enum { common_buf_size = subbuf_size * NR_CPUS };
+
+static int init_buffer(void)
+{
+ common_buf = vmalloc(common_buf_size);
+
+ return common_buf ? 0 : -ENOMEM;
+}
+
+static void exit_buffer(void)
+{
+ vfree(common_buf);
+ common_buf = NULL;
+}
+
+static void *get_current_buf(void)
+{
+ return common_buf + subbuf_size * get_cpu();
+}
+
+static void put_current_buf(void)
+{
+ put_cpu();
+}
+
+
+
+
+
+/* ============================================================================
+ * === FOPS_RAW ===
+ * ============================================================================
+ */
+static ssize_t write_raw(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ void *buf;
+
+ if (count > subbuf_size)
+ return -EINVAL;
+
+ buf = get_current_buf();
+ if (copy_from_user(buf, user_buf, count)) {
+ ret = -EFAULT;
+ goto put_buf;
+ }
+
+ ret = swap_msg_raw(buf, count);
+
+put_buf:
+ put_current_buf();
+ return ret;
+}
+
+static const struct file_operations fops_raw = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .write = write_raw,
+ .llseek = default_llseek
+};
+
+
+
+
+
+/* ============================================================================
+ * === FOPS_AVAILABLE_FILTERS ===
+ * ============================================================================
+ */
+struct read_buf {
+ char *begin;
+ char *ptr;
+ char *end;
+};
+
+static void func_for_read(struct ev_filter *f, void *data)
+{
+ struct read_buf *rbuf = (struct read_buf *)data;
+ int len = strlen(f->name);
+
+ if (rbuf->end - rbuf->ptr < len + 2)
+ return;
+
+ if (rbuf->ptr != rbuf->begin) {
+ *rbuf->ptr = ' ';
+ ++rbuf->ptr;
+ }
+
+ memcpy(rbuf->ptr, f->name, len);
+ rbuf->ptr += len;
+}
+
+static ssize_t read_af(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[512];
+ struct read_buf rbuf = {
+ .begin = buf,
+ .ptr = buf,
+ .end = buf + sizeof(buf)
+ };
+
+ event_filter_on_each(func_for_read, (void *)&rbuf);
+
+ *rbuf.ptr = '\n';
+ ++rbuf.ptr;
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ rbuf.begin, rbuf.ptr - rbuf.begin);
+}
+
+static const struct file_operations fops_available_filters = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .read = read_af,
+ .llseek = default_llseek
+};
+
+
+
+
+
+/* ============================================================================
+ * === FOPS_FILTER ===
+ * ============================================================================
+ */
+static ssize_t read_filter(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *name = event_filter_get();
+ int len = strlen(name);
+ char *buf;
+ ssize_t ret;
+
+ buf = kmalloc(len + 2, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ memcpy(buf, name, len);
+ buf[len] = '\0';
+ buf[len + 1] = '\n';
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len + 2);
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t write_filter(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ enum { len = 32 };
+ char buf[len], name[len];
+ size_t buf_size;
+ ssize_t ret;
+
+ buf_size = min(count, (size_t)(len - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[len - 1] = '\0';
+ ret = sscanf(buf, "%31s", name);
+ if (ret != 1)
+ return -EINVAL;
+
+ ret = event_filter_set(name);
+ if (ret)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations fops_filter = {
+ .owner = THIS_MODULE,
+ .open = swap_init_simple_open,
+ .release = swap_init_simple_release,
+ .read = read_filter,
+ .write = write_filter,
+ .llseek = default_llseek
+};
+
+
+
+
+
+/* ============================================================================
+ * === INIT/EXIT ===
+ * ============================================================================
+ */
+static struct dentry *writer_dir;
+
+/**
+ * @brief Removes writer debugfs.
+ *
+ * @return Void.
+ */
+void exit_debugfs_writer(void)
+{
+ if (writer_dir)
+ debugfs_remove_recursive(writer_dir);
+
+ writer_dir = NULL;
+
+ exit_buffer();
+}
+
+/**
+ * @brief Initializes writer debugfs.
+ *
+ * @return 0 on success, error code on error.
+ */
+int init_debugfs_writer(void)
+{
+ int ret;
+ struct dentry *swap_dir, *dentry;
+
+ ret = init_buffer();
+ if (ret)
+ return ret;
+
+ swap_dir = swap_debugfs_getdir();
+ if (swap_dir == NULL)
+ return -ENOENT;
+
+ writer_dir = swap_debugfs_create_dir("writer", swap_dir);
+ if (writer_dir == NULL)
+ return -ENOMEM;
+
+ dentry = swap_debugfs_create_file("raw", 0600, writer_dir,
+ NULL, &fops_raw);
+ if (dentry == NULL)
+ goto fail;
+
+ dentry = swap_debugfs_create_file("available_filters", 0600, writer_dir,
+ NULL, &fops_available_filters);
+ if (dentry == NULL)
+ goto fail;
+
+ dentry = swap_debugfs_create_file("filter", 0600, writer_dir,
+ NULL, &fops_filter);
+ if (dentry == NULL)
+ goto fail;
+
+ return 0;
+
+fail:
+ exit_debugfs_writer();
+ return -ENOMEM;
+}
--- /dev/null
+/**
+ * @file writer/debugfs_writer.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Writer debugfs interface declaration.
+ */
+
+#ifndef _DEBUGFS_WRITER_H
+#define _DEBUGFS_WRITER_H
+
+int init_debugfs_writer(void);
+void exit_debugfs_writer(void);
+
+#endif /* _DEBUGFS_WRITER_H */
--- /dev/null
+/**
+ * writer/event_filter.c
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Events filter.
+ */
+
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include "event_filter.h"
+
+
+static LIST_HEAD(filter_list);
+
+static int func_none(struct task_struct *task)
+{
+ return 1;
+}
+
+static struct ev_filter filter_none = {
+ .name = "all",
+ .filter = func_none
+};
+
+static struct ev_filter *filter_current = &filter_none;
+
+int check_event(struct task_struct *task)
+{
+ return filter_current->filter(task);
+}
+EXPORT_SYMBOL_GPL(check_event);
+
+static struct ev_filter *event_filter_find(const char *name)
+{
+ struct ev_filter *f, *tmp;
+
+ list_for_each_entry_safe(f, tmp, &filter_list, list) {
+ if (strcmp(f->name, name) == 0)
+ return f;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Registers event filter.
+ *
+ * @param f Pointer to the event filter.
+ * @return 0 on success, error code on error.
+ */
+int event_filter_register(struct ev_filter *f)
+{
+ if (event_filter_find(f->name))
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&f->list);
+ list_add(&f->list, &filter_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(event_filter_register);
+
+/**
+ * @brief Unregisters event filter.
+ *
+ * @param f Pointer to the event filter.
+ * @return Void.
+ */
+void event_filter_unregister(struct ev_filter *f)
+{
+ struct ev_filter *filter, *tmp;
+
+ if (filter_current == f)
+ filter_current = &filter_none;
+
+ list_for_each_entry_safe(filter, tmp, &filter_list, list) {
+ if (filter == f) {
+ list_del(&filter->list);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(event_filter_unregister);
+
+/**
+ * @brief Sets event filter by its name.
+ *
+ * @param name Filter name.
+ * @return 0 on success, error code on error.
+ */
+int event_filter_set(const char *name)
+{
+ struct ev_filter *f;
+
+ f = event_filter_find(name);
+ if (f == NULL)
+ return -EINVAL;
+
+ filter_current = f;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(event_filter_set);
+
+/**
+ * @brief Gets filter name.
+ *
+ * @return Pointer to the filter name string.
+ */
+const char *event_filter_get(void)
+{
+ return filter_current->name;
+}
+
+/**
+ * @brief Runs specified callback for each filter in list.
+ *
+ * @param func Specified callback.
+ * @param data Pointer to the data passed to the callback.
+ * @return Void.
+ */
+void event_filter_on_each(void (*func)(struct ev_filter *, void *),
+ void *data)
+{
+ struct ev_filter *f, *tmp;
+
+ list_for_each_entry_safe(f, tmp, &filter_list, list)
+ func(f, data);
+}
+
+/**
+ * @brief Initializes event filter.
+ *
+ * @return Initialization result.
+ */
+int event_filter_init(void)
+{
+ return event_filter_register(&filter_none);
+}
+
+/**
+ * @brief Uninitializes event filter.
+ *
+ * @return Void.
+ */
+void event_filter_exit(void)
+{
+ event_filter_unregister(&filter_none);
+}
--- /dev/null
+/**
+ * @file writer/event_filter.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Event filter interface declaration.
+ */
+
+
+#ifndef _EVENT_FILTER_H
+#define _EVENT_FILTER_H
+
+
+#include <linux/list.h>
+
+struct task_struct;
+
+/**
+ * @struct ev_filter
+ * @bref Event filter structure.
+ */
+struct ev_filter {
+ struct list_head list; /**< Filter list head. */
+ char *name; /**< Filter name. */
+ int (*filter)(struct task_struct *); /**< Filter function. */
+};
+
+
+int check_event(struct task_struct *task);
+
+int event_filter_register(struct ev_filter *f);
+void event_filter_unregister(struct ev_filter *f);
+int event_filter_set(const char *name);
+const char *event_filter_get(void);
+
+void event_filter_on_each(void (*func)(struct ev_filter *, void *),
+ void *data);
+
+int event_filter_init(void);
+void event_filter_exit(void);
+
+#endif /* _EVENT_FILTER_H */
--- /dev/null
+/**
+ * @file writer/kernel_operations.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Writer kernel operations.
+ */
+
+/* Kernel functions wrap */
+
+#ifndef __KERNEL_OPERATIONS_H__
+#define __KERNEL_OPERATIONS_H__
+
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <asm/ptrace.h>
+
+
+/* ARCH-DEPENDED OPERATIONS */
+
+
+/* Regs manipulations */
+#if defined(CONFIG_ARM)
+
+#define get_regs_ret_func(regs) (regs->ARM_lr) /**< Get lr reg. */
+#define get_regs_ret_val(regs) (regs->ARM_r0) /**< Get ret val. */
+#define get_regs_stack_ptr(regs) (regs->ARM_sp) /**< Get stack pointer. */
+
+#elif defined(CONFIG_X86_32)
+
+#define get_regs_ret_val(regs) (regs->ax) /**< Get ret val. */
+#define get_regs_stack_ptr(regs) (regs->sp) /**< Get stack pointer. */
+
+static inline u32 get_regs_ret_func(struct pt_regs *regs)
+{
+ u32 *sp, addr = 0;
+
+ if (user_mode(regs)) {
+ sp = (u32 *)regs->sp;
+ if (get_user(addr, sp))
+ pr_info("failed to dereference a pointer, sp=%p, "
+ "pc=%lx\n", sp, regs->ip - 1);
+ } else {
+ sp = (u32 *)kernel_stack_pointer(regs);
+ addr = *sp;
+ }
+
+ return addr;
+}
+
+#elif defined(CONFIG_ARM64)
+
+static inline u64 get_regs_ret_func(struct pt_regs *regs)
+{
+ if (compat_user_mode(regs))
+ return regs->compat_lr;
+ else
+ return regs->regs[30];
+}
+
+#endif /* CONFIG_arch */
+
+#endif /* __KERNEL_OPERATIONS_H__ */
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/atomic.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <buffer/swap_buffer_module.h>
+#include <swap-asm/swap_kprobes.h>
+#include <swap-asm/swap_uprobes.h>
+#include "swap_msg.h"
+
+
+#define MSG_PREFIX "[SWAP_MSG] "
+
+
+/* simple buffer */
+struct sb_struct {
+ size_t subbuf_size;
+
+ size_t count;
+ void *data;
+};
+
+static int sb_init(struct sb_struct *sb, size_t count, size_t subbuf_size)
+{
+ sb->data = vmalloc(count * subbuf_size);
+ if (!sb->data)
+ return -ENOMEM;
+
+ sb->count = count;
+ sb->subbuf_size = subbuf_size;
+
+ return 0;
+}
+
+static void sb_uninit(struct sb_struct *sb)
+{
+ vfree(sb->data);
+}
+
+static void *sb_data(struct sb_struct *sb, size_t idx)
+{
+ return sb->data + sb->subbuf_size * idx;
+}
+
+static size_t sb_idx(struct sb_struct *sb, void *data)
+{
+ return (data - sb->data) / sb->subbuf_size;
+}
+
+static bool sb_contains(struct sb_struct *sb, void *data)
+{
+ void *begin = sb->data;
+ void *end = sb->data + sb->count * sb->subbuf_size;
+
+ return data >= begin && data < end;
+}
+
+static size_t sb_count(struct sb_struct *sb)
+{
+ return sb->count;
+}
+
+
+/* pool buffer */
+struct pb_struct {
+ spinlock_t lock;
+ size_t free_first;
+ size_t free_count;
+
+ struct sb_struct buf;
+};
+
+static void *pb_data(struct pb_struct *pb, size_t idx)
+{
+ return sb_data(&pb->buf, idx);
+}
+
+static size_t pb_idx(struct pb_struct *pb, void *data)
+{
+ return sb_idx(&pb->buf, data);
+}
+
+static void pb_val_set(struct pb_struct *pb, size_t idx, size_t val)
+{
+ *(size_t *)pb_data(pb, idx) = val;
+}
+
+static size_t pb_val_get(struct pb_struct *pb, size_t idx)
+{
+ return *(size_t *)pb_data(pb, idx);
+}
+
+static int pb_init(struct pb_struct *pb, size_t count, size_t subbuf_size)
+{
+ int ret;
+ size_t idx;
+
+ ret = sb_init(&pb->buf, count, subbuf_size);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&pb->lock);
+ pb->free_first = 0;
+ pb->free_count = count;
+
+ for (idx = 0; idx < count; ++idx)
+ pb_val_set(pb, idx, idx + 1);
+
+ return 0;
+}
+
+static void pb_uninit(struct pb_struct *pb)
+{
+ WARN(sb_count(&pb->buf) != pb->free_count,
+ "count=%zu free_conut=%zu\n", sb_count(&pb->buf), pb->free_count);
+
+ sb_uninit(&pb->buf);
+}
+
+static void *pb_buf_get(struct pb_struct *pb)
+{
+ void *data;
+ unsigned long flags;
+
+ if (!pb->free_count)
+ return NULL;
+
+ spin_lock_irqsave(&pb->lock, flags);
+ data = pb_data(pb, pb->free_first);
+ pb->free_first = pb_val_get(pb, pb->free_first);
+ --pb->free_count;
+ spin_unlock_irqrestore(&pb->lock, flags);
+
+ return data;
+}
+
+static void pb_buf_put(struct pb_struct *pb, void *data)
+{
+ unsigned long flags;
+ size_t idx = pb_idx(pb, data);
+
+ spin_lock_irqsave(&pb->lock, flags);
+ pb_val_set(pb, idx, pb->free_first);
+ pb->free_first = idx;
+ ++pb->free_count;
+ spin_unlock_irqrestore(&pb->lock, flags);
+}
+
+
+struct swap_msg {
+ u32 msg_id;
+ u32 seq_num;
+ u64 time;
+ u32 len;
+ char payload[0];
+} __packed;
+
+
+static struct sb_struct cpu_buf;
+static struct pb_struct pool_buffer;
+static atomic_t seq_num = ATOMIC_INIT(-1);
+static atomic_t discarded = ATOMIC_INIT(0);
+
+
+int swap_msg_init(void)
+{
+ int ret;
+
+ ret = sb_init(&cpu_buf, NR_CPUS, SWAP_MSG_BUF_SIZE);
+ if (ret) {
+ pr_err(MSG_PREFIX "Cannot init cpu_buf, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = pb_init(&pool_buffer, NR_CPUS * 32, SWAP_MSG_BUF_SIZE);
+ if (ret) {
+ sb_uninit(&cpu_buf);
+ pr_err(MSG_PREFIX "Cannot init ring_buffer, ret=%d\n", ret);
+ }
+
+ return ret;
+}
+
+void swap_msg_exit(void)
+{
+ pb_uninit(&pool_buffer);
+ sb_uninit(&cpu_buf);
+}
+
+void swap_msg_seq_num_reset(void)
+{
+ atomic_set(&seq_num, -1);
+}
+EXPORT_SYMBOL_GPL(swap_msg_seq_num_reset);
+
+void swap_msg_discard_reset(void)
+{
+ atomic_set(&discarded, 0);
+}
+EXPORT_SYMBOL_GPL(swap_msg_discard_reset);
+
+int swap_msg_discard_get(void)
+{
+ return atomic_read(&discarded);
+}
+EXPORT_SYMBOL_GPL(swap_msg_discard_get);
+
+
+u64 swap_msg_timespec2time(struct timespec *ts)
+{
+ return ((u64)ts->tv_nsec) << 32 | ts->tv_sec;
+}
+
+
+
+
+
+struct swap_msg *swap_msg_get(enum swap_msg_id id)
+{
+ struct swap_msg *m;
+
+ m = pb_buf_get(&pool_buffer);
+ if (!m)
+ m = sb_data(&cpu_buf, get_cpu());
+
+ m->msg_id = (u32)id;
+ m->seq_num = atomic_inc_return(&seq_num);
+ m->time = swap_msg_current_time();
+
+ return m;
+}
+EXPORT_SYMBOL_GPL(swap_msg_get);
+
+static int __swap_msg_flush(struct swap_msg *m, size_t size, bool wakeup)
+{
+ if (unlikely(size >= SWAP_MSG_PAYLOAD_SIZE))
+ return -ENOMEM;
+
+ m->len = size;
+
+ if (swap_buffer_write(m, SWAP_MSG_PRIV_DATA + size, wakeup) !=
+ (SWAP_MSG_PRIV_DATA + size)) {
+ atomic_inc(&discarded);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int swap_msg_flush(struct swap_msg *m, size_t size)
+{
+ return __swap_msg_flush(m, size, true);
+}
+EXPORT_SYMBOL_GPL(swap_msg_flush);
+
+int swap_msg_flush_wakeupoff(struct swap_msg *m, size_t size)
+{
+ return __swap_msg_flush(m, size, false);
+}
+EXPORT_SYMBOL_GPL(swap_msg_flush_wakeupoff);
+
+void swap_msg_put(struct swap_msg *m)
+{
+ if (unlikely(sb_contains(&cpu_buf, m)))
+ put_cpu();
+ else
+ pb_buf_put(&pool_buffer, m);
+}
+EXPORT_SYMBOL_GPL(swap_msg_put);
+
+
+
+
+
+
+static unsigned long get_arg(struct pt_regs *regs, unsigned long n)
+{
+ return user_mode(regs) ?
+ swap_get_uarg(regs, n) : /* US argument */
+ swap_get_sarg(regs, n); /* sys_call argument */
+}
+
+int swap_msg_pack_args(char *buf, int len,
+ const char *fmt, struct pt_regs *regs)
+{
+ char *buf_old = buf;
+ u32 *tmp_u32;
+ u64 *tmp_u64;
+ int i, /* the index of the argument */
+ fmt_i, /* format index */
+ fmt_len; /* the number of parameters, in format */
+
+ fmt_len = strlen(fmt);
+
+ for (i = 0, fmt_i = 0; fmt_i < fmt_len; ++i, ++fmt_i) {
+ if (len < 2)
+ return -ENOMEM;
+
+ *buf = fmt[fmt_i];
+ buf += 1;
+ len -= 1;
+
+ switch (fmt[fmt_i]) {
+ case 'b': /* 1 byte(bool) */
+ *buf = (char)!!get_arg(regs, i);
+ buf += 1;
+ len -= 1;
+ break;
+ case 'c': /* 1 byte(char) */
+ *buf = (char)get_arg(regs, i);
+ buf += 1;
+ len -= 1;
+ break;
+ case 'f': /* 4 byte(float) */
+#ifdef CONFIG_ARM64
+ if (len < 4)
+ return -ENOMEM;
+
+ tmp_u32 = (u32 *)buf;
+ *tmp_u32 = swap_get_float(regs, i);
+ buf += 4;
+ len -= 4;
+ break;
+#endif /* CONFIG_ARM64 */
+ /* For others case f == d */
+ case 'd': /* 4 byte(int) */
+ if (len < 4)
+ return -ENOMEM;
+ tmp_u32 = (u32 *)buf;
+ *tmp_u32 = (u32)get_arg(regs, i);
+ buf += 4;
+ len -= 4;
+ break;
+ case 'x': /* 8 byte(long) */
+ case 'p': /* 8 byte(pointer) */
+ if (len < 8)
+ return -ENOMEM;
+ tmp_u64 = (u64 *)buf;
+ *tmp_u64 = (u64)get_arg(regs, i);
+ buf += 8;
+ len -= 8;
+ break;
+ case 'w': /* 8 byte(double) */
+ if (len < 8)
+ return -ENOMEM;
+ tmp_u64 = (u64 *)buf;
+#ifdef CONFIG_ARM64
+ *tmp_u64 = swap_get_double(regs, i);
+#else /* CONFIG_ARM64 */
+ *tmp_u64 = get_arg(regs, i);
+ ++i;
+ *tmp_u64 |= (u64)get_arg(regs, i) << 32;
+#endif /* CONFIG_ARM64 */
+ buf += 8;
+ len -= 8;
+ break;
+ case 's': /* string end with '\0' */
+ {
+ enum { max_str_len = 512 };
+ const char __user *user_s;
+ int len_s, ret;
+
+ user_s = (const char __user *)get_arg(regs, i);
+ len_s = strnlen_user(user_s, max_str_len);
+ if (len < len_s)
+ return -ENOMEM;
+
+ ret = strncpy_from_user(buf, user_s, len_s);
+ if (ret < 0)
+ return -EFAULT;
+
+ buf[ret] = '\0';
+
+ buf += ret + 1;
+ len -= ret + 1;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return buf - buf_old;
+}
+EXPORT_SYMBOL_GPL(swap_msg_pack_args);
+
+int swap_msg_pack_ret_val(char *buf, int len,
+ char ret_type, struct pt_regs *regs)
+{
+ const char *buf_old = buf;
+ u32 *tmp_u32;
+ u64 *tmp_u64;
+
+ *buf = ret_type;
+ ++buf;
+
+ switch (ret_type) {
+ case 'b': /* 1 byte(bool) */
+ if (len < 1)
+ return -ENOMEM;
+ *buf = (char)!!regs_return_value(regs);
+ ++buf;
+ break;
+ case 'c': /* 1 byte(char) */
+ if (len < 1)
+ return -ENOMEM;
+ *buf = (char)regs_return_value(regs);
+ ++buf;
+ break;
+ case 'd': /* 4 byte(int) */
+ if (len < 4)
+ return -ENOMEM;
+ tmp_u32 = (u32 *)buf;
+ *tmp_u32 = regs_return_value(regs);
+ buf += 4;
+ break;
+ case 'x': /* 8 byte(long) */
+ case 'p': /* 8 byte(pointer) */
+ if (len < 8)
+ return -ENOMEM;
+ tmp_u64 = (u64 *)buf;
+ *tmp_u64 = (u64)regs_return_value(regs);
+ buf += 8;
+ break;
+ case 's': /* string end with '\0' */
+ {
+ enum { max_str_len = 512 };
+ const char __user *user_s;
+ int len_s, ret;
+
+ user_s = (const char __user *)regs_return_value(regs);
+ len_s = strnlen_user(user_s, max_str_len);
+ if (len < len_s)
+ return -ENOMEM;
+
+ ret = strncpy_from_user(buf, user_s, len_s);
+ if (ret < 0)
+ return -EFAULT;
+
+ buf[ret] = '\0';
+ buf += ret + 1;
+ }
+ break;
+ case 'n':
+ case 'v':
+ break;
+ case 'f': /* 4 byte(float) */
+ if (len < 4)
+ return -ENOMEM;
+ tmp_u32 = (u32 *)buf;
+ *tmp_u32 = swap_get_urp_float(regs);
+ buf += 4;
+ break;
+ case 'w': /* 8 byte(double) */
+ if (len < 8)
+ return -ENOMEM;
+ tmp_u64 = (u64 *)buf;
+ *tmp_u64 = swap_get_urp_double(regs);
+ buf += 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return buf - buf_old;
+}
+EXPORT_SYMBOL_GPL(swap_msg_pack_ret_val);
+
+
+
+
+
+int swap_msg_raw(void *data, size_t size)
+{
+ struct swap_msg *m = (struct swap_msg *)data;
+
+ if (sizeof(*m) > size) {
+ pr_err(MSG_PREFIX "ERROR: message RAW small size=%zu\n", size);
+ return -EINVAL;
+ }
+
+ if (m->len + sizeof(*m) != size) {
+ pr_err(MSG_PREFIX "ERROR: message RAW wrong format\n");
+ return -EINVAL;
+ }
+
+ m->seq_num = atomic_inc_return(&seq_num);
+
+ /* TODO: What should be returned?! When message was discarded. */
+ if (swap_buffer_write(m, size, true) != size)
+ atomic_inc(&discarded);
+
+ return size;
+}
+EXPORT_SYMBOL_GPL(swap_msg_raw);
+
+void swap_msg_error(const char *fmt, ...)
+{
+ int ret;
+ struct swap_msg *m;
+ void *p;
+ size_t size;
+ va_list args;
+
+ m = swap_msg_get(MSG_ERROR);
+ p = swap_msg_payload(m);
+ size = swap_msg_size(m);
+
+ va_start(args, fmt);
+ ret = vsnprintf(p, size, fmt, args);
+ va_end(args);
+
+ if (ret <= 0) {
+ pr_err(MSG_PREFIX "ERROR: msg error packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret + 1);
+
+put_msg:
+ swap_msg_put(m);
+}
+EXPORT_SYMBOL_GPL(swap_msg_error);
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_MSG_H
+#define _SWAP_MSG_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0)
+
+#include <linux/ktime.h> /* Needed by timekeeping.h */
+#include <linux/timekeeping.h> /* Now getnstimeofday() is here */
+
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
+
+#include <linux/time.h>
+
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
+
+enum swap_msg_id {
+ MSG_PROC_INFO = 0x0001,
+ MSG_TERMINATE = 0x0002,
+ MSG_ERROR = 0x0003,
+ MSG_SAMPLE = 0x0004,
+ MSG_FUNCTION_ENTRY = 0x0008,
+ MSG_FUNCTION_EXIT = 0x0009,
+ MSG_SYSCALL_ENTRY = 0x000a,
+ MSG_SYSCALL_EXIT = 0x000b,
+ MSG_FILE_FUNCTION_ENTRY = 0x000c,
+ MSG_FILE_FUNCTION_EXIT = 0x000d,
+ MSG_PROCESS_STATUS_INFO = 0x000e,
+ MSG_CONTEXT_SWITCH_ENTRY = 0x0010,
+ MSG_CONTEXT_SWITCH_EXIT = 0x0011,
+ MSG_PROC_MAP = 0x0012,
+ MSG_PROC_UNMAP = 0x0013,
+ MSG_PROC_COMM = 0x0014,
+ MSG_WEB_PROFILING = 0x0015,
+ MSG_NSP = 0x0019,
+ MSG_WSP = 0x001a,
+ MSG_FBI = 0x0020
+};
+
+enum {
+ SWAP_MSG_PRIV_DATA = 20,
+ SWAP_MSG_BUF_SIZE = 32 * 1024,
+ SWAP_MSG_PAYLOAD_SIZE = SWAP_MSG_BUF_SIZE - SWAP_MSG_PRIV_DATA
+};
+
+
+struct swap_msg;
+
+
+static inline u64 swap_msg_spec2time(struct timespec *ts)
+{
+ return ((u64)ts->tv_nsec) << 32 | ts->tv_sec;
+}
+
+static inline u64 swap_msg_current_time(void)
+{
+ struct timespec ts;
+ getnstimeofday(&ts);
+ return swap_msg_spec2time(&ts);
+}
+
+struct swap_msg *swap_msg_get(enum swap_msg_id id);
+int swap_msg_flush(struct swap_msg *m, size_t size);
+int swap_msg_flush_wakeupoff(struct swap_msg *m, size_t size);
+void swap_msg_put(struct swap_msg *m);
+
+static inline void *swap_msg_payload(struct swap_msg *m)
+{
+ return (void *)m + SWAP_MSG_PRIV_DATA;
+}
+
+static inline size_t swap_msg_size(struct swap_msg *m)
+{
+ return (size_t)SWAP_MSG_PAYLOAD_SIZE;
+}
+
+
+int swap_msg_pack_args(char *buf, int len,
+ const char *fmt, struct pt_regs *regs);
+int swap_msg_pack_ret_val(char *buf, int len,
+ char ret_type, struct pt_regs *regs);
+
+
+int swap_msg_raw(void *buf, size_t size);
+void swap_msg_error(const char *fmt, ...);
+
+void swap_msg_seq_num_reset(void);
+void swap_msg_discard_reset(void);
+int swap_msg_discard_get(void);
+
+int swap_msg_init(void);
+void swap_msg_exit(void);
+
+
+#endif /* _SWAP_MSG_H */
--- /dev/null
+/**
+ * @file writer/swap_writer_errors.h
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Writer module error codes.
+ */
+
+#ifndef __SWAP_WRITER_ERRORS_H__
+#define __SWAP_WRITER_ERRORS_H__
+
+/**
+ * @enum _swap_writer_errors
+ * Error codes.
+ */
+enum _swap_writer_errors {
+ E_SW_SUCCESS = 0 /**< Success. */
+};
+
+#endif /* __SWAP_WRITER_ERRORS_H__ */
--- /dev/null
+/**
+ * writer/swap_writer_module.c
+ * @author Alexander Aksenov <a.aksenov@samsung.com>
+ * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
+ * @author Vyacheslav Cherkashin
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2013
+ *
+ * @section DESCRIPTION
+ *
+ * Packing and writing data.
+ */
+
+
+#include <linux/module.h>
+#include <master/swap_initializer.h>
+#include "swap_msg.h"
+#include "event_filter.h"
+#include "debugfs_writer.h"
+
+
+static int core_init(void)
+{
+ int ret;
+
+ ret = swap_msg_init();
+ if (ret)
+ return ret;
+
+ ret = event_filter_init();
+ if (ret)
+ swap_msg_exit();
+
+ return ret;
+}
+
+static void core_exit(void)
+{
+ event_filter_exit();
+ swap_msg_exit();
+}
+
+SWAP_LIGHT_INIT_MODULE(NULL, core_init, core_exit,
+ init_debugfs_writer, exit_debugfs_writer);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP Writer module");
+MODULE_AUTHOR("Cherkashin V., Aksenov A.S.");
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_wsp.o
+swap_wsp-y := wsp_module.o \
+ wsp_msg.o \
+ wsp_debugfs.o \
+ wsp.o \
+ wsp_res.o
--- /dev/null
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <linux/string.h>
+#include <us_manager/sspt/sspt.h>
+#include <us_manager/probes/probe_info_new.h>
+#include "wsp.h"
+#include "wsp_res.h"
+#include "wsp_msg.h"
+
+struct wsp_probe {
+ const char *name;
+ struct probe_new probe;
+};
+
+struct wsp_bin {
+ const char *name;
+ unsigned long cnt;
+ struct wsp_probe *probe_array;
+};
+
+static char *webapp_path;
+static char *chromium_path;
+
+#define WSP_PROBE_MAKE(_name_, _offset_, _desc_) \
+{ \
+ .name = (_name_), \
+ .probe.offset = (_offset_), \
+ .probe.desc = (_desc_) \
+}
+
+/*
+ * res_request
+ */
+/* blink::ResourceLoader.m_request.m_url */
+#define URL_OFFSET 84
+/* base::String.m_impl.m_ptr */
+#define URL_LEN_OFFSET 4
+#define URL_DATA_OFFSET 12
+
+static char *path_get_from_object(unsigned long ptr)
+{
+ char *path;
+ unsigned long url, len, ret;
+
+ get_user(url, (unsigned long __user *)(ptr + URL_OFFSET));
+ get_user(len, (unsigned long __user *)(url + URL_LEN_OFFSET));
+ path = kzalloc(len + 1, GFP_KERNEL);
+ if (!path)
+ return NULL;
+
+ ret = copy_from_user(path,
+ (const void __user *)(url + URL_DATA_OFFSET),
+ len);
+ if (ret) {
+ kfree(path);
+ path = NULL;
+ } else {
+ path[len] = '\0';
+ }
+
+ return path;
+}
+
+static int res_request_handle(struct uprobe *p, struct pt_regs *regs)
+{
+ unsigned long ptr;
+ char *path;
+
+ ptr = (unsigned long)swap_get_uarg(regs, 0);
+ path = path_get_from_object(ptr);
+ if (path) {
+ int id = wsp_resource_data_add(ptr, path);
+ if (id >= 0)
+ wsp_msg(WSP_RES_LOAD_BEGIN, id, path);
+ }
+
+ return 0;
+}
+
+static struct probe_desc res_request = MAKE_UPROBE(res_request_handle);
+
+/*
+ * res_finish
+ */
+static int res_finish_ehandle(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int id;
+ unsigned long ptr = (unsigned long)swap_get_uarg(regs, 0);
+
+ id = wsp_resource_data_id(ptr);
+ if (id >= 0) {
+ *(unsigned long *)ri->data = ptr;
+ wsp_msg(WSP_RES_PROC_BEGIN, id, NULL);
+ }
+
+ return 0;
+}
+
+static int res_finish_rhandle(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ int id;
+ unsigned long ptr;
+
+ ptr = *(unsigned long *)ri->data;
+ id = wsp_resource_data_id(ptr);
+ if (id >= 0) {
+ wsp_msg(WSP_RES_PROC_END, id, NULL);
+ wsp_msg(WSP_RES_LOAD_END, id, NULL);
+ wsp_resource_data_del(ptr);
+ }
+
+ return 0;
+}
+
+static struct probe_desc res_finish =
+ MAKE_URPROBE(res_finish_ehandle, res_finish_rhandle,
+ sizeof(unsigned long));
+
+/*
+ * redraw
+ */
+static int redraw_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ wsp_msg(WSP_DRAW_BEGIN, 0, NULL);
+
+ return 0;
+}
+
+static int redraw_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ wsp_msg(WSP_DRAW_END, 0, NULL);
+
+ return 0;
+}
+
+static struct probe_desc redraw = MAKE_URPROBE(redraw_eh, redraw_rh, 0);
+
+/* blink::ResourceLoader::start() */
+#define RES_REQ "_ZN5blink14ResourceLoader5startEv"
+/* blink::ResourceLoader::didFinishLoading(WebURLLoader*, double , int64_t) */
+#define RES_FINISH "_ZN5blink14ResourceLoader16didFinishLoadingEPNS_12WebURLLoaderEdx"
+
+/* content::RenderWidget::DidCommitAndDrawCompositorFrame */
+#define REDRAW "_ZN7content23CompositorOutputSurface11SwapBuffersEPN2cc15CompositorFrameE"
+
+static struct wsp_probe __probe_array[] = {
+ /* res */
+ WSP_PROBE_MAKE(RES_REQ, 0, &res_request),
+ WSP_PROBE_MAKE(RES_FINISH, 0, &res_finish),
+
+ /* redraw */
+ WSP_PROBE_MAKE(REDRAW, 0, &redraw),
+};
+
+static struct wsp_bin chromium_bin = {
+ .name = NULL,
+ .probe_array = __probe_array,
+ .cnt = ARRAY_SIZE(__probe_array)
+};
+
+/* check chromium_bin array on init address */
+static bool wsp_is_addr_init(void)
+{
+ int i;
+
+ for (i = 0; i < chromium_bin.cnt; ++i)
+ if (chromium_bin.probe_array[i].probe.offset == 0)
+ return false;
+
+ return true;
+}
+
+static int wsp_bin_register(struct pf_group *pfg, struct wsp_bin *bin)
+{
+ int i, ret;
+ struct dentry *dentry;
+
+ dentry = dentry_by_path(bin->name);
+ if (!dentry) {
+ pr_err("dentry not found (path='%s'\n", bin->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < bin->cnt; ++i) {
+ struct wsp_probe *p = &bin->probe_array[i];
+
+ ret = pin_register(&p->probe, pfg, dentry);
+ if (ret) {
+ pr_err("failed to register WSP probe (%lx:%d)\n",
+ p->probe.offset, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void wsp_bin_unregister(struct pf_group *pfg, struct wsp_bin *bin)
+{
+ int i;
+ struct dentry *dentry;
+
+ dentry = dentry_by_path(bin->name);
+ if (!dentry) {
+ pr_err("dentry not found (path='%s'\n", bin->name);
+ return;
+ }
+
+ for (i = 0; i < bin->cnt; ++i) {
+ struct wsp_probe *p = &bin->probe_array[i];
+
+ pin_unregister(&p->probe, pfg);
+ }
+}
+
+static char *do_set_path(char *path, size_t len)
+{
+ char *p;
+
+ p = kmalloc(len, GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ strncpy(p, path, len);
+ return p;
+}
+
+static void do_free_path(char **dest)
+{
+ kfree(*dest);
+ *dest = NULL;
+}
+
+static struct pf_group *g_pfg;
+
+static int wsp_app_register(void)
+{
+ struct dentry *dentry;
+
+ if (!webapp_path || !chromium_path) {
+ pr_err("WSP: some required paths are not set!\n");
+ return -EINVAL;
+ }
+
+ chromium_bin.name = chromium_path;
+
+ dentry = dentry_by_path(webapp_path);
+ if (!dentry) {
+ pr_err("dentry not found (path='%s'\n", webapp_path);
+ return -EINVAL;
+ }
+
+ g_pfg = get_pf_group_by_dentry(dentry, (void *)dentry);
+ if (!g_pfg) {
+ pr_err("WSP: g_pfg is NULL (by dentry=%p)\n", dentry);
+ return -ENOMEM;
+ }
+
+ return wsp_bin_register(g_pfg, &chromium_bin);
+}
+
+static void wsp_app_unregister(void)
+{
+ if (!chromium_bin.name) {
+ pr_err("WSP: chromium path is not initialized\n");
+ return;
+ }
+
+ wsp_bin_unregister(g_pfg, &chromium_bin);
+ put_pf_group(g_pfg);
+}
+
+static int do_wsp_on(void)
+{
+ int ret;
+
+ ret = wsp_res_init();
+ if (ret)
+ return ret;
+
+ ret = wsp_app_register();
+ if (ret)
+ wsp_res_exit();
+
+ return ret;
+}
+
+static int do_wsp_off(void)
+{
+ wsp_app_unregister();
+ wsp_res_exit();
+
+ return 0;
+}
+
+static enum wsp_mode g_mode = WSP_OFF;
+static DEFINE_MUTEX(g_mode_mutex);
+
+int wsp_set_addr(const char *name, unsigned long offset)
+{
+ int i, ret = 0;
+
+ if (mutex_trylock(&g_mode_mutex) == 0)
+ return -EBUSY;
+
+ for (i = 0; i < chromium_bin.cnt; ++i) {
+ if (!strcmp(name, chromium_bin.probe_array[i].name)) {
+ chromium_bin.probe_array[i].probe.offset = offset;
+ goto unlock;
+ }
+ }
+
+ ret = -EINVAL;
+
+unlock:
+ mutex_unlock(&g_mode_mutex);
+ return ret;
+}
+
+int wsp_set_mode(enum wsp_mode mode)
+{
+ int ret = -EINVAL;
+
+ if (g_mode == mode)
+ return -EBUSY;
+
+ mutex_lock(&g_mode_mutex);
+ switch (mode) {
+ case WSP_ON:
+ ret = wsp_is_addr_init() ? do_wsp_on() : -EPERM;
+ break;
+ case WSP_OFF:
+ ret = do_wsp_off();
+ break;
+ }
+
+ if (!ret)
+ g_mode = mode;
+
+ mutex_unlock(&g_mode_mutex);
+ return ret;
+}
+
+enum wsp_mode wsp_get_mode(void)
+{
+ return g_mode;
+}
+
+void wsp_set_webapp_path(char *path, size_t len)
+{
+ do_free_path(&webapp_path);
+ webapp_path = do_set_path(path, len);
+}
+
+void wsp_set_chromium_path(char *path, size_t len)
+{
+ do_free_path(&chromium_path);
+ chromium_path = do_set_path(path, len);
+}
+
+int wsp_init(void)
+{
+ return 0;
+}
+
+void wsp_exit(void)
+{
+ wsp_set_mode(WSP_OFF);
+ do_free_path(&webapp_path);
+ do_free_path(&chromium_path);
+}
--- /dev/null
+#ifndef _WSP_H
+#define _WSP_H
+
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+enum wsp_mode {
+ WSP_ON,
+ WSP_OFF
+};
+
+int wsp_set_addr(const char *name, unsigned long offset);
+
+int wsp_set_mode(enum wsp_mode mode);
+enum wsp_mode wsp_get_mode(void);
+
+void wsp_set_webapp_path(char *path, size_t len);
+void wsp_set_chromium_path(char *path, size_t len);
+
+int wsp_init(void);
+void wsp_exit(void);
+
+#endif /* _WSP_H */
--- /dev/null
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <master/swap_debugfs.h>
+#include "wsp.h"
+#include "wsp_debugfs.h"
+
+static int do_write_cmd(const char *buf, size_t count)
+{
+ int n, ret = 0;
+ char *name;
+ unsigned long offset;
+
+ name = kmalloc(count, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ n = sscanf(buf, "%lx %1024s", &offset, name);
+ if (n != 2) {
+ ret = -EINVAL;
+ goto free_name;
+ }
+
+ ret = wsp_set_addr(name, offset);
+
+free_name:
+ kfree(name);
+ return ret;
+}
+
+/* ============================================================================
+ * === DEBUGFS FOR CMD ===
+ * ============================================================================
+ */
+static ssize_t write_cmd(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ enum { max_count = 1024 };
+ int ret;
+ char *buf;
+
+ if (count > max_count)
+ return -ENOMEM;
+
+ buf = kmalloc(count + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, user_buf, count)) {
+ ret = -EFAULT;
+ goto free_buf;
+ }
+
+ buf[count] = '\0';
+ ret = do_write_cmd(buf, count);
+
+free_buf:
+ kfree(buf);
+ return ret ? ret : count;
+}
+
+static const struct file_operations fops_cmd = {
+ .write = write_cmd,
+ .llseek = default_llseek,
+};
+
+/* ============================================================================
+ * === DEBUGFS FOR ENABLE ===
+ * ============================================================================
+ */
+static ssize_t read_enabled(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[2];
+
+ buf[0] = wsp_get_mode() == WSP_OFF ? '0' : '1';
+ buf[1] = '\n';
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t write_enabled(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ char buf[32];
+ size_t buf_size;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ switch (buf[0]) {
+ case '1':
+ ret = wsp_set_mode(WSP_ON);
+ break;
+ case '0':
+ ret = wsp_set_mode(WSP_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_enabled = {
+ .read = read_enabled,
+ .write = write_enabled,
+ .llseek = default_llseek,
+};
+
+/* ============================================================================
+ * === DEBUGFS FOR WEBAPP_PATH ===
+ * ============================================================================
+ */
+static ssize_t write_webapp_path(struct file *file,
+ const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (!path) {
+ ret = -ENOMEM;
+ goto write_webapp_path_failed;
+ }
+
+ if (copy_from_user(path, user_buf, len)) {
+ ret = -EINVAL;
+ goto write_webapp_path_failed;
+ }
+
+ path[len - 1] = '\0';
+ wsp_set_webapp_path(path, len);
+
+ ret = len;
+
+write_webapp_path_failed:
+ kfree(path);
+
+ return ret;
+}
+
+static const struct file_operations fops_webapp_path = {
+ .write = write_webapp_path
+};
+
+/* ============================================================================
+ * === DEBUGFS FOR EWEBKIT_PATH ===
+ * ============================================================================
+ */
+static ssize_t write_ewebkit_path(struct file *file,
+ const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (!path) {
+ ret = -ENOMEM;
+ goto write_ewebkit_path_failed;
+ }
+
+ if (copy_from_user(path, user_buf, len)) {
+ ret = -EINVAL;
+ goto write_ewebkit_path_failed;
+ }
+
+ path[len - 1] = '\0';
+
+ wsp_set_chromium_path(path, len);
+
+ ret = len;
+
+write_ewebkit_path_failed:
+ kfree(path);
+
+ return ret;
+}
+
+static const struct file_operations fops_ewebkit_path = {
+ .write = write_ewebkit_path
+};
+
+static struct dentry *wsp_dir;
+
+void wsp_debugfs_exit(void)
+{
+ debugfs_remove_recursive(wsp_dir);
+ wsp_dir = NULL;
+}
+
+int wsp_debugfs_init(void)
+{
+ struct dentry *dentry;
+
+ dentry = swap_debugfs_getdir();
+ if (!dentry)
+ return -ENOENT;
+
+ wsp_dir = swap_debugfs_create_dir("wsp", dentry);
+ if (!wsp_dir)
+ return -ENOMEM;
+
+ dentry = swap_debugfs_create_file("enabled", 0600, wsp_dir, NULL,
+ &fops_enabled);
+ if (!dentry)
+ goto fail;
+
+ dentry = swap_debugfs_create_file("cmd", 0600, wsp_dir, NULL,
+ &fops_cmd);
+ if (!dentry)
+ goto fail;
+
+ dentry = swap_debugfs_create_file("webapp_path", 0600, wsp_dir, NULL,
+ &fops_webapp_path);
+ if (!dentry)
+ goto fail;
+
+ dentry = swap_debugfs_create_file("ewebkit_path", 0600, wsp_dir, NULL,
+ &fops_ewebkit_path);
+ if (!dentry)
+ goto fail;
+
+ return 0;
+
+fail:
+ wsp_debugfs_exit();
+ return -ENOMEM;
+}
--- /dev/null
+#ifndef _WSP_DEBUGFS_H
+#define _WSP_DEBUGFS_H
+
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+int wsp_debugfs_init(void);
+void wsp_debugfs_exit(void);
+
+#endif /* _WSP_DEBUGFS_H */
--- /dev/null
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <master/swap_initializer.h>
+#include "wsp.h"
+#include "wsp_debugfs.h"
+
+SWAP_LIGHT_INIT_MODULE(NULL, wsp_init, wsp_exit,
+ wsp_debugfs_init, wsp_debugfs_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * wsp/wsp_msg.c
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <writer/swap_msg.h>
+#include "wsp_msg.h"
+
+/*
+ * MSG_WSP (payload):
+ * +-------------+----------+----------+
+ * | name | type | length |
+ * +-------------+----------+----------+
+ * | PID | int | 4 |
+ * | wsp_id | int | 4 |
+ * | wsp_payload | variable | variable |
+ * +-------------+----------+----------+
+
+ * wsp_id:
+ * - WSP_RES_LOAD_BEGIN = 0x0001
+ * - WSP_RES_LOAD_END = 0x0002
+ * - WSP_RES_PROC_BEGIN = 0x0003
+ * - WSP_RES_PROC_END = 0x0004
+ * - WSP_DRAW_BEGIN = 0x0005
+ * - WSP_DRAW_END = 0x0006
+ *
+ * wsp_payload:
+ *
+ * 1. WSP_RES_LOAD_BEGIN:
+ * +--------+--------+----------+
+ * | name | type | length |
+ * +--------+--------+----------+
+ * | res_id | int | 4 |
+ * | path | string | variable |
+ * +--------+--------+----------+
+ *
+ * 2. WSP_RES_LOAD_END, WSP_RES_PROC_BEGIN, WSP_RES_PROC_END:
+ * +--------+--------+----------+
+ * | name | type | length |
+ * +--------+--------+----------+
+ * | res_id | int | 4 |
+ * +--------+--------+----------+
+ *
+ * 3. WSP_DRAW_BEGIN, WSP_DRAW_END:
+ * no wsp_payload
+ */
+
+static int pack_wsp_msg(void *data, size_t size, enum wsp_id id,
+ u32 res_id, const char *path)
+{
+ size_t len;
+ const size_t old_size = size;
+
+ /* write PID */
+ *(u32 *)data = (u32)current->tgid;
+ data += 4;
+ size -= 4;
+
+ /* write wsp_id */
+ *(u32 *)data = (u32)id;
+ data += 4;
+ size -= 4;
+
+ /* pack wsp_payload */
+ switch (id) {
+ case WSP_RES_LOAD_BEGIN:
+ len = strlen(path) + 1;
+ if (size < len + 4)
+ return -ENOMEM;
+
+ /* '+ 4' - skip space for res_id */
+ memcpy(data + 4, path, len);
+ size -= len;
+ case WSP_RES_LOAD_END:
+ case WSP_RES_PROC_BEGIN:
+ case WSP_RES_PROC_END:
+ /* write res_id */
+ *(u32 *)data = res_id;
+ size -= 4;
+ break;
+
+ case WSP_DRAW_BEGIN:
+ case WSP_DRAW_END:
+ break;
+
+ default:
+ pr_err("unknown wsp_id: id=%u\n", (unsigned int)id);
+ return -EINVAL;
+ }
+
+ return old_size - size;
+}
+
+void wsp_msg(enum wsp_id id, u32 res_id, const char *path)
+{
+ int ret;
+ void *data;
+ size_t size;
+ struct swap_msg *m;
+
+ m = swap_msg_get(MSG_WSP);
+ data = swap_msg_payload(m);
+ size = swap_msg_size(m);
+ ret = pack_wsp_msg(data, size, id, res_id, path);
+ if (ret < 0) {
+ pr_err("error MSG_WSP packing, ret=%d\n", ret);
+ goto put_msg;
+ }
+
+ swap_msg_flush(m, ret);
+
+put_msg:
+ swap_msg_put(m);
+}
--- /dev/null
+#ifndef _WSP_MSG_H
+#define _WSP_MSG_H
+
+/*
+ * wsp/wsp_msg.h
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <linux/types.h>
+
+enum wsp_id {
+ WSP_RES_LOAD_BEGIN = 0x0001,
+ WSP_RES_LOAD_END = 0x0002,
+ WSP_RES_PROC_BEGIN = 0x0003,
+ WSP_RES_PROC_END = 0x0004,
+ WSP_DRAW_BEGIN = 0x0005,
+ WSP_DRAW_END = 0x0006
+};
+
+void wsp_msg(enum wsp_id id, u32 res_id, const char *path);
+
+#endif /* _WSP_MSG_H */
--- /dev/null
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include "wsp_res.h"
+
+static atomic_t __resource_id = ATOMIC_INIT(0);
+
+static inline int __wsp_resource_id(void)
+{
+ return atomic_inc_return(&__resource_id);
+}
+
+struct wsp_resource_data {
+ struct list_head list;
+ int id;
+ unsigned long addr;
+ char *path;
+};
+
+static LIST_HEAD(__resources_list);
+static DEFINE_MUTEX(__resources_mutex);
+
+static struct wsp_resource_data *wsp_resource_data_alloc(void)
+{
+ struct wsp_resource_data *p;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ INIT_LIST_HEAD(&p->list);
+
+ return p;
+}
+
+static void wsp_resource_data_free(struct wsp_resource_data *p)
+{
+ if (!p)
+ return;
+
+ kfree(p->path);
+ kfree(p);
+}
+
+static struct wsp_resource_data *wsp_resource_data_find(unsigned long addr)
+{
+ struct wsp_resource_data *p;
+
+ list_for_each_entry(p, &__resources_list, list)
+ if (p->addr == addr)
+ return p;
+
+ return NULL;
+}
+
+int wsp_resource_data_id(unsigned long addr)
+{
+ int ret = -1;
+ struct wsp_resource_data *p;
+
+ mutex_lock(&__resources_mutex);
+ p = wsp_resource_data_find(addr);
+ if (p)
+ ret = p->id;
+ mutex_unlock(&__resources_mutex);
+
+ return ret;
+}
+
+int wsp_resource_data_add(unsigned long addr, char *path)
+{
+ int ret = -1;
+ struct wsp_resource_data *p;
+
+ mutex_lock(&__resources_mutex);
+ p = wsp_resource_data_find(addr);
+ if (p) {
+ ret = p->id;
+ goto out;
+ }
+ p = wsp_resource_data_alloc();
+ if (p) {
+ p->id = __wsp_resource_id();
+ p->addr = addr;
+ p->path = path;
+ list_add_tail(&p->list, &__resources_list);
+ ret = p->id;
+ }
+
+out:
+ mutex_unlock(&__resources_mutex);
+
+ return ret;
+}
+
+void wsp_resource_data_del(unsigned long addr)
+{
+ struct wsp_resource_data *p;
+
+ mutex_lock(&__resources_mutex);
+ p = wsp_resource_data_find(addr);
+ if (p) {
+ list_del(&p->list);
+ wsp_resource_data_free(p);
+ }
+
+ mutex_unlock(&__resources_mutex);
+}
+
+/* ============================================================================
+ * = init/exit() =
+ * ============================================================================
+ */
+int wsp_res_init(void)
+{
+ return 0;
+}
+
+void wsp_res_exit(void)
+{
+ struct wsp_resource_data *p, *tmp;
+
+ mutex_lock(&__resources_mutex);
+ list_for_each_entry_safe(p, tmp, &__resources_list, list) {
+ list_del(&p->list);
+ wsp_resource_data_free(p);
+ }
+ mutex_unlock(&__resources_mutex);
+}
--- /dev/null
+#ifndef _WSP_TDATA_H
+#define _WSP_TDATA_H
+
+/*
+ * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section COPYRIGHT
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * @section DESCRIPTION
+ *
+ * Web startup profiling
+ */
+
+#include <linux/types.h>
+
+int wsp_resource_data_add(unsigned long addr, char *path);
+void wsp_resource_data_del(unsigned long addr);
+int wsp_resource_data_id(unsigned long addr);
+
+int wsp_res_init(void);
+void wsp_res_exit(void);
+
+#endif /* _WSP_TDATA_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_nsp.o
-swap_nsp-y := \
- nsp_module.o \
- nsp.o \
- nsp_msg.o \
- nsp_debugfs.o
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <writer/swap_msg.h>
-#include <kprobe/swap_ktd.h>
-#include <uprobe/swap_uaccess.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include <us_manager/probes/probe_info_new.h>
-#include "nsp.h"
-#include "nsp_msg.h"
-#include "nsp_print.h"
-#include "nsp_debugfs.h"
-
-
-/* ============================================================================
- * = probes =
- * ============================================================================
- */
-
-/* dlopen@plt */
-static int dlopen_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static int dlopen_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static struct probe_desc pin_dlopen = MAKE_URPROBE(dlopen_eh, dlopen_rh, 0);
-static struct probe_new p_dlopen = {
- .desc = &pin_dlopen
-};
-
-/* dlsym@plt */
-static int dlsym_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static int dlsym_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static struct probe_desc pin_dlsym = MAKE_URPROBE(dlsym_eh, dlsym_rh, 0);
-static struct probe_new p_dlsym = {
- .desc = &pin_dlsym
-};
-
-/* main */
-static int main_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static int main_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static struct probe_desc pin_main = MAKE_URPROBE(main_eh, main_rh, 0);
-
-/* appcore_efl_main */
-static int ac_efl_main_h(struct uprobe *p, struct pt_regs *regs);
-static struct probe_desc pin_ac_efl_main = MAKE_UPROBE(ac_efl_main_h);
-static struct probe_new p_ac_efl_main = {
- .desc = &pin_ac_efl_main
-};
-
-/* appcore_init@plt */
-static int ac_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static struct probe_desc pin_ac_init = MAKE_URPROBE(NULL, ac_init_rh, 0);
-static struct probe_new p_ac_init = {
- .desc = &pin_ac_init
-};
-
-/* elm_run@plt */
-static int elm_run_h(struct uprobe *p, struct pt_regs *regs);
-static struct probe_desc pin_elm_run = MAKE_UPROBE(elm_run_h);
-static struct probe_new p_elm_run = {
- .desc = &pin_elm_run
-};
-
-/* __do_app */
-static int do_app_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static int do_app_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static struct probe_desc pin_do_app = MAKE_URPROBE(do_app_eh, do_app_rh, 0);
-static struct probe_new p_do_app = {
- .desc = &pin_do_app
-};
-
-
-
-
-
-/* ============================================================================
- * = the variables are initialized by the user =
- * ============================================================================
- */
-static const char *lpad_path;
-static struct dentry *lpad_dentry;
-
-static const char *libappcore_path;
-static struct dentry *libappcore_dentry;
-static const char *libcapi_path;
-static struct dentry *libcapi_dentry;
-
-static void uninit_variables(void)
-{
- kfree(lpad_path);
- lpad_path = NULL;
- lpad_dentry = NULL;
-
- kfree(libappcore_path);
- libappcore_path = NULL;
- libappcore_dentry = NULL;
-
- kfree(libcapi_path);
- libcapi_path = NULL;
- libcapi_dentry = NULL;
-}
-
-static bool is_init(void)
-{
- return lpad_dentry && libappcore_dentry && libcapi_dentry;
-}
-
-static int do_set_lpad_info(const char *path, unsigned long dlopen,
- unsigned long dlsym)
-{
- struct dentry *dentry;
- const char *new_path;
-
- dentry = dentry_by_path(path);
- if (dentry == NULL) {
- pr_err("dentry not found (path='%s')\n", path);
- return -EINVAL;
- }
-
- new_path = kstrdup(path, GFP_KERNEL);
- if (new_path == NULL) {
- pr_err("out of memory\n");
- return -ENOMEM;
- }
-
- kfree(lpad_path);
-
- lpad_path = new_path;
- lpad_dentry = dentry;
- p_dlopen.offset = dlopen;
- p_dlsym.offset = dlsym;
-
- return 0;
-}
-
-static int do_set_appcore_info(struct nsp_info_data *info)
-{
- struct dentry *dentry;
- const char *new_path;
- int ret = 0;
-
- new_path = kstrdup(info->appcore_path, GFP_KERNEL);
- if (!new_path)
- return -ENOMEM;
- kfree(libappcore_path);
- libappcore_path = new_path;
-
- new_path = kstrdup(info->capi_path, GFP_KERNEL);
- if (!new_path) {
- ret = -ENOMEM;
- goto fail_alloc;
- }
- kfree(libcapi_path);
- libcapi_path = new_path;
-
- dentry = dentry_by_path(info->appcore_path);
- if (!dentry) {
- pr_err("dentry not found (path='%s')\n", info->appcore_path);
- ret = -EINVAL;
- goto fail;
- }
- libappcore_dentry = dentry;
-
- dentry = dentry_by_path(info->capi_path);
- if (!dentry) {
- pr_err("dentry not found (path='%s')\n", info->capi_path);
- ret = -EINVAL;
- goto fail;
- }
- libcapi_dentry = dentry;
-
- p_ac_efl_main.offset = info->ac_efl_init;
- p_do_app.offset = info->do_app;
- p_ac_init.offset = info->ac_init;
- p_elm_run.offset = info->elm_run;
-
- return 0;
-
-fail:
- kfree(libcapi_path);
- libcapi_path = NULL;
-fail_alloc:
- kfree(libappcore_path);
- libappcore_path = NULL;
-
- return ret;
-}
-
-
-
-
-
-/* ============================================================================
- * = nsp_data =
- * ============================================================================
- */
-struct nsp_data {
- struct list_head list;
-
- const char *app_path;
- struct dentry *app_dentry;
- struct probe_new p_main;
-
- struct pf_group *pfg;
-};
-
-static LIST_HEAD(nsp_data_list);
-
-static struct nsp_data *nsp_data_create(const char *app_path,
- unsigned long main_addr)
-{
- struct dentry *dentry;
- struct nsp_data *data;
-
- dentry = dentry_by_path(app_path);
- if (dentry == NULL)
- return ERR_PTR(-ENOENT);
-
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (data == NULL)
- return ERR_PTR(-ENOMEM);
-
- data->app_path = kstrdup(app_path, GFP_KERNEL);
- if (data->app_path == NULL) {
- kfree(data);
- return ERR_PTR(-ENOMEM);
- }
-
- data->app_dentry = dentry;
- data->p_main.desc = &pin_main;
- data->p_main.offset = main_addr;
- data->pfg = NULL;
-
- return data;
-}
-
-static void nsp_data_destroy(struct nsp_data *data)
-{
- kfree(data->app_path);
- kfree(data);
-}
-
-static struct nsp_data *nsp_data_find(const struct dentry *dentry)
-{
- struct nsp_data *data;
-
- list_for_each_entry(data, &nsp_data_list, list) {
- if (data->app_dentry == dentry)
- return data;
- }
-
- return NULL;
-}
-
-static struct nsp_data *nsp_data_find_by_path(const char *path)
-{
- struct nsp_data *data;
-
- list_for_each_entry(data, &nsp_data_list, list) {
- if (strcmp(data->app_path, path) == 0)
- return data;
- }
-
- return NULL;
-}
-
-static void nsp_data_add(struct nsp_data *data)
-{
- list_add(&data->list, &nsp_data_list);
-}
-
-static void nsp_data_rm(struct nsp_data *data)
-{
- list_del(&data->list);
-}
-
-static int nsp_data_inst(struct nsp_data *data)
-{
- int ret;
- struct pf_group *pfg;
-
- pfg = get_pf_group_by_dentry(lpad_dentry, (void *)data->app_dentry);
- if (pfg == NULL)
- return -ENOMEM;
-
- ret = pin_register(&p_dlsym, pfg, lpad_dentry);
- if (ret)
- goto put_g;
-
- ret = pin_register(&p_dlopen, pfg, lpad_dentry);
- if (ret)
- goto ur_dlsym;
-
- ret = pin_register(&data->p_main, pfg, data->app_dentry);
- if (ret)
- goto ur_dlopen;
-
- ret = pin_register(&p_ac_efl_main, pfg, libappcore_dentry);
- if (ret)
- goto ur_main;
-
- ret = pin_register(&p_ac_init, pfg, libcapi_dentry);
- if (ret)
- goto ur_ac_efl_main;
-
- ret = pin_register(&p_elm_run, pfg, libcapi_dentry);
- if (ret)
- goto ur_ac_init;
-
- ret = pin_register(&p_do_app, pfg, libappcore_dentry);
- if (ret)
- goto ur_elm_run;
-
- data->pfg = pfg;
-
- return 0;
-
-ur_elm_run:
- pin_unregister(&p_elm_run, pfg);
-ur_ac_init:
- pin_unregister(&p_ac_init, pfg);
-ur_ac_efl_main:
- pin_unregister(&p_ac_efl_main, pfg);
-ur_main:
- pin_unregister(&data->p_main, pfg);
-ur_dlopen:
- pin_unregister(&p_dlopen, pfg);
-ur_dlsym:
- pin_unregister(&p_dlsym, pfg);
-put_g:
- put_pf_group(pfg);
- return ret;
-}
-
-static void nsp_data_uninst(struct nsp_data *data)
-{
- struct pf_group *pfg = data->pfg;
-
- pin_unregister(&p_do_app, pfg);
- pin_unregister(&p_elm_run, pfg);
- pin_unregister(&p_ac_init, pfg);
- pin_unregister(&p_ac_efl_main, pfg);
- pin_unregister(&data->p_main, pfg);
- pin_unregister(&p_dlopen, pfg);
- pin_unregister(&p_dlsym, pfg);
- put_pf_group(pfg);
-
- data->pfg = NULL;
-}
-
-static int __nsp_add(const char *app_path, unsigned long main_addr)
-{
- struct nsp_data *data;
-
- if (nsp_data_find_by_path(app_path))
- return -EEXIST;
-
- data = nsp_data_create(app_path, main_addr);
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- nsp_data_add(data);
-
- return 0;
-}
-
-static int __nsp_rm(const char *path)
-{
- struct dentry *dentry;
- struct nsp_data *data;
-
- dentry = dentry_by_path(path);
- if (dentry == NULL)
- return -ENOENT;
-
- data = nsp_data_find(dentry);
- if (data == NULL)
- return -ESRCH;
-
- nsp_data_rm(data);
- nsp_data_destroy(data);
-
- return 0;
-}
-
-static int __nsp_rm_all(void)
-{
- struct nsp_data *data, *n;
-
- list_for_each_entry_safe(data, n, &nsp_data_list, list) {
- nsp_data_rm(data);
- nsp_data_destroy(data);
- }
-
- return 0;
-}
-
-static void __nsp_disabel(void)
-{
- struct nsp_data *data;
-
- list_for_each_entry(data, &nsp_data_list, list) {
- if (data->pfg)
- nsp_data_uninst(data);
- }
-}
-
-static int __nsp_enable(void)
-{
- int ret;
- struct nsp_data *data;
-
- list_for_each_entry(data, &nsp_data_list, list) {
- ret = nsp_data_inst(data);
- if (ret)
- goto fail;
- }
-
- return 0;
-
-fail:
- __nsp_disabel();
- return ret;
-}
-
-
-
-
-
-
-
-/* ============================================================================
- * = set parameters =
- * ============================================================================
- */
-#define F_ARG1(m, t, a) m(t, a)
-#define F_ARG2(m, t, a, ...) m(t, a), F_ARG1(m, __VA_ARGS__)
-#define F_ARG3(m, t, a, ...) m(t, a), F_ARG2(m, __VA_ARGS__)
-#define F_ARG(n, m, ...) F_ARG##n(m, __VA_ARGS__)
-
-#define M_TYPE_AND_ARG(t, a) t a
-#define M_ARG(t, a) a
-
-#define DECLARE_SAFE_FUNC(n, func_name, do_func, ...) \
-int func_name(F_ARG(n, M_TYPE_AND_ARG, __VA_ARGS__)) \
-{ \
- int ret; \
- mutex_lock(&stat_mutex); \
- if (stat == NS_ON) { \
- ret = -EBUSY; \
- goto unlock; \
- } \
- ret = do_func(F_ARG(n, M_ARG, __VA_ARGS__)); \
-unlock: \
- mutex_unlock(&stat_mutex); \
- return ret; \
-}
-
-#define DECLARE_SAFE_FUNC0(name, _do) DECLARE_SAFE_FUNC(1, name, _do, void, /* */);
-#define DECLARE_SAFE_FUNC1(name, _do, ...) DECLARE_SAFE_FUNC(1, name, _do, __VA_ARGS__);
-#define DECLARE_SAFE_FUNC2(name, _do, ...) DECLARE_SAFE_FUNC(2, name, _do, __VA_ARGS__);
-#define DECLARE_SAFE_FUNC3(name, _do, ...) DECLARE_SAFE_FUNC(3, name, _do, __VA_ARGS__);
-
-
-static DEFINE_MUTEX(stat_mutex);
-static enum nsp_stat stat = NS_OFF;
-
-DECLARE_SAFE_FUNC2(nsp_add, __nsp_add, const char *, app_path,
- unsigned long, main_addr);
-DECLARE_SAFE_FUNC1(nsp_rm, __nsp_rm, const char *, app_path);
-DECLARE_SAFE_FUNC0(nsp_rm_all, __nsp_rm_all);
-DECLARE_SAFE_FUNC3(nsp_set_lpad_info, do_set_lpad_info,
- const char *, path, unsigned long, dlopen,
- unsigned long, dlsym);
-DECLARE_SAFE_FUNC1(nsp_set_appcore_info, do_set_appcore_info,
- struct nsp_info_data *, info);
-
-
-
-
-
-/* ============================================================================
- * = set stat =
- * ============================================================================
- */
-static int set_stat_off(void)
-{
- if (stat == NS_OFF)
- return -EINVAL;
-
- __nsp_disabel();
-
- stat = NS_OFF;
-
- return 0;
-}
-
-static int set_stat_on(void)
-{
- if (is_init() == false)
- return -EPERM;
-
- if (stat == NS_ON)
- return -EINVAL;
-
- __nsp_enable();
-
- stat = NS_ON;
-
- return 0;
-}
-
-int nsp_set_stat(enum nsp_stat st)
-{
- int ret = -EINVAL;
-
- mutex_lock(&stat_mutex);
- switch (st) {
- case NS_OFF:
- ret = set_stat_off();
- break;
- case NS_ON:
- ret = set_stat_on();
- break;
- }
- mutex_unlock(&stat_mutex);
-
- return ret;
-}
-
-enum nsp_stat nsp_get_stat(void)
-{
- return stat;
-}
-
-
-
-
-
-/* ============================================================================
- * = tdata =
- * ============================================================================
- */
-enum nsp_proc_stat {
- NPS_OPEN_E, /* mapping begin */
- NPS_OPEN_R,
- NPS_SYM_E,
- NPS_SYM_R, /* mapping end */
- NPS_MAIN_E, /* main begin */
- NPS_AC_EFL_MAIN_E, /* main end */
- NPS_AC_INIT_R, /* create begin */
- NPS_ELM_RUN_E, /* create end */
- NPS_DO_APP_E, /* reset begin */
- NPS_DO_APP_R /* reset end */
-};
-
-struct tdata {
- enum nsp_proc_stat stat;
- struct nsp_data *nsp_data;
- u64 time;
- void __user *handle;
- struct probe_new p_main;
-};
-
-
-static void ktd_init(struct task_struct *task, void *data)
-{
- struct tdata *tdata = (struct tdata *)data;
-
- tdata->nsp_data = NULL;
-}
-
-static struct ktask_data ktd = {
- .init = ktd_init,
- .size = sizeof(struct tdata),
-};
-
-static struct tdata *tdata_get(struct task_struct *task)
-{
- return (struct tdata *)swap_ktd(&ktd, task);
-}
-
-
-
-
-
-/* ============================================================================
- * = handlers =
- * ============================================================================
- */
-static int dlopen_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- const char __user *user_s = (const char __user *)swap_get_uarg(regs, 0);
- const char *path;
- struct nsp_data *nsp_data;
-
- path = strdup_from_user(user_s, GFP_ATOMIC);
- if (path == NULL)
- return 0;
-
- nsp_data = nsp_data_find_by_path(path);
- if (nsp_data) {
- struct tdata *tdata = tdata_get(current);
-
- /* init tdata */
- if (tdata->nsp_data == NULL) {
- tdata->stat = NPS_OPEN_E;
- tdata->time = swap_msg_current_time();
- tdata->nsp_data = nsp_data;
- }
- }
-
- kfree(path);
- return 0;
-}
-
-static int dlopen_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct tdata *tdata = tdata_get(current);
- void *handle = (void *)regs_return_value(regs);
-
- if ((tdata->stat == NPS_OPEN_E) && handle) {
- tdata->stat = NPS_OPEN_R;
- tdata->handle = handle;
- } else {
- tdata->handle = NULL;
- }
-
- return 0;
-}
-
-static int dlsym_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct tdata *tdata = tdata_get(current);
- void __user *handle = (void __user *)swap_get_uarg(regs, 0);
- const char __user *str = (const char __user *)swap_get_uarg(regs, 1);
-
- if (handle == tdata->handle && tdata->stat == NPS_OPEN_R) {
- const char *name;
-
- name = strdup_from_user(str, GFP_ATOMIC);
- if (name && (strcmp(name, "main") == 0))
- tdata->stat = NPS_SYM_E;
-
- kfree(name);
- }
-
- return 0;
-}
-
-static int dlsym_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct tdata *tdata = tdata_get(current);
-
- if (tdata->handle && tdata->stat == NPS_SYM_E)
- tdata->stat = NPS_SYM_R;
-
- return 0;
-}
-
-static void stage_begin(enum nsp_proc_stat priv, enum nsp_proc_stat cur)
-{
- struct tdata *tdata = tdata_get(current);
-
- if (tdata->handle && tdata->stat == priv) {
- tdata->stat = cur;
- tdata->time = swap_msg_current_time();
- }
-}
-
-static void stage_end(enum nsp_proc_stat priv, enum nsp_proc_stat cur,
- enum nsp_msg_stage st)
-{
- struct tdata *tdata = tdata_get(current);
- u64 time_start;
- u64 time_end;
-
- if (tdata->handle && tdata->stat == priv) {
- tdata->stat = cur;
- time_start = tdata->time;
-
- time_end = swap_msg_current_time();
- nsp_msg(st, time_start, time_end);
- }
-}
-
-static int main_h(struct uprobe *p, struct pt_regs *regs)
-{
- struct tdata *tdata = tdata_get(current);
- u64 time_start;
- u64 time_end;
-
- if (tdata->handle && tdata->stat == NPS_SYM_R) {
- tdata->stat = NPS_MAIN_E;
- time_start = tdata->time;
- time_end = swap_msg_current_time();
- tdata->time = time_end;
-
- nsp_msg(NMS_MAPPING, time_start, time_end);
- }
-
- return 0;
-}
-
-/* FIXME: workaround for simultaneously nsp and main() function profiling */
-#include <retprobe/rp_msg.h>
-#include <us_manager/us_manager.h>
-
-static int main_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct uretprobe *rp = ri->rp;
-
- if (rp) {
- main_h(&rp->up, regs);
-
- if (get_quiet() == QT_OFF) {
- struct sspt_ip *ip;
- unsigned long func_addr;
-
- ip = container_of(rp, struct sspt_ip, retprobe);
- func_addr = (unsigned long)ip->orig_addr;
- rp_msg_entry(regs, func_addr, "p");
- }
- }
-
- return 0;
-}
-
-static int main_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct uretprobe *rp = ri->rp;
-
- if (rp && get_quiet() == QT_OFF) {
- struct sspt_ip *ip;
- unsigned long func_addr;
- unsigned long ret_addr;
-
- ip = container_of(rp, struct sspt_ip, retprobe);
- func_addr = (unsigned long)ip->orig_addr;
- ret_addr = (unsigned long)ri->ret_addr;
- rp_msg_exit(regs, func_addr, 'n', ret_addr);
- }
-
- return 0;
-}
-
-static int ac_efl_main_h(struct uprobe *p, struct pt_regs *regs)
-{
- stage_end(NPS_MAIN_E, NPS_AC_EFL_MAIN_E, NMS_MAIN);
- return 0;
-}
-
-static int ac_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- stage_begin(NPS_AC_EFL_MAIN_E, NPS_AC_INIT_R);
- return 0;
-}
-
-static int elm_run_h(struct uprobe *p, struct pt_regs *regs)
-{
- stage_end(NPS_AC_INIT_R, NPS_ELM_RUN_E, NMS_CREATE);
- return 0;
-}
-
-static int do_app_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- int event = swap_get_uarg(regs, 0);
- enum { AE_RESET = 5 }; /* FIXME: hardcode */
-
- if (event == AE_RESET)
- stage_begin(NPS_ELM_RUN_E, NPS_DO_APP_E);
-
- return 0;
-}
-
-static int do_app_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- stage_end(NPS_DO_APP_E, NPS_DO_APP_R, NMS_RESET);
- return 0;
-}
-
-
-
-
-
-int nsp_init(void)
-{
- return swap_ktd_reg(&ktd);
-}
-
-void nsp_exit(void)
-{
- if (stat == NS_ON)
- set_stat_off();
-
- uninit_variables();
- swap_ktd_unreg(&ktd);
-}
+++ /dev/null
-#ifndef _NSP_H
-#define _NSP_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-enum offset_t {
- OS_CREATE,
- OS_RESET
-};
-
-enum nsp_stat {
- NS_OFF,
- NS_ON
-};
-
-struct nsp_info_data {
- const char *appcore_path;
- unsigned long ac_efl_init;
- unsigned long do_app;
-
- const char *capi_path;
- unsigned long ac_init;
- unsigned long elm_run;
-};
-
-int nsp_init(void);
-void nsp_exit(void);
-
-int nsp_set_lpad_info(const char *path, unsigned long dlopen,
- unsigned long dlsym);
-int nsp_set_appcore_info(struct nsp_info_data *info);
-
-int nsp_set_stat(enum nsp_stat st);
-enum nsp_stat nsp_get_stat(void);
-
-int nsp_add(const char *app_path, unsigned long main_addr);
-int nsp_rm(const char *app_path);
-int nsp_rm_all(void);
-
-
-#endif /* _NSP_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/limits.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
-#include <master/swap_debugfs.h>
-#include "nsp.h"
-
-
-/* remove end-line symbols */
-static void rm_endline_symbols(char *buf, size_t len)
-{
- char *p, *buf_end;
-
- buf_end = buf + len;
- for (p = buf; p != buf_end; ++p)
- if (*p == '\n' || *p == '\r')
- *p = '\0';
-}
-
-/*
- * format:
- * main:app_path
- *
- * sample:
- * 0x00000d60:/bin/app_sample
- */
-static int do_add(const char *buf, size_t len)
-{
- int n, ret;
- char *app_path;
- unsigned long main_addr;
- const char fmt[] = "%%lx:/%%%ds";
- char fmt_buf[64];
-
- n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2);
- if (n <= 0)
- return -EINVAL;
-
- app_path = kmalloc(PATH_MAX, GFP_KERNEL);
- if (app_path == NULL)
- return -ENOMEM;
-
- n = sscanf(buf, fmt_buf, &main_addr, app_path + 1);
- if (n != 2) {
- ret = -EINVAL;
- goto free_app_path;
- }
- app_path[0] = '/';
-
- ret = nsp_add(app_path, main_addr);
-
-free_app_path:
- kfree(app_path);
- return ret;
-}
-
-/*
- * format:
- * path
- *
- * sample:
- * /tmp/sample
- */
-static int do_rm(const char *buf, size_t len)
-{
- return nsp_rm(buf);
-}
-
-static int do_rm_all(const char *buf, size_t len)
-{
- return nsp_rm_all();
-}
-
-/*
- * format:
- * dlopen_addr@plt:dlsym_addr@plt:launchpad_path
- *
- * sample:
- * 0x000234:0x000342:/usr/bin/launchpad-loader
- */
-static int do_set_lpad_info(const char *data, size_t len)
-{
- int n, ret;
- unsigned long dlopen_addr;
- unsigned long dlsym_addr;
- char *lpad_path;
- const char fmt[] = "%%lx:%%lx:/%%%ds";
- char fmt_buf[64];
-
- n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2);
- if (n <= 0)
- return -EINVAL;
-
- lpad_path = kmalloc(PATH_MAX, GFP_KERNEL);
- if (lpad_path == NULL)
- return -ENOMEM;
-
- n = sscanf(data, fmt_buf, &dlopen_addr, &dlsym_addr, lpad_path + 1);
- if (n != 3) {
- ret = -EINVAL;
- goto free_lpad_path;
- }
- lpad_path[0] = '/';
-
- ret = nsp_set_lpad_info(lpad_path, dlopen_addr, dlsym_addr);
-
-free_lpad_path:
- kfree(lpad_path);
- return ret;
-}
-
-/*
- * format:
- * appcore_efl_init:__do_app:libappcore-efl ui_app_init:elm_run@plt:libcapi-appfw-application
- *
- * sample:
- * 0x2000:0x2ed0:/usr/lib/libappcore-efl.so.1 0x2b20:0x11f0:/usr/lib/libcapi-appfw-application.so.0
- */
-static int do_set_appcore_info(const char *data, size_t len)
-{
- int n, ret;
- struct nsp_info_data info;
- const char fmt[] = "%%lx:%%lx:/%%%ds %%lx:%%lx:/%%%ds";
- char fmt_buf[64];
- char *appcore_path, *capi_path;
-
- n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2, PATH_MAX - 2);
- if (n <= 0)
- return -EINVAL;
-
- appcore_path = kmalloc(PATH_MAX, GFP_KERNEL);
- if (!appcore_path)
- return -ENOMEM;
-
- capi_path = kmalloc(PATH_MAX, GFP_KERNEL);
- if (!capi_path) {
- ret = -ENOMEM;
- goto fail_alloc;
- }
-
- n = sscanf(data, fmt_buf,
- &info.ac_efl_init, &info.do_app, appcore_path + 1,
- &info.ac_init, &info.elm_run, capi_path + 1);
- if (n != 6) {
- ret = -EINVAL;
- goto fail;
- }
- appcore_path[0] = '/';
- capi_path[0] = '/';
-
- info.appcore_path = appcore_path;
- info.capi_path = capi_path;
- ret = nsp_set_appcore_info(&info);
-
-fail:
- kfree(capi_path);
-fail_alloc:
- kfree(appcore_path);
- return ret;
-}
-
-/*
- * format:
- * 0 byte - type
- * 1 byte - ' '
- * 2.. bytes - data
- */
-static int do_cmd(const char *data, size_t len)
-{
- char type;
- size_t len_data;
- const char *cmd_data;
-
- if (len) {
- if (data[0] == 'c')
- return do_rm_all(data + 1, len - 1);
- }
- /*
- * 0 byte - type
- * 1 byte - ' '
- */
- if (len < 2 || data[1] != ' ')
- return -EINVAL;
-
- len_data = len - 2;
- cmd_data = data + 2;
- type = data[0];
- switch (type) {
- case 'a':
- return do_add(cmd_data, len_data);
- case 'b':
- return do_set_lpad_info(cmd_data, len_data);
- case 'l':
- return do_set_appcore_info(cmd_data, len_data);
- case 'r':
- return do_rm(cmd_data, len_data);
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-
-
-
-/* ============================================================================
- * === DEBUGFS FOR CMD ===
- * ============================================================================
- */
-static ssize_t write_cmd(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- char *buf;
- ssize_t ret = count;
-
- buf = kmalloc(count + 1, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- if (copy_from_user(buf, user_buf, count)) {
- ret = -EFAULT;
- goto free_buf;
- }
-
- buf[count] = '\0';
- rm_endline_symbols(buf, count);
-
- if (do_cmd(buf, count))
- ret = -EINVAL;
-
-free_buf:
- kfree(buf);
-
- return ret;
-}
-
-static ssize_t read_cmd(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- const char help[] =
- "use:\n"
- "\ta $app_path - add\n"
- "\tr $app_path - remove\n"
- "\tc - remove all\n"
- "\tb dlopen_addr@plt:dlsym_addr@plt:launchpad_path\n"
- "\tl appcore_efl_init:__do_app:libappcore-efl_path ui_app_init:elm_run@plt:libcapi-appfw-application_path\n";
- ssize_t ret;
-
- ret = simple_read_from_buffer(user_buf, count, ppos,
- help, sizeof(help));
-
- return ret;
-}
-
-static const struct file_operations fops_cmd = {
- .read = read_cmd,
- .write = write_cmd,
- .llseek = default_llseek
-};
-
-
-
-
-/* ============================================================================
- * === DEBUGFS FOR ENABLE ===
- * ============================================================================
- */
-static ssize_t read_enabled(struct file *file, char /*__user*/ *user_buf,
- size_t count, loff_t *ppos)
-{
- char buf[2];
-
- buf[0] = nsp_get_stat() == NS_OFF ? '0' : '1';
- buf[1] = '\n';
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t write_enabled(struct file *file, const char /*__user*/ *user_buf,
- size_t count, loff_t *ppos)
-{
- int ret = 0;
- char buf[32];
- size_t buf_size;
-
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
- switch (buf[0]) {
- case '1':
- ret = nsp_set_stat(NS_ON);
- break;
- case '0':
- ret = nsp_set_stat(NS_OFF);
- break;
- default:
- return -EINVAL;
- }
-
- if (ret)
- return ret;
-
- return count;
-}
-
-static const struct file_operations fops_enabled = {
- .read = read_enabled,
- .write = write_enabled,
- .llseek = default_llseek,
-};
-
-
-
-
-static struct dentry *nsp_dir = NULL;
-
-void nsp_debugfs_exit(void)
-{
- if (nsp_dir)
- debugfs_remove_recursive(nsp_dir);
-
- nsp_dir = NULL;
-}
-
-int nsp_debugfs_init(void)
-{
- struct dentry *dentry;
-
- dentry = swap_debugfs_getdir();
- if (dentry == NULL)
- return -ENOENT;
-
- nsp_dir = swap_debugfs_create_dir("nsp", dentry);
- if (nsp_dir == NULL)
- return -ENOMEM;
-
- dentry = swap_debugfs_create_file("cmd", 0600, nsp_dir, NULL,
- &fops_cmd);
- if (dentry == NULL)
- goto fail;
-
- dentry = swap_debugfs_create_file("enabled", 0600, nsp_dir, NULL,
- &fops_enabled);
- if (dentry == NULL)
- goto fail;
-
- return 0;
-
-fail:
- nsp_debugfs_exit();
- return -ENOMEM;
-}
+++ /dev/null
-#ifndef _NSP_DEBUGFS_H
-#define _NSP_DEBUGFS_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-int nsp_debugfs_init(void);
-void nsp_debugfs_exit(void);
-
-
-#endif /* _NSP_DEBUGFS_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <master/swap_initializer.h>
-#include "nsp.h"
-#include "nsp_debugfs.h"
-
-
-SWAP_LIGHT_INIT_MODULE(NULL, nsp_init, nsp_exit,
- nsp_debugfs_init, nsp_debugfs_exit);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/sched.h>
-#include <writer/swap_msg.h>
-#include "nsp_msg.h"
-
-
-struct nsp_msg_struct {
- u32 pid;
- u32 stage;
- u64 begin_time;
- u64 end_time;
-} __packed;
-
-
-void nsp_msg(enum nsp_msg_stage stage, u64 begin_time, u64 end_time)
-{
- struct swap_msg *m;
- struct nsp_msg_struct *nsp;
-
- m = swap_msg_get(MSG_NSP);
-
- nsp = (struct nsp_msg_struct *)swap_msg_payload(m);
- nsp->pid = (u32)current->tgid;
- nsp->stage = (u32)stage;
- nsp->begin_time = begin_time;
- nsp->end_time = end_time;
-
- swap_msg_flush(m, sizeof(*nsp));
- swap_msg_put(m);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#ifndef _NSP_MSG_H
-#define _NSP_MSG_H
-
-
-#include <linux/types.h>
-
-
-enum nsp_msg_stage {
- NMS_MAPPING = 0x00,
- NMS_MAIN = 0x01,
- NMS_CREATE = 0x02,
- NMS_RESET = 0x03,
-};
-
-
-void nsp_msg(enum nsp_msg_stage stage, u64 begin_time, u64 end_time);
-
-
-#endif /* _NSP_MSG_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#ifndef _NSP_PRINT_H
-#define _NSP_PRINT_H
-
-
-#include <linux/printk.h>
-
-
-#define NSP_PREFIX "[NSP] "
-
-#define nsp_print(...) printk(NSP_PREFIX __VA_ARGS__)
-
-
-#endif /* _NSP_PRINT_H */
+++ /dev/null
-#!/bin/bash -ex
-
-if [ $# -ne 1 ]; then
- echo "Usage $0 <dest_dir>"
- exit 0
-fi
-
-scrdir=$(dirname $(readlink -f $0))
-
-dest=$1
-mkdir -p $dest
-
-mode=0666
-
-modules="master/swap_master.ko
- buffer/swap_buffer.ko
- ksyms/swap_ksyms.ko
- driver/swap_driver.ko
- writer/swap_writer.ko
- kprobe/swap_kprobe.ko
- ks_manager/swap_ks_manager.ko
- uprobe/swap_uprobe.ko
- us_manager/swap_us_manager.ko
- ks_features/swap_ks_features.ko
- sampler/swap_sampler.ko
- energy/swap_energy.ko
- parser/swap_message_parser.ko
- retprobe/swap_retprobe.ko
- loader/swap_loader.ko
- preload/swap_preload.ko
- uihv/swap_uihv.ko
- fbiprobe/swap_fbiprobe.ko
- wsp/swap_wsp.ko
- nsp/swap_nsp.ko
- task_ctx/swap_taskctx.ko
- got_patcher/swap_gtp.ko"
-
-for i in $modules; do
- mv $scrdir/$i $dest/
- chmod 0666 $dest/$(basename $i)
-done
%setup -q
%build
+cd modules
%ifarch armv7l
# TM1
./build.sh --kernel /boot/kernel/devel/kernel-devel-tizen_tm1 --arch arm clean
%endif
%endif
%endif
-install -m 770 deploy.sh -t %{buildroot}/opt/swap/sdk
+install -m 770 modules/deploy.sh -t %{buildroot}/opt/swap/sdk
# mkdir -p %{buildroot}/usr/share/license
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../energy/Module.symvers \
- $(src)/../ks_features/Module.symvers \
- $(src)/../driver/Module.symvers \
- $(src)/../writer/Module.symvers \
- $(src)/../us_manager/Module.symvers \
- $(src)/../sampler/Module.symvers
-
-obj-m := swap_message_parser.o
-swap_message_parser-y := swap_msg_parser.o \
- msg_parser.o \
- msg_buf.o \
- msg_cmd.o \
- features.o \
- us_inst.o \
- cpu_ctrl.o \
- usm_msg.o
+++ /dev/null
-/**
- * parser/cpu_ctrl.c
- * @author Vasiliy Ulyanov <v.ulyanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * CPU controls implementation.
- */
-
-#include <linux/cpumask.h>
-#include <linux/cpu.h>
-#include <ksyms/ksyms.h>
-
-#ifdef CONFIG_SMP
-static void (*swap_cpu_maps_update_begin)(void);
-static void (*swap_cpu_maps_update_done)(void);
-static int (*swap_cpu_down)(unsigned int, int);
-static int (*swap_cpu_up)(unsigned int, int);
-
-/**
- * @brief Disables nonboot CPUs lock.
- *
- * @param mask Pointer to CPU mask struct.
- * @return 0 on success, error code on error.
- */
-int swap_disable_nonboot_cpus_lock(struct cpumask *mask)
-{
- int boot_cpu, cpu;
- int ret = 0;
-
- swap_cpu_maps_update_begin();
- cpumask_clear(mask);
-
- boot_cpu = cpumask_first(cpu_online_mask);
-
- for_each_online_cpu(cpu) {
- if (cpu == boot_cpu)
- continue;
- ret = swap_cpu_down(cpu, 0);
- if (ret == 0)
- cpumask_set_cpu(cpu, mask);
- printk(KERN_INFO "===> SWAP CPU[%d] down(%d)\n", cpu, ret);
- }
-
- WARN_ON(num_online_cpus() > 1);
- return ret;
-}
-
-/**
- * @brief Enables nonboot CPUs unlock.
- *
- * @param mask Pointer to CPU mask struct.
- * @return 0 on success, error code on error.
- */
-int swap_enable_nonboot_cpus_unlock(struct cpumask *mask)
-{
- int cpu, ret = 0;
-
- if (cpumask_empty(mask))
- goto out;
-
- for_each_cpu(cpu, mask) {
- ret = swap_cpu_up(cpu, 0);
- printk(KERN_INFO "===> SWAP CPU[%d] up(%d)\n", cpu, ret);
- }
-
-out:
- swap_cpu_maps_update_done();
-
- return ret;
-}
-
-/**
- * @brief Intializes CPU controls.
- *
- * @return 0 on success, error code on error.
- */
-int init_cpu_deps(void)
-{
- const char *sym = "cpu_maps_update_begin";
-
- swap_cpu_maps_update_begin = (void *)swap_ksyms(sym);
- if (!swap_cpu_maps_update_begin)
- goto not_found;
-
- sym = "cpu_maps_update_done";
- swap_cpu_maps_update_done = (void *)swap_ksyms(sym);
- if (!swap_cpu_maps_update_done)
- goto not_found;
-
- sym = "_cpu_up";
- swap_cpu_up = (void *)swap_ksyms(sym);
- if (!swap_cpu_up)
- goto not_found;
-
- sym = "_cpu_down";
- swap_cpu_down = (void *)swap_ksyms(sym);
- if (!swap_cpu_down)
- goto not_found;
-
- return 0;
-
-not_found:
- printk(KERN_INFO "ERROR: symbol %s(...) not found\n", sym);
- return -ESRCH;
-}
-
-#endif /* CONFIG_SMP */
+++ /dev/null
-/**
- * @file parser/cpu_ctrl.h
- * @author Vasiliy Ulyanov <v.ulyanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * @section DESCRIPTION
- *
- * CPU controls interface.
- */
-
-#ifndef _CPU_CTRL_H_
-#define _CPU_CTRL_H_
-
-struct cpumask;
-
-#ifdef CONFIG_SMP
-int swap_disable_nonboot_cpus_lock(struct cpumask *mask);
-int swap_enable_nonboot_cpus_unlock(struct cpumask *mask);
-
-int init_cpu_deps(void);
-
-#else /* CONFIG_SMP */
-
-static inline int swap_disable_nonboot_cpus_lock(struct cpumask *mask)
-{
- return 0;
-}
-
-static inline int swap_enable_nonboot_cpus_unlock(struct cpumask *mask)
-{
- return 0;
-}
-
-static inline int init_cpu_deps(void)
-{
- return 0;
-}
-
-#endif /* CONFIG_SMP */
-
-#endif /* _CPU_CTRL_H_ */
+++ /dev/null
-/**
- * parser/features.c
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Features control implementation.
- */
-
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <ks_features/ks_features.h>
-#include <us_manager/us_manager.h>
-#include "parser_defs.h"
-#include "features.h"
-#include "msg_parser.h"
-
-#include <writer/swap_msg.h>
-#include <writer/event_filter.h>
-#include <sampler/swap_sampler_module.h>
-#include <energy/energy.h>
-
-/**
- * @enum features_list
- * List of supported features.
- */
-enum features_list {
- syscall_file = (1 << 10), /**< File operation syscalls tracing */
- syscall_ipc = (1 << 11), /**< IPC syscall tracing */
- syscall_process = (1 << 12), /**< Process syscalls tracing */
- syscall_signal = (1 << 13), /**< Signal syscalls tracing */
- syscall_network = (1 << 14), /**< Network syscalls tracing */
- syscall_desc = (1 << 15), /**< Descriptor syscalls tracing */
- context_switch = (1 << 16), /**< Context switch tracing */
- func_sampling = (1 << 19) /**< Function sampling */
-};
-
-/**
- * @brief Start user-space instrumentation event handling.
- *
- * @param conf Pointer to conf_data config.
- * @return 0.
- */
-int set_us_inst(struct conf_data *conf)
-{
- set_quiet(QT_OFF);
-
- return 0;
-}
-
-/**
- * @brief Stop user-space instrumentation event handling.
- *
- * @return 0.
- */
-int unset_us_inst(void)
-{
- set_quiet(QT_ON);
-
- return 0;
-}
-
-/**
- * @brief Set syscall file feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_syscall_file(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_FILE);
-
- return ret;
-}
-
-/**
- * @brief Set syscall file feature off.
- *
- * @return unset_feature results.
- */
-int unset_syscall_file(void)
-{
- int ret;
-
- ret = unset_feature(FID_FILE);
-
- return ret;
-}
-
-/**
- * @brief Set syscall ipc feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_syscall_ipc(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_IPC);
-
- return ret;
-}
-
-/**
- * @brief Set syscall ipc feature off.
- *
- * @return unset_feature results.
- */
-int unset_syscall_ipc(void)
-{
- int ret;
-
- ret = unset_feature(FID_IPC);
-
- return ret;
-}
-
-/**
- * @brief Set syscall process feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_syscall_process(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_PROCESS);
-
- return ret;
-}
-
-/**
- * @brief Set syscall process feature off.
- *
- * @return unset_feature results.
- */
-int unset_syscall_process(void)
-{
- int ret;
-
- ret = unset_feature(FID_PROCESS);
-
- return ret;
-}
-
-/**
- * @brief Set syscall signal feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_syscall_signal(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_SIGNAL);
-
- return ret;
-}
-
-/**
- * @brief Set syscall signal feature off.
- *
- * @return unset_feature results.
- */
-int unset_syscall_signal(void)
-{
- int ret;
-
- ret = unset_feature(FID_SIGNAL);
-
- return ret;
-}
-
-/**
- * @brief Set syscall network feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_syscall_network(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_NET);
-
- return ret;
-}
-
-/**
- * @brief Set syscall network feature off.
- *
- * @return unset_feature results.
- */
-int unset_syscall_network(void)
-{
- int ret;
-
- ret = unset_feature(FID_NET);
-
- return ret;
-}
-
-/**
- * @brief Set syscall desc feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_syscall_desc(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_DESC);
-
- return ret;
-}
-
-/**
- * @brief Set syscall desc feature off.
- *
- * @return unset_feature results.
- */
-int unset_syscall_desc(void)
-{
- int ret;
-
- ret = unset_feature(FID_DESC);
-
- return ret;
-}
-
-/**
- * @brief Set context switch feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_context_switch(struct conf_data *conf)
-{
- int ret;
-
- ret = set_feature(FID_SWITCH);
-
- return ret;
-}
-/**
- * @brief Set context switch feature off.
- *
- * @return unset_feature results.
- */
-int unset_context_switch(void)
-{
- int ret;
-
- ret = unset_feature(FID_SWITCH);
-
- return ret;
-}
-
-
-
-
-
-struct sample {
- u32 pid;
- u64 pc_addr;
- u32 tid;
- u32 cpu_num;
-} __packed;
-
-static void sample_msg(struct pt_regs *regs)
-{
- struct swap_msg *m;
- struct sample *s;
- struct task_struct *task = current;
-
- m = swap_msg_get(MSG_SAMPLE);
-
- s = swap_msg_payload(m);
- s->pid = task->tgid;
- s->pc_addr = instruction_pointer(regs);
- s->tid = task->pid;
- s->cpu_num = raw_smp_processor_id();
-
- swap_msg_flush(m, sizeof(*s));
- swap_msg_put(m);
-}
-
-static void sampler_cb(struct pt_regs *regs)
-{
- if (check_event(current))
- sample_msg(regs);
-}
-
-/**
- * @brief Set sampling feature on.
- *
- * @param conf Pointer to conf_data config.
- * @return set_feature results.
- */
-int set_func_sampling(struct conf_data *conf)
-{
- int ret;
-
- ret = swap_sampler_start(conf->data_msg_period, sampler_cb);
-
- return ret;
-}
-
-/**
- * @brief Set sampling feature off.
- *
- * @return unset_feature results.
- */
-int unset_func_sampling(void)
-{
- int ret;
-
- ret = swap_sampler_stop();
-
- return ret;
-}
-
-static int set_func_energy(struct conf_data *conf)
-{
- return set_energy();
-}
-
-static int unset_func_energy(void)
-{
- return unset_energy();
-}
-
-static int set_sysfile_activity(struct conf_data *conf)
-{
- return set_feature(FID_SYSFILE_ACTIVITY);
-}
-
-static int unset_sysfile_activity(void)
-{
- return unset_feature(FID_SYSFILE_ACTIVITY);
-}
-
-/**
- * @struct feature_item
- * @brief Struct that describes feature.
- * @var feature_item::name
- * Feature name.
- * @var feature_item::set
- * Set feature callback.
- * @var feature_item::unset
- * Unset feature callback.
- */
-struct feature_item {
- char *name;
- int (*set)(struct conf_data *conf);
- int (*unset)(void);
-};
-
-static struct feature_item feature_us_inst = {
- .name = "user space instrumentation",
- .set = set_us_inst,
- .unset = unset_us_inst
-};
-
-static struct feature_item feature_syscall_file = {
- .name = "file operation syscalls",
- .set = set_syscall_file,
- .unset = unset_syscall_file
-};
-
-static struct feature_item feature_syscall_ipc = {
- .name = "IPC syscall",
- .set = set_syscall_ipc,
- .unset = unset_syscall_ipc
-};
-
-static struct feature_item feature_syscall_process = {
- .name = "process syscalls",
- .set = set_syscall_process,
- .unset = unset_syscall_process
-};
-
-static struct feature_item feature_syscall_signal = {
- .name = "signal syscalls",
- .set = set_syscall_signal,
- .unset = unset_syscall_signal
-};
-
-static struct feature_item feature_syscall_network = {
- .name = "network syscalls",
- .set = set_syscall_network,
- .unset = unset_syscall_network
-};
-
-static struct feature_item feature_syscall_desc = {
- .name = "descriptor syscalls",
- .set = set_syscall_desc,
- .unset = unset_syscall_desc
-};
-
-static struct feature_item feature_context_switch = {
- .name = "context switch",
- .set = set_context_switch,
- .unset = unset_context_switch
-};
-
-static struct feature_item feature_func_sampling = {
- .name = "function sampling",
- .set = set_func_sampling,
- .unset = unset_func_sampling
-};
-
-static struct feature_item feature_func_energy = {
- .name = "energy",
- .set = set_func_energy,
- .unset = unset_func_energy
-};
-
-static struct feature_item feature_sysfile_activity = {
- .name = "system file activity",
- .set = set_sysfile_activity,
- .unset = unset_sysfile_activity
-};
-
-static struct feature_item *feature_list[] = {
- /* 0 */ NULL,
- /* 1 */ NULL,
- /* 2 */ &feature_us_inst,
- /* 3 */ NULL,
- /* 4 */ NULL,
- /* 5 */ NULL,
- /* 6 */ NULL,
- /* 7 */ NULL,
- /* 8 */ NULL,
- /* 9 */ NULL,
- /* 10 */ &feature_syscall_file,
- /* 11 */ &feature_syscall_ipc,
- /* 12 */ &feature_syscall_process,
- /* 13 */ &feature_syscall_signal,
- /* 14 */ &feature_syscall_network,
- /* 15 */ &feature_syscall_desc,
- /* 16 */ &feature_context_switch,
- /* 17 */ NULL,
- /* 18 */ NULL,
- /* 19 */ &feature_func_sampling,
- /* 20 */ NULL,
- /* 21 */ NULL,
- /* 22 */ NULL,
- /* 23 */ NULL,
- /* 24 */ NULL,
- /* 25 */ NULL,
- /* 26 */ &feature_func_energy,
- /* 27 */ NULL,
- /* 28 */ NULL,
- /* 29 */ NULL,
- /* 30 */ NULL,
- /* 31 */ NULL,
- /* 32 */ NULL,
- /* 33 */ NULL,
- /* 34 */ NULL,
- /* 35 */ NULL,
- /* 36 */ NULL,
- /* 37 */ NULL,
- /* 38 */ NULL,
- /* 39 */ NULL,
- /* 40 */ NULL,
- /* 41 */ NULL,
- /* 42 */ NULL,
- /* 43 */ NULL,
- /* 44 */ NULL,
- /* 45 */ NULL,
- /* 46 */ NULL,
- /* 47 */ NULL,
- /* 48 */ &feature_sysfile_activity,
-};
-
-/**
- * @brief SIZE_FEATURE_LIST definition.
- */
-enum {
- SIZE_FEATURE_LIST =
- sizeof(feature_list) / sizeof(struct feature_item *),
-};
-
-static u64 feature_inst;
-static u64 feature_mask;
-
-/**
- * @brief Inits features list.
- *
- * @return 0.
- */
-int once_features(void)
-{
- int i;
- for (i = 0; i < SIZE_FEATURE_LIST; ++i) {
- printk(KERN_INFO "### f init_feature_mask[%2d]=%p\n", i,
- feature_list[i]);
- if (feature_list[i] != NULL) {
- feature_mask |= ((u64)1) << i;
- printk(KERN_INFO "### f name=%s\n",
- feature_list[i]->name);
- }
- }
-
- return 0;
-}
-
-static int do_set_features(struct conf_data *conf)
-{
- int i, ret;
- u64 feature_XOR;
- u64 features, features_backup;
-
- /* TODO: field use_features1 is not used*/
- features_backup = features = conf->use_features0;
-
- features &= feature_mask;
- feature_XOR = features ^ feature_inst;
-
- for (i = 0; feature_XOR && i < SIZE_FEATURE_LIST; ++i) {
- if ((feature_XOR & 1) && feature_list[i] != NULL) {
- u64 f_mask;
- if (features & 1)
- ret = feature_list[i]->set(conf);
- else
- ret = feature_list[i]->unset();
-
- if (ret) {
- char *func = features & 1 ? "set" : "unset";
- print_err("%s '%s' ret=%d\n",
- func, feature_list[i]->name, ret);
-
- return ret;
- }
- f_mask = ~((u64)1 << i);
- feature_inst = (feature_inst & f_mask) |
- (features_backup & ~f_mask);
- }
-
- features >>= 1;
- feature_XOR >>= 1;
- }
-
- return 0;
-}
-
-static DEFINE_MUTEX(mutex_features);
-
-/**
- * @brief Wrapper for do_set_features with locking.
- *
- * @param conf Pointer to the current config.
- * @return do_set_features result.
- */
-int set_features(struct conf_data *conf)
-{
- int ret;
-
- mutex_lock(&mutex_features);
- ret = do_set_features(conf);
- mutex_unlock(&mutex_features);
-
- return ret;
-}
-
-void disable_all_features(void)
-{
- struct conf_data conf;
-
- conf.use_features0 = 0;
- conf.use_features1 = 0;
- conf.data_msg_period = 0;
- conf.sys_trace_period = 0;
-
- BUG_ON(set_features(&conf));
- BUG_ON(feature_inst);
-}
+++ /dev/null
-/**
- * @file parser/features.h
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Features control interface declaration.
- */
-
-
-#ifndef _FEATURES_H
-#define _FEATURES_H
-
-struct conf_data;
-
-int once_features(void);
-
-int set_features(struct conf_data *conf);
-void disable_all_features(void);
-
-#endif /* _FEATURES_H */
+++ /dev/null
-/**
- * parser/msg_buf.c
- * @author Vyacheslav Cherkashin
- * @author Vitaliy Cherepanov
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Message buffer controls implementation.
- */
-
-
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include "msg_buf.h"
-#include "parser_defs.h"
-
-/**
- * @brief Initializes message buffer.
- *
- * @param mb Pointer to message buffer struct.
- * @param size Size of the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int init_mb(struct msg_buf *mb, size_t size)
-{
- if (size) {
- mb->begin = vmalloc(size);
- if (mb->begin == NULL) {
- printk(KERN_INFO "Cannot alloc memory!\n");
- return -ENOMEM;
- }
-
- mb->ptr = mb->begin;
- mb->end = mb->begin + size;
- } else
- mb->begin = mb->end = mb->ptr = NULL;
-
- return 0;
-}
-
-/**
- * @brief Uninitializes message buffer.
- *
- * @param mb Pointer to message buffer struct.
- * @return Void.
- */
-void uninit_mb(struct msg_buf *mb)
-{
- vfree(mb->begin);
-}
-
-/**
- * @brief Checks if current buffer data pointer is at the end.
- *
- * @param mb Pointer to the message buffer struct.
- * @param size Size of the message buffer.
- * @return 1 - buffer ptr is not at the end,
- * 0 - buffer ptr is at the end,
- * -1 - buffer ptr is invalid (far away of the end).
- */
-int cmp_mb(struct msg_buf *mb, size_t size)
-{
- char *tmp;
-
- tmp = mb->ptr + size;
- if (mb->end > tmp)
- return 1;
- else if (mb->end < tmp)
- return -1;
-
- return 0;
-}
-
-/**
- * @brief Gets remainder part of message buffer.
- *
- * @param mb Pointer to the message buffer struct.
- * @return Remainder part of message buffer.
- */
-size_t remained_mb(struct msg_buf *mb)
-{
- return mb->end - mb->ptr;
-}
-
-/**
- * @brief Checks whether we reached the end of the message buffer or not.
- *
- * @param mb Pointer to the message buffer struct.
- * @return 1 - if message buffer end has been reached \n
- * 0 - otherwise.
- */
-int is_end_mb(struct msg_buf *mb)
-{
- return mb->ptr == mb->end;
-}
-
-/**
- * @brief Reads 8 bits from message buffer and updates buffer's pointer.
- *
- * @param mb Pointer to the message buffer struct.
- * @param val Pointer to the target variable where to put read value.
- * @return 0 on success, negative error code on error.
- */
-int get_u8(struct msg_buf *mb, u8 *val)
-{
- if (cmp_mb(mb, sizeof(*val)) < 0)
- return -EINVAL;
-
- *val = *((u8 *)mb->ptr);
- mb->ptr += sizeof(*val);
-
- print_parse_debug("u8 ->%d;%08X\n", *val, *val);
-
- return 0;
-}
-
-/**
- * @brief Reads 32 bits from message buffer and updates buffer's pointer.
- *
- * @param mb Pointer to the message buffer struct.
- * @param val Pointer to the target variable where to put read value.
- * @return 0 on success, negative error code on error.
- */
-int get_u32(struct msg_buf *mb, u32 *val)
-{
- if (cmp_mb(mb, sizeof(*val)) < 0)
- return -EINVAL;
-
- *val = *((u32 *)mb->ptr);
- mb->ptr += sizeof(*val);
-
- print_parse_debug("u32->%d;%08X\n", *val, *val);
-
- return 0;
-}
-
-/**
- * @brief Reads 64 bits from message buffer and updates buffer's pointer.
- *
- * @param mb Pointer to the message buffer struct.
- * @param val Pointer to the target variable where to put read value.
- * @return 0 on success, negative error code on error.
- */
-int get_u64(struct msg_buf *mb, u64 *val)
-{
- if (cmp_mb(mb, sizeof(*val)) < 0)
- return -EINVAL;
-
- *val = *((u64 *)mb->ptr);
- mb->ptr += sizeof(*val);
- print_parse_debug("u64->%llu; 0x%016llX\n", *val, *val);
-
- return 0;
-}
-
-/**
- * @brief Reads null-terminated string from message buffer and updates
- * buffer's pointer.
- *
- * @param mb Pointer to the message buffer struct.
- * @param str Pointer to the target variable where to put read string.
- * @return 0 on success, negative error code on error.
- */
-int get_string(struct msg_buf *mb, char **str)
-{
- size_t len, len_max;
- enum { min_len_str = 1 };
-
- if (cmp_mb(mb, min_len_str) < 0)
- return -EINVAL;
-
- len_max = remained_mb(mb) - 1;
- len = strnlen(mb->ptr, len_max);
-
- *str = kmalloc(len + 1, GFP_KERNEL);
- if (*str == NULL)
- return -ENOMEM;
-
- memcpy(*str, mb->ptr, len);
- (*str)[len] = '\0';
-
- mb->ptr += len + 1;
-
- print_parse_debug("str->'%s'\n", *str);
- return 0;
-}
-
-/**
- * @brief Releases string memory allocated in get_string.
- *
- * @param str Pointer to the target string.
- * @return Void.
- */
-void put_string(const char *str)
-{
- kfree(str);
-}
+++ /dev/null
-/**
- * @file parser/msg_buf.h
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Message buffer interface declaration.
- */
-
-
-#ifndef _MSG_BUF_H
-#define _MSG_BUF_H
-
-#include <linux/types.h>
-
-/**
- * @struct msg_buf
- * @brief Stores pointers to the message buffer.
- */
-struct msg_buf {
- char *begin; /**< Pointer to the beginning of the buffer. */
- char *end; /**< Pointer to the end of the buffer. */
- char *ptr; /**< Buffer iterator. */
-};
-
-int init_mb(struct msg_buf *mb, size_t size);
-void uninit_mb(struct msg_buf *mb);
-
-int cmp_mb(struct msg_buf *mb, size_t size);
-size_t remained_mb(struct msg_buf *mb);
-int is_end_mb(struct msg_buf *mb);
-
-int get_u8(struct msg_buf *mb, u8 *val);
-int get_u32(struct msg_buf *mb, u32 *val);
-int get_u64(struct msg_buf *mb, u64 *val);
-
-int get_string(struct msg_buf *mb, char **str);
-void put_string(const char *str);
-
-#endif /* _MSG_BUF_H */
+++ /dev/null
-/**
- * parser/msg_cmd.c
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Module's messages parsing implementation.
- */
-
-
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include "msg_parser.h"
-#include "msg_buf.h"
-#include "features.h"
-#include "parser_defs.h"
-#include "us_inst.h"
-#include <writer/swap_msg.h>
-#include <us_manager/us_manager.h>
-
-
-static int wrt_launcher_port;
-
-static int set_config(struct conf_data *conf)
-{
- int ret;
-
- ret = set_features(conf);
-
- return ret;
-}
-
-/**
- * @brief Message "keep alive" handling.
- *
- * @param mb Pointer to the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int msg_keep_alive(struct msg_buf *mb)
-{
- if (!is_end_mb(mb)) {
- print_err("to long message, remained=%zu", remained_mb(mb));
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * @brief Message "start" handling.
- *
- * @param mb Pointer to the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int msg_start(struct msg_buf *mb)
-{
- int ret = 0;
- struct conf_data conf;
- LIST_HEAD(app_head);
-
- swap_msg_seq_num_reset();
- swap_msg_discard_reset();
-
- if (!create_us_inst_data(mb, &app_head))
- return -EINVAL;
-
- if (!is_end_mb(mb)) {
- pr_info("Too long message, remained=%zu", remained_mb(mb));
- destroy_us_inst_data(&app_head);
- return -EINVAL;
- }
-
- ret = app_list_reg(&app_head);
- if (ret) {
- pr_info("Cannot mod us inst, ret = %d\n", ret);
- return ret;
- }
-
- ret = usm_start();
- if (ret) {
- app_list_unreg_all();
- return ret;
- }
-
- restore_config(&conf);
- set_config(&conf);
-
- return 0;
-}
-
-/**
- * @brief Message "stop" handling.
- *
- * @param mb Pointer to the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int msg_stop(struct msg_buf *mb)
-{
- int ret = 0;
- int discarded;
-
- if (!is_end_mb(mb)) {
- print_err("to long message, remained=%zu", remained_mb(mb));
- return -EINVAL;
- }
-
- ret = usm_stop();
- if (ret)
- return ret;
-
- app_list_unreg_all();
- disable_all_features();
-
- discarded = swap_msg_discard_get();
- printk(KERN_INFO "discarded messages: %d\n", discarded);
- swap_msg_discard_reset();
-
- return ret;
-}
-
-/**
- * @brief Message "config" handling.
- *
- * @param mb Pointer to the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int msg_config(struct msg_buf *mb)
-{
- int ret = 0;
- struct conf_data *conf;
- enum status_type st;
-
- conf = create_conf_data(mb);
- if (conf == NULL)
- return -EINVAL;
-
- if (!is_end_mb(mb)) {
- print_err("to long message, remained=%zu", remained_mb(mb));
- ret = -EINVAL;
- goto free_conf_data;
- }
-
- st = usm_get_status();
- if (st == ST_ON)
- set_config(conf);
-
- save_config(conf);
- usm_put_status(st);
-
-free_conf_data:
- destroy_conf_data(conf);
-
- return ret;
-}
-
-/**
- * @brief Message "swap inst add" handling.
- *
- * @param mb Pointer to the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int msg_swap_inst_add(struct msg_buf *mb)
-{
- LIST_HEAD(app_head);
-
- if (!create_us_inst_data(mb, &app_head))
- return -EINVAL;
-
- if (!is_end_mb(mb)) {
- pr_info("Too long message, remained=%zu", remained_mb(mb));
- destroy_us_inst_data(&app_head);
- return -EINVAL;
- }
-
- return app_list_reg(&app_head);
-}
-
-/**
- * @brief Message "swap inst remove" handling.
- *
- * @param mb Pointer to the message buffer.
- * @return 0 on success, negative error code on error.
- */
-int msg_swap_inst_remove(struct msg_buf *mb)
-{
- LIST_HEAD(app_head);
-
- INIT_LIST_HEAD(&app_head);
- if (!create_us_inst_data(mb, &app_head))
- return -EINVAL;
-
- if (!is_end_mb(mb)) {
- pr_info("Too long message, remained=%zu", remained_mb(mb));
- destroy_us_inst_data(&app_head);
- return -EINVAL;
- }
-
- return app_list_unreg(&app_head);
-}
-
-void set_wrt_launcher_port(int port)
-{
- wrt_launcher_port = port;
-}
-EXPORT_SYMBOL_GPL(set_wrt_launcher_port);
-
-#define GET_PORT_DELAY 100 /* msec */
-#define GET_PORT_TIMEOUT 10000 /* msec */
-
-int get_wrt_launcher_port(void)
-{
- int port;
- int timeout = GET_PORT_TIMEOUT;
-
- do {
- port = wrt_launcher_port;
- timeout -= GET_PORT_DELAY;
- mdelay(GET_PORT_DELAY);
- } while (!port && timeout > 0);
-
- set_wrt_launcher_port(0);
-
- return port;
-}
-
-/**
- * @brief Initializes commands handling.
- *
- * @return Initialization results.
- */
-int once_cmd(void)
-{
- return once_features();
-}
+++ /dev/null
-/**
- * @file parser/msg_cmd.h
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Module's message handling interface declaration.
- */
-
-#ifndef _MSG_CMD_H
-#define _MSG_CMD_H
-
-struct msg_buf;
-
-int once_cmd(void);
-
-int msg_keep_alive(struct msg_buf *mb);
-int msg_start(struct msg_buf *mb);
-int msg_stop(struct msg_buf *mb);
-int msg_config(struct msg_buf *mb);
-int msg_swap_inst_add(struct msg_buf *mb);
-int msg_swap_inst_remove(struct msg_buf *mb);
-int get_wrt_launcher_port(void);
-void set_wrt_launcher_port(int port);
-
-#endif /* _MSG_CMD_H */
+++ /dev/null
-/**
- * parser/msg_parser.c
- *
- * @author Vyacheslav Cherkashin
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @sectionLICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Message parsing implementation.
- */
-
-
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <us_manager/probes/probes.h>
-#include "msg_parser.h"
-#include "msg_buf.h"
-#include "parser_defs.h"
-
-/* ============================================================================
- * == APP_INFO ==
- * ============================================================================
- */
-
-/**
- * @brief Creates and fills pr_app_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param ai Pointer to the target app_inst_data.
- * @return 0 on success, error code on error.
- */
-struct pr_app_info *pr_app_info_create(struct msg_buf *mb)
-{
- int ret;
- struct pr_app_info *app_info;
- u32 app_type;
- char *str_id, *exec_path;
-
- app_info = kmalloc(sizeof(*app_info), GFP_KERNEL);
- if (!app_info)
- return ERR_PTR(-ENOMEM);
-
- print_parse_debug("app_info:\n");
- print_parse_debug("type:");
- ret = get_u32(mb, &app_type);
- if (ret) {
- print_err("failed to read target application type\n");
- goto free_app_info;
- }
-
- print_parse_debug("id:");
- ret = get_string(mb, &str_id);
- if (ret) {
- print_err("failed to read target application ID\n");
- goto free_app_info;
- }
-
- print_parse_debug("exec path:");
- ret = get_string(mb, &exec_path);
- if (ret) {
- print_err("failed to read executable path\n");
- goto free_id;
- }
-
- switch (app_type) {
- case AT_TIZEN_NATIVE_APP:
- case AT_TIZEN_WEB_APP:
- case AT_COMMON_EXEC:
- app_info->tgid = 0;
- break;
- case AT_PID: {
- u32 tgid = 0;
-
- if (*str_id != '\0') {
- ret = kstrtou32(str_id, 10, &tgid);
- if (ret) {
- print_err("converting string to PID, "
- "str='%s'\n", str_id);
- goto free_exec_path;
- }
- }
-
- app_info->tgid = tgid;
- break;
- }
- default:
- print_err("wrong application type(%u)\n", app_type);
- ret = -EINVAL;
- goto free_exec_path;
- }
-
- app_info->type = (enum APP_TYPE)app_type;
- app_info->id = str_id;
- app_info->path = exec_path;
-
- return app_info;
-
-free_exec_path:
- put_string(exec_path);
-free_id:
- put_string(str_id);
-free_app_info:
- kfree(app_info);
- return ERR_PTR(ret);
-}
-
-void pr_app_info_free(struct pr_app_info *app_info)
-{
- put_string(app_info->path);
- put_string(app_info->id);
- kfree(app_info);
-}
-
-int pr_app_info_cmp(struct pr_app_info *app0, struct pr_app_info *app1)
-{
- print_parse_debug("app0: %d, %d, %s, %s\n",
- app0->type, app0->tgid, app0->id, app0->path);
-
- print_parse_debug("app1: %d, %d, %s, %s\n",
- app1->type, app1->tgid, app1->id, app1->path);
-
- if ((app0->type == app1->type) &&
- (app0->tgid == app1->tgid) &&
- !strcmp(app0->id, app1->id) &&
- !strcmp(app0->path, app1->path)) {
- return 0;
- }
-
- return 1;
-}
-
-
-
-/* ============================================================================
- * == CONFIG ==
- * ============================================================================
- */
-
-/**
- * @brief Creates and fills conf_data struct.
- *
- * @param mb Pointer to the message buffer.
- * @return Pointer to the filled conf_data struct on success;\n
- * NULL on error.
- */
-struct conf_data *create_conf_data(struct msg_buf *mb)
-{
- struct conf_data *conf;
- u64 use_features0, use_features1;
- u32 stp, dmp;
-
- print_parse_debug("conf_data:\n");
-
- print_parse_debug("features:");
- if (get_u64(mb, &use_features0)) {
- print_err("failed to read use_features\n");
- return NULL;
- }
-
- if (get_u64(mb, &use_features1)) {
- print_err("failed to read use_features\n");
- return NULL;
- }
-
- print_parse_debug("sys trace period:");
- if (get_u32(mb, &stp)) {
- print_err("failed to read sys trace period\n");
- return NULL;
- }
-
- print_parse_debug("data msg period:");
- if (get_u32(mb, &dmp)) {
- print_err("failed to read data message period\n");
- return NULL;
- }
-
- conf = kmalloc(sizeof(*conf), GFP_KERNEL);
- if (conf == NULL) {
- print_err("out of memory\n");
- return NULL;
- }
-
- conf->use_features0 = use_features0;
- conf->use_features1 = use_features1;
- conf->sys_trace_period = stp;
- conf->data_msg_period = dmp;
-
- return conf;
-}
-
-/**
- * @brief conf_data cleanup.
- *
- * @param conf Pointer to the target conf_data.
- * @return Void.
- */
-void destroy_conf_data(struct conf_data *conf)
-{
- kfree(conf);
-}
-
-static struct conf_data config;
-
-/**
- * @brief Saves config to static config variable.
- *
- * @param conf Variable to save.
- * @return Void.
- */
-void save_config(const struct conf_data *conf)
-{
- memcpy(&config, conf, sizeof(config));
-}
-
-/**
- * @brief Restores config from static config variable.
- *
- * @param conf Variable to restore.
- * @return Void.
- */
-void restore_config(struct conf_data *conf)
-{
- memcpy(conf, &config, sizeof(*conf));
-}
-
-
-
-/* ============================================================================
- * == PROBES PARSING ==
- * ============================================================================
- */
-
-/**
- * @brief Gets retprobe data and puts it to the probe_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param pd Pointer to the probe_desc struct.
- * @return 0 on success, error code on error.
- */
-int get_retprobe(struct msg_buf *mb, struct probe_desc *pd)
-{
- char *args;
- char ret_type;
-
- print_parse_debug("funct args:");
- if (get_string(mb, &args)) {
- print_err("failed to read data function arguments\n");
- return -EINVAL;
- }
-
- print_parse_debug("funct ret type:");
- if (get_u8(mb, (u8 *)&ret_type)) {
- print_err("failed to read data function arguments\n");
- goto free_args;
- }
-
- pd->type = SWAP_RETPROBE;
- pd->info.rp_i.args = args;
- pd->info.rp_i.ret_type = ret_type;
-
- return 0;
-
-free_args:
- put_string(args);
- return -EINVAL;
-}
-
-/**
- * @brief Retprobe data cleanup.
- *
- * @param pi Pointer to the probe_info comprising retprobe.
- * @return Void.
- */
-void put_retprobe(struct probe_info *pi)
-{
- put_string(pi->rp_i.args);
-}
-
-static int cmp_retprobe(struct probe_info *p0, struct probe_info *p1)
-{
- if (p0->rp_i.ret_type == p1->rp_i.ret_type &&
- !strcmp(p0->rp_i.args, p1->rp_i.args)) {
- return 0;
- }
-
- return 1;
-}
-
-/**
- * @brief Gets preload data and puts it to the probe_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param pd Pointer to the probe_desc struct.
- * @return 0 on success, error code on error.
- */
-int get_preload_probe(struct msg_buf *mb, struct probe_desc *pd)
-{
- u64 handler;
- u8 flags;
- char *path;
-
- print_parse_debug("funct handler:");
- if (get_u64(mb, &handler)) {
- print_err("failed to read function handler\n");
- return -EINVAL;
- }
-
- print_parse_debug("collect events flag:");
- if (get_u8(mb, &flags)) {
- print_err("failed to read collect events type\n");
- return -EINVAL;
- }
-
- print_parse_debug("handler library:");
- if (get_string(mb, &path)) {
- print_err("failed to read handler library path\n");
- return -EINVAL;
- }
-
- pd->type = SWAP_PRELOAD_PROBE;
- pd->info.pl_i.handler = handler;
- pd->info.pl_i.flags = flags;
- pd->info.pl_i.path = path;
-
- return 0;
-}
-
-/**
- * @brief Preload probe data cleanup.
- *
- * @param pi Pointer to the probe_info struct.
- * @return Void.
- */
-void put_preload_probe(struct probe_info *pi)
-{
- put_string((char *)pi->pl_i.path);
-}
-
-static int cmp_preload_probe(struct probe_info *p0, struct probe_info *p1)
-{
- if (p0->pl_i.handler == p1->pl_i.handler &&
- p0->pl_i.flags == p1->pl_i.flags) {
- return 0;
- }
-
- return 1;
-}
-
-/**
- * @brief Gets preload get_caller and puts it to the probe_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param pd Pointer to the probe_desc struct.
- * @return 0 on success, error code on error.
- */
-
-int get_get_caller_probe(struct msg_buf *mb, struct probe_desc *pd)
-{
- pd->type = SWAP_GET_CALLER;
-
- return 0;
-}
-
-/**
- * @brief Preload get_caller probe data cleanup.
- *
- * @param pi Pointer to the probe_info struct.
- * @return Void.
- */
-void put_get_caller_probe(struct probe_info *pi)
-{
-}
-
-static int cmp_get_caller_probe(struct probe_info *p0, struct probe_info *p1)
-{
- return 0;
-}
-
-/**
- * @brief Gets preload get_call_type and puts it to the probe_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param pd Pointer to the probe_desc struct.
- * @return 0 on success, error code on error.
- */
-int get_get_call_type_probe(struct msg_buf *mb, struct probe_desc *pd)
-{
- pd->type = SWAP_GET_CALL_TYPE;
-
- return 0;
-}
-
-/**
- * @brief Preload get_call type probe data cleanup.
- *
- * @param pi Pointer to the probe_info struct.
- * @return Void.
- */
-void put_get_call_type_probe(struct probe_info *pi)
-{
-}
-
-static int cmp_get_caller_type_probe(struct probe_info *p0,
- struct probe_info *p1)
-{
- return 0;
-}
-
-/**
- * @brief Gets preload write_msg and puts it to the probe_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param pi Pointer to the probe_info struct.
- * @return 0 on success, error code on error.
- */
-int get_write_msg_probe(struct msg_buf *mb, struct probe_desc *pd)
-{
- pd->type = SWAP_WRITE_MSG;
-
- return 0;
-}
-
-/**
- * @brief Preload write_msg type probe data cleanup.
- *
- * @param pi Pointer to the probe_info comprising retprobe.
- * @return Void.
- */
-void put_write_msg_probe(struct probe_info *pi)
-{
-}
-
-static int cmp_write_msg_probe(struct probe_info *p0, struct probe_info *p1)
-{
- return 0;
-}
-
-/**
- * @brief Gets FBI probe data and puts it to the probe_info struct.
- *
- * @param mb Pointer to the message buffer.
- * @param pi Pointer to the probe_info struct.
- * @return 0 on success, error code on error.
- */
-int get_fbi_data(struct msg_buf *mb, struct fbi_var_data *vd)
-{
- u64 var_id;
- u64 reg_offset;
- u8 reg_n;
- u32 data_size;
- u8 steps_count, i;
- struct fbi_step *steps = NULL;
-
- print_parse_debug("var ID:");
- if (get_u64(mb, &var_id)) {
- print_err("failed to read var ID\n");
- return -EINVAL;
- }
-
- print_parse_debug("register offset:");
- if (get_u64(mb, ®_offset)) {
- print_err("failed to read register offset\n");
- return -EINVAL;
- }
-
- print_parse_debug("register number:");
- if (get_u8(mb, ®_n)) {
- print_err("failed to read number of the register\n");
- return -EINVAL;
- }
-
- print_parse_debug("data size:");
- if (get_u32(mb, &data_size)) {
- print_err("failed to read data size\n");
- return -EINVAL;
- }
-
- print_parse_debug("steps count:");
- if (get_u8(mb, &steps_count)) {
- print_err("failed to read steps count\n");
- return -EINVAL;
- }
-
- if (steps_count > 0) {
- steps = kmalloc(steps_count * sizeof(*vd->steps),
- GFP_KERNEL);
- if (steps == NULL) {
- print_err("MALLOC FAIL\n");
- return -ENOMEM;
- }
-
- for (i = 0; i != steps_count; i++) {
- print_parse_debug("steps #%d ptr_order:", i);
- if (get_u8(mb, &(steps[i].ptr_order))) {
- print_err("failed to read pointer order(step #%d)\n",
- i);
- goto free_steps;
- }
- print_parse_debug("steps #%d data_offset:", i);
- if (get_u64(mb, &(steps[i].data_offset))){
- print_err("failed to read offset (steps #%d)\n",
- i);
- goto free_steps;
- }
- }
- }
-
- vd->reg_n = reg_n;
- vd->reg_offset = reg_offset;
- vd->data_size = data_size;
- vd->var_id = var_id;
- vd->steps_count = steps_count;
- vd->steps = steps;
-
- return 0;
-
-free_steps:
- kfree(steps);
- return -EINVAL;
-}
-
-int get_fbi_probe(struct msg_buf *mb, struct probe_desc *pd)
-{
- uint8_t var_count, i;
- struct fbi_var_data *vars;
-
- print_parse_debug("var count:");
- if (get_u8(mb, &var_count)) {
- print_err("failed to read var ID\n");
- return -EINVAL;
- }
-
- vars = kmalloc(var_count * sizeof(*vars), GFP_KERNEL);
- if (vars == NULL) {
- print_err("alloc vars error\n");
- goto err;
- }
-
- for (i = 0; i != var_count; i++) {
- if (get_fbi_data(mb, &vars[i]) != 0)
- goto free_vars;
- }
-
- pd->type = SWAP_FBIPROBE;
- pd->info.fbi_i.var_count = var_count;
- pd->info.fbi_i.vars = vars;
- return 0;
-
-free_vars:
- kfree(vars);
-
-err:
- return -EINVAL;
-
-}
-
-/**
- * @brief FBI probe data cleanup.
- *
- * @param pi Pointer to the probe_info comprising FBI probe.
- * @return Void.
- */
-void put_fbi_probe(struct probe_info *pi)
-{
- /* FIXME: memory leak (vars) */
- return;
-}
-
-static int cmp_fbi_probe(struct probe_info *p0, struct probe_info *p1)
-{
- /* TODO: to implement */
- return 0;
-}
-
-
-/* ============================================================================
- * == PROBE ==
- * ============================================================================
- */
-
-/**
- * @brief Creates and fills func_inst_data struct.
- *
- * @param mb Pointer to the message buffer.
- * @return Pointer to the filled func_inst_data struct on success;\n
- * 0 on error.
- */
-struct pr_probe_desc *pr_probe_desc_create(struct msg_buf *mb)
-{
- int ret = -EINVAL;
- struct pr_probe_desc *probe;
- int (*get_probe)(struct msg_buf *mb, struct probe_desc *pd);
- u64 addr;
- u8 type;
-
- print_parse_debug("func addr:");
- if (get_u64(mb, &addr)) {
- print_err("failed to read data function address\n");
- return ERR_PTR(-EINVAL);
- }
-
- print_parse_debug("probe type:");
- if (get_u8(mb, &type)) {
- print_err("failed to read data probe type\n");
- return ERR_PTR(-EINVAL);
- }
-
- probe = kmalloc(sizeof(*probe), GFP_KERNEL);
- if (!probe) {
- print_err("out of memory\n");
- return ERR_PTR(-ENOMEM);
- }
- INIT_LIST_HEAD(&probe->list);
-
- switch (type) {
- case SWAP_RETPROBE:
- get_probe = get_retprobe;
- break;
- case SWAP_PRELOAD_PROBE:
- get_probe = get_preload_probe;
- break;
- case SWAP_GET_CALLER:
- get_probe = get_get_caller_probe;
- break;
- case SWAP_GET_CALL_TYPE:
- get_probe = get_get_call_type_probe;
- break;
- case SWAP_FBIPROBE:
- get_probe = get_fbi_probe;
- break;
- case SWAP_WRITE_MSG:
- get_probe = get_write_msg_probe;
- break;
- default:
- printk(KERN_WARNING "SWAP PARSER: Wrong probe type %d!\n",
- type);
- ret = -EINVAL;
- goto err;
- }
-
- ret = get_probe(mb, &probe->p_desc);
- if (ret)
- goto err;
-
- probe->addr = addr;
- return probe;
-
-err:
- kfree(probe);
- return ERR_PTR(ret);
-}
-
-
-/**
- * @brief func_inst_data cleanup.
- *
- * @param fi Pointer to the target func_inst_data.
- * @return Void.
- */
-void pr_probe_desc_free(struct pr_probe_desc *probe)
-{
- switch (probe->p_desc.type) {
- case SWAP_RETPROBE:
- put_retprobe(&(probe->p_desc.info));
- break;
- case SWAP_PRELOAD_PROBE:
- put_preload_probe(&(probe->p_desc.info));
- break;
- case SWAP_GET_CALLER:
- put_get_caller_probe(&(probe->p_desc.info));
- break;
- case SWAP_GET_CALL_TYPE:
- put_get_call_type_probe(&(probe->p_desc.info));
- break;
- case SWAP_FBIPROBE:
- put_fbi_probe(&(probe->p_desc.info));
- break;
- case SWAP_WRITE_MSG:
- put_write_msg_probe(&(probe->p_desc.info));
- break;
- default:
- pr_err("SWAP PARSER: Wrong probe type %d!\n",
- probe->p_desc.type);
- }
-
- kfree(probe);
-}
-
-int probe_inst_info_cmp(struct pr_probe_desc *p0, struct pr_probe_desc *p1)
-{
- enum probe_t type;
- int (*cmp_probe)(struct probe_info *p0, struct probe_info *p1);
-
- if (p0->addr != p1->addr &&
- p0->p_desc.type != p1->p_desc.type)
- return 1;
-
- type = p0->p_desc.type;
- switch (type) {
- case SWAP_RETPROBE:
- cmp_probe = cmp_retprobe;
- break;
- case SWAP_PRELOAD_PROBE:
- cmp_probe = cmp_preload_probe;
- break;
- case SWAP_GET_CALLER:
- cmp_probe = cmp_get_caller_probe;
- break;
- case SWAP_GET_CALL_TYPE:
- cmp_probe = cmp_get_caller_type_probe;
- break;
- case SWAP_FBIPROBE:
- cmp_probe = cmp_fbi_probe;
- break;
- case SWAP_WRITE_MSG:
- cmp_probe = cmp_write_msg_probe;
- break;
- default:
- pr_err("SWAP PARSER: Wrong probe type %d!\n", type);
- return 1;
- }
-
- return cmp_probe(&p0->p_desc.info, &p1->p_desc.info);
-}
-
-static struct pr_bin_info *pr_bin_info_create(const char *path)
-{
- struct pr_bin_info *info;
-
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return ERR_PTR(-ENOMEM);
-
- info->path = kstrdup(path, GFP_KERNEL);
- if (!info->path) {
- kfree(info);
- return ERR_PTR(-ENOMEM);
- }
-
- return info;
-}
-
-static void pr_bin_info_free(struct pr_bin_info *info)
-{
- kfree(info->path);
- kfree(info);
-}
-
-
-struct pr_bin_desc *pr_bin_desc_create(const char *path,
- struct list_head *probe_list)
-{
- struct pr_bin_desc *bin;
-
- bin = kmalloc(sizeof(*bin), GFP_KERNEL);
- if (!bin)
- return ERR_PTR(-ENOMEM);
-
- bin->info = pr_bin_info_create(path);
- if (IS_ERR(bin->info)) {
- long err = PTR_ERR(bin->info);
-
- kfree(bin);
- return ERR_PTR(err);
- }
-
- INIT_LIST_HEAD(&bin->list);
- INIT_LIST_HEAD(&bin->probe_head);
- list_splice_init(probe_list, &bin->probe_head);
-
- return bin;
-}
-
-void pr_bin_desc_free(struct pr_bin_desc *bin)
-{
- struct pr_probe_desc *p, *n;
-
- list_for_each_entry_safe(p, n, &bin->probe_head, list) {
- list_del(&p->list);
- pr_probe_desc_free(p);
- }
-
- pr_bin_info_free(bin->info);
- kfree(bin);
-}
-
-int pr_bin_info_cmp(struct pr_bin_info *b0, struct pr_bin_info *b1)
-{
- return strcmp(b0->path, b1->path);
-}
-
-
-static void pr_probe_desc_list_free(struct list_head *head)
-{
- struct pr_probe_desc *probe, *n;
-
- list_for_each_entry_safe(probe, n, head, list) {
- list_del(&probe->list);
- pr_probe_desc_free(probe);
- }
-}
-
-static int pr_probe_desc_list_create(struct msg_buf *mb, struct list_head *head)
-{
- u32 i, cnt;
-
- if (get_u32(mb, &cnt)) {
- print_err("failed to read count of functions\n");
- return -EINVAL;
- }
-
- print_parse_debug("probe count:%d", cnt);
- if (remained_mb(mb) / MIN_SIZE_FUNC_INST < cnt) {
- print_err("to match count of probes(%u)\n", cnt);
- return -EINVAL;
- }
-
- for (i = 0; i < cnt; ++i) {
- struct pr_probe_desc *probe;
-
- print_parse_debug("probe #%d:\n", i + 1);
- probe = pr_probe_desc_create(mb);
- if (IS_ERR(probe)) {
- pr_probe_desc_list_free(head);
- return PTR_ERR(probe);
- }
-
- list_add(&probe->list, head);
- }
-
- return cnt;
-}
-
-
-struct pr_bin_desc *bin_info_by_lib(struct msg_buf *mb)
-{
- u32 cnt;
- char *path;
- struct pr_bin_desc *bin = ERR_PTR(-EINVAL);
-
- print_parse_debug("bin path:");
- if (get_string(mb, &path)) {
- print_err("failed to read path of binary\n");
- return bin;
- }
-
- print_parse_debug("func count:");
- if (get_u32(mb, &cnt)) {
- print_err("failed to read count of functions\n");
- goto free_path;
- }
-
- if (remained_mb(mb) / MIN_SIZE_FUNC_INST < cnt) {
- print_err("to match count of functions(%u)\n", cnt);
- goto free_path;
- }
-
- if (cnt) {
- u32 i;
- LIST_HEAD(probe_head);
-
- for (i = 0; i < cnt; ++i) {
- struct pr_probe_desc *probe;
- print_parse_debug("func #%d:\n", i + 1);
- probe = pr_probe_desc_create(mb);
- if (IS_ERR(probe)) {
- /* set error to 'bin' */
- bin = ERR_PTR(PTR_ERR(probe));
- pr_probe_desc_list_free(&probe_head);
- goto free_path;
- }
-
- list_add(&probe->list, &probe_head);
- }
-
- bin = pr_bin_desc_create(path, &probe_head);
- if (IS_ERR(bin))
- pr_probe_desc_list_free(&probe_head);
- }
-
-free_path:
- put_string(path);
- return bin;
-}
-
-
-
-
-void bin_info_list_free(struct list_head *head)
-{
- struct pr_bin_desc *bin, *n;
-
- list_for_each_entry_safe(bin, n, head, list) {
- list_del(&bin->list);
- pr_bin_desc_free(bin);
- }
-}
-
-int bin_info_list_create(struct msg_buf *mb, struct list_head *head)
-{
- u32 i, cnt;
-
- if (get_u32(mb, &cnt)) {
- print_err("failed to read count of binaries\n");
- return -EINVAL;
- }
- print_parse_debug("bin count:i%d", cnt);
-
- if (remained_mb(mb) / MIN_SIZE_LIB_INST < cnt) {
- print_err("to match count of binaries(%u)\n", cnt);
- return -EINVAL;
- }
-
- for (i = 0; i < cnt; ++i) {
- struct pr_bin_desc *bin;
-
- print_parse_debug("bin #%d:\n", i_lib + 1);
- bin = bin_info_by_lib(mb);
- if (IS_ERR(bin)) {
- bin_info_list_free(head);
- return PTR_ERR(bin);
- } else if (bin) {
- list_add(&bin->list, head);
- }
- }
-
- return cnt;
-}
-
-
-/* ============================================================================
- * == APP ==
- * ============================================================================
- */
-
-/**
- * @brief Creates and fills pr_app_desc struct.
- *
- * @param mb Pointer to the message buffer.
- * @return Pointer to the filled app_inst_data struct on success;\n
- * 0 on error.
- */
-struct pr_app_desc *pr_app_desc_create(struct msg_buf *mb)
-{
- int cnt_probe, cnt_bin, ret = -EINVAL;
- struct pr_app_info *app_info;
- struct pr_app_desc *app;
- LIST_HEAD(probe_head);
- LIST_HEAD(bin_head);
-
- app = kmalloc(sizeof(*app), GFP_KERNEL);
- if (!app) {
- print_err("%s: Out of memory\n", __func__);
- return ERR_PTR(-ENOMEM);
- }
-
- app_info = pr_app_info_create(mb);
- if (IS_ERR(app_info)) {
- ret = PTR_ERR(app_info);
- goto free_app_inst;
- }
-
- app->info = app_info;
- app->pfg = NULL;
- INIT_LIST_HEAD(&app->list);
- INIT_LIST_HEAD(&app->bin_head);
-
- cnt_probe = pr_probe_desc_list_create(mb, &probe_head);
- if (cnt_probe < 0) {
- ret = cnt_probe;
- goto free_app_info;
- } else if (cnt_probe) {
- struct pr_bin_desc *bin;
-
- bin = pr_bin_desc_create(app_info->path, &probe_head);
- if (IS_ERR(bin)) {
- pr_probe_desc_list_free(&probe_head);
- ret = PTR_ERR(bin);
- goto free_app_info;
- }
-
- /* add pr_bin_desc */
- list_add(&bin->list, &app->bin_head);
- }
-
- cnt_bin = bin_info_list_create(mb, &app->bin_head);
- if (cnt_bin < 0) {
- ret = cnt_bin;
- goto free_bins;
- }
-
- return app;
-
-free_bins:
- bin_info_list_free(&app->bin_head);
-free_app_info:
- pr_app_info_free(app_info);
-free_app_inst:
- kfree(app);
- return ERR_PTR(ret);
-}
-
-/**
- * @brief pr_app_desc cleanup.
- *
- * @param ai Pointer to the target app_inst_data.
- * @return Void.
- */
-void pr_app_desc_free(struct pr_app_desc *app)
-{
- bin_info_list_free(&app->bin_head);
- pr_app_info_free(app->info);
- kfree(app);
-}
-
-
-/* ============================================================================
- * == US_INST ==
- * ============================================================================
- */
-
-/**
- * @brief Creates and fills us_inst_data struct.
- *
- * @param mb Pointer to the message buffer.
- * @param head Pointer to the list head.
- * @return u32 count of created elements.
- */
-u32 create_us_inst_data(struct msg_buf *mb,
- struct list_head *head)
-{
- u32 cnt, i;
-
- print_parse_debug("us_inst_data:\n");
-
- print_parse_debug("app count:");
- if (get_u32(mb, &cnt)) {
- print_err("failed to read count of applications\n");
- return 0;
- }
-
- if (remained_mb(mb) / MIN_SIZE_APP_INST < cnt) {
- print_err("to match count of applications(%u)\n", cnt);
- return 0;
- }
-
- for (i = 0; i < cnt; ++i) {
- struct pr_app_desc *app;
-
- print_parse_debug("app #%d:\n", i + 1);
- app = pr_app_desc_create(mb);
- if (IS_ERR(app))
- goto err;
-
- list_add_tail(&app->list, head);
- }
-
- return cnt;
-
-err:
- destroy_us_inst_data(head);
- return 0;
-}
-
-/**
- * @brief us_inst_data cleanup.
- *
- * @param head Pointer to the list head.
- * @return Void.
- */
-void destroy_us_inst_data(struct list_head *head)
-{
- struct pr_app_desc *app, *n;
-
- list_for_each_entry_safe(app, n, head, list) {
- list_del(&app->list);
- pr_app_desc_free(app);
- }
-}
+++ /dev/null
-/**
- * @file parser/msg_parser.h
- * @author Vyacheslav Cherkashin
- * @author Vitaliy Cherepanov
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Message parsing interface declaration.
- */
-
-
-#ifndef _MSG_PARSER_H
-#define _MSG_PARSER_H
-
-#include <linux/types.h>
-#include <us_manager/probes/probes.h>
-
-struct img_ip;
-struct msg_buf;
-
-/**
- * @enum APP_TYPE
- * Supported application types.
- */
-enum APP_TYPE {
- AT_TIZEN_NATIVE_APP = 0x01, /**< Tizen native application. */
- AT_PID = 0x02, /**< App with specified PID. */
- AT_COMMON_EXEC = 0x03, /**< Common application. */
- AT_TIZEN_WEB_APP = 0x04 /**< Tizen web application. */
-};
-
-/**
- * @brief App type size defenition.
- */
-enum {
- SIZE_APP_TYPE = 4
-};
-
-/**
- * @struct conf_data
- * @brief Configuration struct.
- */
-struct conf_data {
- u64 use_features0; /**< Feature flags. */
- u64 use_features1; /**< Feature flags. */
- u32 sys_trace_period; /**< Trace period. */
- u32 data_msg_period; /**< Data message period. */
-};
-
-/**
- * @brief The pr_app_info struct
- */
-struct pr_app_info {
- enum APP_TYPE type; /**< Application type. */
- pid_t tgid; /**< Application PID. */
- char *id; /**< Application ID */
- char *path; /**< Application execution path. */
-};
-
-/**
- * @brief The pr_bin_info struct
- */
-struct pr_bin_info {
- char *path;
-};
-
-/**
- * @brief The pr_app_desc struct
- */
-struct pr_app_desc {
- struct list_head list;
-
- struct pr_app_info *info;
- struct pf_group *pfg;
- struct list_head bin_head;
-};
-
-/**
- * @brief The pr_bin_desc struct
- */
-struct pr_bin_desc {
- struct list_head list;
-
- struct pr_bin_info *info;
-
- struct list_head probe_head;
-};
-
-struct pr_probe_desc {
- struct list_head list;
-
- /* register info */
- u64 addr;
- struct probe_desc p_desc;
-
- /* unregister info */
- struct img_ip *ip;
-};
-
-
-struct pr_app_info *pr_app_info_create(struct msg_buf *mb);
-void pr_app_info_free(struct pr_app_info *app_info);
-int pr_app_info_cmp(struct pr_app_info *app0, struct pr_app_info *app1);
-
-struct pr_bin_desc *pr_bin_desc_create(const char *path,
- struct list_head *probe_list);
-void pr_bin_desc_free(struct pr_bin_desc *bin);
-
-int pr_bin_info_cmp(struct pr_bin_info *b0, struct pr_bin_info *b1);
-
-struct pr_probe_desc *pr_probe_desc_create(struct msg_buf *mb);
-void pr_probe_desc_free(struct pr_probe_desc *probe_info);
-int probe_inst_info_cmp(struct pr_probe_desc *p0, struct pr_probe_desc *p1);
-
-struct conf_data *create_conf_data(struct msg_buf *mb);
-void destroy_conf_data(struct conf_data *conf);
-
-void save_config(const struct conf_data *conf);
-void restore_config(struct conf_data *conf);
-
-struct pr_app_desc *pr_app_desc_create(struct msg_buf *mb);
-void pr_app_desc_free(struct pr_app_desc *app);
-
-u32 create_us_inst_data(struct msg_buf *mb, struct list_head *head);
-void destroy_us_inst_data(struct list_head *head);
-
-
-/**
- * @brief Constant defenitions.
- */
-enum {
- MIN_SIZE_STRING = 1,
- MIN_SIZE_FUNC_INST = 8 /* address size */ +
- MIN_SIZE_STRING,
- MIN_SIZE_LIB_INST = MIN_SIZE_STRING +
- 4 /* lib counter */,
- MIN_SIZE_APP_INFO = SIZE_APP_TYPE + MIN_SIZE_STRING + MIN_SIZE_STRING,
- MIN_SIZE_APP_INST = MIN_SIZE_APP_INFO +
- 4 /* probe counter */ +
- 4 /* lib counter */,
-};
-
-#endif /* _MSG_PARSER_H */
+++ /dev/null
-/**
- * @file modules/parser/parser_defs.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov:
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Parser defenitions.
- */
-
-
-#ifndef __SWAP_DRIVER_DEVICE_DEFS_H__
-#define __SWAP_DRIVER_DEVICE_DEFS_H__
-
-#include <linux/kernel.h>
-
-/* #define PARSE_DEBUG */
-
-/** Prints debug message. */
-#define print_debug(msg, args...) \
- printk(KERN_DEBUG "SWAP_PARSER DEBUG : " msg, ##args)
-/** Prints info message. */
-#define print_msg(msg, args...) \
- printk(KERN_INFO "SWAP_PARSER : " msg, ##args)
-/** Prints warning message. */
-#define print_warn(msg, args...) \
- printk(KERN_WARNING "SWAP_PARSER WARNING : " msg, ##args)
-/** Prints error message. */
-#define print_err(msg, args...) \
- printk(KERN_ERR "SWAP_PARSER ERROR : " msg, ##args)
-/** Prints critical error message. */
-#define print_crit(msg, args...) \
- printk(KERN_CRIT "SWAP_PARSER CRITICAL : " msg, ##args)
-
-/* debug parse */
-#ifdef PARSE_DEBUG
-#define print_parse_debug(msg, args...) \
- printk(KERN_DEBUG "SWAP_PARSER DEBUG : " msg, ##args)
-#else
-#define print_parse_debug(msg, args...) \
- do {} while (0)
-#endif /* PARSE_DEBUG */
-
-#endif /* __SWAP_DRIVER_DEVICE_DEFS_H__ */
+++ /dev/null
-/**
- * parser/swap_msg_parser.c
- * @author Vyacheslav Cherkashin
- * @author Vitaliy Cherepanov
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Parser module interface implementation.
- */
-
-
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <linux/cpumask.h>
-#include <linux/uaccess.h>
-
-#include "parser_defs.h"
-#include "us_inst.h"
-#include "msg_buf.h"
-#include "msg_cmd.h"
-#include "usm_msg.h"
-#include "cpu_ctrl.h"
-#include "features.h"
-
-#include <driver/driver_to_msg.h>
-#include <driver/swap_ioctl.h>
-#include <master/swap_initializer.h>
-
-/**
- * @enum MSG_ID
- * @brief Message IDs.
- */
-enum MSG_ID {
- MSG_KEEP_ALIVE = 0x0001, /**< Keep alive message. */
- MSG_START = 0x0002, /**< Start message. */
- MSG_STOP = 0x0003, /**< Stop message. */
- MSG_CONFIG = 0x0004, /**< Config message. */
- MSG_SWAP_INST_ADD = 0x0008, /**< Swap inst add message. */
- MSG_SWAP_INST_REMOVE = 0x0009, /**< Swap inst remove message. */
- MSG_WRT_LAUNCHER_PORT = 0x8001 /**< WRT launcher port. */
-};
-
-/**
- * @struct basic_msg_fmt
- * @brief Basic part of each message.
- */
-struct basic_msg_fmt {
- u32 msg_id; /**< Message ID. */
- u32 len; /**< Message length. */
-} __packed;
-
-static int msg_handler(void __user *msg)
-{
- int ret;
- u32 size;
- enum MSG_ID msg_id;
- struct msg_buf mb;
- void __user *payload;
- struct basic_msg_fmt bmf;
- enum { size_max = 128 * 1024 * 1024 };
-
- ret = copy_from_user(&bmf, (void *)msg, sizeof(bmf));
- if (ret)
- return ret;
-
- size = bmf.len;
- if (size >= size_max) {
- printk(KERN_INFO "%s: too large message, size=%u\n",
- __func__, size);
- return -ENOMEM;
- }
-
- ret = init_mb(&mb, size);
- if (ret)
- return ret;
-
- payload = msg + sizeof(bmf);
- if (size) {
- ret = copy_from_user(mb.begin, (void *)payload, size);
- if (ret)
- goto uninit;
- }
-
- msg_id = bmf.msg_id;
- switch (msg_id) {
- case MSG_KEEP_ALIVE:
- print_parse_debug("MSG_KEEP_ALIVE. size=%d\n", size);
- ret = msg_keep_alive(&mb);
- break;
- case MSG_START:
- print_parse_debug("MSG_START. size=%d\n", size);
- ret = msg_start(&mb);
- break;
- case MSG_STOP:
- print_parse_debug("MSG_STOP. size=%d\n", size);
- ret = msg_stop(&mb);
- break;
- case MSG_CONFIG:
- print_parse_debug("MSG_CONFIG. size=%d\n", size);
- ret = msg_config(&mb);
- break;
- case MSG_SWAP_INST_ADD:
- print_parse_debug("MSG_SWAP_INST_ADD. size=%d\n", size);
- ret = msg_swap_inst_add(&mb);
- break;
- case MSG_SWAP_INST_REMOVE:
- print_parse_debug("MSG_SWAP_INST_REMOVE. size=%d\n", size);
- ret = msg_swap_inst_remove(&mb);
- break;
- case MSG_WRT_LAUNCHER_PORT: {
- /* TODO: discuss wrt-launcher port transfer */
- int port;
- print_parse_debug("MSG_WRT_LAUNCHER_PORT. size=%d\n", size);
- port = get_wrt_launcher_port();
- if (copy_to_user(payload, &port, sizeof(port))) {
- ret = -EIO;
- break;
- }
- ret = port ? 0 : -EINVAL;
- break;
- }
- default:
- print_err("incorrect message ID [%u]. size=%d\n", msg_id, size);
- ret = -EINVAL;
- break;
- }
-
-uninit:
- uninit_mb(&mb);
- return ret;
-}
-
-static struct driver_msg_handler dmsg_handler = {
- .mod = THIS_MODULE,
- .handler = msg_handler,
-};
-
-static int reg_msg_handler(void)
-{
- driver_msg_handler_set(&dmsg_handler);
- return 0;
-}
-
-static void unreg_msg_handler(void)
-{
- driver_msg_handler_set(NULL);
- app_list_unreg_all();
-
- disable_all_features();
-}
-
-static int once(void)
-{
- int ret;
-
- ret = once_cmd();
- if (ret)
- return ret;
-
- ret = init_cpu_deps();
-
- return ret;
-}
-
-SWAP_LIGHT_INIT_MODULE(once, reg_msg_handler, unreg_msg_handler, NULL, NULL);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/**
- * parser/us_inst.c
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * User-space instrumentation controls.
- */
-
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/probes/probes.h>
-
-#include "msg_parser.h"
-#include "us_inst.h"
-#include "usm_msg.h"
-
-
-static struct pfg_msg_cb msg_cb = {
- .msg_info = usm_msg_info,
- .msg_status_info = usm_msg_status_info,
- .msg_term = usm_msg_term,
- .msg_map = usm_msg_map,
- .msg_unmap = usm_msg_unmap
-};
-
-
-static int probe_inst_reg(struct pr_probe_desc *probe, struct pf_group *pfg,
- struct dentry *dentry)
-{
- probe->ip = pf_register_probe(pfg, dentry, probe->addr, &probe->p_desc);
- if (IS_ERR(probe->ip))
- return PTR_ERR(probe->ip);
-
- return 0;
-}
-
-static void probe_inst_unreg(struct pr_probe_desc *probe, struct pf_group *pfg)
-{
- pf_unregister_probe(pfg, probe->ip);
-}
-
-static void do_bin_inst_unreg(struct list_head *head, struct pf_group *pfg)
-{
- struct pr_probe_desc *probe;
-
- list_for_each_entry(probe, head, list) {
- probe_inst_unreg(probe, pfg);
- }
-}
-
-static void bin_inst_unreg(struct pr_bin_desc *bin, struct pf_group *pfg)
-{
- do_bin_inst_unreg(&bin->probe_head, pfg);
-}
-
-static int bin_inst_reg(struct pr_bin_desc *bin, struct pf_group *pfg)
-{
- struct pr_probe_desc *probe, *n;
- struct dentry *dentry;
- LIST_HEAD(reg_head);
-
- dentry = dentry_by_path(bin->info->path);
- if (dentry == NULL) {
- pr_warn("Cannot get dentry by path %s\n", bin->info->path);
- return -EINVAL;
- }
-
- list_for_each_entry_safe(probe, n, &bin->probe_head, list) {
- int ret;
-
- ret = probe_inst_reg(probe, pfg, dentry);
- if (!ret) {
- list_move(&probe->list, ®_head);
- } else {
- do_bin_inst_unreg(®_head, pfg);
- return ret;
- }
- }
-
- list_splice(®_head, &bin->probe_head);
- return 0;
-}
-
-static struct pf_group *get_pfg_by_app_info(struct pr_app_info *app)
-{
- struct pf_group *pfg = ERR_PTR(-EINVAL);
- struct dentry *dentry;
-
- dentry = dentry_by_path(app->path);
- if (dentry == NULL)
- return pfg;
-
- switch (app->type) {
- case AT_PID:
- if (app->tgid == 0) {
- if (app->path[0] == '\0')
- pfg = get_pf_group_dumb(dentry);
- else
- goto pf_dentry;
- } else {
- pfg = get_pf_group_by_tgid(app->tgid, dentry);
- }
- break;
- case AT_TIZEN_WEB_APP:
- pfg = get_pf_group_by_comm(app->id, dentry);
- break;
- case AT_TIZEN_NATIVE_APP:
- case AT_COMMON_EXEC:
- pf_dentry:
- pfg = get_pf_group_by_dentry(dentry, dentry);
- break;
- default:
- pr_info("ERROR: app_type=0x%x\n", app->type);
- break;
- }
-
- if (!pfg)
- pfg = ERR_PTR(-ENOMEM);
-
- if (!IS_ERR(pfg)) {
- /* TODO: move to other location and chack return value */
- pfg_msg_cb_set(pfg, &msg_cb);
- }
-
- return pfg;
-}
-
-static void do_us_app_inst_unreg(struct pr_app_desc *app,
- struct list_head *head)
-{
- struct pr_bin_desc *bin;
-
- list_for_each_entry(bin, head, list) {
- bin_inst_unreg(bin, app->pfg);
- }
- put_pf_group(app->pfg);
- app->pfg = NULL;
-}
-
-static void us_app_inst_unreg(struct pr_app_desc *app)
-{
- do_us_app_inst_unreg(app, &app->bin_head);
-}
-
-static int us_app_inst_reg(struct pr_app_desc *app)
-{
- struct pf_group *pfg;
- struct pr_bin_desc *bin, *n;
- LIST_HEAD(reg_head);
-
- pfg = get_pfg_by_app_info(app->info);
- if (IS_ERR(pfg))
- return PTR_ERR(pfg);
-
- app->pfg = pfg;
- list_for_each_entry_safe(bin, n, &app->bin_head, list) {
- int ret;
-
- ret = bin_inst_reg(bin, app->pfg);
- if (!ret) {
- list_move(&bin->list, ®_head);
- } else {
- do_us_app_inst_unreg(app, ®_head);
- return ret;
- }
- }
-
- list_splice(®_head, &app->bin_head);
- return 0;
-}
-
-
-static struct pr_probe_desc *find_probe(struct list_head *head,
- struct pr_probe_desc *probe)
-{
- struct pr_probe_desc *p;
-
- list_for_each_entry(p, head, list) {
- if (!probe_inst_info_cmp(probe, p))
- return p;
- }
-
- return NULL;
-}
-
-static struct pr_bin_desc *find_bin(struct list_head *head,
- struct pr_bin_info *info)
-{
- struct pr_bin_desc *bin;
-
- list_for_each_entry(bin, head, list) {
- if (!pr_bin_info_cmp(bin->info, info))
- return bin;
- }
-
- return NULL;
-}
-
-static struct pr_app_desc *find_app(struct list_head *head,
- struct pr_app_info *app_info)
-{
- struct pr_app_desc *app;
-
- list_for_each_entry(app, head, list) {
- if (!pr_app_info_cmp(app->info, app_info))
- return app;
- }
-
- return NULL;
-}
-
-static void us_probe_get_equal_elements(struct list_head *probe_head,
- struct list_head *test_probe_head,
- struct list_head *out_probe_head)
-{
- struct pr_probe_desc *test_probe, *n;
-
- list_for_each_entry_safe(test_probe, n, test_probe_head, list) {
- struct pr_probe_desc *probe;
-
- probe = find_probe(probe_head, test_probe);
- if (probe) {
- list_move(&probe->list, out_probe_head);
-
- /* remove probe */
- list_del(&test_probe->list);
- pr_probe_desc_free(test_probe);
- } else {
- return;
- }
- }
-}
-
-static void us_bin_get_equal_elements(struct list_head *bin_head,
- struct list_head *test_bin_head,
- struct list_head *out_bin_head)
-{
- struct pr_bin_desc *test_bin, *n;
-
- list_for_each_entry_safe(test_bin, n, test_bin_head, list) {
- struct pr_bin_desc *bin;
- LIST_HEAD(out_probe_head);
-
- bin = find_bin(bin_head, test_bin->info);
- if (!bin)
- return;
-
- us_probe_get_equal_elements(&bin->probe_head,
- &test_bin->probe_head,
- &out_probe_head);
-
- /* check all probes found */
- if (list_empty(&test_bin->probe_head)) {
- list_move(&test_bin->list, out_bin_head);
- list_splice(&out_probe_head, &test_bin->probe_head);
- } else {
- list_splice(&out_probe_head, &bin->probe_head);
- }
- }
-}
-
-static void us_app_get_equal_elements(struct list_head *app_head,
- struct list_head *test_app_head,
- struct list_head *out_app_head)
-{
- struct pr_app_desc *test_app, *n;
-
- list_for_each_entry_safe(test_app, n, test_app_head, list) {
- struct pr_app_desc *app;
- LIST_HEAD(out_bin_head);
-
- app = find_app(app_head, test_app->info);
- if (!app)
- return;
-
- us_bin_get_equal_elements(&app->bin_head,
- &test_app->bin_head,
- &out_bin_head);
-
- /* check all bins found */
- if (list_empty(&test_app->bin_head)) {
- list_move(&test_app->list, out_app_head);
- list_splice(&out_bin_head, &test_app->bin_head);
- } else {
- list_splice(&out_bin_head, &app->bin_head);
- }
- }
-}
-
-
-static void bin_list_splice(struct list_head *list, struct list_head *head)
-{
- struct pr_bin_desc *new_bin, *n;
-
- list_for_each_entry_safe(new_bin, n, list, list) {
- struct pr_bin_desc *bin;
-
- bin = find_bin(head, new_bin->info);
- if (bin) {
- list_splice_init(&new_bin->probe_head,
- &bin->probe_head);
-
- list_del(&new_bin->list);
- pr_bin_desc_free(new_bin);
- } else {
- list_move(&new_bin->list, head);
- }
- }
-}
-
-static void app_list_splice(struct list_head *list, struct list_head *head)
-{
- struct pr_app_desc *new_app, *n;
-
- list_for_each_entry_safe(new_app, n, list, list) {
- struct pr_app_desc *app;
-
- app = find_app(head, new_app->info);
- if (app) {
- bin_list_splice(&new_app->bin_head, &app->bin_head);
-
- list_del(&new_app->list);
- put_pf_group(app->pfg);
- pr_app_desc_free(new_app);
- } else {
- list_move(&new_app->list, head);
- }
- }
-}
-
-static void app_list_free(struct list_head *head)
-{
- struct pr_app_desc *app, *n;
-
- list_for_each_entry_safe(app, n, head, list) {
- list_del(&app->list);
- pr_app_desc_free(app);
- }
-}
-
-static void do_app_list_unreg(struct list_head *head)
-{
- struct pr_app_desc *app;
-
- list_for_each_entry(app, head, list) {
- us_app_inst_unreg(app);
- }
-}
-
-
-
-static LIST_HEAD(app_head);
-
-/* After call the 'head' list is empty, do not free it. */
-int app_list_unreg(struct list_head *head)
-{
- LIST_HEAD(out_app_head);
-
- us_app_get_equal_elements(&app_head, head, &out_app_head);
-
- /* check all apps found */
- if (!list_empty(head)) {
- app_list_splice(&out_app_head, &app_head);
- return -EINVAL;
- }
-
- do_app_list_unreg(&out_app_head);
- app_list_free(&out_app_head);
- return 0;
-}
-
-/* After call the 'head' list is empty, do not free it. */
-int app_list_reg(struct list_head *head)
-{
- LIST_HEAD(reg_head);
- struct pr_app_desc *app, *n;
-
- list_for_each_entry_safe(app, n, head, list) {
- int ret;
-
- ret = us_app_inst_reg(app);
- if (!ret) {
- list_move(&app->list, ®_head);
- } else {
- do_app_list_unreg(®_head);
- list_splice(®_head, head);
- app_list_free(head);
- return ret;
- }
- }
-
- app_list_splice(®_head, &app_head);
- return 0;
-}
-
-void app_list_unreg_all(void)
-{
- do_app_list_unreg(&app_head);
- app_list_free(&app_head);
-}
+++ /dev/null
-/**
- * @file parser/us_inst.h
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * User-space instrumentation controls interface.
- */
-
-
-#ifndef _US_INST_H
-#define _US_INST_H
-
-int app_list_reg(struct list_head *head);
-int app_list_unreg(struct list_head *head);
-void app_list_unreg_all(void);
-
-#endif /* _US_INST_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/fs.h>
-#include <linux/net.h>
-#include <linux/stat.h>
-#include <linux/sched.h>
-#include <linux/dcache.h>
-#include <linux/fdtable.h>
-#include <linux/file.h>
-#include <linux/version.h>
-#include <linux/in.h>
-#include <linux/in6.h>
-#include <linux/net.h>
-#include <writer/swap_msg.h>
-#include <master/swap_deps.h>
-#include <us_manager/sspt/sspt.h> /* ... check_vma() */
-
-
-#define USM_PREFIX KERN_INFO "[USM] "
-
-
-struct kmem_info {
- const char *name;
- unsigned long start;
- unsigned long end;
-};
-
-#if defined(CONFIG_ARM64) || defined(CONFIG_X86_32)
-static void kmem_info_fill_common(struct kmem_info *info, struct task_struct *task)
-{
- unsigned long vdso;
- struct vm_area_struct *vma_vdso;
-
- vdso = (unsigned long)task->mm->context.vdso;
- vma_vdso = find_vma_intersection(task->mm, vdso, vdso + 1);
- if (vma_vdso) {
- info->name = "[vdso]";
- info->start = vma_vdso->vm_start;
- info->end = vma_vdso->vm_end;
- } else {
- pr_err(USM_PREFIX "Cannot get VDSO mapping\n");
- info->name = NULL;
- info->start = 0;
- info->end = 0;
- }
-}
-#endif /* defined(CONFIG_ARM64) || defined(CONFIG_X86_32) */
-
-static void kmem_info_fill(struct kmem_info *info, struct task_struct *task)
-{
-#if defined(CONFIG_ARM)
- info->name = "[vectors]";
- info->start = CONFIG_VECTORS_BASE;
- info->end = CONFIG_VECTORS_BASE + PAGE_SIZE;
-#elif defined(CONFIG_ARM64)
- if (test_ti_thread_flag(task_thread_info(task), TIF_32BIT)) {
- info->name = "[vectors]";
- info->start = AARCH32_VECTORS_BASE;
- info->end = AARCH32_VECTORS_BASE + PAGE_SIZE;
- } else {
- kmem_info_fill_common(info, task);
- }
-#elif defined(CONFIG_X86_32)
- kmem_info_fill_common(info, task);
-#endif /* CONFIG_arch */
-}
-
-static inline struct timespec get_task_start_time(struct task_struct *task)
-{
-#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0)
- return ns_to_timespec(task->real_start_time);
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
- return task->real_start_time;
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
-}
-
-
-
-static int pack_path(void *data, size_t size, struct file *file)
-{
- enum { TMP_BUF_LEN = 512 };
- char tmp_buf[TMP_BUF_LEN];
- const char NA[] = "N/A";
- const char *filename;
- size_t len = sizeof(NA);
-
- if (file == NULL) {
- filename = NA;
- goto cp2buf;
- }
-
- path_get(&file->f_path);
- filename = d_path(&file->f_path, tmp_buf, TMP_BUF_LEN);
- path_put(&file->f_path);
-
- if (IS_ERR_OR_NULL(filename)) {
- filename = NA;
- goto cp2buf;
- }
-
- len = strlen(filename) + 1;
-
-cp2buf:
- if (size < len)
- return -ENOMEM;
-
- memcpy(data, filename, len);
- return len;
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_PROCESS_INFO =
- * ============================================================================
- */
-struct proc_info_top {
- u32 pid;
- char comm[0];
-} __packed;
-
-struct proc_info_bottom {
- u32 ppid;
- u64 start_time;
- u64 low_addr;
- u64 high_addr;
- char bin_path[0];
-} __packed;
-
-struct lib_obj {
- u64 low_addr;
- u64 high_addr;
- char lib_path[0];
-} __packed;
-
-static int pack_lib_obj(void *data, size_t size, struct vm_area_struct *vma)
-{
- int ret;
- struct lib_obj *obj = (struct lib_obj *)data;
-
- if (size < sizeof(*obj))
- return -ENOMEM;
-
- obj->low_addr = vma->vm_start;
- obj->high_addr = vma->vm_end;
- size -= sizeof(*obj);
-
- ret = pack_path(obj->lib_path, size, vma->vm_file);
- if (ret < 0)
- return ret;
-
- return ret + sizeof(*obj);
-}
-
-static int pack_shared_kmem(void *data, size_t size, struct task_struct *task)
-{
- struct lib_obj *obj = (struct lib_obj *)data;
- struct kmem_info info;
- size_t name_len, obj_size;
-
- if (size < sizeof(*obj))
- return -ENOMEM;
-
- kmem_info_fill(&info, task);
-
- if (info.name == NULL)
- return 0;
-
- obj->low_addr = (u64)info.start;
- obj->high_addr = (u64)info.end;
-
- name_len = strlen(info.name) + 1;
- obj_size = sizeof(*obj) + name_len;
- if (size < obj_size)
- return -ENOMEM;
-
- memcpy(obj->lib_path, info.name, name_len);
-
- return obj_size;
-}
-
-static int pack_libs(void *data, size_t size, struct task_struct *task)
-{
- int ret;
- struct vm_area_struct *vma;
- u32 *lib_cnt = (u32 *)data;
- const size_t old_size = size;
-
- if (size < sizeof(*lib_cnt))
- return -ENOMEM;
-
- /* packing libraries count */
- *lib_cnt = 0;
- data += sizeof(*lib_cnt);
- size -= sizeof(*lib_cnt);
-
- /* packing libraries */
- for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
- if (check_vma(vma)) {
- ret = pack_lib_obj(data, size, vma);
- if (ret < 0)
- return ret;
-
- data += ret;
- size -= ret;
- ++(*lib_cnt);
- }
- }
-
- /* packing shared kernel memory */
- ret = pack_shared_kmem(data, size, task);
- if (ret < 0)
- return ret;
-
- *lib_cnt += !!ret;
- size -= ret;
-
- return old_size - size;
-}
-
-static struct vm_area_struct *find_vma_exe_by_dentry(struct mm_struct *mm,
- struct dentry *dentry)
-{
- struct vm_area_struct *vma;
-
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
- if (vma->vm_file && (vma->vm_flags & VM_EXEC) &&
- (vma->vm_file->f_path.dentry == dentry))
- goto out;
- }
-
- vma = NULL;
-out:
-
- return vma;
-}
-
-static int pack_proc_info_top(void *data, size_t size,
- struct task_struct *task)
-{
- struct proc_info_top *pit = (struct proc_info_top *)data;
-
- if (size < sizeof(*pit) + sizeof(task->comm))
- return -ENOMEM;
-
- pit->pid = task->tgid;
- get_task_comm(pit->comm, task);
-
- return sizeof(*pit) + strlen(pit->comm) + 1;
-}
-
-static int pack_proc_info_bottom(void *data, size_t size,
- struct task_struct *task,
- struct dentry *dentry)
-{
- struct proc_info_bottom *pib = (struct proc_info_bottom *)data;
- struct vm_area_struct *vma = find_vma_exe_by_dentry(task->mm, dentry);
- struct timespec boot_time;
- struct timespec start_time;
- int ret;
-
- if (size < sizeof(*pib))
- return -ENOMEM;
-
- getboottime(&boot_time);
- start_time = timespec_add(boot_time, get_task_start_time(task));
-
- pib->ppid = task->real_parent->tgid;
- pib->start_time = swap_msg_spec2time(&start_time);
-
- if (vma) {
- pib->low_addr = vma->vm_start;
- pib->high_addr = vma->vm_end;
- ret = pack_path(pib->bin_path, size, vma->vm_file);
- } else {
- pib->low_addr = 0;
- pib->high_addr = 0;
- ret = pack_path(pib->bin_path, size, NULL);
- }
-
- if (ret < 0)
- return ret;
-
- return sizeof(*pib) + ret;
-}
-
-static int pack_proc_info(void *data, size_t size, struct task_struct *task,
- struct dentry *dentry)
-{
- int ret;
- const size_t old_size = size;
-
- ret = pack_proc_info_top(data, size, task);
- if (ret < 0)
- return ret;
-
- data += ret;
- size -= ret;
-
- ret = pack_proc_info_bottom(data, size, task, dentry);
- if (ret < 0)
- return ret;
-
- data += ret;
- size -= ret;
-
- ret = pack_libs(data, size, task);
- if (ret < 0)
- return ret;
-
- return old_size - size + ret;
-}
-
-/* Called with down\up\_read(&task->mm->mmap_sem). */
-void usm_msg_info(struct task_struct *task, struct dentry *dentry)
-{
- int ret;
- struct swap_msg *m;
- void *p;
- size_t size;
-
- m = swap_msg_get(MSG_PROC_INFO);
- p = swap_msg_payload(m);
- size = swap_msg_size(m);
-
- ret = pack_proc_info(p, size, task, dentry);
- if (ret < 0) {
- printk(USM_PREFIX "ERROR: message process info packing, "
- "ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, ret);
-
-put_msg:
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_TERMINATE =
- * ============================================================================
- */
-struct proc_terminate {
- u32 pid;
-} __packed;
-
-void usm_msg_term(struct task_struct *task)
-{
- struct swap_msg *m;
- struct proc_terminate *term;
-
- m = swap_msg_get(MSG_TERMINATE);
-
- term = swap_msg_payload(m);
- term->pid = task->pid;
-
- swap_msg_flush(m, sizeof(*term));
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_PROCESS_MAP =
- * ============================================================================
- */
-struct proc_map {
- u32 pid;
- u64 low_addr;
- u64 high_addr;
- char bin_path[0];
-} __packed;
-
-static int pack_proc_map(void *data, size_t size, struct vm_area_struct *vma)
-{
- struct proc_map *map = (struct proc_map *)data;
- int ret;
-
- map->pid = current->tgid;
- map->low_addr = vma->vm_start;
- map->high_addr = vma->vm_end;
-
- ret = pack_path(map->bin_path, size - sizeof(*map), vma->vm_file);
- if (ret < 0)
- return ret;
-
- return ret + sizeof(*map);
-}
-
-void usm_msg_map(struct vm_area_struct *vma)
-{
- int ret;
- struct swap_msg *m;
- void *p;
- size_t size;
-
- m = swap_msg_get(MSG_PROC_MAP);
- p = swap_msg_payload(m);
- size = swap_msg_size(m);
-
- ret = pack_proc_map(p, size, vma);
- if (ret < 0) {
- printk(USM_PREFIX "ERROR: message process mapping packing, "
- "ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, ret);
-
-put_msg:
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_PROCESS_UNMAP =
- * ============================================================================
- */
-struct proc_unmap {
- u32 pid;
- u64 low_addr;
- u64 high_addr;
-} __packed;
-
-void usm_msg_unmap(unsigned long start, unsigned long end)
-{
- struct swap_msg *m;
- struct proc_unmap *unmap;
-
- m = swap_msg_get(MSG_PROC_UNMAP);
-
- unmap = swap_msg_payload(m);
- unmap->pid = current->tgid;
- unmap->low_addr = (u64)start;
- unmap->high_addr = (u64)end;
-
- swap_msg_flush(m, sizeof(*unmap));
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_PROCESS_COMM =
- * ============================================================================
- */
-struct proc_comm {
- u32 pid;
- char comm[0];
-} __packed;
-
-void usm_msg_comm(struct task_struct *task)
-{
- struct swap_msg *m;
- struct proc_comm *c;
-
- m = swap_msg_get(MSG_PROC_COMM);
-
- c = swap_msg_payload(m);
- c->pid = task->tgid;
- get_task_comm(c->comm, task);
-
- swap_msg_flush(m, sizeof(*c) + strlen(c->comm) + 1);
- swap_msg_put(m);
-}
-
-
-
-
-
-/* ============================================================================
- * = MSG_PROCESS_STATUS_INFO =
- * ============================================================================
- */
-struct ofile {
- u32 fd;
- u64 size;
- char path[0];
-} __packed;
-
-struct osock {
- u32 fd;
- u32 ip;
- u32 port;
-} __packed;
-
-static int pack_ofile(void *data, size_t size, int fd, struct file *file,
- loff_t fsize)
-{
- int ret;
- struct ofile *ofile;
-
- if (size < sizeof(*ofile))
- return -ENOMEM;
-
- ofile = (struct ofile *)data;
- ofile->fd = (u32)fd;
- ofile->size = (u64)fsize;
-
- ret = pack_path(ofile->path, size - sizeof(*ofile), file);
- if (ret < 0)
- return ret;
-
- return sizeof(*ofile) + ret;
-}
-
-static int is_file_mode(umode_t mode)
-{
- return S_ISREG(mode);
-}
-
-static int pack_osock(void *data, size_t size, int fd, struct file *file,
- loff_t fsize)
-{
- struct osock *pack_sock;
- struct socket *sock;
- struct sockaddr_storage addr;
- struct sockaddr *saddr = (struct sockaddr *)&addr;
- struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
- int ret_size = sizeof(*pack_sock);
- int addrlen;
- int err;
-
- if (size < sizeof(*pack_sock))
- return -ENOMEM;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
- sock = sockfd_lookup(fd, &err);
-#else
- sock = sock_from_file(file, &err);
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) */
- if (!sock)
- return 0;
-
- kernel_getsockname(sock, saddr, &addrlen);
-
- pack_sock = (struct osock *)data;
- pack_sock->fd = (u32)fd;
- pack_sock->ip = (u32)0x0;
- pack_sock->port = 0;
-
- switch (saddr->sa_family) {
- case AF_INET:
- pack_sock->ip = (u32)(sin->sin_addr.s_addr);
- pack_sock->port = ntohs(sin->sin_port);
- break;
- default:
- pr_info("[USM] ignored unknown address type: %u\n",
- (unsigned int)saddr->sa_family);
- ret_size = 0;
- break;
- }
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
- sockfd_put(sock);
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) */
-
- return ret_size;
-}
-
-static int is_sock_mode(umode_t mode)
-{
- return S_ISSOCK(mode);
-}
-
-typedef int (*pack_func_t)(void *data, size_t size, int fd, struct file *file,
- loff_t fsize);
-typedef int (*check_mode_func_t)(umode_t mode);
-
-static int pack_descriptors(void *data, size_t size, pack_func_t pack_func,
- check_mode_func_t check_mode,
- struct files_struct *files)
-{
- int ret, fd;
- const size_t old_size = size;
- u32 *desc_cnt;
-
- /* pack descriptor count */
- desc_cnt = (u32 *)data;
- *desc_cnt = 0;
- data += 4;
- size -= 4;
-
- /* pack descriptors list */
- rcu_read_lock();
- for (fd = 0; fd < files_fdtable(files)->max_fds; ++fd) {
- struct file *file;
- struct inode *inode;
- loff_t fsize;
-
- file = fcheck_files(files, fd);
- if (file == NULL)
- continue;
-
- inode = file->f_path.dentry->d_inode;
- /* check inode and if it is a regular file */
- if (!inode || !check_mode(inode->i_mode))
- continue;
-
- fsize = inode->i_size;
- rcu_read_unlock();
-
- ret = pack_func(data, size, fd, file, fsize);
- if (ret < 0) {
- goto exit;
- } else if (ret > 0) {
- /* increment if data packed only */
- data += ret;
- size -= ret;
- ++(*desc_cnt);
- }
-
- rcu_read_lock();
- }
-
- rcu_read_unlock();
- ret = old_size - size;
-
-exit:
- return ret;
-}
-
-static int pack_status_info(void *data, size_t size, struct task_struct *task)
-{
- int ret;
- const size_t old_size = size;
- struct files_struct *files;
-
- files = swap_get_files_struct(task);
- if (!files)
- return -EIO;
-
- /* pack pid */
- *((u32 *)data) = (u32)task->tgid;
- data += 4;
- size -= 4;
-
- /* pack file descriptors */
- ret = pack_descriptors(data, size, pack_ofile, is_file_mode, files);
- if (ret < 0)
- goto put_fstruct;
- data += ret;
- size -= ret;
-
- /* pack sock descriptors */
- ret = pack_descriptors(data, size, pack_osock, is_sock_mode, files);
- if (ret < 0)
- goto put_fstruct;
- data += ret;
- size -= ret;
-
- ret = old_size - size;
-
-put_fstruct:
- swap_put_files_struct(files);
- return ret;
-}
-
-void usm_msg_status_info(struct task_struct *task)
-{
- int ret;
- void *data;
- size_t size;
- struct swap_msg *m;
-
- m = swap_msg_get(MSG_PROCESS_STATUS_INFO);
-
- data = swap_msg_payload(m);
- size = swap_msg_size(m);
- ret = pack_status_info(data, size, task);
- if (ret < 0) {
- printk(USM_PREFIX "ERROR: MSG_PROCESS_STATUS_INFO "
- "packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, ret);
-
-put_msg:
- swap_msg_put(m);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _USM_MSG_H
-#define _USM_MSG_H
-
-
-struct dentry;
-struct task_struct;
-struct vm_area_struct;
-
-
-void usm_msg_info(struct task_struct *task, struct dentry *dentry);
-void usm_msg_term(struct task_struct *task);
-void usm_msg_map(struct vm_area_struct *vma);
-void usm_msg_unmap(unsigned long start, unsigned long end);
-void usm_msg_comm(struct task_struct *task);
-void usm_msg_status_info(struct task_struct *task);
-
-
-#endif /* _USM_MSG_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_preload.o
-swap_preload-y := preload_module.o \
- preload_control.o \
- preload_debugfs.o \
- preload_probe.o \
- preload_threads.o \
- preload_process.o
+++ /dev/null
-#ifndef __PRELOAD__
-#define __PRELOAD__
-
-#define PRELOAD_PREFIX "SWAP_PRELOAD: "
-#define PRELOAD_MAX_ATTEMPTS 10
-#define PRELOAD_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
-
-#endif /* __PRELOAD__ */
+++ /dev/null
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/limits.h>
-#include <linux/list.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/us_common_file.h>
-
-#include "preload.h"
-#include "preload_module.h"
-#include "preload_control.h"
-#include "preload_probe.h"
-
-struct bin_desc {
- struct list_head list;
- struct dentry *dentry;
- char *filename;
-};
-
-struct list_desc {
- struct list_head list;
- rwlock_t lock;
- int cnt;
-};
-
-static struct list_desc target = {
- .list = LIST_HEAD_INIT(target.list),
- .lock = __RW_LOCK_UNLOCKED(&target.lock),
- .cnt = 0
-};
-
-static struct list_desc ignored = {
- .list = LIST_HEAD_INIT(ignored.list),
- .lock = __RW_LOCK_UNLOCKED(&ignored.lock),
- .cnt = 0
-};
-
-static inline struct task_struct *__get_task_struct(void)
-{
- return current;
-}
-
-static struct bin_desc *__alloc_binary(struct dentry *dentry, char *name,
- int namelen)
-{
- struct bin_desc *p = NULL;
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- return NULL;
-
- INIT_LIST_HEAD(&p->list);
- p->filename = kmalloc(namelen + 1, GFP_KERNEL);
- if (!p->filename)
- goto fail;
- memcpy(p->filename, name, namelen);
- p->filename[namelen] = '\0';
- p->dentry = dentry;
-
- return p;
-fail:
- kfree(p);
- return NULL;
-}
-
-static void __free_binary(struct bin_desc *p)
-{
- kfree(p->filename);
- kfree(p);
-}
-
-static void __free_binaries(struct list_desc *tl)
-{
- struct bin_desc *p, *n;
- struct list_head rm_head;
-
- INIT_LIST_HEAD(&rm_head);
- write_lock(&tl->lock);
- list_for_each_entry_safe(p, n, &tl->list, list) {
- list_move(&p->list, &rm_head);
- }
- tl->cnt = 0;
- write_unlock(&tl->lock);
-
- list_for_each_entry_safe(p, n, &rm_head, list) {
- list_del(&p->list);
- swap_put_dentry(p->dentry);
- __free_binary(p);
- }
-}
-
-static bool __check_dentry_already_exist(struct dentry *dentry,
- struct list_desc *tl)
-{
- struct bin_desc *p;
- bool ret = false;
-
- read_lock(&tl->lock);
- list_for_each_entry(p, &tl->list, list) {
- if (p->dentry == dentry) {
- ret = true;
- goto out;
- }
- }
-out:
- read_unlock(&tl->lock);
-
- return ret;
-}
-
-static int __add_binary(struct dentry *dentry, char *filename,
- struct list_desc *tl)
-{
- struct bin_desc *p;
- size_t len;
-
- if (__check_dentry_already_exist(dentry, tl)) {
- printk(PRELOAD_PREFIX "Binary already exist\n");
- return EALREADY;
- }
-
- /* Filename should be < PATH_MAX */
- len = strnlen(filename, PATH_MAX);
- if (len == PATH_MAX)
- return -EINVAL;
-
- p = __alloc_binary(dentry, filename, len);
- if (!p)
- return -ENOMEM;
-
- write_lock(&tl->lock);
- list_add_tail(&p->list, &tl->list);
- tl->cnt++;
- write_unlock(&tl->lock);
-
- return 0;
-}
-
-static struct dentry *__get_caller_dentry(struct task_struct *task,
- unsigned long caller)
-{
- struct vm_area_struct *vma = NULL;
-
- if (unlikely(task->mm == NULL))
- goto get_caller_dentry_fail;
-
- vma = find_vma_intersection(task->mm, caller, caller + 1);
- if (unlikely(vma == NULL || vma->vm_file == NULL))
- goto get_caller_dentry_fail;
-
- return vma->vm_file->f_path.dentry;
-
-get_caller_dentry_fail:
-
- return NULL;
-}
-
-static bool __check_if_instrumented(struct task_struct *task,
- struct dentry *dentry)
-{
- return __check_dentry_already_exist(dentry, &target);
-}
-
-static bool __is_instrumented(void *caller)
-{
- struct task_struct *task = __get_task_struct();
- struct dentry *caller_dentry = __get_caller_dentry(task,
- (unsigned long) caller);
-
- if (caller_dentry == NULL)
- return false;
-
- return __check_if_instrumented(task, caller_dentry);
-}
-
-static unsigned int __get_names(struct list_desc *tl, char ***filenames_p)
-{
- unsigned int i, ret = 0;
- struct bin_desc *p;
- char **a = NULL;
-
- read_lock(&tl->lock);
- if (tl->cnt == 0)
- goto out;
-
- a = kmalloc(sizeof(*a) * tl->cnt, GFP_KERNEL);
- if (!a)
- goto out;
-
- i = 0;
- list_for_each_entry(p, &tl->list, list) {
- if (i >= tl->cnt)
- break;
- a[i++] = p->filename;
- }
-
- *filenames_p = a;
- ret = i;
-out:
- read_unlock(&tl->lock);
- return ret;
-}
-
-
-/* Called only form handlers. If we're there, then it is instrumented. */
-enum preload_call_type pc_call_type_always_inst(void *caller)
-{
- if (__is_instrumented(caller))
- return INTERNAL_CALL;
-
- return EXTERNAL_CALL;
-
-}
-
-enum preload_call_type pc_call_type(struct sspt_ip *ip, void *caller)
-{
- if (__is_instrumented(caller))
- return INTERNAL_CALL;
-
- if (ip->desc->info.pl_i.flags & SWAP_PRELOAD_ALWAYS_RUN)
- return EXTERNAL_CALL;
-
- return NOT_INSTRUMENTED;
-}
-
-int pc_add_instrumented_binary(char *filename)
-{
- struct dentry *dentry = swap_get_dentry(filename);
- int res = 0;
-
- if (dentry == NULL)
- return -EINVAL;
-
- res = __add_binary(dentry, filename, &target);
- if (res != 0)
- swap_put_dentry(dentry);
-
- return res > 0 ? 0 : res;
-}
-
-int pc_clean_instrumented_bins(void)
-{
- __free_binaries(&target);
-
- return 0;
-}
-
-int pc_add_ignored_binary(char *filename)
-{
- struct dentry *dentry = swap_get_dentry(filename);
- int res = 0;
-
- if (dentry == NULL)
- return -EINVAL;
-
- res = __add_binary(dentry, filename, &ignored);
- if (res != 0)
- swap_put_dentry(dentry);
-
- return res > 0 ? 0 : res;
-}
-
-int pc_clean_ignored_bins(void)
-{
- __free_binaries(&ignored);
-
- return 0;
-}
-
-unsigned int pc_get_target_names(char ***filenames_p)
-{
- return __get_names(&target, filenames_p);
-}
-
-void pc_release_target_names(char ***filenames_p)
-{
- kfree(*filenames_p);
-}
-
-unsigned int pc_get_ignored_names(char ***filenames_p)
-{
- return __get_names(&ignored, filenames_p);
-}
-
-void pc_release_ignored_names(char ***filenames_p)
-{
- kfree(*filenames_p);
-}
-
-bool pc_check_dentry_is_ignored(struct dentry *dentry)
-{
- struct bin_desc *p;
- bool ret = false;
-
- if (dentry == NULL)
- return false;
-
- read_lock(&ignored.lock);
-
- list_for_each_entry(p, &ignored.list, list) {
- if (p->dentry == dentry) {
- ret = true;
- break;
- }
- }
-
- read_unlock(&ignored.lock);
-
- return ret;
-}
-
-int pc_init(void)
-{
- return 0;
-}
-
-void pc_exit(void)
-{
- __free_binaries(&target);
- __free_binaries(&ignored);
-}
+++ /dev/null
-#ifndef __PRELOAD_CONTROL_H__
-#define __PRELOAD_CONTROL_H__
-
-struct sspt_ip;
-
-enum preload_call_type {
- NOT_INSTRUMENTED,
- EXTERNAL_CALL,
- INTERNAL_CALL
-};
-
-int pc_init(void);
-void pc_exit(void);
-
-enum preload_call_type pc_call_type_always_inst(void *caller);
-enum preload_call_type pc_call_type(struct sspt_ip *ip, void *caller);
-int pc_add_instrumented_binary(char *filename);
-int pc_clean_instrumented_bins(void);
-int pc_add_ignored_binary(char *filename);
-int pc_clean_ignored_bins(void);
-
-unsigned int pc_get_target_names(char ***filenames_p);
-void pc_release_target_names(char ***filenames_p);
-
-unsigned int pc_get_ignored_names(char ***filenames_p);
-void pc_release_ignored_names(char ***filenames_p);
-
-bool pc_check_dentry_is_ignored(struct dentry *dentry);
-
-#endif /* __PRELOAD_HANDLERS_CONTROL_H__ */
+++ /dev/null
-#include <linux/kernel.h>
-#include <linux/debugfs.h>
-#include <linux/err.h>
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/limits.h>
-#include <asm/uaccess.h>
-#include <master/swap_debugfs.h>
-#include <master/swap_initializer.h>
-#include "preload.h"
-#include "preload_module.h"
-#include "preload_debugfs.h"
-#include "preload_control.h"
-#include "preload_process.h"
-
-static const char PRELOAD_FOLDER[] = "preload";
-static const char PRELOAD_TARGET[] = "target_binaries";
-static const char PRELOAD_IGNORED[] = "ignored_binaries";
-static const char PRELOAD_BINARIES_LIST[] = "bins_list";
-static const char PRELOAD_BINARIES_ADD[] = "bins_add";
-static const char PRELOAD_BINARIES_REMOVE[] = "bins_remove";
-static const char PRELOAD_ENABLE[] = "enable";
-static const char PRELOAD_BY_PATH[] = "by_path";
-static const char PRELOAD_BY_PID[] = "by_pid";
-static const char PRELOAD_BY_ID[] = "by_id";
-static const char PRELOAD_ADD[] = "add";
-static const char PRELOAD_DEL[] = "del";
-static const char PRELOAD_DEL_ALL[] = "del_all";
-static const char PRELOAD_PTHREAD[] = "pthread";
-static const char PRELOAD_PATH[] = "path";
-static const char PRELOAD_MINIMAL_INIT[] = "minimal_init_off";
-
-static struct dentry *preload_root;
-static struct dentry *target_list = NULL;
-static struct dentry *target_add = NULL;
-static struct dentry *target_remove = NULL;
-static struct dentry *ignored_list = NULL;
-static struct dentry *ignored_add = NULL;
-static struct dentry *ignored_remove = NULL;
-
-
-/* Type for functions that add process by path and by id */
-typedef int (*sh_t)(const char *);
-
-/* Type for functions that add process by pid */
-typedef int (*ph_t)(pid_t);
-
-/* Type for function that handles unsigned long grabbed from userspace */
-typedef int (*ulh_t)(unsigned long);
-
-
-/* remove end-line symbols */
-static void rm_endline_symbols(char *buf, size_t len)
-{
- char *p, *buf_end;
-
- buf_end = buf + len;
- for (p = buf; p != buf_end; ++p)
- if (*p == '\n' || *p == '\r')
- *p = '\0';
-}
-
-static ssize_t get_string(const char __user *buf, size_t len, char **kbuf)
-{
- char *string;
- ssize_t ret;
-
- string = kmalloc(len + 1, GFP_KERNEL);
- if (!string) {
- pr_warn(PRELOAD_PREFIX "No mem for user string!\n");
- return -ENOMEM;
- }
-
- if (copy_from_user(string, buf, len)) {
- pr_warn(PRELOAD_PREFIX "Failed to copy data from user!\n");
- ret = -EINVAL;
- goto get_string_fail;
- }
-
- string[len] = '\0';
- rm_endline_symbols(string, len);
- *kbuf = string;
-
- return len;
-
-get_string_fail:
- kfree(string);
-
- return ret;
-}
-
-
-static ssize_t get_ul_and_call(const char __user *buf, size_t len, ulh_t cb)
-{
- ssize_t ret;
- char *ulstring;
- unsigned long ul;
-
- ret = get_string(buf, len, &ulstring);
- if (ret != len)
- return ret;
-
- ret = kstrtoul(ulstring, 16, &ul);
- if (ret)
- goto get_ul_write_out;
-
- ret = cb(ul);
-
-get_ul_write_out:
- kfree(ulstring);
-
- return ret == 0 ? len : ret;
-}
-
-static ssize_t get_string_and_call(const char __user *buf, size_t len, sh_t cb)
-{
- char *string;
- ssize_t ret;
-
- ret = get_string(buf, len, &string);
- if (ret != len)
- return ret;
-
- ret = cb(string);
- if (ret)
- pr_warn(PRELOAD_PREFIX "Error adding process by <%s>\n",
- string);
-
- kfree(string);
-
- return ret == 0 ? len : ret;
-}
-
-static ssize_t get_pid_and_call(const char __user *buf, size_t len, ph_t cb)
-{
- char *string;
- pid_t pid;
- ssize_t ret;
-
- ret = get_string(buf, len, &string);
- if (ret != len)
- return ret;
-
- ret = kstrtoul(string, 10, (unsigned long *)&pid);
- if (ret) {
- pr_warn(PRELOAD_PREFIX "Invalid PID!\n");
- goto get_pid_out;
- }
-
- ret = cb(pid);
- if (ret)
- pr_warn(PRELOAD_PREFIX "Error adding process by <%s>\n",
- string);
-
-get_pid_out:
- kfree(string);
-
- return ret == 0 ? len : ret;
-}
-
-/* ===========================================================================
- * = TARGET PROCESSES =
- * ===========================================================================
- */
-
-static ssize_t by_path_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, pp_add_by_path);
-}
-
-static ssize_t by_path_del_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, pp_del_by_path);
-}
-
-static ssize_t by_pid_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_pid_and_call(buf, len, pp_add_by_pid);
-}
-
-static ssize_t by_pid_del_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_pid_and_call(buf, len, pp_del_by_pid);
-}
-
-static ssize_t by_id_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, pp_add_by_id);
-}
-
-static ssize_t by_id_del_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, pp_del_by_id);
-}
-
-static ssize_t del_all_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- pp_del_all();
-
- WARN(pc_clean_instrumented_bins(), PRELOAD_PREFIX
- "Error while cleaning target bins\n");
- WARN(pc_clean_ignored_bins(), PRELOAD_PREFIX
- "Error while cleaning ignored bins\n");
-
- return len;
-}
-
-static const struct file_operations by_path_add_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = by_path_add_write,
-};
-
-static const struct file_operations by_path_del_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = by_path_del_write,
-};
-
-static const struct file_operations by_pid_add_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = by_pid_add_write,
-};
-
-static const struct file_operations by_pid_del_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = by_pid_del_write,
-};
-
-static const struct file_operations by_id_add_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = by_id_add_write,
-};
-
-static const struct file_operations by_id_del_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = by_id_del_write,
-};
-
-static const struct file_operations del_all_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = del_all_write,
-};
-
-/* ===========================================================================
- * = ENABLE =
- * ===========================================================================
- */
-
-static ssize_t enable_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
-{
- char val[2];
-
- val[0] = (pm_status() == PRELOAD_ON ? '1' : '0');
- val[1] = '\0';
-
- return simple_read_from_buffer(buf, len, ppos, val, 2);
-}
-
-static ssize_t enable_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret = 0;
- char val[2];
- size_t val_size;
-
- val_size = min(len, (sizeof(val) - 1));
- if (copy_from_user(val, buf, val_size))
- return -EFAULT;
-
- val[1] = '\0';
- switch (val[0]) {
- case '0':
- ret = pm_switch(PRELOAD_OFF);
- break;
- case '1':
- ret = pm_switch(PRELOAD_ON);
- break;
- default:
- printk(PRELOAD_PREFIX "Invalid state!\n");
- return -EINVAL;
- }
-
- return ret == 0 ? len : ret;
-}
-
-static const struct file_operations enable_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = enable_write,
- .read = enable_read,
-};
-
-
-/* ===========================================================================
- * = BIN PATH =
- * ===========================================================================
- */
-
-static ssize_t bin_add_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *path;
-
- path = kmalloc(len, GFP_KERNEL);
- if (path == NULL) {
- ret = -ENOMEM;
- goto bin_add_write_out;
- }
-
- if (copy_from_user(path, buf, len)) {
- ret = -EINVAL;
- goto bin_add_write_out;
- }
-
- path[len - 1] = '\0';
-
- if (file->f_path.dentry == target_add)
- ret = pc_add_instrumented_binary(path);
- else if (file->f_path.dentry == ignored_add)
- ret = pc_add_ignored_binary(path);
- else {
- /* Should never occur */
- printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
- file->f_path.dentry->d_name.name);
- ret = -EINVAL;
- goto bin_add_write_out;
- }
-
-
- if (ret != 0) {
- printk(PRELOAD_PREFIX "Cannot add binary %s\n", path);
- ret = -EINVAL;
- goto bin_add_write_out;
- }
-
- ret = len;
-
-bin_add_write_out:
- kfree(path);
-
- return ret;
-}
-
-static ssize_t bin_remove_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
-
- if (file->f_path.dentry == target_remove)
- ret = pc_clean_instrumented_bins();
- else if (file->f_path.dentry == ignored_remove)
- ret = pc_clean_ignored_bins();
- else {
- /* Should never occur */
- printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
- file->f_path.dentry->d_name.name);
- ret = -EINVAL;
- goto bin_remove_write_out;
- }
-
- if (ret != 0) {
- printk(PRELOAD_PREFIX "Error during clean!\n");
- ret = -EINVAL;
- goto bin_remove_write_out;
- }
-
- ret = len;
-
-bin_remove_write_out:
- return ret;
-}
-
-static ssize_t bin_list_read(struct file *file, char __user *usr_buf,
- size_t count, loff_t *ppos)
-{
- unsigned int i;
- unsigned int files_cnt = 0;
- ssize_t len = 0, tmp, ret = 0;
- char **filenames = NULL;
- char *buf = NULL;
- char *ptr = NULL;
-
- if (file->f_path.dentry == target_list) {
- files_cnt = pc_get_target_names(&filenames);
- } else if (file->f_path.dentry == ignored_list) {
- files_cnt = pc_get_ignored_names(&filenames);
- } else {
- /* Should never occur */
- printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
- file->f_path.dentry->d_name.name);
- ret = 0;
- goto bin_list_read_out;
- }
-
- if (files_cnt == 0) {
- printk(PRELOAD_PREFIX "Cannot read binaries names!\n");
- ret = 0;
- goto bin_list_read_fail;
- }
-
- for (i = 0; i < files_cnt; i++)
- len += strlen(filenames[i]);
-
- buf = kmalloc(len + files_cnt, GFP_KERNEL);
- if (buf == NULL) {
- ret = 0;
- goto bin_list_read_fail;
- }
-
- ptr = buf;
-
- for (i = 0; i < files_cnt; i++) {
- tmp = strlen(filenames[i]);
- memcpy(ptr, filenames[i], tmp);
- ptr += tmp;
- *ptr = '\n';
- ptr += 1;
- }
-
- ret = simple_read_from_buffer(usr_buf, count, ppos, buf, len);
-
- kfree(buf);
-
-bin_list_read_fail:
- if (file->f_path.dentry == target_list) {
- pc_release_target_names(&filenames);
- } else if (file->f_path.dentry == ignored_list) {
- pc_release_ignored_names(&filenames);
- } else {
- /* Should never occur */
- printk(PRELOAD_PREFIX "%s() called for invalid file %s!\n", __func__,
- file->f_path.dentry->d_name.name);
- ret = 0;
- }
-
-bin_list_read_out:
- return ret;
-}
-
-static const struct file_operations bin_list_file_ops = {
- .owner = THIS_MODULE,
- .read = bin_list_read
-};
-
-static const struct file_operations bin_add_file_ops = {
- .owner = THIS_MODULE,
- .write = bin_add_write,
-};
-
-static const struct file_operations bin_remove_file_ops = {
- .owner = THIS_MODULE,
- .write = bin_remove_write,
-};
-
-
-
-/* ===========================================================================
- * = PTHREAD =
- * ===========================================================================
- */
-
-static ssize_t pthread_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_string_and_call(buf, len, pp_set_pthread_path);
-}
-
-static ssize_t init_off_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- return get_ul_and_call(buf, len, pp_set_init_offset);
-}
-
-static const struct file_operations pthread_path_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = pthread_path_write,
-};
-
-static const struct file_operations pthread_init_off_fops = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = init_off_write,
-};
-
-
-
-
-
-
-int pd_init(void)
-{
- struct dentry *swap_dentry, *root, *target_path, *ignored_path,
- *by_path, *by_pid, *by_id, *pthread, *dentry;
- int ret;
-
- ret = -ENODEV;
- if (!debugfs_initialized())
- goto fail;
-
- ret = -ENOENT;
- swap_dentry = swap_debugfs_getdir();
- if (!swap_dentry)
- goto fail;
-
- ret = -ENOMEM;
- root = swap_debugfs_create_dir(PRELOAD_FOLDER, swap_dentry);
- if (IS_ERR_OR_NULL(root))
- goto fail;
-
- preload_root = root;
-
- target_path = swap_debugfs_create_dir(PRELOAD_TARGET, root);
- if (IS_ERR_OR_NULL(target_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- target_list = swap_debugfs_create_file(PRELOAD_BINARIES_LIST,
- PRELOAD_DEFAULT_PERMS,
- target_path,
- NULL, &bin_list_file_ops);
- if (IS_ERR_OR_NULL(target_list)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- target_add = swap_debugfs_create_file(PRELOAD_BINARIES_ADD,
- PRELOAD_DEFAULT_PERMS,
- target_path,
- NULL, &bin_add_file_ops);
- if (IS_ERR_OR_NULL(target_add)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- target_remove = swap_debugfs_create_file(PRELOAD_BINARIES_REMOVE,
- PRELOAD_DEFAULT_PERMS,
- target_path,
- NULL, &bin_remove_file_ops);
- if (IS_ERR_OR_NULL(target_remove)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- ignored_path = swap_debugfs_create_dir(PRELOAD_IGNORED, root);
- if (IS_ERR_OR_NULL(ignored_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- ignored_list = swap_debugfs_create_file(PRELOAD_BINARIES_LIST,
- PRELOAD_DEFAULT_PERMS,
- ignored_path,
- NULL, &bin_list_file_ops);
- if (IS_ERR_OR_NULL(ignored_list)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- ignored_add = swap_debugfs_create_file(PRELOAD_BINARIES_ADD,
- PRELOAD_DEFAULT_PERMS,
- ignored_path,
- NULL, &bin_add_file_ops);
- if (IS_ERR_OR_NULL(ignored_add)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- ignored_remove = swap_debugfs_create_file(PRELOAD_BINARIES_REMOVE,
- PRELOAD_DEFAULT_PERMS,
- ignored_path, NULL,
- &bin_remove_file_ops);
- if (IS_ERR_OR_NULL(ignored_remove)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- by_path = swap_debugfs_create_dir(PRELOAD_BY_PATH, root);
- if (IS_ERR_OR_NULL(by_path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
- by_path, NULL, &by_path_add_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
- by_path, NULL, &by_path_del_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- by_pid = swap_debugfs_create_dir(PRELOAD_BY_PID, root);
- if (IS_ERR_OR_NULL(by_pid)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
- by_pid, NULL, &by_pid_add_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
- by_pid, NULL, &by_pid_del_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- by_id = swap_debugfs_create_dir(PRELOAD_BY_ID, root);
- if (IS_ERR_OR_NULL(by_id)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
- by_id, NULL, &by_id_add_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
- by_id, NULL, &by_id_del_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_DEL_ALL,
- PRELOAD_DEFAULT_PERMS,
- root, NULL, &del_all_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_ENABLE, PRELOAD_DEFAULT_PERMS,
- root, NULL, &enable_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- pthread = swap_debugfs_create_dir(PRELOAD_PTHREAD, root);
- if (IS_ERR_OR_NULL(pthread)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_PATH, PRELOAD_DEFAULT_PERMS,
- pthread, NULL, &pthread_path_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- dentry = swap_debugfs_create_file(PRELOAD_MINIMAL_INIT,
- PRELOAD_DEFAULT_PERMS, pthread, NULL,
- &pthread_init_off_fops);
- if (IS_ERR_OR_NULL(dentry)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- return 0;
-
-remove:
-
- debugfs_remove_recursive(root);
-
-fail:
- printk(PRELOAD_PREFIX "Debugfs initialization failure: %d\n", ret);
-
- return ret;
-}
-
-void pd_exit(void)
-{
- if (preload_root)
- debugfs_remove_recursive(preload_root);
- target_list = NULL;
- target_add = NULL;
- target_remove = NULL;
- ignored_list = NULL;
- ignored_add = NULL;
- ignored_remove = NULL;
- preload_root = NULL;
-}
+++ /dev/null
-#ifndef __PRELOAD_HANDLERS_DEBUGFS_H__
-#define __PRELOAD_HANDLERS_DEBUGFS_H__
-
-struct dentry;
-
-int pd_init(void);
-void pd_exit(void);
-
-#endif /* __PRELOAD_HANDLERS_DEBUGFS_H__ */
+++ /dev/null
-#include <linux/bug.h>
-#include <linux/slab.h>
-#include <linux/namei.h>
-#include <linux/mutex.h>
-#include <linux/sched.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <writer/kernel_operations.h>
-#include <writer/swap_msg.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/sspt/sspt_page.h>
-#include <us_manager/sspt/sspt_file.h>
-#include <us_manager/us_common_file.h>
-#include <loader/loader.h>
-#include <master/swap_initializer.h>
-#include "preload.h"
-#include "preload_module.h"
-#include "preload_debugfs.h"
-#include "preload_control.h"
-#include "preload_threads.h"
-#include "preload_process.h"
-
-enum {
- /* task preload flags */
- HANDLER_RUNNING = 0x1
-};
-
-struct user_ptrs {
- char *caller;
- char *orig;
- char *call_type;
-};
-
-static enum preload_status _status = PRELOAD_OFF;
-static DEFINE_MUTEX(status_change);
-
-
-static inline bool _is_enable_no_lock(void)
-{
- return _status;
-}
-
-static inline struct vm_area_struct *__get_vma_by_addr(struct task_struct *task,
- unsigned long caddr)
-{
- struct vm_area_struct *vma = NULL;
-
- if ((task == NULL) || (task->mm == NULL))
- return NULL;
- vma = find_vma_intersection(task->mm, caddr, caddr + 1);
-
- return vma;
-}
-
-static inline bool __is_probe_non_block(struct sspt_ip *ip)
-{
- if (ip->desc->info.pl_i.flags & SWAP_PRELOAD_NON_BLOCK_PROBE)
- return true;
-
- return false;
-}
-
-static inline bool __inverted(struct sspt_ip *ip)
-{
- unsigned long flags = ip->desc->info.pl_i.flags;
-
- if (flags & SWAP_PRELOAD_INVERTED_PROBE)
- return true;
-
- return false;
-}
-
-static inline bool __check_flag_and_call_type(struct sspt_ip *ip,
- enum preload_call_type ct)
-{
- bool inverted = __inverted(ip);
-
- if (ct != NOT_INSTRUMENTED || inverted)
- return true;
-
- return false;
-}
-
-static inline bool __is_handlers_call(struct vm_area_struct *caller,
- struct pd_t *pd)
-{
- struct hd_t *hd;
-
- if (caller == NULL || caller->vm_file == NULL ||
- caller->vm_file->f_path.dentry == NULL) {
- return false;
- }
-
- hd = lpd_get_hd(pd, caller->vm_file->f_path.dentry);
- if (hd != NULL)
- return true;
-
- return false;
-}
-
-static inline bool __should_drop(struct sspt_ip *ip, enum preload_call_type ct)
-{
- if (ct == NOT_INSTRUMENTED)
- return true;
-
- return false;
-}
-
-static inline int __msg_sanitization(char *user_msg, size_t len,
- struct user_ptrs *ptrs)
-{
- if (ptrs->caller &&
- (ptrs->caller < user_msg || ptrs->caller > user_msg + len))
- return -EINVAL;
-
- if (ptrs->orig &&
- (ptrs->orig < user_msg || ptrs->orig > user_msg + len))
- return -EINVAL;
-
- if (ptrs->call_type &&
- (ptrs->call_type < user_msg || ptrs->call_type > user_msg + len))
- return -EINVAL;
-
- return 0;
-}
-
-
-
-
-static unsigned long __do_preload_entry(struct uretprobe_instance *ri,
- struct pt_regs *regs,
- struct hd_t *hd)
-{
- struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
- unsigned long offset = ip->desc->info.pl_i.handler;
- unsigned long vaddr = 0;
- unsigned long base;
- struct vm_area_struct *cvma;
- struct pd_t *pd;
- struct pt_data_t ptd;
-
- base = lpd_get_handlers_base(hd);
- if (base == 0)
- return 0; /* handlers isn't mapped */
-
- /* jump to preloaded handler */
- vaddr = base + offset;
- ptd.caller = get_regs_ret_func(regs);
- cvma = __get_vma_by_addr(current, ptd.caller);
- ptd.call_type = pc_call_type(ip, (void *)ptd.caller);
- ptd.disable_addr = __is_probe_non_block(ip) ? ip->orig_addr : 0;
- ptd.orig = ip->orig_addr;
- pd = lpd_get_parent_pd(hd);
-
- /* jump only if caller is instumented and it is not a system lib -
- * this leads to some errors
- */
- if (cvma != NULL && cvma->vm_file != NULL &&
- !pc_check_dentry_is_ignored(cvma->vm_file->f_path.dentry) &&
- __check_flag_and_call_type(ip, ptd.call_type) &&
- !__is_handlers_call(cvma, pd)) {
-
- ptd.drop = __should_drop(ip, ptd.call_type);
- if (pt_set_data(current, &ptd) != 0)
- printk(PRELOAD_PREFIX "Error! Failed to store data for %d/%d\n",
- current->tgid, current->pid);
- /* args are not changed */
- loader_module_prepare_ujump(ri, regs, vaddr);
- if (ptd.disable_addr == 0)
- pt_set_flags(current, HANDLER_RUNNING);
- }
-
- return vaddr;
-}
-
-static int preload_us_entry(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
- struct hd_t *hd;
- unsigned long old_pc = swap_get_upc(regs);
- unsigned long flags = pt_get_flags(current);
- struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
- unsigned long vaddr = 0;
- struct dentry *dentry = ip->desc->info.pl_i.dentry;
-
- if (dentry == NULL)
- goto out_set_orig;
-
- if ((flags & HANDLER_RUNNING) ||
- pt_check_disabled_probe(current, ip->orig_addr))
- goto out_set_orig;
-
- hd = lpd_get_hd(pd, dentry);
- if (hd == NULL)
- goto out_set_orig;
-
- if ((lpd_get_state(hd) == NOT_LOADED ||
- lpd_get_state(hd) == FAILED) && lpd_get_init_state(pd))
- vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
- else if (lpd_get_state(hd) == LOADED)
- vaddr =__do_preload_entry(ri, regs, hd);
-
-out_set_orig:
- loader_set_priv_origin(ri, vaddr);
-
- /* PC change check */
- return old_pc != swap_get_upc(regs);
-}
-
-static void __do_preload_ret(struct uretprobe_instance *ri, struct hd_t *hd)
-{
- struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
- unsigned long flags = pt_get_flags(current);
- unsigned long offset = ip->desc->info.pl_i.handler;
- unsigned long vaddr = 0;
-
- if ((flags & HANDLER_RUNNING) ||
- pt_check_disabled_probe(current, ip->orig_addr)) {
- bool non_blk_probe = __is_probe_non_block(ip);
-
- /* drop the flag if the handler has completed */
- vaddr = lpd_get_handlers_base(hd) + offset;
- if (vaddr && (loader_get_priv_origin(ri) == vaddr)) {
- if (pt_put_data(current) != 0)
- printk(PRELOAD_PREFIX "Error! Failed to put "
- "caller slot for %d/%d\n", current->tgid,
- current->pid);
- if (!non_blk_probe) {
- flags &= ~HANDLER_RUNNING;
- pt_set_flags(current, flags);
- }
- }
- }
-}
-
-static int preload_us_ret(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
- struct sspt_ip *ip = container_of(ri->rp, struct sspt_ip, retprobe);
- struct dentry *dentry = ip->desc->info.pl_i.dentry;
- struct hd_t *hd;
-
- if (dentry == NULL)
- return 0;
-
- hd = lpd_get_hd(pd, dentry);
- if (hd == NULL)
- return 0;
-
- switch (lpd_get_state(hd)) {
- case NOT_LOADED:
- /* loader has not yet been mapped... just ignore */
- break;
- case LOADING:
- loader_loading_ret(ri, regs, pd, hd);
- break;
- case LOADED:
- __do_preload_ret(ri, hd);
- break;
- case FAILED:
- loader_failed_ret(ri, regs, pd, hd);
- break;
- case ERROR:
- default:
- break;
- }
-
- return 0;
-}
-
-
-
-
-
-static void __write_data_on_demand(char *user, char *msg, size_t len,
- struct user_ptrs *ptrs,
- unsigned long caller_addr)
-{
- unsigned long caller_ptr = 0, orig_ptr = 0, ct_ptr = 0;
- unsigned long caller = 0, orig = 0;
- unsigned char call_type = 0;
- int ret;
-
- /* Evaluate addresses whereto write data as:
- * pointer from user - user buffer beggining + kernel buffer beginning
- * If incoming pointer is NULL - do not write
- */
-
- if (ptrs->orig) {
- orig_ptr = (unsigned long)(ptrs->orig - user + msg);
-
- ret = pt_get_orig(current, &orig);
- if (ret) {
- orig = 0xbadbeef;
- printk(PRELOAD_PREFIX "No orig for %d/%d\n",
- current->tgid, current->pid);
- }
-
- /* Types should be the same as in preload lib!!! */
- *(uint64_t *)orig_ptr = (uint64_t)orig;
- }
-
- if (caller_addr && ptrs->caller && ptrs->call_type) {
- caller_ptr = (unsigned long)(ptrs->caller - user + msg);
- ct_ptr = (unsigned long)(ptrs->call_type - user + msg);
-
- caller = caller_addr;
- call_type =
- pc_call_type_always_inst((void *)caller);
-
- /* Types should be the same as in preload lib!!! */
- *(uint64_t *)caller_ptr = (uint64_t)caller;
- *(uint32_t *)ct_ptr = (uint32_t)call_type;
-
- return;
- }
-
- if (ptrs->caller) {
- caller_ptr = (unsigned long)(ptrs->caller - user + msg);
-
- ret = pt_get_caller(current, &caller);
- if (ret) {
- caller = 0xbadbeef;
- printk(PRELOAD_PREFIX "No caller for %d/%d\n",
- current->tgid, current->pid);
- }
-
- /* Types should be the same as in preload lib!!! */
- *(uint64_t *)caller_ptr = (uint64_t)caller;
- }
-
- if (ptrs->call_type) {
- ct_ptr = (unsigned long)(ptrs->call_type - user + msg);
-
- ret = pt_get_call_type(current, &call_type);
- if (ret) {
- call_type = 0xff;
- printk(PRELOAD_PREFIX "No call type for %d/%d\n",
- current->tgid, current->pid);
- }
-
- /* Types should be the same as in preload lib!!! */
- *(uint32_t *)ct_ptr = (uint32_t)call_type;
- }
-}
-
-static int write_msg_handler(struct uprobe *p, struct pt_regs *regs)
-{
- struct user_ptrs ptrs;
- char *user_buf, *buf;
- size_t len;
- unsigned long caller_addr;
- int ret;
-
- /* FIXME: swap_get_uarg uses get_user(), it might sleep */
- user_buf = (char *)swap_get_uarg(regs, 0);
- len = swap_get_uarg(regs, 1);
- ptrs.call_type = (char *)swap_get_uarg(regs, 2);
- ptrs.caller = (char *)swap_get_uarg(regs, 3);
- caller_addr = swap_get_uarg(regs, 4);
- ptrs.orig = (char *)swap_get_uarg(regs, 5);
-
- if (ptrs.caller || ptrs.call_type || ptrs.orig) {
- ret = __msg_sanitization(user_buf, len, &ptrs);
- if (ret != 0) {
- printk(PRELOAD_PREFIX "Invalid message pointers!\n");
- return 0;
- }
- ret = pt_get_drop(current);
- if (ret > 0)
- return 0;
- }
-
- buf = kmalloc(len, GFP_ATOMIC);
- if (buf == NULL) {
- printk(PRELOAD_PREFIX "No mem for buffer! Size = %zd\n", len);
- return 0;
- }
-
- ret = read_proc_vm_atomic(current, (unsigned long)user_buf, buf, len);
- if (ret < 0) {
- printk(PRELOAD_PREFIX "Cannot copy data from userspace! Size = "
- "%zd ptr 0x%lx ret %d\n", len,
- (unsigned long)user_buf, ret);
- goto write_msg_fail;
- }
-
- __write_data_on_demand(user_buf, buf, len, &ptrs, caller_addr);
-
- ret = swap_msg_raw(buf, len);
- if (ret != len)
- printk(PRELOAD_PREFIX "Error writing probe lib message\n");
-
-write_msg_fail:
- kfree(buf);
-
- return 0;
-}
-
-static int get_caller_handler(struct uprobe *p, struct pt_regs *regs)
-{
- unsigned long caller;
- int ret;
-
- ret = pt_get_caller(current, &caller);
- if (ret != 0) {
- caller = 0xbadbeef;
- printk(PRELOAD_PREFIX "Error! Cannot get caller address for "
- "%d/%d\n", current->tgid, current->pid);
- }
-
- swap_put_uarg(regs, 0, caller);
-
- return 0;
-}
-
-static int get_call_type_handler(struct uprobe *p, struct pt_regs *regs)
-{
- unsigned char call_type;
- int ret;
-
- ret = pt_get_call_type(current, &call_type);
- if (ret != 0) {
- call_type = 0xff;
- printk(PRELOAD_PREFIX "Error! Cannot get call type for %d/%d\n",
- current->tgid, current->pid);
- }
-
- swap_put_uarg(regs, 0, call_type);
-
- return 0;
-}
-
-
-
-
-
-int pm_get_caller_init(struct sspt_ip *ip)
-{
- struct uprobe *up = &ip->uprobe;
-
- up->pre_handler = get_caller_handler;
-
- return 0;
-}
-
-void pm_get_caller_exit(struct sspt_ip *ip)
-{
-}
-
-int pm_get_call_type_init(struct sspt_ip *ip)
-{
- struct uprobe *up = &ip->uprobe;
-
- up->pre_handler = get_call_type_handler;
-
- return 0;
-}
-
-void pm_get_call_type_exit(struct sspt_ip *ip)
-{
-}
-
-int pm_write_msg_init(struct sspt_ip *ip)
-{
- struct uprobe *up = &ip->uprobe;
-
- up->pre_handler = write_msg_handler;
-
- return 0;
-}
-
-void pm_write_msg_exit(struct sspt_ip *ip)
-{
-}
-
-
-
-int pm_uprobe_init(struct sspt_ip *ip)
-{
- struct uretprobe *rp = &ip->retprobe;
- struct dentry *dentry;
- const char *path = ip->desc->info.pl_i.path;
-
- rp->entry_handler = preload_us_entry;
- rp->handler = preload_us_ret;
-
- /* Get dentry and set it in probe info struct */
- dentry = swap_get_dentry(path);
- if (dentry == NULL) {
- pr_warn(PRELOAD_PREFIX "Error! Cannot get handler %s\n", path);
- return -EINVAL;
- }
- ip->desc->info.pl_i.dentry = dentry;
-
- /* Add handler to loader */
- loader_add_handler(path);
-
- /* FIXME actually additional data_size is needed only when we jump
- * to dlopen */
- loader_set_rp_data_size(rp);
-
- return 0;
-}
-
-void pm_uprobe_exit(struct sspt_ip *ip)
-{
- struct dentry *dentry = ip->desc->info.pl_i.dentry;
-
- WARN_ON(!dentry);
-
- if (dentry)
- swap_put_dentry(dentry);
-}
-
-int pm_switch(enum preload_status stat)
-{
- int ret = 0;
-
- mutex_lock(&status_change);
- switch (stat) {
- case PRELOAD_ON:
- if (_is_enable_no_lock())
- goto pm_switch_unlock;
-
- ret = pp_enable();
- if (!ret)
- _status = PRELOAD_ON;
- break;
- case PRELOAD_OFF:
- if (!_is_enable_no_lock())
- goto pm_switch_unlock;
-
- pp_disable();
- _status = PRELOAD_OFF;
- break;
- default:
- ret = -EINVAL;
- }
-
-pm_switch_unlock:
- mutex_unlock(&status_change);
-
- return ret;
-}
-
-enum preload_status pm_status(void)
-{
- enum preload_status s;
-
- mutex_lock(&status_change);
- s = _status;
- mutex_unlock(&status_change);
-
- return s;
-}
-
-static int pm_init(void)
-{
- int ret;
-
- ret = pd_init();
- if (ret)
- goto out_err;
-
- ret = pc_init();
- if (ret)
- goto control_init_fail;
-
- ret = pt_init();
- if (ret)
- goto threads_init_fail;
-
- ret = register_preload_probes();
- if (ret)
- goto probes_register_fail;
-
- return 0;
-
-probes_register_fail:
- pt_exit();
-
-threads_init_fail:
- pc_exit();
-
-control_init_fail:
- pd_exit();
-
-out_err:
- return ret;
-}
-
-static void pm_exit(void)
-{
- unregister_preload_probes();
- pt_exit();
- pc_exit();
- pd_exit();
-}
-
-SWAP_LIGHT_INIT_MODULE(NULL, pm_init, pm_exit, NULL, NULL);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP Preload Module");
-MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>");
+++ /dev/null
-#ifndef __PRELOAD_MODULE_H__
-#define __PRELOAD_MODULE_H__
-
-struct sspt_ip;
-struct dentry;
-
-
-enum preload_status {
- PRELOAD_ON,
- PRELOAD_OFF
-};
-
-enum preload_status pm_status(void);
-int pm_switch(enum preload_status stat);
-
-int pm_uprobe_init(struct sspt_ip *ip);
-void pm_uprobe_exit(struct sspt_ip *ip);
-
-int pm_get_caller_init(struct sspt_ip *ip);
-void pm_get_caller_exit(struct sspt_ip *ip);
-int pm_get_call_type_init(struct sspt_ip *ip);
-void pm_get_call_type_exit(struct sspt_ip *ip);
-int pm_write_msg_init(struct sspt_ip *ip);
-void pm_write_msg_exit(struct sspt_ip *ip);
-
-#endif /* __PRELOAD_MODULE_H__ */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/probes/preload_probe.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov: Preload implement
- *
- */
-
-#include <linux/module.h>
-#include <us_manager/us_manager.h>
-#include <us_manager/probes/register_probes.h>
-#include <us_manager/sspt/sspt_page.h>
-#include <uprobe/swap_uprobes.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include "preload_probe.h"
-#include "preload.h"
-#include "preload_module.h"
-
-static int preload_info_copy(struct probe_info *dest,
- const struct probe_info *source)
-{
- memcpy(dest, source, sizeof(*source));
-
- return 0;
-}
-
-static void preload_info_cleanup(struct probe_info *probe_i)
-{
-}
-
-static struct uprobe *preload_get_uprobe(struct sspt_ip *ip)
-{
- return &ip->retprobe.up;
-}
-
-/* Registers probe if preload is 'running' or 'ready'.
- */
-static int preload_register_probe(struct sspt_ip *ip)
-{
- return swap_register_uretprobe(&ip->retprobe);
-}
-
-static void preload_unregister_probe(struct sspt_ip *ip, int disarm)
-{
- __swap_unregister_uretprobe(&ip->retprobe, disarm);
-}
-
-static void preload_init(struct sspt_ip *ip)
-{
- pm_uprobe_init(ip);
-}
-
-static void preload_uninit(struct sspt_ip *ip)
-{
- pm_uprobe_exit(ip);
-
- preload_info_cleanup(&ip->desc->info);
-}
-
-static struct probe_iface preload_iface = {
- .init = preload_init,
- .uninit = preload_uninit,
- .reg = preload_register_probe,
- .unreg = preload_unregister_probe,
- .get_uprobe = preload_get_uprobe,
- .copy = preload_info_copy,
- .cleanup = preload_info_cleanup
-};
-
-static int get_caller_info_copy(struct probe_info *dest,
- const struct probe_info *source)
-{
- memcpy(dest, source, sizeof(*source));
-
- return 0;
-}
-
-static void get_caller_info_cleanup(struct probe_info *probe_i)
-{
-}
-
-static struct uprobe *get_caller_get_uprobe(struct sspt_ip *ip)
-{
- return &ip->uprobe;
-}
-
-static int get_caller_register_probe(struct sspt_ip *ip)
-{
- return swap_register_uprobe(&ip->uprobe);
-}
-
-static void get_caller_unregister_probe(struct sspt_ip *ip, int disarm)
-{
- __swap_unregister_uprobe(&ip->uprobe, disarm);
-}
-
-static void get_caller_init(struct sspt_ip *ip)
-{
- pm_get_caller_init(ip);
-}
-
-static void get_caller_uninit(struct sspt_ip *ip)
-{
- pm_get_caller_exit(ip);
-
- get_caller_info_cleanup(&ip->desc->info);
-}
-
-static struct probe_iface get_caller_iface = {
- .init = get_caller_init,
- .uninit = get_caller_uninit,
- .reg = get_caller_register_probe,
- .unreg = get_caller_unregister_probe,
- .get_uprobe = get_caller_get_uprobe,
- .copy = get_caller_info_copy,
- .cleanup = get_caller_info_cleanup
-};
-
-static void get_call_type_init(struct sspt_ip *ip)
-{
- pm_get_call_type_init(ip);
-}
-
-static void get_call_type_uninit(struct sspt_ip *ip)
-{
- pm_get_call_type_exit(ip);
-
- get_caller_info_cleanup(&ip->desc->info);
-}
-
-static struct probe_iface get_call_type_iface = {
- .init = get_call_type_init,
- .uninit = get_call_type_uninit,
- .reg = get_caller_register_probe,
- .unreg = get_caller_unregister_probe,
- .get_uprobe = get_caller_get_uprobe,
- .copy = get_caller_info_copy,
- .cleanup = get_caller_info_cleanup
-};
-
-static void write_msg_init(struct sspt_ip *ip)
-{
- pm_write_msg_init(ip);
-}
-
-static int write_msg_reg(struct sspt_ip *ip)
-{
- return get_caller_register_probe(ip);
-}
-
-static void write_msg_uninit(struct sspt_ip *ip)
-{
- pm_write_msg_exit(ip);
-
- get_caller_info_cleanup(&ip->desc->info);
-}
-
-static struct probe_iface write_msg_iface = {
- .init = write_msg_init,
- .uninit = write_msg_uninit,
- .reg = write_msg_reg,
- .unreg = get_caller_unregister_probe,
- .get_uprobe = get_caller_get_uprobe,
- .copy = get_caller_info_copy,
- .cleanup = get_caller_info_cleanup
-};
-
-int register_preload_probes(void)
-{
- int ret;
-
- ret = swap_register_probe_type(SWAP_PRELOAD_PROBE, &preload_iface);
- if (ret != 0)
- return ret;
-
- ret = swap_register_probe_type(SWAP_GET_CALLER, &get_caller_iface);
- if (ret != 0)
- return ret;
-
- ret = swap_register_probe_type(SWAP_GET_CALL_TYPE, &get_call_type_iface);
- if (ret != 0)
- return ret;
-
- ret = swap_register_probe_type(SWAP_WRITE_MSG, &write_msg_iface);
-
- return ret;
-}
-
-void unregister_preload_probes(void)
-{
- swap_unregister_probe_type(SWAP_PRELOAD_PROBE);
- swap_unregister_probe_type(SWAP_GET_CALLER);
- swap_unregister_probe_type(SWAP_GET_CALL_TYPE);
- swap_unregister_probe_type(SWAP_WRITE_MSG);
-}
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/probes/preload_probe.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov: FBI implement
- *
- */
-
-#ifndef __PRELOAD_HANDLERS_PROBE_H__
-#define __PRELOAD_HANDLERS_PROBE_H__
-
-/* Probe flags description:
- *
- * 0 - handler is ran only when probe has fired from a target binary;
- * 1 - handler is always ran;
- *
- * 00 - probe is disabling internal probes;
- * 10 - probe is non blocking one;
- *
- * 000 - probe is executed for instrumented binaries
- * 100 - probe is executed for non-instrumented binaries
- */
-
-enum {
- SWAP_PRELOAD_ALWAYS_RUN = (1 << 0),
- SWAP_PRELOAD_NON_BLOCK_PROBE = (1 << 1),
- SWAP_PRELOAD_INVERTED_PROBE = (1 << 2)
-};
-
-/* Preload probe info. */
-struct preload_info {
- unsigned long handler; /* Handler offset in probe library. */
- unsigned char flags; /* Preload probe flags. */
- const char *path; /* Library with handler */
- struct dentry *dentry; /* Handler file dentry */
-};
-
-/* Get caller probe info */
-struct get_caller_info {
-};
-
-/* Get call type probe info */
-struct get_call_type_info {
-};
-
-/* Write message probe info */
-struct write_msg_info {
-};
-
-int register_preload_probes(void);
-void unregister_preload_probes(void);
-
-#endif /* __PRELOAD_HANDLERS_PROBE_H__ */
+++ /dev/null
-#include <linux/err.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/namei.h>
-#include <linux/slab.h>
-#include <us_manager/us_common_file.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/sspt/sspt_page.h>
-#include <us_manager/sspt/sspt_file.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/probes/probe_info_new.h>
-#include <loader/loader.h>
-#include "preload.h"
-#include "preload_process.h"
-
-
-enum task_id_t {
- SET_BY_PATH,
- SET_BY_PID,
- SET_BY_ID
-};
-
-struct process_t {
- struct list_head list;
- struct pf_group *pfg;
- enum task_id_t idt;
- union {
- struct dentry *dentry;
- pid_t pid;
- char *id;
- };
- struct probe_new p_init;
-};
-
-struct bin_data_t {
- struct dentry *dentry;
- unsigned long off;
-};
-
-/* For process list elements checker */
-typedef struct process_t *(*checker_t)(struct process_t *, void *);
-
-static LIST_HEAD(_reg_proc_list);
-static LIST_HEAD(_unreg_proc_list);
-static DEFINE_MUTEX(_proc_list_lock);
-
-static struct bin_data_t _pthread_init;
-
-static inline void _lock_proc_list(void)
-{
- mutex_lock(&_proc_list_lock);
-}
-
-static inline void _unlock_proc_list(void)
-{
- mutex_unlock(&_proc_list_lock);
-}
-
-static inline struct process_t *_check_by_dentry(struct process_t *proc,
- void *data)
-{
- struct dentry *dentry = data;
-
- if (proc->idt == SET_BY_PATH && proc->dentry == dentry)
- return proc;
-
- return NULL;
-}
-
-static inline struct process_t *_check_by_pid(struct process_t *proc,
- void *data)
-{
- pid_t pid = *(pid_t *)data;
-
- if (proc->idt == SET_BY_PID && proc->pid == pid)
- return proc;
-
- return NULL;
-}
-
-static inline struct process_t *_check_by_id(struct process_t *proc, void *data)
-{
- char *id = data;
-
- if (proc->idt == SET_BY_ID && proc->id == id)
- return proc;
-
- return NULL;
-}
-
-static inline bool _is_pthread_data_available(void)
-{
- return (_pthread_init.dentry && _pthread_init.off);
-}
-
-
-
-
-static int pthread_init_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
-
- lpd_set_init_state(pd, false);
-
- return 0;
-}
-
-static int pthread_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
-
- lpd_set_init_state(pd, true);
-
- return 0;
-}
-
-static struct probe_desc pin_pinit = MAKE_URPROBE(pthread_init_eh,
- pthread_init_rh, 0);
-
-static int _proc_reg_no_lock(struct process_t *proc)
-{
- int ret;
-
- proc->p_init.desc = &pin_pinit;
- proc->p_init.offset = _pthread_init.off;
- ret = pin_register(&proc->p_init, proc->pfg, _pthread_init.dentry);
- if (ret)
- pr_warn(PRELOAD_PREFIX "Can't register pthread init probe\n");
-
- return ret;
-}
-
-static void _proc_unreg_no_lock(struct process_t *proc)
-{
- pin_unregister(&proc->p_init, proc->pfg);
-}
-
-static struct process_t *_proc_create(struct pf_group *pfg)
-{
- struct process_t *proc;
-
- proc = kzalloc(sizeof(*proc), GFP_KERNEL);
- if (!proc) {
- pr_warn(PRELOAD_PREFIX "No mem to alloc proccess_t struct!\n");
- return ERR_PTR(-ENOMEM);
- }
-
- INIT_LIST_HEAD(&proc->list);
- proc->pfg = pfg;
-
- return proc;
-}
-
-static void _proc_destroy(struct process_t *proc)
-{
- if (proc->pfg)
- put_pf_group(proc->pfg);
-
- if (proc->idt == SET_BY_PATH)
- swap_put_dentry(proc->dentry);
-
- if (proc->idt == SET_BY_ID)
- kfree(proc->id);
-
- kfree(proc);
-}
-
-static int _add_to_list(struct process_t *proc)
-{
- _lock_proc_list();
- list_add_tail(&proc->list, &_unreg_proc_list);
- _unlock_proc_list();
-
- return 0;
-}
-
-/* checker - used to check process list candidates, chosen by type of
- * registration (path, pid, id)
- * data - pointer to target process data, checker compares candidate with
- */
-
-static struct process_t *_get_from_list_no_lock(checker_t checker, void *data,
- struct list_head *list)
-{
- struct process_t *proc;
-
- list_for_each_entry(proc, list, list) {
- if (checker(proc, data))
- return proc;
- }
-
- return NULL;
-}
-
-static void _delete_from_list(checker_t checker, void *data)
-{
- struct process_t *proc;
-
- _lock_proc_list();
- proc = _get_from_list_no_lock(checker, data, &_unreg_proc_list);
- if (proc) {
- list_del(&proc->list);
- _proc_destroy(proc);
- goto delete_from_list_unlock;
- }
-
- proc = _get_from_list_no_lock(checker, data, &_reg_proc_list);
- if (proc) {
- list_del(&proc->list);
- _proc_unreg_no_lock(proc);
- _proc_destroy(proc);
- }
-
-delete_from_list_unlock:
- _unlock_proc_list();
-}
-
-
-
-int pp_add_by_path(const char *path)
-{
- struct dentry *dentry;
- struct pf_group *pfg;
- struct process_t *proc;
- int ret;
-
- dentry = swap_get_dentry(path);
- if (!dentry) {
- pr_warn(PRELOAD_PREFIX "Can't get dentry for <%s>\n", path);
- return -EINVAL;
- }
-
- pfg = get_pf_group_by_dentry(dentry, dentry);
- if (pfg == NULL) {
- ret = -ENOMEM;
- goto by_path_put_dentry;
- }
-
- proc = _proc_create(pfg);
- if (!proc) {
- ret = -EINVAL;
- goto by_path_put_pfg;
- }
-
- proc->idt = SET_BY_PATH;
- proc->dentry = dentry;
-
- ret = _add_to_list(proc);
- if (ret)
- goto by_path_free;
-
- return 0;
-
-by_path_free:
- kfree(proc);
-
-by_path_put_pfg:
- put_pf_group(pfg);
-
-by_path_put_dentry:
- swap_put_dentry(dentry);
-
- return ret;
-}
-
-int pp_add_by_pid(pid_t pid)
-{
- struct pf_group *pfg;
- struct process_t *proc;
- int ret;
-
- pfg = get_pf_group_by_tgid(pid, NULL);
- if (pfg == NULL)
- return -ENOMEM;
-
- proc = _proc_create(pfg);
- if (!proc) {
- ret = -EINVAL;
- goto by_pid_put_pfg;
- }
-
- proc->idt = SET_BY_PID;
- proc->pid = pid;
-
- ret = _add_to_list(proc);
- if (ret)
- goto by_pid_free;
-
- return 0;
-
-by_pid_free:
- kfree(proc);
-
-by_pid_put_pfg:
- put_pf_group(pfg);
-
- return ret;
-}
-
-int pp_add_by_id(const char *id)
-{
- char *new_id;
- struct pf_group *pfg;
- struct process_t *proc;
- int ret;
-
- new_id = kstrdup(id, GFP_KERNEL);
- if (!new_id)
- return -ENOMEM;
-
- pfg = get_pf_group_by_comm((char *)id, NULL);
- if (pfg == NULL) {
- ret = -ENOMEM;
- goto by_id_free_new_id;
- }
-
- proc = _proc_create(pfg);
- if (!proc) {
- ret = -EINVAL;
- goto by_id_put_pfg;
- }
-
- proc->idt = SET_BY_ID;
- proc->id = new_id;
-
- ret = _add_to_list(proc);
- if (ret)
- goto by_id_free;
-
- return 0;
-
-by_id_free:
- kfree(proc);
-
-by_id_put_pfg:
- put_pf_group(pfg);
-
-by_id_free_new_id:
- kfree(new_id);
-
- return ret;
-}
-
-int pp_del_by_path(const char *path)
-{
- struct dentry *dentry;
-
- dentry = swap_get_dentry(path);
- if (!dentry) {
- pr_warn(PRELOAD_PREFIX "No dentry for <%s>\n", path);
- return -EINVAL;
- }
-
- _delete_from_list(_check_by_dentry, dentry);
-
- swap_put_dentry(dentry);
-
- return 0;
-}
-
-int pp_del_by_pid(pid_t pid)
-{
- _delete_from_list(_check_by_pid, &pid);
-
- return 0;
-}
-
-int pp_del_by_id(const char *id)
-{
- _delete_from_list(_check_by_id, (void *)id);
-
- return 0;
-}
-
-void pp_del_all(void)
-{
- struct process_t *tmp, *proc;
-
- _lock_proc_list();
-
- list_for_each_entry_safe(proc, tmp, &_unreg_proc_list, list) {
- list_del(&proc->list);
- _proc_destroy(proc);
- }
-
- list_for_each_entry_safe(proc, tmp, &_reg_proc_list, list) {
- list_del(&proc->list);
- _proc_unreg_no_lock(proc);
- _proc_destroy(proc);
- }
-
- _unlock_proc_list();
-}
-
-void pp_disable(void)
-{
- struct process_t *proc;
-
- _lock_proc_list();
-
- list_for_each_entry(proc, &_reg_proc_list, list) {
- _proc_unreg_no_lock(proc);
- list_move_tail(&proc->list, &_unreg_proc_list);
- }
-
- _unlock_proc_list();
-}
-
-int pp_enable(void)
-{
- struct process_t *proc;
- int ret;
-
- if (!_is_pthread_data_available())
- return -EINVAL;
-
- _lock_proc_list();
-
- list_for_each_entry(proc, &_unreg_proc_list, list) {
- ret = _proc_reg_no_lock(proc);
- if (ret)
- goto enable_pp_fail;
- else
- list_move_tail(&proc->list, &_reg_proc_list);
- }
-
- _unlock_proc_list();
-
- return 0;
-
-enable_pp_fail:
- _unlock_proc_list();
-
- pr_warn(PRELOAD_PREFIX "Error register probes, disabling...\n");
- pp_disable();
-
- return ret;
-}
-
-int pp_set_pthread_path(const char *path)
-{
- struct dentry *dentry;
-
- dentry = swap_get_dentry(path);
- if (dentry == NULL)
- return -EINVAL;
-
- if (_pthread_init.dentry != NULL)
- swap_put_dentry(_pthread_init.dentry);
-
- _pthread_init.dentry = dentry;
-
- return 0;
-}
-
-int pp_set_init_offset(unsigned long off)
-{
- _pthread_init.off = off;
-
- return 0;
-}
+++ /dev/null
-#ifndef __PRELOAD_PROCESS_H__
-#define __PRELOAD_PROCESS_H__
-
-int pp_add_by_path(const char *path);
-int pp_add_by_pid(pid_t pid);
-int pp_add_by_id(const char *id);
-
-int pp_del_by_path(const char *path);
-int pp_del_by_pid(pid_t pid);
-int pp_del_by_id(const char *id);
-void pp_del_all(void);
-
-int pp_enable(void);
-void pp_disable(void);
-
-int pp_set_pthread_path(const char *path);
-int pp_set_init_offset(unsigned long off);
-
-#endif /* __PRELOAD_PROCESS_H__ */
+++ /dev/null
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/list.h>
-#include <kprobe/swap_ktd.h>
-#include "preload.h"
-#include "preload_threads.h"
-
-struct preload_td {
- struct list_head slots;
- unsigned long flags;
-};
-
-struct thread_slot {
- struct list_head list;
- struct list_head disabled_addrs;
-
- unsigned long caller;
- unsigned long orig;
- unsigned char call_type;
- bool drop; /* TODO Workaround, remove when will be possible to install
- * several us probes at the same addr. */
-};
-
-struct disabled_addr {
- struct list_head list;
- unsigned long addr;
-};
-
-static void preload_ktd_init(struct task_struct *task, void *data)
-{
- struct preload_td *td = (struct preload_td *)data;
-
- INIT_LIST_HEAD(&td->slots);
- td->flags = 0;
-}
-
-static void preload_ktd_exit(struct task_struct *task, void *data)
-{
- /* TODO: to be implement */
-}
-
-static struct ktask_data preload_ktd = {
- .init = preload_ktd_init,
- .exit = preload_ktd_exit,
- .size = sizeof(struct preload_td),
-};
-
-
-static inline struct preload_td *get_preload_td(struct task_struct *task)
-{
- return (struct preload_td *)swap_ktd(&preload_ktd, task);
-}
-
-unsigned long pt_get_flags(struct task_struct *task)
-{
- return get_preload_td(task)->flags;
-}
-
-void pt_set_flags(struct task_struct *task, unsigned long flags)
-{
- get_preload_td(task)->flags = flags;
-}
-
-
-static inline bool __is_addr_found(struct disabled_addr *da,
- unsigned long addr)
-{
- if (da->addr == addr)
- return true;
-
- return false;
-}
-
-static inline void __remove_from_disable_list(struct disabled_addr *da)
-{
- list_del(&da->list);
- kfree(da);
-}
-
-static inline void __remove_whole_disable_list(struct thread_slot *slot)
-{
- struct disabled_addr *da, *n;
-
- list_for_each_entry_safe(da, n, &slot->disabled_addrs, list)
- __remove_from_disable_list(da);
-}
-
-static inline void __init_slot(struct thread_slot *slot)
-{
- slot->caller = 0;
- slot->orig = 0;
- slot->call_type = 0;
- slot->drop = false;
- INIT_LIST_HEAD(&slot->disabled_addrs);
-}
-
-static inline void __reinit_slot(struct thread_slot *slot)
-{
- __remove_whole_disable_list(slot);
- __init_slot(slot);
-}
-
-static inline void __set_slot(struct thread_slot *slot,
- struct task_struct *task, unsigned long caller,
- unsigned long orig, unsigned char call_type,
- bool drop)
-{
- slot->caller = caller;
- slot->orig = orig;
- slot->call_type = call_type;
- slot->drop = drop;
-}
-
-static inline int __add_to_disable_list(struct thread_slot *slot,
- unsigned long disable_addr)
-{
- struct disabled_addr *da = kmalloc(sizeof(*da), GFP_ATOMIC);
-
- if (da == NULL)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&da->list);
- da->addr = disable_addr;
- list_add_tail(&da->list, &slot->disabled_addrs);
-
- return 0;
-}
-
-static inline struct disabled_addr *__find_disabled_addr(struct thread_slot *slot,
- unsigned long addr)
-{
- struct disabled_addr *da;
-
- list_for_each_entry(da, &slot->disabled_addrs, list)
- if (__is_addr_found(da, addr))
- return da;
-
- return NULL;
-}
-
-/* Adds a new slot */
-static inline struct thread_slot *__grow_slot(void)
-{
- struct thread_slot *tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);
-
- if (tmp == NULL)
- return NULL;
-
- INIT_LIST_HEAD(&tmp->list);
- __init_slot(tmp);
-
- return tmp;
-}
-
-/* Free slot */
-static void __clean_slot(struct thread_slot *slot)
-{
- list_del(&slot->list);
- kfree(slot);
-}
-
-/* There is no list_last_entry in Linux 3.10 */
-#ifndef list_last_entry
-#define list_last_entry(ptr, type, member) \
- list_entry((ptr)->prev, type, member)
-#endif /* list_last_entry */
-
-static inline struct thread_slot *__get_task_slot(struct task_struct *task)
-{
- struct preload_td *td = get_preload_td(task);
-
- return list_empty(&td->slots) ? NULL :
- list_last_entry(&td->slots, struct thread_slot, list);
-}
-
-
-
-
-int pt_set_data(struct task_struct *task, struct pt_data_t *data)
-{
- struct preload_td *td = get_preload_td(task);
- struct thread_slot *slot;
- unsigned long caller, disable_addr, orig;
- unsigned char call_type;
- bool drop;
- int ret = 0;
-
- caller = data->caller;
- disable_addr = data->disable_addr;
- orig = data->orig;
- call_type = data->call_type;
- drop = data->drop;
-
- slot = __grow_slot();
- if (slot == NULL) {
- ret = -ENOMEM;
- goto set_data_done;
- }
-
- if ((disable_addr != 0) &&
- (__add_to_disable_list(slot, disable_addr) != 0)) {
- printk(KERN_ERR PRELOAD_PREFIX "Cannot alloc memory!\n");
- ret = -ENOMEM;
- goto set_data_done;
- }
-
- __set_slot(slot, task, caller, orig, call_type, drop);
- list_add_tail(&slot->list, &td->slots);
-
-set_data_done:
- return ret;
-}
-
-int pt_get_caller(struct task_struct *task, unsigned long *caller)
-{
- struct thread_slot *slot;
-
- slot = __get_task_slot(task);
- if (slot != NULL) {
- *caller = slot->caller;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int pt_get_orig(struct task_struct *task, unsigned long *orig)
-{
- struct thread_slot *slot;
-
- slot = __get_task_slot(task);
- if (slot != NULL) {
- *orig = slot->caller;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int pt_get_call_type(struct task_struct *task,
- unsigned char *call_type)
-{
- struct thread_slot *slot;
-
- slot = __get_task_slot(task);
- if (slot != NULL) {
- *call_type = slot->call_type;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int pt_get_drop(struct task_struct *task)
-{
- struct thread_slot *slot;
-
- slot = __get_task_slot(task);
- if (slot != NULL)
- return (int)slot->drop;
-
- return -EINVAL;
-}
-
-bool pt_check_disabled_probe(struct task_struct *task, unsigned long addr)
-{
- struct thread_slot *slot;
- bool ret = false;
-
- slot = __get_task_slot(task);
- if (slot != NULL)
- ret = __find_disabled_addr(slot, addr) == NULL ? false : true;
-
- return ret;
-}
-
-void pt_enable_probe(struct task_struct *task, unsigned long addr)
-{
- struct thread_slot *slot;
- struct disabled_addr *da;
-
- slot = __get_task_slot(task);
- if (slot == NULL) {
- printk(KERN_ERR PRELOAD_PREFIX "Error! Slot not found!\n");
- goto enable_probe_failed;
- }
-
- da = __find_disabled_addr(slot, addr);
- if (da != NULL)
- __remove_from_disable_list(da);
-
-enable_probe_failed:
- return; /* make gcc happy: cannot place label right before '}' */
-}
-
-int pt_put_data(struct task_struct *task)
-{
- struct thread_slot *slot;
- int ret = 0;
-
- slot = __get_task_slot(task);
- if (slot != NULL) {
- __reinit_slot(slot);
- __clean_slot(slot); /* remove from list */
- }
-
- return ret;
-}
-
-int pt_init(void)
-{
- return swap_ktd_reg(&preload_ktd);
-}
-
-void pt_exit(void)
-{
- swap_ktd_unreg(&preload_ktd);
-}
+++ /dev/null
-#ifndef __PRELOAD_HANDLERS_THREADS_H__
-#define __PRELOAD_HANLDERS_THREADS_H__
-
-struct task_struct;
-
-struct pt_data_t {
- unsigned long caller;
- unsigned long disable_addr;
- unsigned long orig;
- unsigned char call_type;
- bool drop;
-};
-
-unsigned long pt_get_flags(struct task_struct *task);
-void pt_set_flags(struct task_struct *task, unsigned long flags);
-
-int pt_set_data(struct task_struct *task, struct pt_data_t *data);
-
-int pt_get_caller(struct task_struct *task, unsigned long *caller);
-int pt_get_orig(struct task_struct *task, unsigned long *orig);
-int pt_get_call_type(struct task_struct *task, unsigned char *call_type);
-int pt_get_drop(struct task_struct *task);
-bool pt_check_disabled_probe(struct task_struct *task, unsigned long addr);
-
-void pt_enable_probe(struct task_struct *task, unsigned long addr);
-int pt_put_data(struct task_struct *task);
-int pt_init(void);
-void pt_exit(void);
-
-#endif /* __PRELOAD_HANDLERS_THREADS_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_retprobe.o
-swap_retprobe-y := \
- retprobe.o \
- rp_msg.o
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/retprobe/retprobe.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov: Probes interface implement
- *
- */
-
-#include "retprobe.h"
-#include <us_manager/us_manager.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/probes/register_probes.h>
-#include <uprobe/swap_uprobes.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include "rp_msg.h"
-
-
-static int retprobe_copy(struct probe_info *dest,
- const struct probe_info *source)
-{
- size_t len;
-
- memcpy(dest, source, sizeof(*source));
-
- len = strlen(source->rp_i.args) + 1;
- dest->rp_i.args = kmalloc(len, GFP_ATOMIC);
- if (dest->rp_i.args == NULL)
- return -ENOMEM;
- memcpy(dest->rp_i.args, source->rp_i.args, len);
-
- return 0;
-}
-
-
-static void retprobe_cleanup(struct probe_info *probe_i)
-{
- kfree(probe_i->rp_i.args);
-}
-
-
-
-static struct uprobe *retprobe_get_uprobe(struct sspt_ip *ip)
-{
- return &ip->retprobe.up;
-}
-
-static int retprobe_register_probe(struct sspt_ip *ip)
-{
- return swap_register_uretprobe(&ip->retprobe);
-}
-
-static void retprobe_unregister_probe(struct sspt_ip *ip, int disarm)
-{
- __swap_unregister_uretprobe(&ip->retprobe, disarm);
-}
-
-
-static int retprobe_entry_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct uretprobe *rp = ri->rp;
-
- if (rp && get_quiet() == QT_OFF) {
- struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
- const char *fmt = ip->desc->info.rp_i.args;
- const unsigned long func_addr = (unsigned long)ip->orig_addr;
-
- rp_msg_entry(regs, func_addr, fmt);
- }
-
- return 0;
-}
-
-static int retprobe_ret_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct uretprobe *rp = ri->rp;
-
- if (rp && get_quiet() == QT_OFF) {
- struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
- const unsigned long func_addr = (unsigned long)ip->orig_addr;
- const unsigned long ret_addr = (unsigned long)ri->ret_addr;
- const char ret_type = ip->desc->info.rp_i.ret_type;
-
- rp_msg_exit(regs, func_addr, ret_type, ret_addr);
- }
-
- return 0;
-}
-
-static void retprobe_init(struct sspt_ip *ip)
-{
- ip->retprobe.entry_handler = retprobe_entry_handler;
- ip->retprobe.handler = retprobe_ret_handler;
- ip->retprobe.maxactive = 0;
-}
-
-static void retprobe_uninit(struct sspt_ip *ip)
-{
- retprobe_cleanup(&ip->desc->info);
-}
-
-
-static struct probe_iface retprobe_iface = {
- .init = retprobe_init,
- .uninit = retprobe_uninit,
- .reg = retprobe_register_probe,
- .unreg = retprobe_unregister_probe,
- .get_uprobe = retprobe_get_uprobe,
- .copy = retprobe_copy,
- .cleanup = retprobe_cleanup
-};
-
-static int __init retprobe_module_init(void)
-{
- return swap_register_probe_type(SWAP_RETPROBE, &retprobe_iface);
-}
-
-static void __exit retprobe_module_exit(void)
-{
- swap_unregister_probe_type(SWAP_RETPROBE);
-}
-
-module_init(retprobe_module_init);
-module_exit(retprobe_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP retprobe");
-MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>");
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/probes/uretprobe.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov: Probes interface implement
- *
- */
-
-#ifndef __URETPROBE_H__
-#define __URETPROBE_H__
-
-/* Common retprobe info */
-struct retprobe_info {
- char *args;
- char ret_type;
-};
-
-#endif /* __URETPROBE_H__ */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <writer/swap_msg.h>
-#include <writer/kernel_operations.h>
-#include "rp_msg.h"
-
-
-#define RP_PREFIX KERN_INFO "[RP] "
-
-
-struct msg_entry {
- u32 pid;
- u32 tid;
- u64 pc_addr;
- u64 caller_pc_addr;
- u32 cpu_num;
- u32 cnt_args;
- char args[0];
-} __packed;
-
-struct msg_exit {
- u32 pid;
- u32 tid;
- u64 pc_addr;
- u64 caller_pc_addr;
- u32 cpu_num;
- char ret_val[0];
-} __packed;
-
-
-void rp_msg_entry(struct pt_regs *regs, unsigned long func_addr,
- const char *fmt)
-{
- int ret;
- struct task_struct *task = current;
- struct swap_msg *m;
- struct msg_entry *ent;
- void *p;
- size_t size;
-
- m = swap_msg_get(MSG_FUNCTION_ENTRY);
- p = swap_msg_payload(m);
-
- ent = p;
- ent->pid = task->tgid;
- ent->tid = task->pid;
- ent->pc_addr = func_addr;
- ent->caller_pc_addr = get_regs_ret_func(regs);
- ent->cpu_num = raw_smp_processor_id();
- ent->cnt_args = strlen(fmt);
-
- size = swap_msg_size(m);
- ret = swap_msg_pack_args(p + sizeof(*ent), size - sizeof(*ent),
- fmt, regs);
- if (ret < 0) {
- printk(RP_PREFIX "ERROR: arguments packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, sizeof(*ent) + ret);
-
-put_msg:
- swap_msg_put(m);
-}
-EXPORT_SYMBOL_GPL(rp_msg_entry);
-
-void rp_msg_exit(struct pt_regs *regs, unsigned long func_addr,
- char ret_type, unsigned long ret_addr)
-{
- int ret;
- struct task_struct *task = current;
- struct swap_msg *m;
- struct msg_exit *ext;
- void *p;
- size_t size;
-
- m = swap_msg_get(MSG_FUNCTION_EXIT);
- p = swap_msg_payload(m);
-
- ext = p;
- ext->pid = task->tgid;
- ext->tid = task->pid;
- ext->pc_addr = func_addr;
- ext->caller_pc_addr = ret_addr;
- ext->cpu_num = raw_smp_processor_id();
-
- size = swap_msg_size(m);
- ret = swap_msg_pack_ret_val(p + sizeof(*ext), size - sizeof(*ext),
- ret_type, regs);
- if (ret < 0) {
- printk(RP_PREFIX "ERROR: return value packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, sizeof(*ext) + ret);
-
-put_msg:
- swap_msg_put(m);
-}
-EXPORT_SYMBOL_GPL(rp_msg_exit);
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _RP_MSG_H
-#define _RP_MSG_H
-
-
-struct pt_regs;
-
-
-void rp_msg_entry(struct pt_regs *regs, unsigned long func_addr,
- const char *fmt);
-void rp_msg_exit(struct pt_regs *regs, unsigned long func_addr,
- char ret_type, unsigned long ret_addr);
-
-
-#endif /* _RP_MSG_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../writer/Module.symvers
-
-obj-m := swap_sampler.o
-swap_sampler-y := swap_sampler_module.o
-
-ifdef CONFIG_HIGH_RES_TIMERS
- swap_sampler-y += sampler_hrtimer.o
-else
- swap_sampler-y += sampler_timer.o
-endif
+++ /dev/null
-/**
- * @file sampler/kernel_operations.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Parser definitions.
- */
-
-#ifndef __KERNEL_OPERATIONS_H__
-#define __KERNEL_OPERATIONS_H__
-
-#include <linux/kernel.h>
-
-/** Prints debug message.*/
-#define print_debug(msg, args...) \
- printk(KERN_DEBUG "SWAP_SAMPLER DEBUG : " msg, ##args)
-/** Prints info message.*/
-#define print_msg(msg, args...) \
- printk(KERN_INFO "SWAP_SAMPLER : " msg, ##args)
-/** Prints warning message.*/
-#define print_warn(msg, args...) \
- printk(KERN_WARNING "SWAP_SAMPLER WARNING : " msg, ##args)
-/** Prints error message.*/
-#define print_err(msg, args...) \
- printk(KERN_ERR "SWAP_SAMPLER ERROR : " msg, ##args)
-/** Prints critical error message.*/
-#define print_crit(msg, args...) \
- printk(KERN_CRIT "SWAP_SAMPLER CRITICAL : " msg, ##args)
-
-#endif /* __KERNEL_OPERATIONS_H__ */
+++ /dev/null
-/**
- * sampler/sampler_hrtimer.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Sampler for high resolution timers.
- */
-
-
-
-#include <linux/types.h>
-#include <linux/version.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include "sampler_timers.h"
-
-
-static u64 sampler_timer_quantum;
-static DEFINE_PER_CPU(struct hrtimer, swap_hrtimer);
-static int swap_hrtimer_running;
-
-/**
- * @brief Restarts sampling.
- *
- * @param timer Pointer to hrtimer struct.
- * @return hrtimer_restart flag.
- */
-restart_ret sampler_timers_restart(swap_timer *timer)
-{
- restart_ret ret;
-
- hrtimer_forward_now(timer, ns_to_ktime(sampler_timer_quantum));
- ret = HRTIMER_RESTART;
-
- return ret;
-}
-
-/**
- * @brief Sets running flag true.
- *
- * @return Void.
- */
-void sampler_timers_set_run(void)
-{
- swap_hrtimer_running = 1;
-}
-
-/**
- * @brief Sets running flag false.
- *
- * @return Void.
- */
-void sampler_timers_set_stop(void)
-{
- swap_hrtimer_running = 0;
-}
-
-/**
- * @brief Starts timer sampling.
- *
- * @param restart_func Pointer to restart function.
- * @return Void.
- */
-void sampler_timers_start(void *restart_func)
-{
- struct hrtimer *hrtimer = &__get_cpu_var(swap_hrtimer);
-
- if (!swap_hrtimer_running)
- return;
-
- hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- hrtimer->function = restart_func;
- hrtimer_start(hrtimer, ns_to_ktime(sampler_timer_quantum),
- HRTIMER_MODE_REL_PINNED);
-}
-
-/**
- * @brief Stops timer sampling.
- *
- * @param cpu Online CPUs.
- * @return Void.
- */
-void sampler_timers_stop(int cpu)
-{
- struct hrtimer *hrtimer = &per_cpu(swap_hrtimer, cpu);
-
- if (!swap_hrtimer_running)
- return;
-
- hrtimer_cancel(hrtimer);
-}
-
-/**
- * @brief Sets timer quantum.
- *
- * @param timer_quantum Timer quantum.
- * @return Void.
- */
-void sampler_timers_set_quantum(unsigned int timer_quantum)
-{
- u64 tmp = (u64)timer_quantum;
- sampler_timer_quantum = tmp * 1000 * 1000;
-}
+++ /dev/null
-/**
- * sampler/sampler_timer.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Sampler based on common timers.
- */
-
-
-
-#include "sampler_timers.h"
-
-
-
-static unsigned long sampler_timer_quantum;
-static DEFINE_PER_CPU(struct timer_list, swap_timer);
-static int swap_timer_running;
-
-/**
- * @brief Restarts sampling.
- *
- * @param timer Pointer to timer_list struct.
- * @return 0.
- */
-restart_ret sampler_timers_restart(swap_timer *timer)
-{
- restart_ret ret;
-
- mod_timer_pinned((struct timer_list *)timer,
- jiffies + sampler_timer_quantum);
- ret = 0;
-
- return ret;
-}
-
-/**
- * @brief Sets running flag true.
- *
- * @return Void.
- */
-void sampler_timers_set_run(void)
-{
- swap_timer_running = 1;
-}
-
-/**
- * @brief Sets running flag false.
- *
- * @return Void.
- */
-void sampler_timers_set_stop(void)
-{
- swap_timer_running = 0;
-}
-
-/**
- * @brief Starts timer sampling.
- *
- * @param restart_func Pointer to restart function.
- * @return Void.
- */
-void sampler_timers_start(void *restart_func)
-{
- struct timer_list *timer = &__get_cpu_var(swap_timer);
-
- if (!swap_timer_running)
- return;
-
- init_timer(timer);
- timer->data = (unsigned long)timer;
- timer->function = restart_func;
-
- mod_timer_pinned(timer, jiffies + sampler_timer_quantum);
-}
-
-/**
- * @brief Stops timer sampling.
- *
- * @param cpu Online CPUs.
- * @return Void.
- */
-void sampler_timers_stop(int cpu)
-{
- struct timer_list *timer = &per_cpu(swap_timer, cpu);
-
- if (!swap_timer_running)
- return;
- del_timer_sync(timer);
-}
-
-/**
- * @brief Sets timer quantum.
- *
- * @param timer_quantum Timer quantum.
- * @return Void.
- */
-void sampler_timers_set_quantum(unsigned int timer_quantum)
-{
- sampler_timer_quantum = timer_quantum;
-}
+++ /dev/null
-/*
- * SWAP sampler
- * modules/sampler/sampler_timers.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Alexander Aksenov <a.aksenov@samsung.com>: SWAP sampler porting
- *
- */
-
-
-
-#ifndef __SAMPLER_TIMERS_H__
-#define __SAMPLER_TIMERS_H__
-
-
-/* ===================== INCLUDE ==================== */
-
-#if defined(CONFIG_HIGH_RES_TIMERS)
-
-#include <linux/hrtimer.h>
-
-#else /* CONFIG_HIGH_RES_TIMERS */
-
-#include <linux/timer.h>
-
-#endif /* CONFIG_HIGH_RES_TIMERS */
-
-/* ==================== TYPE DEFS =================== */
-
-#if defined(CONFIG_HIGH_RES_TIMERS)
-
-typedef struct hrtimer swap_timer;
-typedef enum hrtimer_restart restart_ret;
-
-#else /* CONFIG_HIGH_RES_TIMERS */
-
-typedef struct timer_list swap_timer;
-typedef int restart_ret;
-
-#endif /* CONFIG_HIGH_RES_TIMERS */
-
-
-/* ====================== FUNCS ===================== */
-
-restart_ret sampler_timers_restart(swap_timer *timer);
-void sampler_timers_stop(int cpu);
-void sampler_timers_start(void *unused);
-void sampler_timers_set_quantum(unsigned int timer_quantum);
-void sampler_timers_set_run(void);
-void sampler_timers_set_stop(void);
-
-#endif /* __SAMPLER_TIMERS_H__ */
+++ /dev/null
-/**
- * @file sampler/swap_sampler_errors.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Sampler error codes.
- */
-
-
-/**
- * @enum _swap_sampler_errors
- * @brief Sampler errors.
- */
-enum _swap_sampler_errors {
- E_SS_SUCCESS = 0, /**< Success. */
- E_SS_WRONG_QUANTUM = 1 /**< Wrong timer quantum set. */
-};
+++ /dev/null
-/**
- * sampler/swap_sampler_module.c
- * @author Andreev S.V.: SWAP Sampler implementation
- * @author Alexander Aksenov <a.aksenov@samsung.com>: SWAP sampler porting
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Timer-based sampling module.
- */
-
-#include <linux/ptrace.h>
-#include <linux/jiffies.h>
-#include <linux/sched.h>
-#include <linux/notifier.h>
-#include <linux/cpu.h>
-#include <linux/module.h>
-
-#include "swap_sampler_module.h"
-#include "swap_sampler_errors.h"
-#include "kernel_operations.h"
-#include "sampler_timers.h"
-
-
-static BLOCKING_NOTIFIER_HEAD(swap_sampler_notifier_list);
-static swap_sample_cb_t sampler_cb;
-
-static restart_ret swap_timer_restart(swap_timer *timer)
-{
- sampler_cb(task_pt_regs(current));
-
- return sampler_timers_restart(timer);
-}
-
-static int swap_timer_start(void)
-{
- get_online_cpus();
- sampler_timers_set_run();
-
- on_each_cpu(sampler_timers_start, swap_timer_restart, 1);
- put_online_cpus();
-
- return E_SS_SUCCESS;
-}
-
-static void swap_timer_stop(void)
-{
- int cpu;
-
- get_online_cpus();
-
- for_each_online_cpu(cpu)
- sampler_timers_stop(cpu);
- sampler_timers_set_stop();
- put_online_cpus();
-}
-
-static int swap_cpu_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
-{
- long cpu = (long) hcpu;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- smp_call_function_single(cpu, sampler_timers_start,
- swap_timer_restart, 1);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- sampler_timers_stop(cpu);
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block __refdata swap_cpu_notifier = {
- .notifier_call = swap_cpu_notify,
-};
-
-static int do_swap_sampler_start(unsigned int timer_quantum)
-{
- if (timer_quantum <= 0)
- return -EINVAL;
-
- sampler_timers_set_quantum(timer_quantum);
- swap_timer_start();
-
- return 0;
-}
-
-static void do_swap_sampler_stop(void)
-{
- swap_timer_stop();
-}
-
-static DEFINE_MUTEX(mutex_run);
-static int sampler_run;
-
-
-/**
- * @brief Starts sampling with specified timer quantum.
- *
- * @param timer_quantum Timer quantum for sampling.
- * @return 0 on success, error code on error.
- */
-int swap_sampler_start(unsigned int timer_quantum, swap_sample_cb_t cb)
-{
- int ret = -EINVAL;
-
- mutex_lock(&mutex_run);
- if (sampler_run) {
- printk(KERN_INFO "sampler profiling is already run!\n");
- goto unlock;
- }
-
- sampler_cb = cb;
-
- ret = do_swap_sampler_start(timer_quantum);
- if (ret == 0)
- sampler_run = 1;
-
-unlock:
- mutex_unlock(&mutex_run);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_sampler_start);
-
-
-/**
- * @brief Stops sampling.
- *
- * @return 0 on success, error code on error.
- */
-int swap_sampler_stop(void)
-{
- int ret = 0;
-
- mutex_lock(&mutex_run);
- if (sampler_run == 0) {
- printk(KERN_INFO "energy profiling is not running!\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- do_swap_sampler_stop();
-
- sampler_run = 0;
-unlock:
- mutex_unlock(&mutex_run);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_sampler_stop);
-
-static int __init sampler_init(void)
-{
- int retval;
-
- retval = register_hotcpu_notifier(&swap_cpu_notifier);
- if (retval) {
- print_err("Error of register_hotcpu_notifier()\n");
- return retval;
- }
-
- print_msg("Sample ininitialization success\n");
-
- return E_SS_SUCCESS;
-}
-
-static void __exit sampler_exit(void)
-{
- if (sampler_run)
- do_swap_sampler_stop();
-
- unregister_hotcpu_notifier(&swap_cpu_notifier);
-
- print_msg("Sampler uninitialized\n");
-}
-
-module_init(sampler_init);
-module_exit(sampler_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP sampling module");
-MODULE_AUTHOR("Andreev S.V., Aksenov A.S.");
+++ /dev/null
-/**
- * @file sampler/swap_sampler_module.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Sampling module interface declaration.
- */
-
-
-/* SWAP Sampler interface */
-
-#ifndef __SWAP_SAMPLER_MODULE_H__
-#define __SWAP_SAMPLER_MODULE_H__
-
-
-typedef void (*swap_sample_cb_t)(struct pt_regs *);
-
-
-/* Starts the SWAP Sampler */
-int swap_sampler_start(unsigned int timer_quantum, swap_sample_cb_t cb);
-
-/* Stops the SWAP Sampler */
-int swap_sampler_stop(void);
-
-#endif /* __SWAP_SAMPLER_MODULE_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
-
-obj-m := swap_taskctx.o
-swap_taskctx-y := task_ctx.o
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/sched.h>
-#include <linux/module.h>
-#include <linux/kconfig.h>
-#include <linux/completion.h>
-#include <ksyms/ksyms.h>
-#include <kprobe/swap_ktd.h>
-#include <master/swap_initializer.h>
-#include "task_ctx.h"
-
-
-enum { WAIT_TIMEOUT = 1500 }; /* max waiting time the signal delivery */
-
-struct call_task {
- taskctx_t func;
- void *data;
-
- bool is_running;
- struct completion comp0;
- struct completion comp1;
-};
-
-
-static void (*swap_signal_wake_up_state)(struct task_struct *t,
- unsigned int state);
-
-static struct sighand_struct *swap_lock_task_sighand(struct task_struct *tsk,
- unsigned long *flags)
-{
- struct sighand_struct *sighand;
-
- for (;;) {
- local_irq_save(*flags);
- rcu_read_lock();
-
- sighand = rcu_dereference(tsk->sighand);
- if (unlikely(sighand == NULL)) {
- rcu_read_unlock();
- local_irq_restore(*flags);
- break;
- }
-
- spin_lock(&sighand->siglock);
- if (likely(sighand == tsk->sighand)) {
- rcu_read_unlock();
- break;
- }
- spin_unlock(&sighand->siglock);
-
- rcu_read_unlock();
- local_irq_restore(*flags);
- }
-
- return sighand;
-}
-
-static inline void swap_unlock_task_sighand(struct task_struct *tsk,
- unsigned long *flags)
-{
- spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
-}
-
-static int fake_signal_wake_up(struct task_struct *p)
-{
- unsigned long flags;
-
- if (swap_lock_task_sighand(p, &flags) == NULL)
- return -ESRCH;
-
- swap_signal_wake_up_state(p, 0);
- swap_unlock_task_sighand(p, &flags);
-
- return 0;
-}
-
-
-static void ktd_init(struct task_struct *task, void *data)
-{
- struct call_task **call = (struct call_task **)data;
-
- *call = NULL;
-}
-
-static void ktd_exit(struct task_struct *task, void *data)
-{
- struct call_task **call = (struct call_task **)data;
-
- WARN(*call, "call is not NULL");
-}
-
-static struct ktask_data ktd = {
- .init = ktd_init,
- .exit = ktd_exit,
- .size = sizeof(struct call_task *),
-};
-
-static void call_set(struct task_struct *task, struct call_task *call)
-{
- *(struct call_task **)swap_ktd(&ktd, task) = call;
-}
-
-static struct call_task *call_get(struct task_struct *task)
-{
- return *(struct call_task **)swap_ktd(&ktd, task);
-}
-
-
-int taskctx_run(struct task_struct *task, taskctx_t func, void *data)
-{
- if (IS_ENABLED(CONFIG_SWAP_KERNEL_IMMUTABLE))
- return -ENOSYS;
-
- if (task == current) {
- func(data);
- } else {
- int ret;
- unsigned long jiff;
- struct call_task call = {
- .func = func,
- .data = data,
- .comp0 = COMPLETION_INITIALIZER(call.comp0),
- .comp1 = COMPLETION_INITIALIZER(call.comp1),
- .is_running = false,
- };
-
- /* check task possibility to receive signals */
- if (task->flags & (PF_KTHREAD | PF_EXITING | PF_SIGNALED))
- return -EINVAL;
-
- /* set call by task */
- call_set(task, &call);
-
- ret = fake_signal_wake_up(task);
- if (ret) {
- /* reset call by task */
- call_set(task, NULL);
- pr_err("cannot send signal to task[%u %u %s] flags=%08x state=%08lx\n",
- task->tgid, task->pid, task->comm,
- task->flags, task->state);
- return ret;
- }
-
- jiff = msecs_to_jiffies(WAIT_TIMEOUT);
- wait_for_completion_timeout(&call.comp0, jiff);
-
- /* reset call by task */
- call_set(task, NULL);
-
- /* wait the return from sig_pre_handler() */
- synchronize_sched();
-
- if (call.is_running)
- wait_for_completion(&call.comp1);
-
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(taskctx_run);
-
-
-static void sig_handler(void)
-{
- struct call_task *call = call_get(current);
-
- if (call) {
- call_set(current, NULL);
- call->is_running = true;
-
- complete(&call->comp0);
- call->func(call->data);
- complete(&call->comp1);
- }
-}
-
-
-#ifdef CONFIG_SWAP_HOOK_SIGNAL
-# include <swap/hook_signal.h>
-void hook_handler(struct ksignal *ksig)
-{
- sig_handler();
-}
-
-struct hook_signal hook_signal = {
- .owner = THIS_MODULE,
- .hook = hook_handler,
-};
-
-static int signal_reg(void)
-{
- int ret = hook_signal_reg(&hook_signal);
- if (ret)
- pr_err("Cannot register hook_signal, ret=%d\n", ret);
-
- return ret;
-}
-
-static void signal_unreg(void)
-{
- hook_signal_unreg(&hook_signal);
-}
-
-static int signal_once(void)
-{
- return 0;
-}
-
-#else /* !CONFIG_SWAP_HOOK_SIGNAL */
-# include <kprobe/swap_kprobes.h>
-
-static int sig_pre_handler(struct kprobe *p, struct pt_regs *regs)
-{
- sig_handler();
-
- return 0;
-}
-
-static struct kprobe sig_kprobe = {
- .pre_handler = sig_pre_handler,
-};
-
-static int signal_reg(void)
-{
- int ret = swap_register_kprobe(&sig_kprobe);
- if (ret)
- pr_err("register sig_kprobe ret=%d\n", ret);
-
- return ret;
-}
-
-static void signal_unreg(void)
-{
- swap_unregister_kprobe(&sig_kprobe);
-}
-
-static int signal_once(void)
-{
- const char *sym;
-
- sym = "signal_wake_up_state";
- swap_signal_wake_up_state = (void *)swap_ksyms(sym);
- if (swap_signal_wake_up_state == NULL)
- goto not_found;
-
-# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
- sym = "get_signal";
-# else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) */
- sym = "get_signal_to_deliver";
-# endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) */
- sig_kprobe.addr = swap_ksyms(sym);
- if (sig_kprobe.addr == 0)
- goto not_found;
-
- return 0;
-
-not_found:
- printk("Cannot find address for '%s'!\n", sym);
- return -ESRCH;
-}
-#endif /* CONFIG_SWAP_HOOK_SIGNAL */
-
-
-static int use_cnt = 0;
-static DEFINE_MUTEX(use_lock);
-
-int taskctx_get(void)
-{
- int ret = 0;
-
- mutex_lock(&use_lock);
- if (use_cnt == 0) {
- ret = signal_reg();
- if (ret)
- goto unlock;
- }
-
- ++use_cnt;
-
-unlock:
- mutex_unlock(&use_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(taskctx_get);
-
-void taskctx_put(void)
-{
- mutex_lock(&use_lock);
- if (use_cnt == 0) {
- WARN_ON("call put_task_context() without get_task_context");
- goto unlock;
- }
-
- --use_cnt;
- if (use_cnt == 0)
- signal_unreg();
-
-unlock:
- mutex_unlock(&use_lock);
-}
-EXPORT_SYMBOL_GPL(taskctx_put);
-
-
-static int taskctx_once(void)
-{
- return signal_once();
-}
-
-static int taskctx_init(void)
-{
- return swap_ktd_reg(&ktd);
-}
-
-static void taskctx_uninit(void)
-{
- WARN(use_cnt, "use_cnt=%d\n", use_cnt);
- swap_ktd_unreg(&ktd);
-}
-
-SWAP_LIGHT_INIT_MODULE(taskctx_once, taskctx_init, taskctx_uninit, NULL, NULL);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-#ifndef _TASK_CTX_H
-#define _TASK_CTX_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-struct task_struct;
-
-typedef void (*taskctx_t)(void *info);
-
-
-int taskctx_run(struct task_struct *task, taskctx_t func, void *data);
-
-int taskctx_get(void);
-void taskctx_put(void);
-
-
-#endif /* _TASK_CTX_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := kprobe_tests/
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_kp_tests.o
-swap_kp_tests-y := \
- kp_module.o \
- kp_tests.o \
- krp_tests.o \
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/uaccess.h>
-#include <linux/kallsyms.h>
-#include "kp_module.h"
-
-
-static asmlinkage long (*olog_sys_write)(unsigned int, const char __user *, size_t);
-
-static long write_to_stdout(const char *buf, size_t len)
-{
- long ret;
-
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- ret = olog_sys_write(1, buf, len);
- set_fs(fs);
-
- return ret;
-}
-
-static int olog_init(void)
-{
- olog_sys_write = (void *)kallsyms_lookup_name("sys_write");
- if (olog_sys_write == NULL) {
- pr_err("ERR: not found 'sys_write' symbol\n");
- return -ESRCH;
- }
-
- return 0;
-}
-
-void olog(const char *fmt, ...)
-{
- char buf[256];
- va_list args;
-
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
-
- printk("%s", buf);
- write_to_stdout(buf, strlen(buf));
-}
-
-
-
-
-static void print_mod_info(void)
-{
- struct module *mod = THIS_MODULE;
-
- printk("### MOD_INFO:\n");
- printk(" core: %p..%p\n", mod->module_init, mod->module_init + mod->init_text_size);
- printk(" init: %p..%p\n", mod->module_core, mod->module_core + mod->core_text_size);
- printk("\n");
-}
-
-
-/* TODO: move declare to header */
-int kp_tests_run(void);
-int krp_tests_run(void);
-
-static int __init tests_init(void)
-{
- int ret;
-
- ret = olog_init();
- if (ret)
- return ret;
-
- print_mod_info();
-
- olog("### Begin tests ###\n");
- kp_tests_run();
- krp_tests_run();
- olog("### End tests ###\n");
-
- return -1;
-}
-
-static void __exit tests_exit(void)
-{
-}
-
-module_init(tests_init);
-module_exit(tests_exit);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _KP_MODULE_H
-#define _KP_MODULE_H
-
-
-void olog(const char *fmt, ...);
-
-
-#endif /* _KP_MODULE_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/kthread.h>
-#include <kprobe/swap_kprobes.h>
-#include "kp_module.h"
-
-
-static struct task_struct *cur_task;
-
-
-static struct kprobe *kp_create(char *name,
- int (*pre_h)(struct kprobe *, struct pt_regs *))
-{
- struct kprobe *p;
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (p) {
- p->symbol_name = name;
- p->pre_handler = pre_h;
- }
-
- return p;
-}
-
-static void kp_free(struct kprobe *p)
-{
- memset(p, 0x10, sizeof(*p));
-}
-
-#define kp_reg(ptr, name, handler) \
- do { \
- ptr = kp_create(name, handler); \
- swap_register_kprobe(ptr); \
- } while (0)
-
-#define kp_unreg(ptr) \
- do { \
- swap_unregister_kprobe(ptr); \
- kp_free(ptr); \
- ptr = NULL; \
- } while (0)
-
-
-noinline char *my_kstrdup(const char *s, gfp_t gfp)
-{
- return kstrdup(s, gfp);
-}
-
-noinline void my_kfree(const void *data)
-{
- kfree(data);
-}
-
-
-
-
-/*
- ******************************************************************************
- * recursion *
- ******************************************************************************
- */
-static int kstrdup_cnt;
-static int kfree_cnt;
-
-static struct kprobe *kp_kstrdup;
-static int kstrdup_h(struct kprobe *kp, struct pt_regs *regs)
-{
- char *str;
-
- str = my_kstrdup("from_kfree_h", GFP_ATOMIC);
- my_kfree(str);
-
- ++kstrdup_cnt;
-
- return 0;
-}
-
-static struct kprobe *kp_kfree;
-static int kfree_h(struct kprobe *kp, struct pt_regs *regs)
-{
- ++kfree_cnt;
-
- return 0;
-}
-
-static void run_test_recursion(void)
-{
- char *str;
-
- str = my_kstrdup("test_string_0", GFP_KERNEL);
- my_kfree(str);
-
- str = my_kstrdup("test_string_1", GFP_KERNEL);
- my_kfree(str);
-}
-
-static void do_test_recursion(void)
-{
- kp_reg(kp_kfree, "my_kfree", kfree_h);
- kp_reg(kp_kstrdup, "my_kstrdup", kstrdup_h);
-
- run_test_recursion();
-
- kp_unreg(kp_kstrdup);
- kp_unreg(kp_kfree);
-}
-
-
-static void test_recursion(void)
-{
- olog("Recursion:\n");
-
- kstrdup_cnt = 0;
- kfree_cnt = 0;
-
- do_test_recursion();
-
- if (kstrdup_cnt == 2 && kfree_cnt == 2) {
- olog(" OK\n");
- } else {
- olog(" ERROR: kstrdup_cnt=%d kfree_cnt=%d\n",
- kstrdup_cnt, kfree_cnt);
- }
-}
-
-
-
-
-/*
- ******************************************************************************
- * recursion and multiple handlers (Aggregate probe) *
- ******************************************************************************
- */
-static int kfree2_cnt;
-
-static struct kprobe *kp_kfree2;
-static int kfree2_h(struct kprobe *kp, struct pt_regs *regs)
-{
- if (current != cur_task || in_interrupt())
- return 0;
-
- ++kfree2_cnt;
- return 0;
-}
-
-static void pre_test_recursion_and_mh(void)
-{
- kstrdup_cnt = 0;
- kfree_cnt = 0;
- kfree2_cnt = 0;
-}
-
-static void post_test_recursion_and_mh(void)
-{
- if (kstrdup_cnt == 2 && kfree_cnt == 2 && kfree2_cnt == 2) {
- olog(" OK\n");
- } else {
- olog(" ERROR: kstrdup_cnt=%d kfree_cnt=%d kfree2_cnt=%d\n",
- kstrdup_cnt, kfree_cnt, kfree2_cnt);
- }
-}
-
-static void test_recursion_and_multiple_handlers(void)
-{
- olog("Recursion and multiple handlers:\n");
-
- pre_test_recursion_and_mh();
-
- kp_reg(kp_kfree2, "my_kfree", kfree2_h);
- do_test_recursion();
- kp_unreg(kp_kfree2);
-
- post_test_recursion_and_mh();
-}
-
-static void test_recursion_and_multiple_handlers2(void)
-{
- olog("Recursion and multiple handlers [II]:\n");
-
- pre_test_recursion_and_mh();
-
- kp_reg(kp_kfree, "my_kfree", kfree_h);
- kp_reg(kp_kstrdup, "my_kstrdup", kstrdup_h);
- kp_reg(kp_kfree2, "my_kfree", kfree2_h);
-
- run_test_recursion();
-
- kp_unreg(kp_kstrdup);
- kp_unreg(kp_kfree);
- kp_unreg(kp_kfree2);
-
- post_test_recursion_and_mh();
-}
-
-
-
-
-/*
- ******************************************************************************
- * swap_unregister_kprobe(), sync *
- ******************************************************************************
- */
-
-static const char task_name[] = "my_task";
-
-static int is_my_task(void)
-{
- return !strcmp(task_name, current->comm);
-}
-
-static int find_module_cnt;
-
-static struct kprobe *kp_find_module;
-static int find_module_h(struct kprobe *kp, struct pt_regs *regs)
-{
- if (is_my_task()) {
- might_sleep();
- ++find_module_cnt;
-
- /* sleep 0.5 sec */
- msleep(500);
- schedule();
-
- ++find_module_cnt;
- }
-
- return 0;
-}
-
-static int kthread_my_fn(void *data)
-{
- find_module("o_lo_lo");
- find_module("o_lo_lo");
-
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
-
- return 0;
-}
-
-
-static void do_test_sync_unreg(unsigned int ms)
-{
- struct task_struct *task;
-
- kp_reg(kp_find_module, "find_module", find_module_h);
-
- task = kthread_run(kthread_my_fn, NULL, task_name);
- if (IS_ERR(task)) {
- olog("ERROR: kthread_run()\n");
- goto unreg;
- }
-
- /* waiting for kthread_my_fn() call */
- msleep(ms);
-unreg:
- kp_unreg(kp_find_module);
- if (!IS_ERR(task))
- kthread_stop(task);
-}
-
-static void test_sync_unreg(void)
-{
- olog("Unreg kp:\n");
-
- find_module_cnt = 0;
-
- do_test_sync_unreg(200);
-
- if (find_module_cnt == 2) {
- olog(" OK\n");
- } else {
- olog(" ERROR: find_module_cnt=%d\n", find_module_cnt);
- }
-}
-
-
-
-/*
- ******************************************************************************
- * swap_unregister_kprobe(), sync and multiple handlers *
- ******************************************************************************
- */
-static int find_module2_cnt;
-
-static struct kprobe *kp_find_module2;
-static int find_module2_h(struct kprobe *kp, struct pt_regs *regs)
-{
- if (is_my_task()) {
- ++find_module2_cnt;
-
- /* sleep 0.5 sec */
- msleep(500);
-
- ++find_module2_cnt;
- }
-
- return 0;
-}
-
-static void pre_test_sync_unreg_and_mh(void)
-{
- find_module_cnt = 0;
- find_module2_cnt = 0;
-}
-
-static void post_test_sync_unreg_and_mh(int cnt, int cnt2)
-{
- if (find_module_cnt == cnt && find_module2_cnt == cnt2) {
- olog(" OK\n");
- } else {
- olog(" ERROR: find_module_cnt=%d find_module2_cnt=%d\n",
- find_module_cnt, find_module2_cnt);
- }
-}
-
-static void do_test_sync_unreg_and_mh(unsigned int ms)
-{
- struct task_struct *task;
-
- kp_reg(kp_find_module, "find_module", find_module_h);
- kp_reg(kp_find_module2, "find_module", find_module2_h);
-
- task = kthread_run(kthread_my_fn, NULL, task_name);
- if (IS_ERR(task)) {
- olog("ERROR: kthread_run()\n");
- goto unreg;
- }
-
- /* waiting for kthread_my_fn() call */
- msleep(ms);
-unreg:
- kp_unreg(kp_find_module2);
- kp_unreg(kp_find_module);
- if (!IS_ERR(task))
- kthread_stop(task);
-}
-
-static void test_sync_unreg_and_multiple_handlers(void)
-{
- olog("Unreg kp and multiple handlers:\n");
-
- pre_test_sync_unreg_and_mh();
-
- do_test_sync_unreg_and_mh(700);
-
- post_test_sync_unreg_and_mh(2, 2);
-}
-
-static void do_test_sync_unreg_and_mh2(unsigned int ms)
-{
- struct task_struct *task;
-
- kp_reg(kp_find_module, "find_module", find_module_h);
- kp_reg(kp_find_module2, "find_module", find_module2_h);
-
- task = kthread_run(kthread_my_fn, NULL, task_name);
- if (IS_ERR(task)) {
- olog("ERROR: kthread_run()\n");
- goto unreg;
- }
-
- /* waiting for kthread_my_fn() call */
- msleep(ms);
-unreg:
- kp_unreg(kp_find_module);
- kp_unreg(kp_find_module2);
- if (!IS_ERR(task))
- kthread_stop(task);
-}
-
-static void test_sync_unreg_and_multiple_handlers2(void)
-{
- olog("Unreg kp and multiple handlers [II]:\n");
-
- pre_test_sync_unreg_and_mh();
-
- do_test_sync_unreg_and_mh2(700);
-
- post_test_sync_unreg_and_mh(2, 2);
-}
-
-int kp_tests_run(void)
-{
- cur_task = current;
-
- test_recursion();
- test_recursion_and_multiple_handlers();
- test_recursion_and_multiple_handlers2();
- // add 3
-
- test_sync_unreg();
- test_sync_unreg_and_multiple_handlers();
- test_sync_unreg_and_multiple_handlers2();
-
- return 0;
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <kprobe/swap_kprobes.h>
-#include "kp_module.h"
-
-
-static struct kretprobe *krp_create(char *name,
- int (*eh)(struct kretprobe_instance *, struct pt_regs *),
- int (*rh)(struct kretprobe_instance *, struct pt_regs *),
- size_t data_size)
-{
- struct kretprobe *rp;
-
- rp = kzalloc(sizeof(*rp), GFP_KERNEL);
- if (rp) {
- rp->kp.symbol_name = name;
- rp->entry_handler = eh;
- rp->handler = rh;
- rp->data_size = data_size;
- }
-
- return rp;
-}
-
-static void krp_free(struct kretprobe *rp)
-{
- memset(rp, 0x10, sizeof(*rp));
-}
-
-#define krp_reg(ptr, name, eh, rh, sz) \
- do { \
- ptr = krp_create(name, eh, rh, sz); \
- swap_register_kretprobe(ptr); \
- } while (0)
-
-#define krp_unreg(ptr) \
- do { \
- swap_unregister_kretprobe(ptr); \
- krp_free(ptr); \
- ptr = NULL; \
- } while (0)
-
-
-
-
-
-struct test_func_data {
- long v0, v1, v2, v3, v4, v5, v6, v7;
-};
-
-static struct test_func_data tf_data_tmp;
-static unsigned long tf_data_tmp_ret;
-
-
-static long do_test_func(long v0, long v1, long v2, long v3,
- long v4, long v5, long v6, long v7)
-{
- return v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7;
-}
-
-static noinline long test_func(long v0, long v1, long v2, long v3,
- long v4, long v5, long v6, long v7)
-{
- return do_test_func(v0, v1, v2, v3, v4, v5, v6, v7);
-}
-
-static int test_func_eh(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct test_func_data *data = (struct test_func_data *)ri->data;
-
- data->v0 = swap_get_karg(regs, 0);
- data->v1 = swap_get_karg(regs, 1);
- data->v2 = swap_get_karg(regs, 2);
- data->v3 = swap_get_karg(regs, 3);
- data->v4 = swap_get_karg(regs, 4);
- data->v5 = swap_get_karg(regs, 5);
- data->v6 = swap_get_karg(regs, 6);
- data->v7 = swap_get_karg(regs, 7);
-
- pr_info("E data=[%ld %ld %ld %ld %ld %ld %ld %ld]\n",
- data->v0, data->v1, data->v2, data->v3,
- data->v4, data->v5, data->v6, data->v7);
-
- return 0;
-}
-
-static int test_func_rh(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct test_func_data *data = (struct test_func_data *)ri->data;
-
- tf_data_tmp_ret = regs_return_value(regs);
- tf_data_tmp = *data;
-
- pr_info("R data=[%ld %ld %ld %ld %ld %ld %ld %ld] ret=%ld\n",
- data->v0, data->v1, data->v2, data->v3,
- data->v4, data->v5, data->v6, data->v7,
- tf_data_tmp_ret);
-
- return 0;
-}
-
-struct kretprobe *test_func_krp;
-
-static struct test_func_data tf_data_gage = {
- .v0 = 0,
- .v1 = 1,
- .v2 = 2,
- .v3 = 3,
- .v4 = 4,
- .v5 = 5,
- .v6 = 6,
- .v7 = 7,
-};
-
-static void pre_test_krp(void)
-{
- memset(&tf_data_tmp, 0, sizeof(tf_data_tmp));
- tf_data_tmp_ret = 0;
-}
-
-static void post_test_krp(void)
-{
- long ret;
-
- ret = do_test_func(tf_data_gage.v0, tf_data_gage.v1, tf_data_gage.v2, tf_data_gage.v3,
- tf_data_gage.v4, tf_data_gage.v5, tf_data_gage.v6, tf_data_gage.v7);
-
- if (tf_data_tmp_ret == ret &&
- memcmp(&tf_data_gage, &tf_data_tmp, sizeof(tf_data_gage)) == 0) {
- olog(" OK\n");
- } else {
- olog(" ERROR:\n"
- " tf_data_gage=[%ld %ld %ld %ld %ld %ld %ld %ld] ret=%ld\n"
- " tf_data_tmp =[%ld %ld %ld %ld %ld %ld %ld %ld] ret=%ld\n",
- tf_data_gage.v0, tf_data_gage.v1,
- tf_data_gage.v2, tf_data_gage.v3,
- tf_data_gage.v4, tf_data_gage.v5,
- tf_data_gage.v6, tf_data_gage.v7, ret,
- tf_data_tmp.v0, tf_data_tmp.v1,
- tf_data_tmp.v2, tf_data_tmp.v3,
- tf_data_tmp.v4, tf_data_tmp.v5,
- tf_data_tmp.v6, tf_data_tmp.v7, tf_data_tmp_ret);
- }
-}
-
-static void do_test_krp(void)
-{
- krp_reg(test_func_krp, "test_func",
- test_func_eh, test_func_rh,
- sizeof(struct test_func_data));
-
- test_func(tf_data_gage.v0, tf_data_gage.v1, tf_data_gage.v2, tf_data_gage.v3,
- tf_data_gage.v4, tf_data_gage.v5, tf_data_gage.v6, tf_data_gage.v7);
-
- krp_unreg(test_func_krp);
-}
-
-static void test_krp(void)
-{
- olog("Kretprobe (get_args{8} and ri->data):\n");
-
- pre_test_krp();
-
- do_test_krp();
-
- post_test_krp();
-}
-
-int krp_tests_run(void)
-{
- test_krp();
-
- return 0;
-}
+++ /dev/null
-
-cd /opt/swap/sdk
-
-insmod swap_master.ko
-echo 1 > /sys/kernel/debug/swap/enable
-insmod swap_kprobe.ko
-
-insmod swap_kp_tests.ko 2>/dev/null
-
-rmmod swap_kprobe
-rmmod swap_master
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_uihv.o
-swap_uihv-y := uihv_module.o \
- uihv_debugfs.o
+++ /dev/null
-#ifndef __UIHV_H__
-#define __UIHV_H__
-
-#define UIHV_PREFIX "SWAP_UIHV: "
-#define UIHV_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
-
-#endif /* __UIHV_H__ */
+++ /dev/null
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <master/swap_debugfs.h>
-#include "uihv.h"
-#include "uihv_module.h"
-
-static const char UIHV_FOLDER[] = "uihv";
-static const char UIHV_PATH[] = "path";
-static const char UIHV_APP_INFO[] = "app_info";
-static const char UIHV_ENABLE[] = "enable";
-
-static struct dentry *uihv_root;
-
-
-
-/* ===========================================================================
- * = UI VIEWER PATH =
- * ===========================================================================
- */
-
-
-static ssize_t uihv_path_write(struct file *file, const char __user *buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *path;
-
- path = kmalloc(len, GFP_KERNEL);
- if (path == NULL) {
- ret = -ENOMEM;
- goto uihv_path_write_out;
- }
-
- if (copy_from_user(path, buf, len)) {
- ret = -EINVAL;
- goto uihv_path_write_out;
- }
-
- path[len - 1] = '\0';
-
- if (uihv_set_handler(path) != 0) {
- printk(UIHV_PREFIX "Cannot set ui viewer path %s\n", path);
- ret = -EINVAL;
- goto uihv_path_write_out;
- }
-
- ret = len;
-
- printk(UIHV_PREFIX "Set ui viewer path %s\n", path);
-
-uihv_path_write_out:
- kfree(path);
-
- return ret;
-}
-
-static const struct file_operations uihv_path_file_ops = {
- .owner = THIS_MODULE,
- .write = uihv_path_write,
-};
-
-
-/*
- * format:
- * main:app_path
- *
- * sample:
- * 0x00000d60:/bin/app_sample
- */
-static int uihv_add_app_info(const char *buf, size_t len)
-{
- int n, ret;
- char *app_path;
- unsigned long main_addr;
- const char fmt[] = "%%lx:/%%%ds";
- char fmt_buf[64];
-
- n = snprintf(fmt_buf, sizeof(fmt_buf), fmt, PATH_MAX - 2);
- if (n <= 0)
- return -EINVAL;
-
- app_path = kmalloc(PATH_MAX, GFP_KERNEL);
- if (app_path == NULL)
- return -ENOMEM;
-
- n = sscanf(buf, fmt_buf, &main_addr, app_path + 1);
- if (n != 2) {
- ret = -EINVAL;
- goto free_app_path;
- }
- app_path[0] = '/';
-
- printk(UIHV_PREFIX "Set ui viewer app path %s, main offset 0x%lx\n", app_path, main_addr);
-
- ret = uihv_data_set(app_path, main_addr);
-
-free_app_path:
- kfree(app_path);
- return ret;
-}
-
-static ssize_t write_uihv_app_info(struct file *file,
- const char __user *user_buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret = len;
- char *buf;
-
- buf = kmalloc(len, GFP_KERNEL);
- if (buf == NULL) {
- ret = -ENOMEM;
- goto free_buf;
- }
-
- if (copy_from_user(buf, user_buf, len)) {
- ret = -EINVAL;
- goto free_buf;
- }
-
- buf[len - 1] = '\0';
-
- if (uihv_add_app_info(buf, len))
- ret = -EINVAL;
-
-free_buf:
- kfree(buf);
-
- return ret;
-}
-
-static const struct file_operations uihv_app_info_file_ops = {
- .owner = THIS_MODULE,
- .write = write_uihv_app_info,
-};
-
-static ssize_t write_uihv_enable(struct file *file,
- const char __user *user_buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret = len;
- char *buf;
-
- buf = kmalloc(len, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (copy_from_user(buf, user_buf, len)) {
- ret = -EINVAL;
- goto free_buf;
- }
-
- buf[len - 1] = '\0';
-
- if (buf[0] == '0')
- ret = uihv_disable();
- else
- ret = uihv_enable();
-
-free_buf:
- kfree(buf);
-
-out:
- return ret;
-}
-
-static const struct file_operations uihv_enable_file_ops = {
- .owner = THIS_MODULE,
- .write = write_uihv_enable,
-};
-
-int uihv_dfs_init(void)
-{
- struct dentry *swap_dentry, *root, *path, *app_info, *uihv_enable;
- int ret;
-
- ret = -ENODEV;
- if (!debugfs_initialized())
- goto fail;
-
- ret = -ENOENT;
- swap_dentry = swap_debugfs_getdir();
- if (!swap_dentry)
- goto fail;
-
- ret = -ENOMEM;
- root = swap_debugfs_create_dir(UIHV_FOLDER, swap_dentry);
- if (IS_ERR_OR_NULL(root))
- goto fail;
-
- uihv_root = root;
-
- path = swap_debugfs_create_file(UIHV_PATH, UIHV_DEFAULT_PERMS, root,
- NULL, &uihv_path_file_ops);
- if (IS_ERR_OR_NULL(path)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- app_info = swap_debugfs_create_file(UIHV_APP_INFO,
- UIHV_DEFAULT_PERMS, root, NULL,
- &uihv_app_info_file_ops);
- if (IS_ERR_OR_NULL(app_info)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- uihv_enable = swap_debugfs_create_file(UIHV_ENABLE,
- UIHV_DEFAULT_PERMS, root, NULL,
- &uihv_enable_file_ops);
- if (IS_ERR_OR_NULL(uihv_enable)) {
- ret = -ENOMEM;
- goto remove;
- }
-
- return 0;
-
-remove:
- debugfs_remove_recursive(root);
-
-fail:
- printk(UIHV_PREFIX "Debugfs initialization failure: %d\n", ret);
-
- return ret;
-}
-
-void uihv_dfs_exit(void)
-{
- if (uihv_root)
- debugfs_remove_recursive(uihv_root);
-
- uihv_root = NULL;
-}
+++ /dev/null
-#ifndef __UIHV_DEBUGFS_H__
-#define __UIHV_DEBUGFS_H__
-
-int uihv_dfs_init(void);
-void uihv_dfs_exit(void);
-
-#endif /* __UIHV_DEBUGFS_H__ */
+++ /dev/null
-#include <linux/namei.h>
-#include <us_manager/us_manager_common.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/sspt/sspt_page.h>
-#include <us_manager/sspt/sspt_file.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/callbacks.h>
-#include <us_manager/probes/probe_info_new.h>
-#include <us_manager/us_common_file.h>
-#include <writer/kernel_operations.h>
-#include <master/swap_initializer.h>
-#include <writer/swap_msg.h>
-#include <loader/loader.h>
-#include "uihv.h"
-#include "uihv_module.h"
-#include "uihv_debugfs.h"
-
-#define page_to_proc(page) ((page)->file->proc)
-#define ip_to_proc(ip) page_to_proc((ip)->page)
-#define urp_to_ip(rp) container_of(rp, struct sspt_ip, retprobe)
-
-static DEFINE_MUTEX(mutex_enable);
-
-static struct dentry *uihv_dentry = NULL;
-
-
-/* ============================================================================
- * = ui_viewer =
- * ============================================================================
- */
-
-/* main handler for ui viewer */
-static int uihv_main_eh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static int uihv_main_rh(struct uretprobe_instance *ri, struct pt_regs *regs);
-static struct probe_desc pin_main = MAKE_URPROBE(uihv_main_eh,
- uihv_main_rh, 0);
-
-struct ui_viewer_data {
- struct dentry *app_dentry;
- struct probe_new p_main;
- struct pf_group *pfg;
- bool enable;
-};
-
-static struct ui_viewer_data __ui_data;
-
-static int uihv_data_inst(void)
-{
- struct pf_group *pfg;
-
- pfg = get_pf_group_by_dentry(__ui_data.app_dentry,
- (void *)__ui_data.app_dentry);
- if (!pfg)
- return -ENOMEM;
-
- __ui_data.pfg = pfg;
-
- return 0;
-}
-
-int uihv_data_set(const char *app_path, unsigned long main_addr)
-{
- struct dentry *dentry;
-
- if (__ui_data.enable) {
- pr_err("UIHV already enabled, can't set data\n");
- return -EBUSY;
- }
-
- dentry = dentry_by_path(app_path);
- if (dentry == NULL)
- return -ENOENT;
-
- __ui_data.app_dentry = dentry;
- __ui_data.p_main.desc = &pin_main;
- __ui_data.p_main.offset = main_addr;
-
- return uihv_data_inst();
-}
-
-int uihv_set_handler(char *path)
-{
- struct dentry *dentry;
- int ret;
-
- if (uihv_dentry != NULL) {
- swap_put_dentry(uihv_dentry);
- uihv_dentry = NULL;
- }
-
- dentry = swap_get_dentry(path);
- if (dentry == NULL) {
- printk(KERN_WARNING UIHV_PREFIX "Error! Cannot get handler %s\n",
- path);
- return -EINVAL;
- }
-
- ret = loader_add_handler(path);
- if (ret != 0)
- return ret;
-
- uihv_dentry = dentry;
-
- return 0;
-}
-
-
-
-/* ============================================================================
- * = ui viewer handlers =
- * ============================================================================
- */
-static int uihv_main_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
- struct hd_t *hd;
- unsigned long old_pc = swap_get_upc(regs);
- unsigned long vaddr = 0;
-
- if (uihv_dentry == NULL)
- return 0;
-
- hd = lpd_get_hd(pd, uihv_dentry);
- if (hd == NULL)
- return 0;
-
- if (lpd_get_state(hd) == NOT_LOADED)
- vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
-
- loader_set_priv_origin(ri, vaddr);
-
- /* PC change check */
- return old_pc != swap_get_upc(regs);
-}
-
-static int uihv_main_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pd_t *pd = lpd_get_by_task(current);
- struct hd_t *hd;
-
- if (uihv_dentry == NULL)
- return 0;
-
- hd = lpd_get_hd(pd, uihv_dentry);
- if (hd == NULL)
- return 0;
-
- if (lpd_get_state(hd) == LOADING)
- loader_loading_ret(ri, regs, pd, hd);
-
- return 0;
-}
-
-int uihv_enable(void)
-{
- int ret = 0;
-
- mutex_lock(&mutex_enable);
- if (__ui_data.enable) {
- pr_err("UIHV already enabled\n");
- ret = -EBUSY;
- goto out;
- }
-
- ret = pin_register(&__ui_data.p_main, __ui_data.pfg,
- __ui_data.app_dentry);
- if (ret)
- goto out;
-
- __ui_data.enable = true;
-
-out:
- mutex_unlock(&mutex_enable);
- return ret;
-}
-
-int uihv_disable(void)
-{
- int ret = 0;
-
- mutex_lock(&mutex_enable);
- if (!__ui_data.enable) {
- pr_err("UIHV already disabled\n");
- ret = -EBUSY;
- goto out;
- }
-
- pin_unregister(&__ui_data.p_main, __ui_data.pfg);
- put_pf_group(__ui_data.pfg);
- __ui_data.pfg = NULL;
- __ui_data.enable = false;
-
-out:
- mutex_unlock(&mutex_enable);
- return ret;
-}
-
-static int uihv_init(void)
-{
- int ret;
-
- ret = uihv_dfs_init();
-
- return ret;
-}
-
-static void uihv_exit(void)
-{
- if (uihv_dentry != NULL) {
- swap_put_dentry(uihv_dentry);
- uihv_dentry = NULL;
- }
-
- uihv_dfs_exit();
-}
-
-SWAP_LIGHT_INIT_MODULE(NULL, uihv_init, uihv_exit, NULL, NULL);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP UI Hierarchy Viewer");
-MODULE_AUTHOR("Alexander Aksenov <a.aksenov@samsung.com>, Anastasia Lypa");
+++ /dev/null
-#ifndef __UIHV_MODULE_H__
-#define __UIHV_MODULE_H__
-
-int uihv_data_set(const char *app_path, unsigned long main_addr);
-int uihv_set_handler(char *path);
-int uihv_enable(void);
-int uihv_disable(void);
-
-#endif /* __UIHV_MODULE_H__ */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
-
-obj-m := swap_uprobe.o
-swap_uprobe-y := swap_uprobes.o
-
-### ARM
-swap_uprobe-$(CONFIG_ARM) += \
- arch/arm/swap-asm/swap_uprobes.o \
- ../arch/arm/probes/probes_thumb.o \
- ../arch/arm/probes/decode_thumb.o \
- ../arch/arm/probes/probes.o \
- ../arch/arm/uprobe/swap_uprobe.o
-
-
-### ARM64
-swap_uprobe-$(CONFIG_ARM64) += \
- arch/arm64/swap-asm/swap_uprobes.o \
- arch/arm64/swap-asm/uprobes-arm64.o \
- ../arch/arm/probes/probes_arm.o \
- ../arch/arm/probes/decode_thumb.o \
- ../arch/arm/probes/probes_thumb.o \
- ../arch/arm/probes/probes.o \
- ../arch/arm/uprobe/swap_uprobe.o
-
-
-### X86
-swap_uprobe-$(CONFIG_X86) += arch/x86/swap-asm/swap_uprobes.o \
- arch/x86/swap-asm/swap_sc_patch.o
+++ /dev/null
-/**
- * uprobe/arch/asm-arm/swap_uprobes.c
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Arch-dependent uprobe interface implementation for ARM.
- */
-
-
-#include <linux/init.h> /* need for asm/traps.h */
-#include <linux/sched.h> /* need for asm/traps.h */
-
-#include <linux/ptrace.h> /* need for asm/traps.h */
-#include <asm/traps.h>
-
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <uprobe/swap_uprobes.h>
-#include <arch/arm/probes/probes_arm.h>
-#include <arch/arm/probes/probes_thumb.h>
-#include <swap-asm/swap_kprobes.h>
-#include "swap_uprobes.h"
-
-
-/**
- * @brief Prepares uprobe for ARM.
- *
- * @param up Pointer to the uprobe.
- * @return 0 on success,\n
- * negative error code on error.
- */
-int arch_prepare_uprobe(struct uprobe *p)
-{
- int ret;
-
- ret = arch_prepare_uprobe_arm(p);
- if (!ret) {
- /* for uretprobe */
- add_uprobe_table(p);
- }
-
- return ret;
-}
-
-/**
- * @brief Analysis opcodes.
- *
- * @param rp Pointer to the uretprobe.
- * @return Void.
- */
-void arch_opcode_analysis_uretprobe(struct uretprobe *rp)
-{
- /* Remove retprobe if first insn overwrites lr */
- rp->thumb_noret = noret_thumb(rp->up.opcode);
- rp->arm_noret = noret_arm(rp->up.opcode);
-}
-
-/**
- * @brief Prepates uretprobe for ARM.
- *
- * @param ri Pointer to the uretprobe instance.
- * @param regs Pointer to CPU register data.
- * @return Error code.
- */
-int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- unsigned long thumb, bp_offset;
-
- thumb = ri->preload.use ? ri->preload.thumb : thumb_mode(regs);
- bp_offset = thumb ? 0x1b : sizeof(long) * PROBES_TRAMP_RET_BREAK_IDX;
-
- /* save original return address */
- ri->ret_addr = (uprobe_opcode_t *)regs->ARM_lr;
-
- /* replace return address with break point adddress */
- regs->ARM_lr = (unsigned long)(ri->rp->up.insn) + bp_offset;
-
- /* save stack pointer address */
- ri->sp = (uprobe_opcode_t *)regs->ARM_sp;
-
- /* Set flag of current mode */
- ri->sp = (uprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
-
- return 0;
-}
-
-unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
-{
- /* Understand function mode */
- return ((unsigned long)ri->sp & 1) ?
- ((unsigned long)ri->rp->up.insn + 0x1b) :
- (unsigned long)(ri->rp->up.insn +
- PROBES_TRAMP_RET_BREAK_IDX);
-}
-
-/**
- * @brief Disarms uretprobe instance.
- *
- * @param ri Pointer to the uretprobe instance
- * @param task Pointer to the task for which the uretprobe instance
- * @return 0 on success,\n
- * negative error code on error.
- */
-int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task)
-{
- struct pt_regs *uregs = task_pt_regs(ri->task);
- unsigned long ra = uregs->ARM_lr;
- unsigned long *tramp = (unsigned long *)arch_tramp_by_ri(ri);
- unsigned long *sp = (unsigned long *)((long)ri->sp & ~1);
- unsigned long *stack = sp - URETPROBE_STACK_DEPTH + 1;
- unsigned long *found = NULL;
- unsigned long *buf[URETPROBE_STACK_DEPTH];
- unsigned long vaddr = (unsigned long)ri->rp->up.addr;
- int i, retval;
-
- /* check stack */
- retval = read_proc_vm_atomic(task, (unsigned long)stack,
- buf, sizeof(buf));
- if (retval != sizeof(buf)) {
- printk(KERN_INFO "---> %s (%d/%d): failed to read "
- "stack from %08lx\n", task->comm, task->tgid, task->pid,
- (unsigned long)stack);
- retval = -EFAULT;
- goto check_lr;
- }
-
- /* search the stack from the bottom */
- for (i = URETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
- if (buf[i] == tramp) {
- found = stack + i;
- break;
- }
- }
-
- if (!found) {
- retval = -ESRCH;
- goto check_lr;
- }
-
- printk(KERN_INFO "---> %s (%d/%d): trampoline found at "
- "%08lx (%08lx /%+d) - %lx, set ret_addr=%p\n",
- task->comm, task->tgid, task->pid,
- (unsigned long)found, (unsigned long)sp,
- found - sp, vaddr, ri->ret_addr);
- retval = write_proc_vm_atomic(task, (unsigned long)found,
- &ri->ret_addr,
- sizeof(ri->ret_addr));
- if (retval != sizeof(ri->ret_addr)) {
- printk(KERN_INFO "---> %s (%d/%d): "
- "failed to write value to %08lx",
- task->comm, task->tgid, task->pid, (unsigned long)found);
- retval = -EFAULT;
- } else {
- retval = 0;
- }
-
-check_lr: /* check lr anyway */
- if (ra == (unsigned long)tramp) {
- printk(KERN_INFO "---> %s (%d/%d): trampoline found at "
- "lr = %08lx - %lx, set ret_addr=%p\n",
- task->comm, task->tgid, task->pid, ra, vaddr, ri->ret_addr);
-
- swap_set_uret_addr(uregs, (unsigned long)ri->ret_addr);
- retval = 0;
- } else if (retval) {
- printk(KERN_INFO "---> %s (%d/%d): trampoline NOT found at "
- "sp = %08lx, lr = %08lx - %lx, ret_addr=%p\n",
- task->comm, task->tgid, task->pid,
- (unsigned long)sp, ra, vaddr, ri->ret_addr);
- }
-
- return retval;
-}
-
-/**
- * @brief Jump pre-handler.
- *
- * @param p Pointer to the uprobe.
- * @param regs Pointer to CPU register data.
- * @return 0.
- */
-int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs)
-{
- struct ujprobe *jp = container_of(p, struct ujprobe, up);
- entry_point_t entry = (entry_point_t)jp->entry;
-
- if (entry) {
- entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
- regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
- } else {
- arch_ujprobe_return();
- }
-
- return 0;
-}
-
-/**
- * @brief Gets trampoline address.
- *
- * @param p Pointer to the uprobe.
- * @param regs Pointer to CPU register data.
- * @return Trampoline address.
- */
-unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
-{
- return thumb_mode(regs) ?
- (unsigned long)(p->insn) + 0x1b :
- (unsigned long)(p->insn +
- PROBES_TRAMP_RET_BREAK_IDX);
-}
-
-/**
- * @brief Restores return address.
- *
- * @param orig_ret_addr Original return address.
- * @param regs Pointer to CPU register data.
- * @return Void.
- */
-void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
-{
- regs->ARM_lr = orig_ret_addr;
- regs->ARM_pc = orig_ret_addr & ~0x1;
-
- if (regs->ARM_lr & 0x1)
- regs->ARM_cpsr |= PSR_T_BIT;
- else
- regs->ARM_cpsr &= ~PSR_T_BIT;
-}
-
-/**
- * @brief Removes uprobe.
- *
- * @param up Pointer to the uprobe.
- * @return Void.
- */
-void arch_remove_uprobe(struct uprobe *up)
-{
- swap_slot_free(up->sm, up->insn);
-}
-
-static int urp_handler(struct pt_regs *regs, pid_t tgid)
-{
- struct uprobe *p;
- unsigned long flags;
- unsigned long vaddr = regs->ARM_pc;
- unsigned long offset_bp = thumb_mode(regs) ?
- 0x1a :
- 4 * PROBES_TRAMP_RET_BREAK_IDX;
- unsigned long tramp_addr = vaddr - offset_bp;
-
- local_irq_save(flags);
- p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
- if (unlikely(p == NULL)) {
- local_irq_restore(flags);
-
- pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n",
- vaddr);
- return 1;
- }
-
- get_up(p);
- local_irq_restore(flags);
- trampoline_uprobe_handler(p, regs);
- put_up(p);
-
- return 0;
-}
-/**
- * @brief Prepares singlestep for current CPU.
- *
- * @param p Pointer to kprobe.
- * @param regs Pointer to CPU registers data.
- * @return Void.
- */
-static void arch_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
-{
- if (p->ainsn.insn.handler) {
- regs->ARM_pc += 4;
- p->ainsn.insn.handler(p->opcode, &p->ainsn.insn, regs);
- } else {
- regs->ARM_pc = (unsigned long)p->insn;
- }
-}
-
-/**
- * @brief Breakpoint instruction handler.
- *
- * @param regs Pointer to CPU register data.
- * @param instr Instruction.
- * @return uprobe_handler results.
- */
-static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
-{
- int ret = 0;
- struct uprobe *p;
- unsigned long flags;
- unsigned long vaddr = regs->ARM_pc | !!thumb_mode(regs);
- pid_t tgid = current->tgid;
-
- local_irq_save(flags);
- p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
- if (p) {
- get_up(p);
- local_irq_restore(flags);
- if (!p->pre_handler || !p->pre_handler(p, regs))
- arch_prepare_singlestep(p, regs);
- put_up(p);
- } else {
- local_irq_restore(flags);
- ret = urp_handler(regs, tgid);
-
- /* check ARM/THUMB CPU mode matches installed probe mode */
- if (ret) {
- vaddr ^= 1;
-
- local_irq_save(flags);
- p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
- if (p) {
- get_up(p);
- local_irq_restore(flags);
- pr_err("invalid mode: thumb=%d addr=%p insn=%08x\n",
- !!thumb_mode(regs), p->addr, p->opcode);
- ret = 0;
-
- disarm_uprobe(p, current);
- put_up(p);
- } else {
- local_irq_restore(flags);
- }
- }
- }
-
- return ret;
-}
-
-/* userspace probes hook (arm) */
-static struct undef_hook undef_hook_for_us_arm = {
- .instr_mask = 0xffffffff,
- .instr_val = BREAK_ARM,
- .cpsr_mask = MODE_MASK,
- .cpsr_val = USR_MODE,
- .fn = uprobe_trap_handler
-};
-
-/* userspace probes hook (thumb) */
-static struct undef_hook undef_hook_for_us_thumb = {
- .instr_mask = 0xffffffff,
- .instr_val = BREAK_THUMB,
- .cpsr_mask = MODE_MASK,
- .cpsr_val = USR_MODE,
- .fn = uprobe_trap_handler
-};
-
-/**
- * @brief Installs breakpoint hooks.
- *
- * @return 0.
- */
-int swap_arch_init_uprobes(void)
-{
- swap_register_undef_hook(&undef_hook_for_us_arm);
- swap_register_undef_hook(&undef_hook_for_us_thumb);
-
- return 0;
-}
-
-/**
- * @brief Uninstalls breakpoint hooks.
- *
- * @return Void.
- */
-void swap_arch_exit_uprobes(void)
-{
- swap_unregister_undef_hook(&undef_hook_for_us_thumb);
- swap_unregister_undef_hook(&undef_hook_for_us_arm);
-}
+++ /dev/null
-/**
- * @file uprobe/arch/asm-arm/swap_uprobes.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Arch-dependent uprobe interface declaration.
- */
-
-
-#ifndef _ARM_SWAP_UPROBES_H
-#define _ARM_SWAP_UPROBES_H
-
-
-#include <linux/uaccess.h>
-#include <arch/arm/probes/probes.h>
-#include <arch/arm/uprobe/swap_uprobe.h>
-
-
-/** Uprobes trampoline length */
-#define UPROBES_TRAMP_LEN PROBES_TRAMP_LEN
-
-
-struct task_struct;
-struct uprobe;
-struct arch_insn;
-struct uretprobe;
-struct uretprobe_instance;
-
-typedef u32 uprobe_opcode_t;
-
-
-/**
- * @struct arch_insn
- * @brief Architecture depend copy of original instruction.
- * @var arch_insn::insn
- * Copy of the original instruction.
- */
-struct arch_insn {
- struct arch_insn_arm insn;
-};
-
-
-static inline u32 swap_get_urp_float(struct pt_regs *regs)
-{
- return regs->ARM_r0;
-}
-
-static inline u64 swap_get_urp_double(struct pt_regs *regs)
-{
-
- return regs->ARM_r0 | (u64)regs->ARM_r1 << 32;
-}
-
-static inline void arch_ujprobe_return(void)
-{
-}
-
-int arch_prepare_uprobe(struct uprobe *up);
-int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs);
-static inline int longjmp_break_uhandler(struct uprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-
-#define arch_urp_check_opcode NULL /* Arch doesn't requires special callback */
-void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
-int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
-int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task);
-
-unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
-void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
-void arch_remove_uprobe(struct uprobe *up);
-
-static inline int arch_arm_uprobe(struct uprobe *p)
-{
- return arch_arm_uprobe_arm(p);
-}
-
-static inline void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
-{
- arch_disarm_uprobe_arm(p, task);
-}
-
-static inline unsigned long swap_get_upc(struct pt_regs *regs)
-{
- return swap_get_upc_arm(regs);
-}
-
-static inline void swap_set_upc(struct pt_regs *regs, unsigned long val)
-{
- swap_set_upc_arm(regs, val);
-}
-
-static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
-{
- return swap_get_uarg_arm(regs, n);
-}
-
-static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
- unsigned long val)
-{
- swap_put_uarg_arm(regs, n, val);
-}
-
-static inline unsigned long swap_get_uret_addr(struct pt_regs *regs)
-{
- return swap_get_uret_addr_arm(regs);
-}
-
-static inline void swap_set_uret_addr(struct pt_regs *regs, unsigned long val)
-{
- swap_set_uret_addr_arm(regs, val);
-}
-
-int swap_arch_init_uprobes(void);
-void swap_arch_exit_uprobes(void);
-
-#endif /* _ARM_SWAP_UPROBES_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/kconfig.h>
-#include <linux/types.h>
-#include <asm/traps.h>
-#include <arch/arm/uprobe/swap_uprobe.h>
-#include <uprobe/swap_uprobes.h>
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_td_raw.h>
-#include <kprobe/swap_kprobes_deps.h> /* FIXME: remove it */
-#include <swap-asm/swap_probes.h>
-#include <arch/arm/uprobe/swap_uprobe.h>
-#include <swap-asm/dbg_interface.h>
-#include "uprobes-arm64.h"
-
-
-#define BRK_BP 0x45
-#define BRK_PSEUDO_SS 0x54
-#define BRK_URP 0x67
-#define BRK64_OPCODE_BP MAKE_BRK(BRK_BP)
-#define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS)
-#define BRK64_OPCODE_URP MAKE_BRK(BRK_URP)
-
-
-enum arch_mode {
- AM_UNKNOWN,
- AM_THUMB,
- AM_ARM,
- AM_ARM64
-};
-
-#define ARM64_MODE_VADDR_MASK ((unsigned long)1 << 63)
-
-static enum arch_mode get_arch_mode(unsigned long vaddr)
-{
- if (vaddr & 1)
- return AM_THUMB;
-
- if (vaddr & ARM64_MODE_VADDR_MASK)
- return AM_ARM64;
-
- return AM_ARM;
-}
-
-static unsigned long get_real_addr(unsigned long vaddr)
-{
- return vaddr & ~(ARM64_MODE_VADDR_MASK | 1);
-}
-
-static unsigned long get_64bit_addr(unsigned long raddr)
-{
- return raddr | ARM64_MODE_VADDR_MASK;
-}
-
-struct uprobe_ctlblk {
- struct uprobe *p;
-};
-
-static struct td_raw td_raw;
-
-static struct uprobe_ctlblk *current_ctlblk(void)
-{
- return (struct uprobe_ctlblk *)swap_td_raw(&td_raw, current);
-}
-
-static struct uprobe *get_current_uprobe(void)
-{
- return current_ctlblk()->p;
-}
-
-static void set_current_uprobe(struct uprobe *p)
-{
- current_ctlblk()->p = p;
-}
-
-static void reset_current_uprobe(void)
-{
- set_current_uprobe(NULL);
-}
-
-
-static unsigned long trampoline_addr_arm64(struct uprobe *p)
-{
- return (unsigned long)(p->insn + URP_RET_BREAK_IDX);
-}
-
-static int prepare_uretprobe_arm64(struct uretprobe_instance *ri,
- struct pt_regs *regs)
-{
- unsigned long bp_addr, ret_addr;
-
- ret_addr = regs->regs[30] | ARM64_MODE_VADDR_MASK;
- bp_addr = trampoline_addr_arm64(&ri->rp->up);
-
- ri->sp = (uprobe_opcode_t *)regs->sp;
- ri->ret_addr = (uprobe_opcode_t *)ret_addr;
-
- /* replace the return address (regs[30] - lr) */
- regs->regs[30] = bp_addr;
-
- return 0;
-}
-
-int arch_prepare_uretprobe(struct uretprobe_instance *ri,
- struct pt_regs *regs)
-{
- if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
- return prepare_uretprobe_arm64(ri, regs);
- else
- return prepare_uretprobe_arm(ri, regs);
-}
-
-static void arch_opcode_analysis_uretprobe_arm64(struct uretprobe *rp)
-{
- /* FIXME: to implement */
-}
-
-void arch_opcode_analysis_uretprobe(struct uretprobe *rp)
-{
- if (get_arch_mode((unsigned long)rp->up.addr) == AM_ARM64)
- arch_opcode_analysis_uretprobe_arm64(rp);
- else
- arch_opcode_analysis_uretprobe_arm(rp);
-}
-
-/**
- * @brief Gets trampoline address.
- *
- * @param p Pointer to the kprobe.
- * @param regs Pointer to CPU register data.
- * @return Trampoline address.
- */
-unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
-{
- if (get_arch_mode((unsigned long)p->addr) == AM_ARM64)
- return trampoline_addr_arm64(p);
- else
- return arch_get_trampoline_addr_arm(p, regs);
-}
-
-static unsigned long arch_tramp_by_ri_arm64(struct uretprobe_instance *ri)
-{
- return trampoline_addr_arm64(&ri->rp->up);
-}
-
-unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
-{
- if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
- return arch_tramp_by_ri_arm64(ri);
- else
- return arch_tramp_by_ri_arm(ri);
-}
-
-static int arch_disarm_urp_inst_arm64(struct uretprobe_instance *ri,
- struct task_struct *task)
-{
- struct pt_regs *uregs = task_pt_regs(ri->task);
- u64 ra = uregs->regs[30];
- u64 raddr, tramp, found = 0;
- u64 sp = (u64)ri->sp;
- u64 ret_addr = (u64)ri->ret_addr;
- u64 stack = sp - 8 * (URETPROBE_STACK_DEPTH + 1);
- u64 buf[URETPROBE_STACK_DEPTH];
- int i, ret;
-
- raddr = get_real_addr((unsigned long)ri->rp->up.addr);
- tramp = arch_tramp_by_ri_arm64(ri);
-
- /* check stack */
- ret = read_proc_vm_atomic(task, stack, buf, sizeof(buf));
- if (ret != sizeof(buf)) {
- pr_info("---> %s (%d/%d): failed to read stack from %016llx\n",
- task->comm, task->tgid, task->pid, stack);
- ret = -EFAULT;
- goto check_lr;
- }
-
- /* search the stack from the bottom */
- for (i = URETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
- if (buf[i] == tramp) {
- found = stack + 8 * i;
- break;
- }
- }
-
- if (!found) {
- ret = -ESRCH;
- goto check_lr;
- }
-
- pr_info("---> %s (%d/%d): trampoline found at "
- "%016llx (%016llx /%+lld) - %llx, set ret_addr=%016llx\n",
- task->comm, task->tgid, task->pid,
- found, sp,
- found - sp, raddr, ret_addr);
- ret = write_proc_vm_atomic(task, found, &ret_addr, 8);
- if (ret != 8) {
- pr_info("---> %s (%d/%d): failed to write value to %016llx",
- task->comm, task->tgid, task->pid, found);
- ret = -EFAULT;
- } else {
- ret = 0;
- }
-
-check_lr: /* check lr anyway */
- if (ra == tramp) {
- pr_info("---> %s (%d/%d): trampoline found at "
- "lr = %016llx - %llx, set ret_addr=%016llx\n",
- task->comm, task->tgid, task->pid, ra, raddr,
- ret_addr);
-
- /* set ret_addr */
- uregs->regs[30] = ret_addr;
- ret = 0;
- } else if (ret) {
- pr_info("---> %s (%d/%d): trampoline NOT found at "
- "sp=%016llx, lr=%016llx - %llx, ret_addr=%016llx\n",
- task->comm, task->tgid, task->pid,
- sp, ra, raddr, ret_addr);
- }
-
- return ret;
-}
-
-/**
- * @brief Disarms uretprobe instance.
- *
- * @param ri Pointer to the uretprobe instance
- * @param task Pointer to the task for which the uretprobe instance
- * @return 0 on success,\n
- * negative error code on error.
- */
-int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task)
-{
- if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
- return arch_disarm_urp_inst_arm64(ri, task);
- else
- return arch_disarm_urp_inst_arm(ri, task);
-}
-
-void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
-{
- if (get_arch_mode(orig_ret_addr) == AM_ARM64) {
- regs->pc = get_real_addr(orig_ret_addr);
- } else {
- set_orig_ret_addr_arm(orig_ret_addr, regs);
- }
-}
-
-static enum dbg_code urp_handler(struct pt_regs *regs, unsigned int esr)
-{
- struct uprobe *p;
- unsigned long pc = regs->pc;
- unsigned long insn_addr = pc - sizeof(u32) * URP_RET_BREAK_IDX;
-
- p = get_uprobe_by_insn_slot((void *)insn_addr, current->tgid, regs);
- if (p == NULL) {
- pr_err("no_uretprobe: Not one of ours: let "
- "kernel handle it %lx\n", pc);
- return DBG_ERROR;
- }
-
- local_irq_enable();
- trampoline_uprobe_handler(p, regs);
-
- return DBG_HANDLED;
-}
-
-static int arch_arm_uprobe_arm64(struct uprobe *p)
-{
- int ret;
- unsigned long vaddr = (unsigned long)p->addr;
- unsigned long raddr = get_real_addr(vaddr);
- u32 insn = BRK64_OPCODE_BP;
-
- ret = write_proc_vm_atomic(p->task, raddr, &insn, sizeof(insn));
- if (!ret) {
- pr_err("failed to write memory addr=%lx\n", vaddr);
- return -EACCES;
- }
-
- return 0;
-}
-
-static void arch_disarm_uprobe_arm64(struct uprobe *p,
- struct task_struct *task)
-{
- unsigned long vaddr = (unsigned long)p->addr;
- unsigned long raddr = get_real_addr(vaddr);
- int ret;
-
- ret = write_proc_vm_atomic(task, raddr, &p->opcode, sizeof(p->opcode));
- if (!ret)
- pr_err("failed to write memory vaddr=%lx\n", vaddr);
-}
-
-int arch_arm_uprobe(struct uprobe *p)
-{
- int ret;
- unsigned long vaddr = (unsigned long)p->addr;
-
- switch (get_arch_mode(vaddr)) {
- case AM_THUMB:
- case AM_ARM:
- ret = arch_arm_uprobe_arm(p);
- break;
- case AM_ARM64:
- ret = arch_arm_uprobe_arm64(p);
- break;
- default:
- pr_err("Error: unknown mode vaddr=%lx\n", vaddr);
- return -EINVAL;
- }
-
- return ret;
-}
-
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
-{
- unsigned long vaddr = (unsigned long)p->addr;
-
- switch (get_arch_mode(vaddr)) {
- case AM_THUMB:
- case AM_ARM:
- arch_disarm_uprobe_arm(p, task);
- break;
- case AM_ARM64:
- arch_disarm_uprobe_arm64(p, task);
- break;
- default:
- pr_err("Error: unknown mode vaddr=%lx\n", vaddr);
- break;
- }
-}
-
-
-static void arch_prepare_simulate_arm64(struct uprobe *p)
-{
- if (p->ainsn.prepare)
- p->ainsn.prepare(p, &p->ainsn);
-}
-
-static int arch_prepare_ss_arm64(struct uprobe *p, u32 insn, u32 __user *utramp)
-{
- u32 tramp[2];
-
- /* prepare insn slot */
- tramp[0] = insn;
- tramp[1] = BRK64_OPCODE_PSEUDO_SS;
-
- if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp, 8)) {
- pr_err("%s: failed to write memory %p\n", __func__, utramp);
- return -EACCES;
- }
-
- return 0;
-}
-
-static int arch_uretprobe_set_break_arm64(struct uprobe *p, u32 __user *utramp)
-{
- int ret;
- u32 urp_brk = BRK64_OPCODE_URP;
- unsigned long brk_addr = (unsigned long)(utramp + URP_RET_BREAK_IDX);
-
- ret = write_proc_vm_atomic(p->task, brk_addr, &urp_brk, sizeof(urp_brk));
- if (ret != sizeof(urp_brk)) {
- pr_err("%s failed to write memory %016lx\n",
- __func__, brk_addr);
- return -EACCES;
- }
-
- return 0;
-}
-
-static int arch_prepare_uprobe_arm64(struct uprobe *p)
-{
- int ret = 0;
- struct task_struct *task = p->task;
- unsigned long vaddr = (unsigned long)p->addr;
- unsigned long raddr = get_real_addr(vaddr);
- u32 insn, __user *utramp;
-
- ret = read_proc_vm_atomic(task, raddr, &insn, sizeof(insn));
- if (ret != sizeof(insn)) {
- pr_err("failed to read memory %lx!\n", raddr);
- return -EINVAL;
- }
-
- utramp = swap_slot_alloc(p->sm);
- if (utramp == NULL) {
- pr_err("ERROR: cannot allocate trampoline (%016lx)\n", vaddr);
- return -ENOMEM;
- }
-
- switch (arm64_uprobe_decode_insn(insn, &p->ainsn)) {
- case INSN_REJECTED: /* insn not supported */
- ret = -EINVAL;
- break;
-
- case INSN_GOOD_NO_SLOT: /* insn need simulation */
- arch_prepare_simulate_arm64(p);
- ret = 0;
- break;
-
- case INSN_GOOD: /* instruction uses slot */
- ret = arch_prepare_ss_arm64(p, insn, utramp);
- break;
- };
-
- if (ret)
- goto fail;
-
- ret = arch_uretprobe_set_break_arm64(p, utramp);
- if (ret)
- goto fail;
-
- p->insn = utramp;
- p->opcode = insn;
-
- return 0;
-
-fail:
- swap_slot_free(p->sm, utramp);
- return ret;
-}
-
-int arch_prepare_uprobe(struct uprobe *p)
-{
- int ret;
-
- if (get_arch_mode((unsigned long)p->addr) == AM_ARM64) {
- ret = arch_prepare_uprobe_arm64(p);
- } else {
- ret = arch_prepare_uprobe_arm(p);
- }
-
- if (!ret) {
- /* for uretprobe */
- add_uprobe_table(p);
- }
-
- return ret;
-}
-
-void arch_remove_uprobe(struct uprobe *p)
-{
- swap_slot_free(p->sm, p->insn);
-}
-
-static void simulate_insn_arm64(struct uprobe *p, struct pt_regs *regs)
-{
- if (p->ainsn.handler)
- p->ainsn.handler(p->opcode, (long)p->addr, regs);
-
- reset_current_uprobe();
-}
-
-static void setup_ss_arm64(struct uprobe *p, struct pt_regs *regs)
-{
- /* set trampoline */
- regs->pc = (u64)p->insn;
-
- set_current_uprobe(p);
-}
-
-static void setup_singlestep(struct uprobe *p, struct pt_regs *regs)
-{
- if (p->ainsn.handler)
- simulate_insn_arm64(p, regs);
- else
- setup_ss_arm64(p, regs);
-}
-
-static enum dbg_code uprobe_handler_compat(struct pt_regs *regs)
-{
- pr_err("ARM and THUMB modes not supported\n");
- return DBG_ERROR;
-}
-
-static enum dbg_code uprobe_handler_arm64(struct pt_regs *regs)
-{
- struct uprobe *p;
-
- p = get_uprobe((void *)get_64bit_addr(regs->pc), current->tgid);
- if (p) {
- if (!p->pre_handler || !p->pre_handler(p, regs))
- setup_singlestep(p, regs);
- } else {
- return DBG_ERROR;
- }
-
- return DBG_HANDLED;
-}
-
-static enum dbg_code uprobe_handler(struct pt_regs *regs, unsigned int esr)
-{
- local_irq_enable();
-
- return compat_user_mode(regs) ?
- uprobe_handler_compat(regs) :
- uprobe_handler_arm64(regs);
-}
-
-static enum dbg_code uprobe_ss_handler(struct pt_regs *regs, unsigned int esr)
-{
- struct uprobe *p;
-
- p = get_current_uprobe();
- if (p) {
- regs->pc = (u64)p->addr + 4;
- reset_current_uprobe();
- }
-
- return DBG_HANDLED;
-}
-
-
-static struct brk_hook dbg_up_bp = {
- .spsr_mask = PSR_MODE_MASK,
- .spsr_val = PSR_MODE_EL0t,
- .esr_mask = DBG_BRK_ESR_MASK,
- .esr_val = DBG_BRK_ESR(BRK_BP),
- .fn = uprobe_handler,
-};
-
-static struct brk_hook dbg_up_ss = {
- .spsr_mask = PSR_MODE_MASK,
- .spsr_val = PSR_MODE_EL0t,
- .esr_mask = DBG_BRK_ESR_MASK,
- .esr_val = DBG_BRK_ESR(BRK_PSEUDO_SS),
- .fn = uprobe_ss_handler,
-};
-
-static struct brk_hook dbg_urp_bp = {
- .spsr_mask = PSR_MODE_MASK,
- .spsr_val = PSR_MODE_EL0t,
- .esr_mask = DBG_BRK_ESR_MASK,
- .esr_val = DBG_BRK_ESR(BRK_URP),
- .fn = urp_handler,
-};
-
-
-static void arch_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
-{
- if (p->ainsn.insn.handler) {
- regs->pc += 4;
- p->ainsn.insn.handler(p->opcode, &p->ainsn.insn, regs);
- } else {
- regs->pc = (unsigned long)p->insn;
- }
-}
-
-static int urp_handler_aarch32(struct pt_regs *regs, pid_t tgid)
-{
- struct uprobe *p;
- unsigned long vaddr = regs->pc;
- unsigned long offset_bp = compat_thumb_mode(regs) ?
- 0x1a :
- 4 * PROBES_TRAMP_RET_BREAK_IDX;
- unsigned long tramp_addr = vaddr - offset_bp;
- unsigned long flags;
-
- local_irq_save(flags);
- p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
- if (unlikely(p == NULL)) {
- local_irq_restore(flags);
-
- pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n",
- vaddr);
- return 1;
- }
-
- get_up(p);
- local_irq_restore(flags);
- trampoline_uprobe_handler(p, regs);
- put_up(p);
-
- return 0;
-}
-
-static int uprobe_handler_aarch32(struct pt_regs *regs, u32 instr)
-{
- int ret = 0;
- struct uprobe *p;
- unsigned long flags;
- unsigned long vaddr = regs->pc | !!compat_thumb_mode(regs);
- pid_t tgid = current->tgid;
-
- local_irq_enable();
-
- local_irq_save(flags);
- p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
- if (p) {
- get_up(p);
- local_irq_restore(flags);
-
- if (!p->pre_handler || !p->pre_handler(p, regs))
- arch_prepare_singlestep(p, regs);
-
- put_up(p);
- } else {
- local_irq_restore(flags);
- ret = urp_handler_aarch32(regs, tgid);
-
- /* check ARM/THUMB CPU mode matches installed probe mode */
- if (ret == 1) {
- vaddr ^= 1;
-
- local_irq_save(flags);
- p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
- if (p) {
- get_up(p);
- local_irq_restore(flags);
- pr_err("invalid mode: thumb=%d addr=%p insn=%08x\n",
- !!compat_thumb_mode(regs), p->addr, p->opcode);
- ret = 0;
-
- disarm_uprobe(p, current);
- put_up(p);
- } else {
- local_irq_restore(flags);
- }
- }
- }
-
- return ret;
-}
-
-
-static void (*__register_undef_hook)(struct undef_hook *hook);
-static void (*__unregister_undef_hook)(struct undef_hook *hook);
-
-static int undef_hook_once(void)
-{
- const char *sym;
-
- sym = "register_undef_hook";
- __register_undef_hook = (void *)swap_ksyms(sym);
- if (__register_undef_hook == NULL)
- goto not_found;
-
- sym = "unregister_undef_hook";
- __unregister_undef_hook = (void *)swap_ksyms(sym);
- if (__unregister_undef_hook == NULL)
- goto not_found;
-
- return 0;
-
-not_found:
- pr_err("ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-
-}
-
-static struct undef_hook undef_hook_arm = {
- .instr_mask = 0xffffffff,
- .instr_val = BREAK_ARM,
- .pstate_mask = COMPAT_PSR_MODE_MASK,
- .pstate_val = COMPAT_PSR_MODE_USR,
- .fn = uprobe_handler_aarch32,
-};
-
-static struct undef_hook undef_hook_thumb = {
- .instr_mask = 0xffff,
- .instr_val = BREAK_THUMB,
- .pstate_mask = COMPAT_PSR_MODE_MASK,
- .pstate_val = COMPAT_PSR_MODE_USR,
- .fn = uprobe_handler_aarch32,
-};
-
-int swap_arch_init_uprobes(void)
-{
- int ret;
-
- ret = undef_hook_once();
- if (ret)
- return ret;
-
- ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk));
- if (ret)
- return ret;
-
- if (!IS_ENABLED(CONFIG_SWAP_KERNEL_IMMUTABLE)) {
- /* for aarch64 */
- dbg_brk_hook_reg(&dbg_up_ss);
- dbg_brk_hook_reg(&dbg_up_bp);
- dbg_brk_hook_reg(&dbg_urp_bp);
- } else {
- pr_err("64-bit uprobes doesn't supported in case of Immutable kernel\n");
- }
-
- /* for aarch32 */
- __register_undef_hook(&undef_hook_arm);
- __register_undef_hook(&undef_hook_thumb);
-
- return 0;
-}
-
-void swap_arch_exit_uprobes(void)
-{
- /* for aarch32 */
- __unregister_undef_hook(&undef_hook_thumb);
- __unregister_undef_hook(&undef_hook_arm);
-
- if (!IS_ENABLED(CONFIG_SWAP_KERNEL_IMMUTABLE)) {
- /* for aarch64 */
- dbg_brk_hook_unreg(&dbg_urp_bp);
- dbg_brk_hook_unreg(&dbg_up_bp);
- dbg_brk_hook_unreg(&dbg_up_ss);
- }
-
- swap_td_raw_unreg(&td_raw);
-}
+++ /dev/null
-#ifndef _ASM_ARM64_UPROBES_H
-#define _ASM_ARM64_UPROBES_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/types.h>
-#include <linux/ptrace.h>
-#include <arch/arm/probes/probes.h>
-#include <arch/arm/uprobe/swap_uprobe.h>
-
-
-#define UP_TRAMP_INSN_CNT 3 /* | opcode | ss_bp | urp_bp | */
-#define UPROBES_TRAMP_LEN (UP_TRAMP_INSN_CNT * 4) /* 4 - instruction size */
-#define URP_RET_BREAK_IDX 2
-
-
-struct uprobe;
-struct arch_insn;
-struct uretprobe;
-struct task_struct;
-struct uretprobe_instance;
-
-
-typedef unsigned long (uprobes_pstate_check_t)(unsigned long pstate);
-typedef unsigned long (uprobes_condition_check_t)(struct uprobe *p,
- struct pt_regs *regs);
-typedef void (uprobes_prepare_t)(struct uprobe *p, struct arch_insn *asi);
-typedef void (uprobes_handler_t)(u32 opcode, long addr, struct pt_regs *regs);
-
-
-struct arch_insn {
- /* arm */
- struct arch_insn_arm insn;
-
- /* arm64 */
- uprobes_pstate_check_t *pstate_cc;
- uprobes_condition_check_t *check_condn;
- uprobes_prepare_t *prepare;
- uprobes_handler_t *handler;
-};
-
-
-typedef u32 uprobe_opcode_t;
-
-static inline u32 swap_get_float(struct pt_regs *regs, unsigned long n)
-{
- u32 *ptr;
- register unsigned long w0 asm ("w0");
-
- switch (n) {
- case 0:
- asm volatile("fmov w0, s0");
- break;
- case 1:
- asm volatile("fmov w0, s1");
- break;
- case 2:
- asm volatile("fmov w0, s2");
- break;
- case 3:
- asm volatile("fmov w0, s3");
- break;
- case 4:
- asm volatile("fmov w0, s4");
- break;
- case 5:
- asm volatile("fmov w0, s5");
- break;
- case 6:
- asm volatile("fmov w0, s6");
- break;
- case 7:
- asm volatile("fmov w0, s7");
- break;
- default:
- w0 = 0;
- ptr = (u32 *)((u64 *)regs->sp + n - 8);
- if (get_user(w0, ptr))
- pr_err("failed to dereference a pointer\n");
-
- break;
- }
-
- return w0;
-}
-
-static inline u64 swap_get_double(struct pt_regs *regs, unsigned long n)
-{
- u64 *ptr;
- register unsigned long x0 asm ("x0");
-
- switch (n) {
- case 0:
- asm volatile("fmov x0, d0");
- break;
- case 1:
- asm volatile("fmov x0, d1");
- break;
- case 2:
- asm volatile("fmov x0, d2");
- break;
- case 3:
- asm volatile("fmov x0, d3");
- break;
- case 4:
- asm volatile("fmov x0, d4");
- break;
- case 5:
- asm volatile("fmov x0, d5");
- break;
- case 6:
- asm volatile("fmov x0, d6");
- break;
- case 7:
- asm volatile("fmov x0, d7");
- break;
- default:
- x0 = 0;
- ptr = (u64 *)regs->sp + n - 8;
- if (get_user(x0, ptr))
- pr_err("failed to dereference a pointer\n");
-
- break;
- }
-
- return x0;
-}
-
-static inline u32 swap_get_urp_float(struct pt_regs *regs)
-{
- return swap_get_float(regs, 0);
-}
-
-static inline u64 swap_get_urp_double(struct pt_regs *regs)
-{
- return swap_get_double(regs, 0);
-}
-
-static inline unsigned long swap_get_upc_arm64(struct pt_regs *regs)
-{
- return regs->pc;
-}
-
-static inline void swap_set_upc_arm64(struct pt_regs *regs, unsigned long val)
-{
- regs->pc = val;
-}
-
-static inline unsigned long swap_get_uarg_arm64(struct pt_regs *regs,
- unsigned long n)
-{
- u64 *ptr, val;
-
- if (n < 8)
- return regs->regs[n];
-
- ptr = (u64 *)regs->sp + n - 8;
- if (get_user(val, ptr))
- pr_err("failed to dereference a pointer, ptr=%p\n", ptr);
-
- return val;
-}
-
-static inline void swap_put_uarg_arm64(struct pt_regs *regs, unsigned long n,
- unsigned long val)
-{
- if (n < 8) {
- regs->regs[n] = val;
- } else {
- u64 *ptr = (u64 *)regs->sp + n - 8;
- if (put_user(val, ptr))
- pr_err("Failed to dereference a pointer, ptr=%p\n", ptr);
- }
-}
-
-static inline unsigned long swap_get_uret_addr_arm64(struct pt_regs *regs)
-{
- return regs->regs[30];
-}
-
-static inline void swap_set_uret_addr_arm64(struct pt_regs *regs,
- unsigned long val)
-{
- regs->regs[30] = val;
-}
-
-static inline unsigned long swap_get_upc(struct pt_regs *regs)
-{
- if (compat_user_mode(regs))
- return swap_get_upc_arm(regs);
- else
- return swap_get_upc_arm64(regs);
-}
-
-static inline void swap_set_upc(struct pt_regs *regs, unsigned long val)
-{
- if (compat_user_mode(regs))
- swap_set_upc_arm(regs, val);
- else
- swap_set_upc_arm64(regs, val);
-}
-
-static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
-{
- if (compat_user_mode(regs))
- return swap_get_uarg_arm(regs, n);
- else
- return swap_get_uarg_arm64(regs, n);
-}
-
-static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
- unsigned long val)
-{
- if (compat_user_mode(regs))
- return swap_put_uarg_arm(regs, n, val);
- else
- return swap_put_uarg_arm64(regs, n, val);
-}
-
-static inline unsigned long swap_get_uret_addr(struct pt_regs *regs)
-{
- if (compat_user_mode(regs))
- return swap_get_uret_addr_arm(regs);
- else
- return swap_get_uret_addr_arm64(regs);
-}
-
-static inline void swap_set_uret_addr(struct pt_regs *regs, unsigned long val)
-{
- if (compat_user_mode(regs))
- swap_set_uret_addr_arm(regs, val);
- else
- swap_set_uret_addr_arm64(regs, val);
-}
-
-int arch_prepare_uprobe(struct uprobe *p);
-void arch_remove_uprobe(struct uprobe *p);
-
-static inline int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0;
-}
-
-static inline int longjmp_break_uhandler(struct uprobe *p,
- struct pt_regs *regs)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0;
-}
-
-
-int arch_arm_uprobe(struct uprobe *p);
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
-
-
-unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
-void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
-int arch_prepare_uretprobe(struct uretprobe_instance *ri,
- struct pt_regs *regs);
-
-#define arch_urp_check_opcode NULL /* Arch doesn't requires special callback */
-void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
-int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task);
-
-static inline void arch_ujprobe_return(void)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
-}
-
-int swap_arch_init_uprobes(void);
-void swap_arch_exit_uprobes(void);
-
-
-#endif /* _ASM_ARM64_UPROBES_H */
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/kprobes-arm64.c
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-
-#include <asm/ptrace.h>
-#include <uprobe/swap_uprobes.h>
-#include <swap-asm/simulate-insn.h>
-#include <swap-asm/condn-helpers.h>
-
-#include "uprobes-decode.h"
-#include "uprobes-arm64.h"
-
-
-/*
- * condition check functions for uprobes simulation
- */
-static unsigned long __check_pstate(struct uprobe *p, struct pt_regs *regs)
-{
- struct arch_insn *asi = &p->ainsn;
- unsigned long pstate = regs->pstate & 0xffffffff;
-
- return asi->pstate_cc(pstate);
-}
-
-static unsigned long __check_cbz(struct uprobe *p, struct pt_regs *regs)
-{
- return check_cbz((u32)p->opcode, regs);
-}
-
-static unsigned long __check_cbnz(struct uprobe *p, struct pt_regs *regs)
-{
- return check_cbnz((u32)p->opcode, regs);
-}
-
-static unsigned long __check_tbz(struct uprobe *p, struct pt_regs *regs)
-{
- return check_tbz((u32)p->opcode, regs);
-}
-
-static unsigned long __check_tbnz(struct uprobe *p, struct pt_regs *regs)
-{
- return check_tbnz((u32)p->opcode, regs);
-}
-
-/*
- * prepare functions for instruction simulation
- */
-static void prepare_none(struct uprobe *p, struct arch_insn *asi)
-{
-}
-
-static void prepare_bcond(struct uprobe *p, struct arch_insn *asi)
-{
- uprobe_opcode_t insn = p->opcode;
-
- asi->check_condn = __check_pstate;
- asi->pstate_cc = probe_condition_checks[insn & 0xf];
-}
-
-static void prepare_cbz_cbnz(struct uprobe *p, struct arch_insn *asi)
-{
- uprobe_opcode_t insn = p->opcode;
-
- asi->check_condn = (insn & (1 << 24)) ? __check_cbnz : __check_cbz;
-}
-
-static void prepare_tbz_tbnz(struct uprobe *p, struct arch_insn *asi)
-{
- uprobe_opcode_t insn = p->opcode;
-
- asi->check_condn = (insn & (1 << 24)) ? __check_tbnz : __check_tbz;
-}
-
-
-/* Load literal (PC-relative) instructions
- * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
- *
- * opcode[26]: V=0, Load GP registers, simulate them.
- * Encoding: xx01 1000 xxxx xxxx xxxx xxxx xxxx xxxx
- * opcode[31:30]: op = 00, 01 - LDR literal
- * opcode[31:30]: op = 10, - LDRSW literal
- *
- * 1. V=1 -Load FP/AdvSIMD registers
- * Encoding: xx01 1100 xxxx xxxx xxxx xxxx xxxx xxxx
- * 2. V=0,opc=11 -PRFM(Prefetch literal)
- * Encoding: 1101 1000 xxxx xxxx xxxx xxxx xxxx xxxx
- * Reject FP/AdvSIMD literal load & PRFM literal.
- */
-static const struct aarch64_decode_item load_literal_subtable[] = {
- DECODE_REJECT(0x1C000000, 0x3F000000),
- DECODE_REJECT(0xD8000000, 0xFF000000),
- DECODE_LITERAL(0x18000000, 0xBF000000, prepare_none,
- simulate_ldr_literal),
- DECODE_LITERAL(0x98000000, 0xFF000000, prepare_none,
- simulate_ldrsw_literal),
- DECODE_END,
-};
-
-/* AArch64 instruction decode table for kprobes:
- * The instruction will fall into one of the 3 groups:
- * 1. Single stepped out-of-the-line slot.
- * -Most instructions fall in this group, those does not
- * depend on PC address.
- *
- * 2. Should be simulated because of PC-relative/literal access.
- * -All branching and PC-relative insrtcutions are simulated
- * in C code, making use of saved pt_regs
- * Catch: SIMD/NEON register context are not saved while
- * entering debug exception, so are rejected for now.
- *
- * 3. Cannot be probed(not safe) so are rejected.
- * - Exception generation and exception return instructions
- * - Exclusive monitor(LDREX/STREX family)
- *
- */
-static const struct aarch64_decode_item aarch64_decode_table[] = {
- /*
- * Data processing - PC relative(literal) addressing:
- * Encoding: xxx1 0000 xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_LITERAL(0x10000000, 0x1F000000, prepare_none,
- simulate_adr_adrp),
-
- /*
- * Data processing - Add/Substract Immediate:
- * Encoding: xxx1 0001 xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x11000000, 0x1F000000),
-
- /*
- * Data processing
- * Encoding:
- * xxx1 0010 0xxx xxxx xxxx xxxx xxxx xxxx (Logical)
- * xxx1 0010 1xxx xxxx xxxx xxxx xxxx xxxx (Move wide)
- * xxx1 0011 0xxx xxxx xxxx xxxx xxxx xxxx (Bitfield)
- * xxx1 0011 1xxx xxxx xxxx xxxx xxxx xxxx (Extract)
- */
- DECODE_SINGLESTEP(0x12000000, 0x1E000000),
-
- /*
- * Data processing - SIMD/FP/AdvSIMD/Crypto-AES/SHA
- * Encoding: xxx0 111x xxxx xxxx xxxx xxxx xxxx xxxx
- * Encoding: xxx1 111x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x0E000000, 0x0E000000),
-
- /*
- * Data processing - Register
- * Encoding: xxxx 101x xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x0A000000, 0x0E000000),
-
- /* Branching Instructions
- *
- * Encoding:
- * x001 01xx xxxx xxxx xxxx xxxx xxxx xxxx (uncondtional Branch)
- * x011 010x xxxx xxxx xxxx xxxx xxxx xxxx (compare & branch)
- * x011 011x xxxx xxxx xxxx xxxx xxxx xxxx (Test & Branch)
- * 0101 010x xxxx xxxx xxxx xxxx xxxx xxxx (Conditional, immediate)
- * 1101 011x xxxx xxxx xxxx xxxx xxxx xxxx (Unconditional,register)
- */
- DECODE_BRANCH(0x14000000, 0x7C000000, prepare_none,
- simulate_b_bl),
- DECODE_BRANCH(0x34000000, 0x7E000000, prepare_cbz_cbnz,
- simulate_cbz_cbnz),
- DECODE_BRANCH(0x36000000, 0x7E000000, prepare_tbz_tbnz,
- simulate_tbz_tbnz),
- DECODE_BRANCH(0x54000000, 0xFE000000, prepare_bcond,
- simulate_b_cond),
- DECODE_BRANCH(0xD6000000, 0xFE000000, prepare_none,
- simulate_br_blr_ret),
-
- /* System insn:
- * Encoding: 1101 0101 00xx xxxx xxxx xxxx xxxx xxxx
- *
- * Note: MSR immediate (update PSTATE daif) is not safe handling
- * within kprobes, rejected.
- *
- * Don't re-arrange these decode table entries.
- */
- DECODE_REJECT(0xD500401F, 0xFFF8F01F),
- DECODE_SINGLESTEP(0xD5000000, 0xFFC00000),
-
- /* Exception Generation:
- * Encoding: 1101 0100 xxxx xxxx xxxx xxxx xxxx xxxx
- * Instructions: SVC, HVC, SMC, BRK, HLT, DCPS1, DCPS2, DCPS3
- */
- DECODE_REJECT(0xD4000000, 0xFF000000),
-
- /*
- * Load/Store - Exclusive monitor
- * Encoding: xx00 1000 xxxx xxxx xxxx xxxx xxxx xxxx
- *
- * Reject exlusive monitor'ed instructions
- */
- DECODE_REJECT(0x08000000, 0x3F000000),
-
- /*
- * Load/Store - PC relative(literal):
- * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_TABLE(0x18000000, 0x3B000000, load_literal_subtable),
-
- /*
- * Load/Store - Register Pair
- * Encoding:
- * xx10 1x00 0xxx xxxx xxxx xxxx xxxx xxxx
- * xx10 1x00 1xxx xxxx xxxx xxxx xxxx xxxx
- * xx10 1x01 0xxx xxxx xxxx xxxx xxxx xxxx
- * xx10 1x01 1xxx xxxx xxxx xxxx xxxx xxxx
- */
- DECODE_SINGLESTEP(0x28000000, 0x3A000000),
-
- /*
- * Load/Store - Register
- * Encoding:
- * xx11 1x00 xx0x xxxx xxxx 00xx xxxx xxxx (unscaled imm)
- * xx11 1x00 xx0x xxxx xxxx 01xx xxxx xxxx (imm post-indexed)
- * xx11 1x00 xx0x xxxx xxxx 10xx xxxx xxxx (unpriviledged)
- * xx11 1x00 xx0x xxxx xxxx 11xx xxxx xxxx (imm pre-indexed)
- *
- * xx11 1x00 xx10 xxxx xxxx xx10 xxxx xxxx (register offset)
- *
- * xx11 1x01 xxxx xxxx xxxx xxxx xxxx xxxx (unsigned imm)
- */
- DECODE_SINGLESTEP(0x38000000, 0x3B200000),
- DECODE_SINGLESTEP(0x38200200, 0x38300300),
- DECODE_SINGLESTEP(0x39000000, 0x3B000000),
-
- /*
- * Load/Store - AdvSIMD
- * Encoding:
- * 0x00 1100 0x00 0000 xxxx xxxx xxxx xxxx (Multiple-structure)
- * 0x00 1100 1x0x xxxx xxxx xxxx xxxx xxxx (Multi-struct post-indexed)
- * 0x00 1101 0xx0 0000 xxxx xxxx xxxx xxxx (Single-structure))
- * 0x00 1101 1xxx xxxx xxxx xxxx xxxx xxxx (Single-struct post-index)
- */
- DECODE_SINGLESTEP(0x0C000000, 0xBFBF0000),
- DECODE_SINGLESTEP(0x0C800000, 0xBFA00000),
- DECODE_SINGLESTEP(0x0D000000, 0xBF9F0000),
- DECODE_SINGLESTEP(0x0D800000, 0xBF800000),
-
- /* Unallocated: xxx0 0xxx xxxx xxxx xxxx xxxx xxxx xxxx */
- DECODE_REJECT(0x00000000, 0x18000000),
- DECODE_END,
-};
-
-static int uprobe_decode_insn(u32 insn, struct arch_insn *asi,
- const struct aarch64_decode_item *tbl)
-{
- unsigned int entry, ret = INSN_REJECTED;
-
- for (entry = 0; !decode_table_end(tbl[entry]); entry++) {
- if (decode_table_hit(tbl[entry], insn))
- break;
- }
-
- switch (decode_get_type(tbl[entry])) {
- case DECODE_TYPE_END:
- case DECODE_TYPE_REJECT:
- default:
- ret = INSN_REJECTED;
- break;
-
- case DECODE_TYPE_SINGLESTEP:
- ret = INSN_GOOD;
- break;
-
- case DECODE_TYPE_SIMULATE:
- asi->prepare = decode_prepare_fn(tbl[entry]);
- asi->handler = decode_handler_fn(tbl[entry]);
- ret = INSN_GOOD_NO_SLOT;
- break;
-
- case DECODE_TYPE_TABLE:
- /* recurse with next level decode table */
- ret = uprobe_decode_insn(insn, asi,
- decode_sub_table(tbl[entry]));
- };
-
- return ret;
-}
-
-/* Return:
- * INSN_REJECTED If instruction is one not allowed to kprobe,
- * INSN_GOOD If instruction is supported and uses instruction slot,
- * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
- */
-enum uprobe_insn arm64_uprobe_decode_insn(u32 insn, struct arch_insn *asi)
-{
- return uprobe_decode_insn(insn, asi, aarch64_decode_table);
-}
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/kprobes-arm64.h
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#ifndef _ARM_UPROBES_ARM64_H
-#define _ARM_UPROBES_ARM64_H
-
-
-enum uprobe_insn {
- INSN_REJECTED,
- INSN_GOOD_NO_SLOT,
- INSN_GOOD,
-};
-
-
-enum uprobe_insn arm64_uprobe_decode_insn(u32 insn, struct arch_insn *asi);
-
-
-#endif /* _ARM_UPROBES_ARM64_H */
+++ /dev/null
-/*
- * Copyright (C) Samsung Electronics, 2014
- *
- * Copied from: arch/arm64/kernel/probes-decode.h
- *
- * Copyright (C) 2013 Linaro Limited.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#ifndef _ARM64_KERNEL_UPROBES_DECODE_H
-#define _ARM64_KERNEL_UPROBES_DECODE_H
-
-
-#include "swap_uprobes.h"
-
-
-/*
- * The following definitions and macros are used to build instruction
- * decoding tables.
- */
-enum decode_type {
- DECODE_TYPE_END,
- DECODE_TYPE_SINGLESTEP,
- DECODE_TYPE_SIMULATE,
- DECODE_TYPE_TABLE,
- DECODE_TYPE_REJECT,
-};
-
-struct aarch64_decode_item;
-
-struct aarch64_decode_header {
- enum decode_type type;
- u32 mask;
- u32 val;
-};
-
-struct aarch64_decode_actions {
- uprobes_prepare_t *prepare;
- uprobes_handler_t *handler;
-};
-
-struct aarch64_decode_table {
- const struct aarch64_decode_item *tbl;
-};
-
-union aarch64_decode_handler {
- struct aarch64_decode_actions actions;
- struct aarch64_decode_table table;
-};
-
-struct aarch64_decode_item {
- struct aarch64_decode_header header;
- union aarch64_decode_handler decode;
-};
-
-#define decode_get_type(_entry) ((_entry).header.type)
-
-#define decode_table_end(_entry) \
- ((_entry).header.type == DECODE_TYPE_END)
-
-#define decode_table_hit(_entry, insn) \
- ((insn & (_entry).header.mask) == (_entry).header.val)
-
-#define decode_prepare_fn(_entry) ((_entry).decode.actions.prepare)
-#define decode_handler_fn(_entry) ((_entry).decode.actions.handler)
-#define decode_sub_table(_entry) ((_entry).decode.table.tbl)
-
-#define DECODE_ADD_HEADER(_type, _val, _mask) \
- .header = { \
- .type = _type, \
- .mask = _mask, \
- .val = _val, \
- }
-
-#define DECODE_ADD_ACTION(_prepare, _handler) \
- .decode = { \
- .actions = { \
- .prepare = _prepare, \
- .handler = _handler, \
- } \
- }
-
-#define DECODE_ADD_TABLE(_table) \
- .decode = { \
- .table = {.tbl = _table} \
- }
-
-#define DECODE_REJECT(_v, _m) \
- { DECODE_ADD_HEADER(DECODE_TYPE_REJECT, _v, _m) }
-
-#define DECODE_SINGLESTEP(_v, _m) \
- { DECODE_ADD_HEADER(DECODE_TYPE_SINGLESTEP, _v, _m) }
-
-#define DECODE_SIMULATE(_v, _m, _p, _h) \
- { DECODE_ADD_HEADER(DECODE_TYPE_SIMULATE, _v, _m), \
- DECODE_ADD_ACTION(_p, _h) }
-
-#define DECODE_TABLE(_v, _m, _table) \
- { DECODE_ADD_HEADER(DECODE_TYPE_TABLE, _v, _m), \
- DECODE_ADD_TABLE(_table) }
-
-#define DECODE_LITERAL(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
-#define DECODE_BRANCH(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h)
-
-/* should be the last element in decode structure */
-#define DECODE_END { .header = {.type = DECODE_TYPE_END, } }
-
-#endif /* _ARM64_KERNEL_UPROBES_DECODE_H */
+++ /dev/null
-/**
- * swap_sc_patch.c
- * @author Dmitry Kovalenko <d.kovalenko@samsung.com>
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Patching of sys_call_table
- */
-
-#include <ksyms/ksyms.h>
-#include "swap_sc_patch.h"
-
-static unsigned long original_syscall;
-static int patched_syscall = -1;
-
-/* disable write protection */
-#define swap_disable_wprot() \
- asm("pushl %eax \n" \
- "movl %cr0, %eax \n" \
- "andl $0xfffeffff, %eax \n" \
- "movl %eax, %cr0 \n" \
- "popl %eax");
-
-/* enable write protection */
-#define swap_enable_wprot() \
- asm("push %eax \n" \
- "movl %cr0, %eax \n" \
- "orl $0x00010000, %eax \n" \
- "movl %eax, %cr0 \n" \
- "popl %eax");
-
-void patch_syscall(int syscall_n, unsigned long new_syscall_addr)
-{
- unsigned long tmp;
- unsigned long *sc_table;
-
- /*
- * Search for sys_call_table (4 bytes before sysenter_after_call)
- * sysenter_do_call function which locates before sysenter_after_call
- * has sys_call_table address in call instruction (latest instruction)
- */
- tmp = swap_ksyms("sysenter_after_call");
- sc_table = *(unsigned long **)(tmp - 4);
-
- swap_disable_wprot();
- original_syscall = sc_table[syscall_n];
- sc_table[syscall_n] = new_syscall_addr;
- patched_syscall = syscall_n;
- swap_enable_wprot();
-}
-
-void swap_depatch_syscall(void)
-{
- if (patched_syscall == -1) {
- printk(KERN_WARNING
- "SWAP SC_PATCH: there is no patched syscalls");
- return;
- }
-
- patch_syscall(patched_syscall, original_syscall);
- patched_syscall = -1;
-}
-
-asmlinkage long sys_swap_func(void)
-{
- /* Your code here */
-
- return -ENOSYS;
-}
-
-#define NI_SYSCALL4SWAP 31
-void swap_patch_syscall(void)
-{
- patch_syscall(NI_SYSCALL4SWAP, (unsigned long)&sys_swap_func);
-}
+++ /dev/null
-void swap_depatch_syscall(void);
-void swap_patch_syscall(void);
+++ /dev/null
-/**
- * uprobe/arch/asm-x86/swap_uprobes.c
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Arch-dependent uprobe interface implementation for x86.
- */
-
-
-#include <linux/kdebug.h>
-
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_td_raw.h>
-#include <kprobe/swap_kprobes.h>
-#include <uprobe/swap_uprobes.h>
-
-#include "swap_uprobes.h"
-
-
-struct save_context {
- struct pt_regs save_regs;
- struct pt_regs *ptr_regs;
- unsigned long val;
- int (*handler)(struct uprobe *, struct pt_regs *);
-};
-
-/**
- * @struct uprobe_ctlblk
- * @brief Uprobe control block
- */
-struct uprobe_ctlblk {
- unsigned long flags; /**< Flags */
- struct uprobe *p; /**< Pointer to the uprobe */
-
- struct save_context ctx;
-};
-
-
-static struct td_raw td_raw;
-
-
-static unsigned long trampoline_addr(struct uprobe *up)
-{
- return (unsigned long)(up->insn + UPROBES_TRAMP_RET_BREAK_IDX);
-}
-
-unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
-{
- return trampoline_addr(&ri->rp->up);
-}
-
-static struct uprobe_ctlblk *current_ucb(void)
-{
- return (struct uprobe_ctlblk *)swap_td_raw(&td_raw, current);
-}
-
-static struct save_context *current_ctx(void)
-{
- return ¤t_ucb()->ctx;
-}
-
-static struct uprobe *get_current_probe(void)
-{
- return current_ucb()->p;
-}
-
-static void set_current_probe(struct uprobe *p)
-{
- current_ucb()->p = p;
-}
-
-static void save_current_flags(struct pt_regs *regs)
-{
- current_ucb()->flags = regs->flags;
-}
-
-static void restore_current_flags(struct pt_regs *regs, unsigned long flags)
-{
- regs->flags &= ~IF_MASK;
- regs->flags |= flags & IF_MASK;
-}
-
-/**
- * @brief Prepares uprobe for x86.
- *
- * @param up Pointer to the uprobe.
- * @return 0 on success,\n
- * -1 on error.
- */
-int arch_prepare_uprobe(struct uprobe *p)
-{
- struct task_struct *task = p->task;
- u8 tramp[UPROBES_TRAMP_LEN + BP_INSN_SIZE]; /* BP for uretprobe */
-
- if (!read_proc_vm_atomic(task, (unsigned long)p->addr,
- tramp, MAX_INSN_SIZE)) {
- printk(KERN_ERR "failed to read memory %p!\n", p->addr);
- return -EINVAL;
- }
-
- if (p->check_opcode_cb) {
- int ret;
- ret = p->check_opcode_cb((uprobe_opcode_t *)tramp);
- if (ret)
- return ret;
- }
-
- tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
-
- p->opcode = tramp[0];
- p->ainsn.boostable = swap_can_boost(tramp) ? 0 : -1;
-
- p->insn = swap_slot_alloc(p->sm);
- if (p->insn == NULL) {
- printk(KERN_ERR "trampoline out of memory\n");
- return -ENOMEM;
- }
-
- if (!write_proc_vm_atomic(task, (unsigned long)p->insn,
- tramp, sizeof(tramp))) {
- swap_slot_free(p->sm, p->insn);
- printk(KERN_INFO "failed to write memory %p!\n", tramp);
- return -EINVAL;
- }
-
- /* for uretprobe */
- add_uprobe_table(p);
-
- return 0;
-}
-
-/**
- * @brief Jump pre-handler.
- *
- * @param p Pointer to the uprobe.
- * @param regs Pointer to CPU register data.
- * @return 0.
- */
-int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs)
-{
- struct ujprobe *jp = container_of(p, struct ujprobe, up);
- entry_point_t entry = (entry_point_t)jp->entry;
- unsigned long args[6];
-
- /* FIXME some user space apps crash if we clean interrupt bit */
- /* regs->EREG(flags) &= ~IF_MASK; */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
- trace_hardirqs_off();
-#endif
-
- /* read first 6 args from stack */
- if (!read_proc_vm_atomic(current, regs->EREG(sp) + 4,
- args, sizeof(args)))
- printk(KERN_WARNING
- "failed to read user space func arguments %lx!\n",
- regs->sp + 4);
-
- if (entry)
- entry(args[0], args[1], args[2], args[3], args[4], args[5]);
- else
- arch_ujprobe_return();
-
- return 0;
-}
-
-/**
- * @brief Check opcode is applicable for retprobe
- *
- * @param opcode Pointer to opcode
- * @return result of checking
- */
-int arch_urp_check_opcode(uprobe_opcode_t *opcode)
-{
- enum { call_relative_opcode = 0xe8 };
-
- /* do not set uprobe for retprobe if insn is relative call */
- if (opcode[0] == call_relative_opcode) {
- pr_err("failed to set retprobe to relative call\n");
- return -ENOEXEC;
- }
-
- return 0;
-}
-
-/**
- * @brief Prepares uretprobe for x86.
- *
- * @param ri Pointer to the uretprobe instance.
- * @param regs Pointer to CPU register data.
- * @return Void.
- */
-int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- /* Replace the return addr with trampoline addr */
- unsigned long ra = trampoline_addr(&ri->rp->up);
- unsigned long ret_addr;
- ri->sp = (kprobe_opcode_t *)regs->sp;
-
- if (get_user(ret_addr, (unsigned long *)regs->sp)) {
- pr_err("failed to read user space func ra %lx addr=%p!\n",
- regs->sp, ri->rp->up.addr);
- return -EINVAL;
- }
-
- if (put_user(ra, (unsigned long *)regs->sp)) {
- pr_err("failed to write user space func ra %lx!\n", regs->sp);
- return -EINVAL;
- }
-
- ri->ret_addr = (uprobe_opcode_t *)ret_addr;
-
- return 0;
-}
-
-static bool get_long(struct task_struct *task,
- unsigned long vaddr, unsigned long *val)
-{
- return sizeof(*val) != read_proc_vm_atomic(task, vaddr,
- val, sizeof(*val));
-}
-
-static bool put_long(struct task_struct *task,
- unsigned long vaddr, unsigned long *val)
-{
- return sizeof(*val) != write_proc_vm_atomic(task, vaddr,
- val, sizeof(*val));
-}
-
-/**
- * @brief Disarms uretprobe on x86 arch.
- *
- * @param ri Pointer to the uretprobe instance.
- * @param task Pointer to the task for which the probe.
- * @return 0 on success,\n
- * negative error code on error.
- */
-int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task)
-{
- unsigned long ret_addr;
- unsigned long sp = (unsigned long)ri->sp;
- unsigned long tramp_addr = trampoline_addr(&ri->rp->up);
-
- if (get_long(task, sp, &ret_addr)) {
- printk(KERN_INFO "---> %s (%d/%d): failed to read stack from %08lx\n",
- task->comm, task->tgid, task->pid, sp);
- return -EFAULT;
- }
-
- if (tramp_addr == ret_addr) {
- if (put_long(task, sp, (unsigned long *)&ri->ret_addr)) {
- printk(KERN_INFO "---> %s (%d/%d): failed to write "
- "orig_ret_addr to %08lx",
- task->comm, task->tgid, task->pid, sp);
- return -EFAULT;
- }
- } else {
- printk(KERN_INFO "---> %s (%d/%d): trampoline NOT found at sp = %08lx\n",
- task->comm, task->tgid, task->pid, sp);
- return -ENOENT;
- }
-
- return 0;
-}
-
-/**
- * @brief Gets trampoline address.
- *
- * @param p Pointer to the uprobe.
- * @param regs Pointer to CPU register data.
- * @return Trampoline address.
- */
-unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
-{
- return trampoline_addr(p);
-}
-
-/**
- * @brief Restores return address.
- *
- * @param orig_ret_addr Original return address.
- * @param regs Pointer to CPU register data.
- * @return Void.
- */
-void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
-{
- regs->EREG(ip) = orig_ret_addr;
-}
-
-/**
- * @brief Removes uprobe.
- *
- * @param up Pointer to the target uprobe.
- * @return Void.
- */
-void arch_remove_uprobe(struct uprobe *p)
-{
- swap_slot_free(p->sm, p->insn);
-}
-
-int arch_arm_uprobe(struct uprobe *p)
-{
- int ret;
- uprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
- unsigned long vaddr = (unsigned long)p->addr;
-
- ret = write_proc_vm_atomic(p->task, vaddr, &insn, sizeof(insn));
- if (!ret) {
- pr_err("arch_arm_uprobe: failed to write memory tgid=%u vaddr=%08lx\n",
- p->task->tgid, vaddr);
-
- return -EACCES;
- }
-
- return 0;
-}
-
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
-{
- int ret;
- unsigned long vaddr = (unsigned long)p->addr;
-
- ret = write_proc_vm_atomic(task, vaddr, &p->opcode, sizeof(p->opcode));
- if (!ret) {
- pr_err("arch_disarm_uprobe: failed to write memory tgid=%u, vaddr=%08lx\n",
- task->tgid, vaddr);
- }
-}
-
-static void set_user_jmp_op(void *from, void *to)
-{
- struct __arch_jmp_op {
- char op;
- long raddr;
- } __packed jop;
-
- jop.raddr = (long)(to) - ((long)(from) + 5);
- jop.op = RELATIVEJUMP_INSTRUCTION;
-
- if (put_user(jop.op, (char *)from) ||
- put_user(jop.raddr, (long *)(from + 1)))
- pr_err("failed to write jump opcode to user space %p\n", from);
-}
-
-static void resume_execution(struct uprobe *p,
- struct pt_regs *regs,
- unsigned long flags)
-{
- unsigned long *tos, tos_dword = 0;
- unsigned long copy_eip = (unsigned long)p->insn;
- unsigned long orig_eip = (unsigned long)p->addr;
- uprobe_opcode_t insns[2];
-
- regs->EREG(flags) &= ~TF_MASK;
-
- tos = (unsigned long *)&tos_dword;
- if (get_user(tos_dword, (unsigned long *)regs->sp)) {
- pr_err("failed to read from user space sp=%lx!\n", regs->sp);
- return;
- }
-
- if (get_user(*(unsigned short *)insns, (unsigned short *)p->insn)) {
- pr_err("failed to read first 2 opcodes %p!\n", p->insn);
- return;
- }
-
- switch (insns[0]) {
- case 0x9c: /* pushfl */
- *tos &= ~(TF_MASK | IF_MASK);
- *tos |= flags & (TF_MASK | IF_MASK);
- break;
- case 0xc2: /* iret/ret/lret */
- case 0xc3:
- case 0xca:
- case 0xcb:
- case 0xcf:
- case 0xea: /* jmp absolute -- eip is correct */
- /* eip is already adjusted, no more changes required */
- p->ainsn.boostable = 1;
- goto no_change;
- case 0xe8: /* call relative - Fix return addr */
- *tos = orig_eip + (*tos - copy_eip);
- break;
- case 0x9a: /* call absolute -- same as call absolute, indirect */
- *tos = orig_eip + (*tos - copy_eip);
-
- if (put_user(tos_dword, (unsigned long *)regs->sp)) {
- pr_err("failed to write dword to sp=%lx\n", regs->sp);
- return;
- }
-
- goto no_change;
- case 0xff:
- if ((insns[1] & 0x30) == 0x10) {
- /*
- * call absolute, indirect
- * Fix return addr; eip is correct.
- * But this is not boostable
- */
- *tos = orig_eip + (*tos - copy_eip);
-
- if (put_user(tos_dword, (unsigned long *)regs->sp)) {
- pr_err("failed to write dword to sp=%lx\n", regs->sp);
- return;
- }
-
- goto no_change;
- } else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute
- * indirect */
- ((insns[1] & 0x31) == 0x21)) {
- /* jmp far, absolute indirect */
- /* eip is correct. And this is boostable */
- p->ainsn.boostable = 1;
- goto no_change;
- }
- case 0xf3:
- if (insns[1] == 0xc3)
- /* repz ret special handling: no more changes */
- goto no_change;
- break;
- default:
- break;
- }
-
- if (put_user(tos_dword, (unsigned long *)regs->sp)) {
- pr_err("failed to write dword to sp=%lx\n", regs->sp);
- return;
- }
-
- if (p->ainsn.boostable == 0) {
- if ((regs->EREG(ip) > copy_eip) && (regs->EREG(ip) - copy_eip) +
- 5 < MAX_INSN_SIZE) {
- /*
- * These instructions can be executed directly if it
- * jumps back to correct address.
- */
- set_user_jmp_op((void *) regs->EREG(ip),
- (void *)orig_eip +
- (regs->EREG(ip) - copy_eip));
- p->ainsn.boostable = 1;
- } else {
- p->ainsn.boostable = -1;
- }
- }
-
- regs->EREG(ip) = orig_eip + (regs->EREG(ip) - copy_eip);
-
-no_change:
- return;
-}
-
-static void prepare_tramp(struct uprobe *p, struct pt_regs *regs)
-{
- regs->ip = (unsigned long)p->insn;
-}
-
-static void prepare_ss(struct pt_regs *regs)
-{
- /* set single step mode */
- regs->flags |= TF_MASK;
- regs->flags &= ~IF_MASK;
-}
-
-
-static unsigned long resume_userspace_addr;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
-static void __rcu_nmi_enter(void) {}
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
-#error "This kernel is not support"
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) */
-static void (*__rcu_nmi_enter)(void);
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) */
-
-static void __used __up_handler(void)
-{
- struct pt_regs *regs = current_ctx()->ptr_regs;
- struct thread_info *tinfo = current_thread_info();
- struct uprobe *p = current_ucb()->p;
-
- /* restore KS regs */
- *regs = current_ctx()->save_regs;
-
- /* call handler */
- current_ctx()->handler(p, regs);
-
- /* resume_userspace */
- asm volatile (
- "movl %0, %%esp\n"
- "movl %1, %%ebp\n"
- "jmpl *%2\n"
- : /* No outputs. */
- : "r" (regs), "r" (tinfo) , "r" (resume_userspace_addr)
- );
-}
-
-void up_handler(void);
-__asm(
- "up_handler:\n"
- /* skip hex tractor-driver bytes to make some free space (skip regs) */
- "sub $0x300, %esp\n"
- "jmp __up_handler\n"
-);
-
-static int exceptions_handler(struct pt_regs *regs,
- int (*handler)(struct uprobe *, struct pt_regs *))
-{
- /* save regs */
- current_ctx()->save_regs = *regs;
- current_ctx()->ptr_regs = regs;
-
- /* set handler */
- current_ctx()->handler = handler;
-
- /* setup regs to return to KS */
- regs->ip = (unsigned long)up_handler;
- regs->ds = __USER_DS;
- regs->es = __USER_DS;
- regs->fs = __KERNEL_PERCPU;
- regs->cs = __KERNEL_CS | get_kernel_rpl();
- regs->gs = 0;
- regs->flags = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
-
- /*
- * Here rcu_nmi_enter() call is needed, because we change
- * US context to KS context as a result rcu_nmi_exit() will
- * be called on exiting exception and rcu_nmi_enter() and
- * rcu_nmi_exit() calls must be consistent
- */
- __rcu_nmi_enter();
-
- return 1;
-}
-
-static int uprobe_handler_retprobe(struct uprobe *p, struct pt_regs *regs)
-{
- int ret;
-
- ret = trampoline_uprobe_handler(p, regs);
- set_current_probe(NULL);
- put_up(p);
-
- return ret;
-}
-
-static int uprobe_handler_part2(struct uprobe *p, struct pt_regs *regs)
-{
- if (p->pre_handler && !p->pre_handler(p, regs)) {
- prepare_tramp(p, regs);
- if (p->ainsn.boostable == 1 && !p->post_handler)
- goto exit_and_put_up;
-
- save_current_flags(regs);
- set_current_probe(p);
- prepare_ss(regs);
-
- return 1;
- }
-
-exit_and_put_up:
- set_current_probe(NULL);
- put_up(p);
- return 1;
-}
-
-static int uprobe_handler_atomic(struct pt_regs *regs)
-{
- pid_t tgid = current->tgid;
- unsigned long vaddr = regs->ip - 1;
- struct uprobe *p = get_uprobe((void *)vaddr, tgid);
-
- if (p) {
- get_up(p);
- if (p->pre_handler) {
- set_current_probe(p);
- exceptions_handler(regs, uprobe_handler_part2);
- } else {
- uprobe_handler_part2(p, regs);
- }
- } else {
- unsigned long tramp_vaddr;
-
- tramp_vaddr = vaddr - UPROBES_TRAMP_RET_BREAK_IDX;
- p = get_uprobe_by_insn_slot((void *)tramp_vaddr, tgid, regs);
- if (p == NULL) {
- pr_info("no_uprobe\n");
- return 0;
- }
-
- set_current_probe(p);
- get_up(p);
- exceptions_handler(regs, uprobe_handler_retprobe);
- }
-
- return 1;
-}
-
-static int post_uprobe_handler(struct uprobe *p, struct pt_regs *regs)
-{
- unsigned long flags = current_ucb()->flags;
-
- resume_execution(p, regs, flags);
- restore_current_flags(regs, flags);
-
- /* reset current probe */
- set_current_probe(NULL);
- put_up(p);
-
- return 1;
-}
-
-static int post_uprobe_handler_atomic(struct pt_regs *regs)
-{
- struct uprobe *p = get_current_probe();
-
- if (p) {
- exceptions_handler(regs, post_uprobe_handler);
- } else {
- pr_info("task[%u %u %s] current uprobe is not found\n",
- current->tgid, current->pid, current->comm);
- }
-
- return !!p;
-}
-
-static int uprobe_exceptions_notify(struct notifier_block *self,
- unsigned long val, void *data)
-{
- struct die_args *args = (struct die_args *)data;
- int ret = NOTIFY_DONE;
-
- if (args->regs == NULL || !swap_user_mode(args->regs))
- return ret;
-
- switch (val) {
-#ifdef CONFIG_KPROBES
- case DIE_INT3:
-#else
- case DIE_TRAP:
-#endif
- if (uprobe_handler_atomic(args->regs))
- ret = NOTIFY_STOP;
- break;
- case DIE_DEBUG:
- if (post_uprobe_handler_atomic(args->regs))
- ret = NOTIFY_STOP;
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static struct notifier_block uprobe_exceptions_nb = {
- .notifier_call = uprobe_exceptions_notify,
- .priority = INT_MAX
-};
-
-struct up_valid_struct {
- struct uprobe *p;
- bool found;
-};
-
-static int __uprobe_is_valid(struct uprobe *p, void *data)
-{
- struct up_valid_struct *valid = (struct up_valid_struct *)data;
-
- if (valid->p == p) {
- valid->found = true;
- return 1;
- }
-
- return 0;
-}
-
-static bool uprobe_is_valid(struct uprobe *p)
-{
- struct up_valid_struct valid = {
- .p = p,
- .found = false,
- };
-
- for_each_uprobe(__uprobe_is_valid, (void *)&valid);
-
- return valid.found;
-}
-
-static int do_exit_handler(struct kprobe *kp, struct pt_regs *regs)
-{
- struct uprobe *p;
-
- p = get_current_probe();
- if (p && uprobe_is_valid(p)) {
- set_current_probe(NULL);
- put_up(p);
- }
-
- return 0;
-}
-
-static struct kprobe kp_do_exit = {
- .pre_handler = do_exit_handler
-};
-
-/**
- * @brief Registers notify.
- *
- * @return register_die_notifier result.
- */
-int swap_arch_init_uprobes(void)
-{
- int ret;
- const char *sym;
-
- sym = "resume_userspace";
- resume_userspace_addr = swap_ksyms(sym);
- if (resume_userspace_addr == 0)
- goto not_found;
-
- sym = "do_exit";
- kp_do_exit.addr = swap_ksyms(sym);
- if (kp_do_exit.addr == 0)
- goto not_found;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
- sym = "rcu_nmi_enter";
- __rcu_nmi_enter = (void *)swap_ksyms(sym);
- if (__rcu_nmi_enter == NULL)
- goto not_found;
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) */
-
- ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk));
- if (ret)
- return ret;
-
- ret = register_die_notifier(&uprobe_exceptions_nb);
- if (ret)
- goto unreg_td;
-
- ret = swap_register_kprobe(&kp_do_exit);
- if (ret)
- goto unreg_exeption;
-
- return 0;
-
-unreg_exeption:
- unregister_die_notifier(&uprobe_exceptions_nb);
-unreg_td:
- swap_td_raw_unreg(&td_raw);
- return ret;
-
-not_found:
- pr_err("symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
-/**
- * @brief Unregisters notify.
- *
- * @return Void.
- */
-void swap_arch_exit_uprobes(void)
-{
- swap_unregister_kprobe(&kp_do_exit);
- unregister_die_notifier(&uprobe_exceptions_nb);
- swap_td_raw_unreg(&td_raw);
-}
-
+++ /dev/null
-/**
- * @file uprobe/arch/asm-x86/swap_uprobes.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Arch-dependent uprobe interface declaration.
- */
-
-#ifndef _X86_SWAP_UPROBES_H
-#define _X86_SWAP_UPROBES_H
-
-
-#include <swap-asm/swap_kprobes.h> /* FIXME: for UPROBES_TRAMP_LEN */
-
-
-struct uprobe;
-struct uretprobe;
-struct uretprobe_instance;
-
-typedef u8 uprobe_opcode_t;
-
-/**
- * @struct arch_insn
- * @brief Architecture depend copy of original instruction.
- * @var arch_insn::insn
- * Copy of the original instruction.
- * @var arch_insn::boostable
- * If this flag is not 0, this kprobe can be boost when its
- * post_handler and break_handler is not set.
- */
-struct arch_insn {
- int boostable;
-};
-
-
-static inline u32 swap_get_urp_float(struct pt_regs *regs)
-{
- u32 st0;
-
- asm volatile ("fstps %0" : "=m" (st0));
-
- return st0;
-}
-
-static inline u64 swap_get_urp_double(struct pt_regs *regs)
-{
- u64 st1;
-
- asm volatile ("fstpl %0" : "=m" (st1));
-
- return st1;
-}
-
-static inline void arch_ujprobe_return(void)
-{
-}
-
-int arch_prepare_uprobe(struct uprobe *up);
-int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs);
-static inline int longjmp_break_uhandler(struct uprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-
-static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp)
-{
- return 0;
-}
-
-int arch_urp_check_opcode(uprobe_opcode_t *opcode);
-int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
-int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task);
-unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
-void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
-void arch_remove_uprobe(struct uprobe *up);
-int arch_arm_uprobe(struct uprobe *p);
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
-
-static inline unsigned long swap_get_upc(struct pt_regs *regs)
-{
- return regs->ip;
-}
-
-static inline void swap_set_upc(struct pt_regs *regs, unsigned long val)
-{
- regs->ip = val;
-}
-
-static inline unsigned long swap_get_ustack_val(struct pt_regs *regs,
- unsigned long n)
-{
- u32 *ptr, addr = 0;
-
- ptr = (u32 *)regs->sp + n;
- if (get_user(addr, ptr))
- pr_err("Failed to dereference a pointer, ptr=%p\n", ptr);
-
- return addr;
-}
-
-static inline void swap_set_ustack_val(struct pt_regs *regs, unsigned long n,
- unsigned long val)
-{
- u32 *ptr;
-
- ptr = (u32 *)regs->sp + n;
- if (put_user(val, ptr))
- pr_err("Failed to dereference a pointer, ptr=%p\n", ptr);
-}
-
-static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
-{
- /* 1 - return address saved on top of the stack */
- return swap_get_ustack_val(regs, n + 1);
-}
-
-static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
- unsigned long val)
-{
- /* 1 - return address saved on top of the stack */
- swap_set_ustack_val(regs, n + 1, val);
-}
-
-static inline unsigned long swap_get_uret_addr(struct pt_regs *regs)
-{
- return swap_get_ustack_val(regs, 0);
-}
-
-static inline void swap_set_uret_addr(struct pt_regs *regs, unsigned long val)
-{
- swap_set_ustack_val(regs, 0, val);
-}
-
-
-int swap_arch_init_uprobes(void);
-void swap_arch_exit_uprobes(void);
-
-#endif /* _X86_SWAP_UPROBES_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/uaccess.h>
-
-
-static const char *strdup_from_user(const char __user *user_s, gfp_t gfp)
-{
- enum { max_str_len = 1024 };
- char *str;
- int len_s, ret;
-
- len_s = strnlen_user(user_s, max_str_len - 1);
- str = kmalloc(len_s + 1, gfp);
- if (str == NULL)
- return NULL;
-
- ret = copy_from_user(str, user_s, len_s);
- if (ret < 0) {
- kfree(str);
- return NULL;
- }
-
- str[len_s] = '\0';
-
- return str;
-}
+++ /dev/null
-/**
- * uprobe/swap_uprobes.c
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Uprobes implementation.
- */
-
-
-#include <linux/hash.h>
-#include <linux/sched.h>
-#include <linux/mempolicy.h>
-#include <linux/module.h>
-
-#include <master/swap_initializer.h>
-#include <kprobe/swap_slots.h>
-#include <kprobe/swap_kdebug.h>
-#include <kprobe/swap_kprobes_deps.h>
-
-#include <swap-asm/swap_uprobes.h>
-
-#include "swap_uprobes.h"
-
-
-enum {
- UPROBE_HASH_BITS = 10,
- UPROBE_TABLE_SIZE = (1 << UPROBE_HASH_BITS)
-};
-
-static DEFINE_RWLOCK(st_lock);
-static struct hlist_head slot_table[UPROBE_TABLE_SIZE];
-static DEFINE_MUTEX(up_mtx); /* Protects uprobe_table */
-struct hlist_head uprobe_table[UPROBE_TABLE_SIZE];
-
-static DEFINE_MUTEX(urp_mtx); /* Protects uretprobe_inst_table */
-static struct hlist_head uretprobe_inst_table[UPROBE_TABLE_SIZE];
-
-#define DEBUG_PRINT_HASH_TABLE 0
-
-#if DEBUG_PRINT_HASH_TABLE
-void print_uprobe_hash_table(void)
-{
- int i;
- struct hlist_head *head;
- struct uprobe *p;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- /* print uprobe table */
- for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
- head = &uprobe_insn_slot_table[i];
- swap_hlist_for_each_entry_rcu(p, node, head, is_hlist) {
- printk(KERN_INFO "####### find U tgid=%u, addr=0x%lx\n",
- p->task->tgid, (unsigned long)p->addr);
- }
- }
-}
-#endif
-
-/*
- * Keep all fields in the uprobe consistent
- */
-static inline void copy_uprobe(struct uprobe *old_p, struct uprobe *p)
-{
- memcpy(&p->opcode, &old_p->opcode, sizeof(uprobe_opcode_t));
- memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_insn));
- memcpy(&p->insn, &old_p->insn, sizeof(uprobe_opcode_t *));
-}
-
-/*
- * Aggregate handlers for multiple uprobes support - these handlers
- * take care of invoking the individual uprobe handlers on p->list
- */
-static int aggr_pre_uhandler(struct uprobe *p, struct pt_regs *regs)
-{
- struct uprobe *up;
- int ret;
-
- list_for_each_entry_rcu(up, &p->list, list) {
- if (up->pre_handler) {
- ret = up->pre_handler(up, regs);
- if (ret)
- return ret;
- }
- }
-
- return 0;
-}
-
-static void aggr_post_uhandler(struct uprobe *p, struct pt_regs *regs,
- unsigned long flags)
-{
- struct uprobe *up;
-
- list_for_each_entry_rcu(up, &p->list, list) {
- if (up->post_handler)
- up->post_handler(up, regs, flags);
- }
-}
-
-static int aggr_fault_uhandler(struct uprobe *p,
- struct pt_regs *regs,
- int trapnr)
-{
- return 0;
-}
-
-static int aggr_break_uhandler(struct uprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-
-/*
- * Add the new probe to old_p->list. Fail if this is the
- * second ujprobe at the address - two ujprobes can't coexist
- */
-static int add_new_uprobe(struct uprobe *old_p, struct uprobe *p)
-{
- if (p->break_handler) {
- if (old_p->break_handler)
- return -EEXIST;
-
- list_add_tail_rcu(&p->list, &old_p->list);
- old_p->break_handler = aggr_break_uhandler;
- } else {
- list_add_rcu(&p->list, &old_p->list);
- }
-
- if (p->post_handler && !old_p->post_handler)
- old_p->post_handler = aggr_post_uhandler;
-
- return 0;
-}
-
-/*
- * Fill in the required fields of the "manager uprobe". Replace the
- * earlier uprobe in the hlist with the manager uprobe
- */
-static inline void add_aggr_uprobe(struct uprobe *ap, struct uprobe *p)
-{
- copy_uprobe(p, ap);
-
- ap->addr = p->addr;
- ap->pre_handler = aggr_pre_uhandler;
- ap->fault_handler = aggr_fault_uhandler;
-
- if (p->post_handler)
- ap->post_handler = aggr_post_uhandler;
-
- if (p->break_handler)
- ap->break_handler = aggr_break_uhandler;
-
- INIT_LIST_HEAD(&ap->list);
- list_add_rcu(&p->list, &ap->list);
-
- hlist_replace_rcu(&p->hlist, &ap->hlist);
-}
-
-/*
- * This is the second or subsequent uprobe at the address - handle
- * the intricacies
- */
-static int register_aggr_uprobe(struct uprobe *old_p, struct uprobe *p)
-{
- int ret = 0;
-
- if (old_p->pre_handler == aggr_pre_uhandler) {
- copy_uprobe(old_p, p);
- ret = add_new_uprobe(old_p, p);
- } else {
- struct uprobe *uap = kzalloc(sizeof(*uap), GFP_KERNEL);
- if (!uap)
- return -ENOMEM;
-
- uap->task = p->task;
- add_aggr_uprobe(uap, old_p);
- copy_uprobe(uap, p);
- ret = add_new_uprobe(uap, p);
- }
-
- return ret;
-}
-
-static int arm_uprobe(struct uprobe *p)
-{
- return arch_arm_uprobe(p);
-}
-
-/**
- * @brief Disarms uprobe.
- *
- * @param p Pointer to the uprobe.
- * @param task Pointer to the target task.
- * @return Void.
- */
-void disarm_uprobe(struct uprobe *p, struct task_struct *task)
-{
- arch_disarm_uprobe(p, task);
-}
-EXPORT_SYMBOL_GPL(disarm_uprobe);
-
-static void init_uprobes_insn_slots(void)
-{
- int i;
- for (i = 0; i < UPROBE_TABLE_SIZE; ++i)
- INIT_HLIST_HEAD(&slot_table[i]);
-}
-
-static void init_uprobe_table(void)
-{
- int i;
- for (i = 0; i < UPROBE_TABLE_SIZE; ++i)
- INIT_HLIST_HEAD(&uprobe_table[i]);
-}
-
-static void init_uretprobe_inst_table(void)
-{
- int i;
- for (i = 0; i < UPROBE_TABLE_SIZE; ++i)
- INIT_HLIST_HEAD(&uretprobe_inst_table[i]);
-}
-
-/**
- * @brief Gets uprobe.
- *
- * @param addr Probe's address.
- * @param tgid Probes's thread group ID.
- * @return Pointer to the uprobe on success,\n
- * NULL otherwise.
- */
-struct uprobe *get_uprobe(void *addr, pid_t tgid)
-{
- struct hlist_head *head;
- struct uprobe *p;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- head = &uprobe_table[hash_ptr(addr, UPROBE_HASH_BITS)];
- swap_hlist_for_each_entry_rcu(p, node, head, hlist) {
- if (p->addr == addr && p->task->tgid == tgid)
- return p;
- }
-
- return NULL;
-}
-
-/**
- * @brief Adds uprobe to hlist when trampoline have been made.
- *
- * @param p Pointer to the uprobe.
- * @return Void.
- */
-void add_uprobe_table(struct uprobe *p)
-{
- write_lock(&st_lock);
- hlist_add_head(&p->is_hlist,
- &slot_table[hash_ptr(p->insn, UPROBE_HASH_BITS)]);
- write_unlock(&st_lock);
-}
-
-static void del_uprobe_table(struct uprobe *p)
-{
- write_lock(&st_lock);
- if (!hlist_unhashed(&p->is_hlist))
- hlist_del(&p->is_hlist);
- write_unlock(&st_lock);
-}
-
-/**
- * @brief Gets uprobe by insn slot.
- *
- * @param addr Probe's address.
- * @param tgit Probe's thread group ID.
- * @param regs Pointer to CPU registers data.
- * @return Pointer to the uprobe on success,\n
- * NULL otherwise.
- */
-struct uprobe *get_uprobe_by_insn_slot(void *addr,
- pid_t tgid,
- struct pt_regs *regs)
-{
- struct hlist_head *head;
- struct uprobe *p;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- read_lock(&st_lock);
- head = &slot_table[hash_ptr(addr, UPROBE_HASH_BITS)];
- swap_hlist_for_each_entry(p, node, head, is_hlist) {
- if (p->insn == addr && p->task->tgid == tgid) {
- read_unlock(&st_lock);
- return p;
- }
- }
- read_unlock(&st_lock);
-
- return NULL;
-}
-
-
-static void remove_uprobe(struct uprobe *up)
-{
- del_uprobe_table(up);
- arch_remove_uprobe(up);
-}
-
-static struct hlist_head *uretprobe_inst_table_head(void *hash_key)
-{
- return &uretprobe_inst_table[hash_ptr(hash_key, UPROBE_HASH_BITS)];
-}
-
-/* Called with urp_mtx held */
-static void add_urp_inst(struct uretprobe_instance *ri)
-{
- /*
- * Remove rp inst off the free list -
- * Add it back when probed function returns
- */
- hlist_del(&ri->uflist);
-
- /* Add rp inst onto table */
- INIT_HLIST_NODE(&ri->hlist);
- hlist_add_head(&ri->hlist, uretprobe_inst_table_head(ri->task->mm));
-
- /* Also add this rp inst to the used list. */
- INIT_HLIST_NODE(&ri->uflist);
- hlist_add_head(&ri->uflist, &ri->rp->used_instances);
-}
-
-/* Called with urp_mtx held */
-static void recycle_urp_inst(struct uretprobe_instance *ri)
-{
- if (ri->rp) {
- hlist_del(&ri->hlist);
- /* remove rp inst off the used list */
- hlist_del(&ri->uflist);
- /* put rp inst back onto the free list */
- INIT_HLIST_NODE(&ri->uflist);
- hlist_add_head(&ri->uflist, &ri->rp->free_instances);
- }
-}
-
-/* Called with urp_mtx held */
-static struct uretprobe_instance *get_used_urp_inst(struct uretprobe *rp)
-{
- struct uretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->used_instances, uflist) {
- return ri;
- }
-
- return NULL;
-}
-
-/**
- * @brief Gets free uretprobe instanse for the specified uretprobe without
- * allocation. Called with urp_mtx held.
- *
- * @param rp Pointer to the uretprobe.
- * @return Pointer to the uretprobe_instance on success,\n
- * NULL otherwise.
- */
-struct uretprobe_instance *get_free_urp_inst_no_alloc(struct uretprobe *rp)
-{
- struct uretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
- return ri;
- }
-
- return NULL;
-}
-
-/* Called with urp_mtx held */
-static void free_urp_inst(struct uretprobe *rp)
-{
- struct uretprobe_instance *ri;
- while ((ri = get_free_urp_inst_no_alloc(rp)) != NULL) {
- hlist_del(&ri->uflist);
- kfree(ri);
- }
-}
-
-#define COMMON_URP_NR 10
-
-static int alloc_nodes_uretprobe(struct uretprobe *rp)
-{
- int alloc_nodes;
- struct uretprobe_instance *inst;
- int i;
-
-#if 1 /* def CONFIG_PREEMPT */
- rp->maxactive += max(COMMON_URP_NR, 2 * NR_CPUS);
-#else
- rp->maxacpptive += NR_CPUS;
-#endif
- alloc_nodes = COMMON_URP_NR;
-
- for (i = 0; i < alloc_nodes; ++i) {
- inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_ATOMIC);
- if (inst == NULL) {
- free_urp_inst(rp);
- return -ENOMEM;
- }
- INIT_HLIST_NODE(&inst->uflist);
- hlist_add_head(&inst->uflist, &rp->free_instances);
- }
-
- return 0;
-}
-
-/* Called with urp_mtx held */
-static struct uretprobe_instance *get_free_urp_inst(struct uretprobe *rp)
-{
- struct uretprobe_instance *ri;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(ri, node, &rp->free_instances, uflist) {
- return ri;
- }
-
- if (!alloc_nodes_uretprobe(rp)) {
- swap_hlist_for_each_entry(ri, node,
- &rp->free_instances, uflist) {
- return ri;
- }
- }
-
- return NULL;
-}
-/* =================================================================== */
-
-
-void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data)
-{
- int i;
- struct uprobe *p;
- struct hlist_head *head;
- struct hlist_node *tnode;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
- head = &uprobe_table[i];
- swap_hlist_for_each_entry_safe(p, node, tnode, head, hlist) {
- if (func(p, data))
- return;
- }
- }
-}
-
-static int wait_up_action(atomic_t *val)
-{
- BUG_ON(atomic_read(val));
- schedule();
- return 0;
-}
-
-static void wait_up(struct uprobe *p)
-{
- wait_on_atomic_t(&p->usage, wait_up_action, TASK_UNINTERRUPTIBLE);
-}
-
-/**
- * @brief Registers uprobe.
- *
- * @param up Pointer to the uprobe to register.
- * @return 0 on success,\n
- * negative error code on error.
- */
-int swap_register_uprobe(struct uprobe *p)
-{
- int ret = 0;
- struct uprobe *old_p;
-
- if (!p->addr)
- return -EINVAL;
-
- p->insn = NULL;
- INIT_LIST_HEAD(&p->list);
- atomic_set(&p->usage, 1);
-
- /* get the first item */
- old_p = get_uprobe(p->addr, p->task->tgid);
- if (old_p) {
- ret = register_aggr_uprobe(old_p, p);
- goto out;
- }
-
- INIT_HLIST_NODE(&p->is_hlist);
-
- ret = arch_prepare_uprobe(p);
- if (ret) {
- DBPRINTF("goto out\n", ret);
- goto out;
- }
-
- DBPRINTF("before out ret = 0x%x\n", ret);
-
- /* TODO: add uprobe (must be in function) */
- INIT_HLIST_NODE(&p->hlist);
- hlist_add_head_rcu(&p->hlist,
- &uprobe_table[hash_ptr(p->addr, UPROBE_HASH_BITS)]);
-
- ret = arm_uprobe(p);
- if (ret) {
- hlist_del_rcu(&p->hlist);
- synchronize_rcu();
- remove_uprobe(p);
- }
-
-out:
- DBPRINTF("out ret = 0x%x\n", ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_register_uprobe);
-
-/**
- * @brief Unregisters uprobe.
- *
- * @param up Pointer to the uprobe.
- * @param disarm Disarm flag. When true uprobe is disarmed.
- * @return Void.
- */
-void __swap_unregister_uprobe(struct uprobe *p, int disarm)
-{
- struct uprobe *old_p, *list_p;
- int cleanup_p;
-
- /* we MUST check probe for uncreated process */
- if (!p->task)
- return;
-
- mutex_lock(&up_mtx);
- rcu_read_lock();
- old_p = get_uprobe(p->addr, p->task->tgid);
- rcu_read_unlock();
- if (unlikely(!old_p))
- goto out_unlock;
-
- if (p != old_p) {
- rcu_read_lock();
- list_for_each_entry_rcu(list_p, &old_p->list, list) {
- if (list_p == p) {
- /* uprobe p is a valid probe */
- rcu_read_unlock();
- goto valid_p;
- }
- }
- rcu_read_unlock();
- goto out_unlock;
- }
-
-valid_p:
- if ((old_p == p) || ((old_p->pre_handler == aggr_pre_uhandler) &&
- (p->list.next == &old_p->list) && (p->list.prev == &old_p->list))) {
- /* Only probe on the hash list */
- if (disarm)
- disarm_uprobe(p, p->task);
-
- hlist_del_rcu(&old_p->hlist);
- cleanup_p = 1;
- } else {
- list_del_rcu(&p->list);
- cleanup_p = 0;
- }
-
- if (cleanup_p) {
- if (p != old_p) {
- list_del_rcu(&p->list);
- kfree(old_p);
- }
-
- if (!in_atomic()) {
- synchronize_sched();
-
- atomic_dec(&p->usage);
- wait_up(p);
- }
-
- remove_uprobe(p);
- } else {
- if (p->break_handler)
- old_p->break_handler = NULL;
-
- if (p->post_handler) {
- rcu_read_lock();
- list_for_each_entry_rcu(list_p, &old_p->list, list) {
- if (list_p->post_handler) {
- cleanup_p = 2;
- break;
- }
- }
- rcu_read_unlock();
-
- if (cleanup_p == 0)
- old_p->post_handler = NULL;
- }
- }
-
-out_unlock:
- mutex_unlock(&up_mtx);
-}
-EXPORT_SYMBOL_GPL(__swap_unregister_uprobe);
-
-/**
- * @brief Unregisters uprobe. Main interface function, wrapper for
- * __swap_unregister_uprobe.
- *
- * @param up Pointer to the uprobe.
- * @return Void.
- */
-void swap_unregister_uprobe(struct uprobe *up)
-{
- __swap_unregister_uprobe(up, 1);
-}
-
-/**
- * @brief Registers ujprobe.
- *
- * @param uj Pointer to the ujprobe function.
- * @return 0 on success,\n
- * error code on error.
- */
-int swap_register_ujprobe(struct ujprobe *jp)
-{
- int ret = 0;
-
- /* Todo: Verify probepoint is a function entry point */
- jp->up.pre_handler = setjmp_upre_handler;
- jp->up.break_handler = longjmp_break_uhandler;
-
- ret = swap_register_uprobe(&jp->up);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_register_ujprobe);
-
-/**
- * @brief Unregisters ujprobe.
- *
- * @param jp Pointer to the ujprobe.
- * @param disarm Disarm flag, passed to __swap_unregister_uprobe.
- * @return Void.
- */
-void __swap_unregister_ujprobe(struct ujprobe *jp, int disarm)
-{
- __swap_unregister_uprobe(&jp->up, disarm);
-}
-EXPORT_SYMBOL_GPL(__swap_unregister_ujprobe);
-
-/**
- * @brief Unregisters ujprobe. Main interface function, wrapper for
- * __swap_unregister_ujprobe.
- *
- * @param jp Pointer to the jprobe.
- * @return Void.
- */
-void swap_unregister_ujprobe(struct ujprobe *jp)
-{
- __swap_unregister_ujprobe(jp, 1);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_ujprobe);
-
-/**
- * @brief Trampoline uprobe handler.
- *
- * @param p Pointer to the uprobe.
- * @param regs Pointer to CPU register data.
- * @return 1
- */
-int trampoline_uprobe_handler(struct uprobe *p, struct pt_regs *regs)
-{
- struct uretprobe_instance *ri = NULL;
- struct uprobe *up;
- struct hlist_head *head = uretprobe_inst_table_head(current->mm);
- unsigned long tramp_addr = arch_get_trampoline_addr(p, regs);
- unsigned long orig_ret_addr = 0;
- struct hlist_node *tmp;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- /*
- * It is possible to have multiple instances associated with a given
- * task either because an multiple functions in the call path
- * have a return probe installed on them, and/or more then one
- * return probe was registered for a target function.
- *
- * We can handle this because:
- * - instances are always inserted at the head of the list
- * - when multiple return probes are registered for the same
- * function, the first instance's ret_addr will point to the
- * real return address, and all the rest will point to
- * uretprobe_trampoline
- */
- mutex_lock(&urp_mtx);
- swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
- if (ri->task != current) {
- /* another task is sharing our hash bucket */
- continue;
- }
-
- up = NULL;
- if (ri->rp) {
- up = &ri->rp->up;
-
- if (ri->rp->handler)
- ri->rp->handler(ri, regs);
- }
-
- orig_ret_addr = (unsigned long)ri->ret_addr;
- recycle_urp_inst(ri);
-
- if (orig_ret_addr != tramp_addr || up == NULL) {
- /*
- * This is the real return address. Any other
- * instances associated with this task are for
- * other calls deeper on the call stack
- */
- break;
- }
- }
- mutex_unlock(&urp_mtx);
-
- /* orig_ret_addr is NULL when there is no need to restore anything
- * (all the magic is performed inside handler) */
- if (likely(orig_ret_addr))
- arch_set_orig_ret_addr(orig_ret_addr, regs);
-
- return 1;
-}
-
-static int pre_handler_uretprobe(struct uprobe *p, struct pt_regs *regs)
-{
- struct uretprobe *rp = container_of(p, struct uretprobe, up);
- struct uretprobe_instance *ri;
- int ret = 0;
-
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
-# if defined(CONFIG_ARM64)
-# define thumb_mode(regs) compat_thumb_mode(regs)
-# endif /* defined(CONFIG_ARM64) */
- int noret = thumb_mode(regs) ? rp->thumb_noret : rp->arm_noret;
-
- if (noret)
- return 0;
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-
- /* TODO: consider to only swap the
- * RA after the last pre_handler fired */
-
- /* TODO: test - remove retprobe after func entry but before its exit */
- mutex_lock(&urp_mtx);
- ri = get_free_urp_inst(rp);
- if (ri != NULL) {
- int err;
-
- ri->rp = rp;
- ri->task = current;
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- ri->preload.use = false;
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
- if (rp->entry_handler)
- ret = rp->entry_handler(ri, regs);
-
- add_urp_inst(ri);
-
- err = arch_prepare_uretprobe(ri, regs);
- if (err) {
- recycle_urp_inst(ri);
- ++rp->nmissed;
- }
- } else {
- ++rp->nmissed;
- }
- mutex_unlock(&urp_mtx);
-
- return ret;
-}
-
-/**
- * @brief Registers uretprobe.
- *
- * @param rp Pointer to the uretprobe.
- * @return 0 on success,\n
- * negative error code on error.
- */
-int swap_register_uretprobe(struct uretprobe *rp)
-{
- int i, ret = 0;
- struct uretprobe_instance *inst;
-
- DBPRINTF("START\n");
-
- rp->up.pre_handler = pre_handler_uretprobe;
- rp->up.post_handler = NULL;
- rp->up.fault_handler = NULL;
- rp->up.break_handler = NULL;
- /* Set callback to check for unsupported insns */
- rp->up.check_opcode_cb = arch_urp_check_opcode;
-
- /* Pre-allocate memory for max kretprobe instances */
- if (rp->maxactive <= 0) {
-#if 1 /* def CONFIG_PREEMPT */
- rp->maxactive = max(10, 2 * NR_CPUS);
-#else
- rp->maxactive = NR_CPUS;
-#endif
- }
-
- INIT_HLIST_HEAD(&rp->used_instances);
- INIT_HLIST_HEAD(&rp->free_instances);
-
- for (i = 0; i < rp->maxactive; i++) {
- inst = kmalloc(sizeof(*inst) + rp->data_size, GFP_KERNEL);
- if (inst == NULL) {
- ret = -ENOMEM;
- goto register_err;
- }
-
- INIT_HLIST_NODE(&inst->uflist);
- hlist_add_head(&inst->uflist, &rp->free_instances);
- }
-
- rp->nmissed = 0;
-
- /* Establish function entry probe point */
- ret = swap_register_uprobe(&rp->up);
- if (ret)
- goto register_err;
-
- arch_opcode_analysis_uretprobe(rp);
-
- return 0;
-
-register_err:
- free_urp_inst(rp);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(swap_register_uretprobe);
-
-/**
- * @brief Unregisters uretprobe.
- *
- * @param rp Pointer to the ureprobe.
- * @param disarm Disarm flag, passed to __swap_unregister_uprobe
- * @return Void.
- */
-void __swap_unregister_uretprobe(struct uretprobe *rp, int disarm)
-{
- struct uretprobe_instance *ri;
-
- __swap_unregister_uprobe(&rp->up, disarm);
-
- mutex_lock(&urp_mtx);
- while ((ri = get_used_urp_inst(rp)) != NULL) {
- /* FIXME: arch_disarm_urp_inst() for no current context */
- if (arch_disarm_urp_inst(ri, ri->task) != 0)
- printk(KERN_INFO "%s (%d/%d): "
- "cannot disarm urp instance (%08lx)\n",
- ri->task->comm, ri->task->tgid, ri->task->pid,
- (unsigned long)rp->up.addr);
-
- recycle_urp_inst(ri);
- }
-
- while ((ri = get_used_urp_inst(rp)) != NULL) {
- ri->rp = NULL;
- hlist_del(&ri->uflist);
- }
- mutex_unlock(&urp_mtx);
-
- free_urp_inst(rp);
-}
-EXPORT_SYMBOL_GPL(__swap_unregister_uretprobe);
-
-/**
- * @brief Unregistets uretprobe. Main interface function, wrapper for
- * __swap_unregister_uretprobe.
- *
- * @param rp Pointer to the uretprobe.
- * @return Void.
- */
-void swap_unregister_uretprobe(struct uretprobe *rp)
-{
- __swap_unregister_uretprobe(rp, 1);
-}
-EXPORT_SYMBOL_GPL(swap_unregister_uretprobe);
-
-/**
- * @brief Unregisters all uprobes for task's thread group ID.
- *
- * @param task Pointer to the task_struct
- * @return Void.
- */
-void swap_unregister_all_uprobes(struct task_struct *task)
-{
- struct hlist_head *head;
- struct uprobe *p;
- int i;
- struct hlist_node *tnode;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- for (i = 0; i < UPROBE_TABLE_SIZE; ++i) {
- head = &uprobe_table[i];
- swap_hlist_for_each_entry_safe(p, node, tnode, head, hlist) {
- if (p->task->tgid == task->tgid) {
- printk(KERN_INFO "%s: delete uprobe at %p[%lx]"
- " for %s/%d\n", __func__, p->addr,
- (unsigned long)p->opcode,
- task->comm, task->pid);
- swap_unregister_uprobe(p);
- }
- }
- }
-}
-EXPORT_SYMBOL_GPL(swap_unregister_all_uprobes);
-
-/**
- * @brief Arch-independent wrapper for arch_ujprobe_return.
- *
- * @return Void.
- */
-void swap_ujprobe_return(void)
-{
- arch_ujprobe_return();
-}
-EXPORT_SYMBOL_GPL(swap_ujprobe_return);
-
-void swap_uretprobe_free_task(struct task_struct *armed,
- struct task_struct *will_disarm, bool recycle)
-{
- struct uretprobe_instance *ri;
- struct hlist_head *hhead = uretprobe_inst_table_head(armed->mm);
- struct hlist_node *n;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- mutex_lock(&urp_mtx);
- swap_hlist_for_each_entry_safe(ri, node, n, hhead, hlist) {
- if (armed != ri->task)
- continue;
-
- if (will_disarm)
- arch_disarm_urp_inst(ri, will_disarm);
-
- if (recycle)
- recycle_urp_inst(ri);
- }
- mutex_unlock(&urp_mtx);
-}
-EXPORT_SYMBOL_GPL(swap_uretprobe_free_task);
-
-
-static int once(void)
-{
- init_uprobe_table();
- init_uprobes_insn_slots();
- init_uretprobe_inst_table();
-
- return 0;
-}
-
-SWAP_LIGHT_INIT_MODULE(once, swap_arch_init_uprobes, swap_arch_exit_uprobes,
- NULL, NULL);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/**
- * @file uprobe/swap_uprobes.h
- * @author Alexey Gerenkov <a.gerenkov@samsung.com> User-Space Probes initial
- * implementation; Support x86/ARM/MIPS for both user and kernel spaces.
- * @author Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for
- * separating core and arch parts
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2006-2010
- *
- * @section DESCRIPTION
- *
- * Uprobes interface declaration.
- */
-
-#ifndef _SWAP_UPROBES_H
-#define _SWAP_UPROBES_H
-
-
-#include <master/wait.h>
-#include <swap-asm/swap_uprobes.h>
-
-
-#define URETPROBE_STACK_DEPTH 64
-
-
-/**
- * @brief Uprobe pre-handler pointer.
- */
-typedef int (*uprobe_pre_handler_t) (struct uprobe *, struct pt_regs *);
-
-/**
- * @brief Uprobe break handler pointer.
- */
-typedef int (*uprobe_break_handler_t) (struct uprobe *, struct pt_regs *);
-
-/**
- * @brief Uprobe post handler pointer.
- */
-typedef void (*uprobe_post_handler_t) (struct uprobe *,
- struct pt_regs *,
- unsigned long flags);
-
-/**
- * @brief Uprobe fault handler pointer.
- */
-typedef int (*uprobe_fault_handler_t) (struct uprobe *,
- struct pt_regs *,
- int trapnr);
-
-/**
- * @struct uprobe
- * @brief Stores uprobe data.
- */
-struct uprobe {
- struct hlist_node hlist; /**< Hash list.*/
- /** List of probes to search by instruction slot.*/
- struct hlist_node is_hlist;
- /** List of uprobes for multi-handler support.*/
- struct list_head list;
- /** Location of the probe point. */
- uprobe_opcode_t *addr;
- /** Called before addr is executed.*/
- uprobe_pre_handler_t pre_handler;
- /** Called after addr is executed, unless...*/
- uprobe_post_handler_t post_handler;
- /** ... called if executing addr causes a fault (eg. page fault).*/
- uprobe_fault_handler_t fault_handler;
- /** Return 1 if it handled fault, otherwise kernel will see it.*/
- uprobe_break_handler_t break_handler;
- /** Saved opcode (which has been replaced with breakpoint).*/
- uprobe_opcode_t opcode;
- atomic_t usage;
-#ifdef CONFIG_ARM
- /** Safe/unsafe to use probe on ARM.*/
- unsigned safe_arm:1;
- /** Safe/unsafe to use probe on Thumb.*/
- unsigned safe_thumb:1;
-#endif
- uprobe_opcode_t __user *insn;
- struct arch_insn ainsn; /**< Copy of the original instruction.*/
- struct task_struct *task; /**< Pointer to the task struct */
- struct slot_manager *sm; /**< Pointer to slot manager */
- int (*check_opcode_cb)(uprobe_opcode_t *opcode); /**< Callback check opcode */
-};
-
-
-void swap_uretprobe_free_task(struct task_struct *task,
- struct task_struct *dtask, bool recycle);
-
-
-/**
- * @brief Uprobe pre-entry handler.
- */
-typedef unsigned long (*uprobe_pre_entry_handler_t)(void *priv_arg,
- struct pt_regs *regs);
-
-/**
- * @struct ujprobe
- * @brief Stores ujprobe data, based on uprobe.
- */
-struct ujprobe {
- struct uprobe up; /**< Uprobe for this ujprobe */
- void *entry; /**< Probe handling code to jump to */
- /** Handler which will be called before 'entry' */
- uprobe_pre_entry_handler_t pre_entry;
- void *priv_arg; /**< Private args for handler */
- char *args; /**< Function args format string */
-};
-
-struct uretprobe_instance;
-
-/**
- * @brief Uretprobe handler.
- */
-typedef int (*uretprobe_handler_t)(struct uretprobe_instance *,
- struct pt_regs *);
-
-/**
- * @strict uretprobe
- * @brief Function-return probe.
- *
- * Note:
- * User needs to provide a handler function, and initialize maxactive.
- */
-struct uretprobe {
- struct uprobe up; /**< Uprobe for this uretprobe */
- uretprobe_handler_t handler; /**< Uretprobe handler */
- uretprobe_handler_t entry_handler; /**< Uretprobe entry handler */
- /** Maximum number of instances of the probed function that can be
- * active concurrently. */
- int maxactive;
- /** Tracks the number of times the probed function's return was
- * ignored, due to maxactive being too low. */
- int nmissed;
- size_t data_size; /**< Instance data size */
- struct hlist_head free_instances; /**< Free instances list */
- struct hlist_head used_instances; /**< Used instances list */
-
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- unsigned arm_noret:1; /**< No-return flag for ARM */
- unsigned thumb_noret:1; /**< No-return flag for Thumb */
-#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
-};
-
-/**
- * @struct uretprobe_instance
- * @brief Structure for each uretprobe instance.
- */
-struct uretprobe_instance {
- /* either on free list or used list */
- struct hlist_node uflist; /**< Free list */
- struct hlist_node hlist; /**< Used list */
- struct uretprobe *rp; /**< Pointer to the parent uretprobe */
- uprobe_opcode_t *ret_addr; /**< Return address */
- uprobe_opcode_t *sp; /**< Pointer to stack */
- struct task_struct *task; /**< Pointer to the task struct */
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- /* FIXME Preload: if this flag is set then ignore the thumb_mode(regs)
- * check in arch_prepare_uretprobe and use thumb trampoline. For the
- * moment we have to explicitly force arm mode when jumping to preload
- * handlers but we need the correct (i.e. original) retprobe tramp set
- * anyway. */
- struct {
- unsigned use:1;
- unsigned thumb:1;
- } preload;
-#endif
- char data[0]; /**< Custom data */
-};
-
-
-static void inline get_up(struct uprobe *p)
-{
- atomic_inc(&p->usage);
-}
-
-static void inline put_up(struct uprobe *p)
-{
- if (atomic_dec_and_test(&p->usage))
- wake_up_atomic_t(&p->usage);
-}
-
-void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data);
-int swap_register_uprobe(struct uprobe *p);
-void swap_unregister_uprobe(struct uprobe *p);
-void __swap_unregister_uprobe(struct uprobe *up, int disarm);
-
-int swap_register_ujprobe(struct ujprobe *jp);
-void swap_unregister_ujprobe(struct ujprobe *jp);
-void __swap_unregister_ujprobe(struct ujprobe *jp, int disarm);
-
-int swap_register_uretprobe(struct uretprobe *rp);
-void swap_unregister_uretprobe(struct uretprobe *rp);
-void __swap_unregister_uretprobe(struct uretprobe *rp, int disarm);
-
-void swap_unregister_all_uprobes(struct task_struct *task);
-
-void swap_ujprobe_return(void);
-struct uprobe *get_uprobe(void *addr, pid_t tgid);
-struct uprobe *get_uprobe_by_insn_slot(void *addr,
- pid_t tgid,
- struct pt_regs *regs);
-
-void disarm_uprobe(struct uprobe *p, struct task_struct *task);
-
-int trampoline_uprobe_handler(struct uprobe *p, struct pt_regs *regs);
-
-void add_uprobe_table(struct uprobe *p);
-
-#endif /* _SWAP_UPROBES_H */
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../writer/Module.symvers \
- $(src)/../kprobe/Module.symvers \
- $(src)/../uprobe/Module.symvers
-
-obj-m := swap_us_manager.o
-swap_us_manager-y := \
- helper.o \
- us_manager.o \
- us_slot_manager.o \
- debugfs_us_manager.o \
- sspt/sspt_ip.o \
- sspt/sspt_page.o \
- sspt/sspt_file.o \
- sspt/sspt_proc.o \
- sspt/sspt_feature.o \
- sspt/sspt_filter.o \
- pf/pf_group.o \
- pf/proc_filters.o \
- img/img_ip.o \
- img/img_file.o \
- img/img_proc.o \
- probes/probes.o \
- probes/probe_info_new.o \
- callbacks.o \
- usm_hook.o
+++ /dev/null
-#include <linux/mutex.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include "callbacks.h"
-
-static LIST_HEAD(cbs_list);
-static DEFINE_MUTEX(cbs_mutex);
-static int cur_handle = 0;
-
-struct cb_item {
- struct list_head list;
- enum callback_t type;
- int handle;
- void (*func)(void);
-};
-
-static inline void __lock_cbs_list(void)
-{
- mutex_lock(&cbs_mutex);
-}
-
-static inline void __unlock_cbs_list(void)
-{
- mutex_unlock(&cbs_mutex);
-}
-
-static inline int __get_new_handle(void)
-{
- return cur_handle++;
-}
-
-static inline void __free_cb(struct cb_item *cb)
-{
- list_del(&cb->list);
- kfree(cb);
-}
-
-static struct cb_item *__get_cb_by_handle(int handle)
-{
- struct cb_item *cb;
-
- list_for_each_entry(cb, &cbs_list, list)
- if (cb->handle == handle)
- return cb;
-
- return NULL;
-}
-
-
-/**
- * @brief Executes callbacks on start/stop
- *
- * @param cbt Callback type
- * @return Void
- */
-void exec_cbs(enum callback_t cbt)
-{
- struct cb_item *cb;
-
- __lock_cbs_list();
-
- list_for_each_entry(cb, &cbs_list, list)
- if (cb->type == cbt)
- cb->func();
-
- __unlock_cbs_list();
-}
-
-/**
- * @brief Removes all callbacks from list
- *
- * @return Void
- */
-void remove_all_cbs(void)
-{
- struct cb_item *cb, *n;
-
- __lock_cbs_list();
-
- list_for_each_entry_safe(cb, n, &cbs_list, list)
- __free_cb(cb);
-
- __unlock_cbs_list();
-}
-
-/**
- * @brief Registers callback on event
- *
- * @param cbt Callback type
- * @param func Callback function
- * @return Handle on succes, error code on error
- */
-int us_manager_reg_cb(enum callback_t cbt, void (*func)(void))
-{
- struct cb_item *cb;
- int handle;
-
- cb = kmalloc(sizeof(*cb), GFP_KERNEL);
- if (cb == NULL)
- return -ENOMEM;
-
- handle = __get_new_handle();
-
- INIT_LIST_HEAD(&cb->list);
- cb->type = cbt;
- cb->handle = handle;
- cb->func = func;
-
- __lock_cbs_list();
- list_add_tail(&cb->list, &cbs_list);
- __unlock_cbs_list();
-
- return handle;
-}
-EXPORT_SYMBOL_GPL(us_manager_reg_cb);
-
-/**
- * @brief Unegisters callback by handle
- *
- * @param handle Callback handle
- * @return Void
- */
-void us_manager_unreg_cb(int handle)
-{
- struct cb_item *cb;
-
- __lock_cbs_list();
-
- cb = __get_cb_by_handle(handle);
- if (cb == NULL)
- goto handle_not_found;
-
- __free_cb(cb);
-
-handle_not_found:
- __unlock_cbs_list();
-}
-EXPORT_SYMBOL_GPL(us_manager_unreg_cb);
+++ /dev/null
-#ifndef __CALLBACKS_H__
-#define __CALLBACKS_H__
-
-enum callback_t {
- START_CB = 0,
- STOP_CB,
-};
-
-/* Gets callback type (on start or on stop) and function pointer.
- * Returns positive callback's handle that is used to unregister on success,
- * negative error code otherwise.
- * Exported function. */
-int us_manager_reg_cb(enum callback_t cbt, void (*func)(void));
-
-/* Gets handle and unregisters function with this handle.
- * Exported function. */
-void us_manager_unreg_cb(int handle);
-
-/* Used to execute callbacks when start/stop is occuring. */
-void exec_cbs(enum callback_t cbt);
-
-/* Removes all callbacks */
-void remove_all_cbs(void);
-
-#endif /* __CALLBACKS_H__ */
+++ /dev/null
-#include <linux/debugfs.h>
-#include <linux/module.h>
-
-#include <master/swap_debugfs.h>
-#include <master/swap_initializer.h>
-#include <us_manager/sspt/sspt_proc.h>
-
-#include "debugfs_us_manager.h"
-
-#define MAX_APPS_COUNT 8 /* According to daemon defenitions */
-#define PID_STRING 21 /* Maximum pid string = 20 (max digits count in
- * unsigned int on 64-bit arch) + 1 (for \n) */
-
-/* ============================================================================
- * = FOPS_TASKS =
- * ============================================================================
- */
-
-struct read_buf {
- char *begin;
- char *ptr;
- char *end;
-};
-
-static void on_each_proc_callback(struct sspt_proc *proc, void *data)
-{
- struct read_buf *rbuf = (struct read_buf *)data;
- char pid_str[PID_STRING];
- int len;
-
- /* skip process */
- if (!sspt_proc_is_send_event(proc))
- return;
-
- snprintf(pid_str, sizeof(pid_str), "%d", proc->tgid);
-
- len = strlen(pid_str);
-
- if (rbuf->end - rbuf->ptr < len + 2)
- return;
-
- memcpy(rbuf->ptr, pid_str, len);
- rbuf->ptr += len;
-
- *rbuf->ptr = ' ';
- ++rbuf->ptr;
-}
-
-static ssize_t read_tasks(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- char buf[PID_STRING * MAX_APPS_COUNT];
- struct read_buf rbuf = {
- .begin = buf,
- .ptr = buf,
- .end = buf + sizeof(buf)
- };
-
- on_each_proc_no_lock(on_each_proc_callback, (void *)&rbuf);
-
- if (rbuf.ptr != rbuf.begin)
- rbuf.ptr--;
-
- *rbuf.ptr = '\n';
-
- return simple_read_from_buffer(user_buf, count, ppos, rbuf.begin,
- rbuf.ptr - rbuf.begin);
-}
-
-static const struct file_operations fops_tasks = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .read = read_tasks,
- .llseek = default_llseek
-};
-
-/* ============================================================================
- * = INIT/EXIT =
- * ============================================================================
- */
-
-static struct dentry *us_manager_dir;
-
-/**
- * @brief Destroy debugfs for us_manager
- *
- * @return Void
- */
-void exit_debugfs_us_manager(void)
-{
- if (us_manager_dir)
- debugfs_remove_recursive(us_manager_dir);
-
- us_manager_dir = NULL;
-}
-
-/**
- * @brief Create debugfs for us_manager
- *
- * @return Error code
- */
-int init_debugfs_us_manager(void)
-{
- struct dentry *swap_dir, *dentry;
-
- swap_dir = swap_debugfs_getdir();
- if (swap_dir == NULL)
- return -ENOENT;
-
- us_manager_dir = swap_debugfs_create_dir(US_MANAGER_DFS_DIR, swap_dir);
- if (us_manager_dir == NULL)
- return -ENOMEM;
-
- dentry = swap_debugfs_create_file(US_MANAGER_TASKS, 0600,
- us_manager_dir, NULL, &fops_tasks);
- if (dentry == NULL)
- goto fail;
-
- return 0;
-
-fail:
- exit_debugfs_us_manager();
- return -ENOMEM;
-}
+++ /dev/null
-#ifndef __DEBUGFS_US_MANAGER_H__
-#define __DEBUGFS_US_MANAGER_H__
-
-/**
- * @file us_manager/debugfs_us_manager.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2014
- */
-
-/**
- * @def US_MANAGER_DFS_DIR @hideinitializer
- * Name in debugfs
- */
-#define US_MANAGER_DFS_DIR "us_manager"
-
-/**
- * @def US_MANAGER_DFS_DIR @hideinitializer
- * Name in debugfs
- */
-#define US_MANAGER_TASKS "tasks"
-
-int init_debugfs_us_manager(void);
-void exit_debugfs_us_manager(void);
-
-#endif /* __DEBUGFS_US_MANAGER_H__ */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/helper.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include "sspt/sspt.h"
-#include "sspt/sspt_filter.h"
-#include "helper.h"
-#include "usm_hook.h"
-
-
-/* do_page_fault() */
-static void hh_page_fault(unsigned long addr)
-{
- unsigned long page_addr = addr & PAGE_MASK;
-
- call_page_fault(current, page_addr);
-}
-
-
-/* copy_process() */
-static void disarm_ip(struct sspt_ip *ip, void *data)
-{
- struct task_struct *child = (struct task_struct *)data;
- struct uprobe *up;
-
- up = probe_info_get_uprobe(ip->desc->type, ip);
- if (up)
- disarm_uprobe(up, child);
-}
-
-static void hh_clean_task(struct task_struct *parent, struct task_struct *child)
-{
- struct sspt_proc *proc;
-
- proc = sspt_proc_get_by_task(parent);
- if (proc) {
- /* disarm up for child */
- sspt_proc_on_each_ip(proc, disarm_ip, (void *)child);
-
- /* disarm urp for child */
- swap_uretprobe_free_task(parent, child, false);
-
- sspt_proc_put(proc);
- }
-}
-
-
-/* mm_release() */
-static void hh_mm_release(struct task_struct *task)
-{
- struct mm_struct *mm = task->mm;
-
- if (mm == NULL) {
- pr_err("mm is NULL\n");
- return;
- }
-
- /* TODO: this lock for synchronizing to disarm urp */
- down_write(&mm->mmap_sem);
- if (task != task->group_leader) {
- struct sspt_proc *proc;
-
- if (task != current) {
- pr_err("call mm_release in isn't current context\n");
- goto up_mmsem;
- }
-
- /* if the thread is killed we need to discard pending
- * uretprobe instances which have not triggered yet */
- proc = sspt_proc_by_task(task);
- if (proc)
- swap_uretprobe_free_task(task, task, true);
- } else {
- call_mm_release(task);
- }
-
-up_mmsem:
- up_write(&mm->mmap_sem);
-}
-
-
-/* do_munmap() */
-struct msg_unmap_data {
- unsigned long start;
- unsigned long end;
-};
-
-static void msg_unmap(struct sspt_filter *f, void *data)
-{
- if (f->pfg_is_inst) {
- struct pfg_msg_cb *cb = pfg_msg_cb_get(f->pfg);
-
- if (cb && cb->msg_unmap) {
- struct msg_unmap_data *msg_data;
-
- msg_data = (struct msg_unmap_data *)data;
- cb->msg_unmap(msg_data->start, msg_data->end);
- }
- }
-}
-
-static void __remove_unmap_probes(struct sspt_proc *proc,
- unsigned long start, unsigned long end)
-{
- LIST_HEAD(head);
-
- if (sspt_proc_get_files_by_region(proc, &head, start, end)) {
- struct sspt_file *file, *n;
- struct task_struct *task = proc->leader;
-
- list_for_each_entry_safe(file, n, &head, list) {
- if (file->vm_start >= end)
- continue;
-
- if (file->vm_start >= start)
- sspt_file_uninstall(file, task, US_UNINSTALL);
- /* TODO: else: uninstall pages: * start..file->vm_end */
- }
-
- sspt_proc_insert_files(proc, &head);
- }
-}
-
-static void hh_munmap(unsigned long start, unsigned long end)
-{
- struct sspt_proc *proc;
-
- proc = sspt_proc_get_by_task(current);
- if (proc) {
- struct msg_unmap_data msg_data = {
- .start = start,
- .end = end,
- };
-
- __remove_unmap_probes(proc, start, end);
-
- /* send unmap region */
- sspt_proc_on_each_filter(proc, msg_unmap, (void *)&msg_data);
-
- sspt_proc_put(proc);
- }
-}
-
-
-/* do_mmap_pgoff() */
-static void msg_map(struct sspt_filter *f, void *data)
-{
- if (f->pfg_is_inst) {
- struct pfg_msg_cb *cb = pfg_msg_cb_get(f->pfg);
-
- if (cb && cb->msg_map)
- cb->msg_map((struct vm_area_struct *)data);
- }
-}
-
-static void hh_mmap(struct file *file, unsigned long addr)
-{
- struct sspt_proc *proc;
- struct task_struct *task;
- struct vm_area_struct *vma;
-
- task = current->group_leader;
- if (is_kthread(task))
- return;
-
- if (IS_ERR_VALUE(addr))
- return;
-
- proc = sspt_proc_get_by_task(task);
- if (proc == NULL)
- return;
-
- vma = find_vma_intersection(task->mm, addr, addr + 1);
- if (vma && check_vma(vma)) {
- usm_hook_mmap(proc, vma);
- sspt_proc_on_each_filter(proc, msg_map, (void *)vma);
- }
-
- sspt_proc_put(proc);
-}
-
-
-/* set_task_comm() */
-static void hh_set_comm(struct task_struct *task)
-{
- if (task == current)
- check_task_and_install(current);
-}
-
-
-/* release_task() */
-static void hh_change_leader(struct task_struct *prev,
- struct task_struct *next)
-{
- sspt_change_leader(prev, next);
-}
-
-
-#ifdef CONFIG_SWAP_HOOK_USAUX
-# include "helper_hook.c"
-#else /* CONFIG_SWAP_HOOK_USAUX */
-# include "helper_kprobe.c"
-#endif /* CONFIG_SWAP_HOOK_USAUX */
+++ /dev/null
-/**
- * @file us_manager/helper.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-#ifndef _HELPER_H
-#define _HELPER_H
-
-#include <linux/sched.h>
-
-static inline int is_kthread(struct task_struct *task)
-{
- return !task->mm;
-}
-
-int helper_once(void);
-int helper_init(void);
-void helper_uninit(void);
-
-int helper_reg(void);
-void helper_unreg_top(void);
-void helper_unreg_bottom(void);
-
-#endif /* _HELPER_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- */
-
-
-#include <linux/rwsem.h>
-#include <swap/hook_usaux.h>
-#include <kprobe/swap_ktd.h>
-
-
-struct hooks_td {
- bool in_copy_process;
-};
-
-static void hooks_td_init(struct task_struct *task, void *data)
-{
- struct hooks_td *td = (struct hooks_td *)data;
-
- td->in_copy_process = false;
-}
-
-static void hooks_td_exit(struct task_struct *task, void *data)
-{
- struct hooks_td *td = (struct hooks_td *)data;
-
- WARN(td->in_copy_process, "in_copy_process=%d", td->in_copy_process);
-}
-
-static struct ktask_data hooks_ktd = {
- .init = hooks_td_init,
- .exit = hooks_td_exit,
- .size = sizeof(struct hooks_td),
-};
-
-static struct hooks_td *current_hooks_td(void)
-{
- return (struct hooks_td *)swap_ktd(&hooks_ktd, current);
-}
-
-
-static atomic_t run_flag = ATOMIC_INIT(0);
-
-static void hook_start(void)
-{
- atomic_set(&run_flag, 1);
-}
-
-static void hook_stop(void)
-{
- atomic_set(&run_flag, 0);
-}
-
-static bool hook_is_running(void)
-{
- return atomic_read(&run_flag);
-}
-
-
-static void hook_page_fault(unsigned long addr)
-{
- if (hook_is_running())
- hh_page_fault(addr);
-}
-
-static DECLARE_RWSEM(copy_process_sem);
-
-static void hook_copy_process_pre(void)
-{
- if (hook_is_running()) {
- down_read(©_process_sem);
- if (hook_is_running())
- current_hooks_td()->in_copy_process = true;
- else
- up_read(©_process_sem);
- }
-}
-
-static void hook_copy_process_post(struct task_struct *task)
-{
- struct task_struct *parent = current;
- struct hooks_td *td = current_hooks_td();
-
- if (!td->in_copy_process)
- return;
-
- if (IS_ERR(task))
- goto out;
-
- /* check flags CLONE_VM */
- if (task->mm != parent->mm)
- hh_clean_task(current, task);
-
-out:
- up_read(©_process_sem);
- td->in_copy_process = false;
-}
-
-static void hook_mm_release(struct task_struct *task)
-{
- hh_mm_release(task);
-}
-
-static void hook_munmap(unsigned long start, unsigned long end)
-{
- hh_munmap(start, end);
-}
-
-static void hook_mmap(struct file *file, unsigned long addr)
-{
- hh_mmap(file, addr);
-}
-
-static void hook_set_comm(struct task_struct *task)
-{
- if (hook_is_running())
- hh_set_comm(task);
-}
-
-static void hook_change_leader(struct task_struct *prev,
- struct task_struct *next)
-{
- hh_change_leader(prev, next);
-}
-
-static struct hook_usaux hook_usaux = {
- .owner = THIS_MODULE,
- .page_fault = hook_page_fault,
- .copy_process_pre = hook_copy_process_pre,
- .copy_process_post = hook_copy_process_post,
- .mm_release = hook_mm_release,
- .munmap = hook_munmap,
- .mmap = hook_mmap,
- .set_comm = hook_set_comm,
- .change_leader = hook_change_leader,
-};
-
-int helper_once(void)
-{
- return 0;
-}
-
-int helper_init(void)
-{
- return swap_ktd_reg(&hooks_ktd);
-}
-
-void helper_uninit(void)
-{
- swap_ktd_unreg(&hooks_ktd);
-}
-
-int helper_reg(void)
-{
- int ret;
-
- ret = hook_usaux_set(&hook_usaux);
- if (ret)
- return ret;
-
- hook_start();
- return ret;
-}
-
-void helper_unreg_top(void)
-{
- hook_stop();
-}
-
-void helper_unreg_bottom(void)
-{
- /* waiting for copy_process() finishing */
- down_write(©_process_sem);
- up_write(©_process_sem);
-
- hook_usaux_reset();
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2016
- *
- * 2016 Vyacheslav Cherkashin
- */
-
-
-#include <linux/kconfig.h>
-#include <kprobe/swap_kprobes.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <ksyms/ksyms.h>
-#include <writer/kernel_operations.h>
-#include "us_slot_manager.h"
-#include "sspt/sspt.h"
-#include "sspt/sspt_filter.h"
-#include "helper.h"
-
-
-static atomic_t stop_flag = ATOMIC_INIT(0);
-
-
-/*
- ******************************************************************************
- * do_page_fault() *
- ******************************************************************************
- */
-
-struct pf_data {
- unsigned long addr;
-
- struct pt_regs *pf_regs;
- unsigned long save_pc;
-};
-
-static int entry_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct pf_data *data = (struct pf_data *)ri->data;
-
-#if defined(CONFIG_ARM)
- data->addr = swap_get_karg(regs, 0);
- data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 2);
- data->save_pc = data->pf_regs->ARM_pc;
-#elif defined(CONFIG_X86_32)
- data->addr = read_cr2();
- data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 0);
- data->save_pc = data->pf_regs->ip;
-#elif defined(CONFIG_ARM64)
- data->addr = swap_get_karg(regs, 0);
- data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 2);
- data->save_pc = data->pf_regs->pc;
- /* FIXME: to implement */
-#else
- #error "this architecture is not supported"
-#endif /* CONFIG_arch */
-
- if (data->addr) {
- int ret = 0;
- struct sspt_proc *proc;
-
- proc = sspt_proc_get_by_task(current);
- if (proc) {
- if (proc->r_state_addr == data->addr) {
- /* skip ret_handler_pf() for current task */
- ret = 1;
- }
-
- sspt_proc_put(proc);
- }
-
- return ret;
- }
-
- return 0;
-}
-
-static unsigned long cb_pf(void *data)
-{
- unsigned long addr = *(unsigned long *)data;
-
- hh_page_fault(addr);
-
- return 0;
-}
-
-/* Detects when IPs are really loaded into phy mem and installs probes. */
-static int ret_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct task_struct *task = current;
- struct pf_data *data = (struct pf_data *)ri->data;
- unsigned long addr;
- int ret;
-
- if (is_kthread(task))
- return 0;
-
- /* skip fixup page_fault */
-#if defined(CONFIG_ARM)
- if (data->save_pc != data->pf_regs->ARM_pc)
- return 0;
-#elif defined(CONFIG_X86_32)
- if (data->save_pc != data->pf_regs->ip)
- return 0;
-#elif defined(CONFIG_ARM64)
- if (data->save_pc != data->pf_regs->pc)
- return 0;
-#endif /* CONFIG_arch */
-
- /* TODO: check return value */
- addr = data->addr;
- ret = set_jump_cb((unsigned long)ri->ret_addr, regs, cb_pf,
- &addr, sizeof(addr));
-
- if (ret == 0)
- ri->ret_addr = (unsigned long *)get_jump_addr();
-
- return 0;
-}
-
-static struct kretprobe mf_kretprobe = {
- .entry_handler = entry_handler_pf,
- .handler = ret_handler_pf,
- .data_size = sizeof(struct pf_data)
-};
-
-static int register_mf(void)
-{
- int ret;
-
- ret = swap_register_kretprobe(&mf_kretprobe);
- if (ret)
- pr_err("swap_register_kretprobe(handle_mm_fault) ret=%d!\n", ret);
-
- return ret;
-}
-
-static void unregister_mf(void)
-{
- swap_unregister_kretprobe(&mf_kretprobe);
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * copy_process() *
- ******************************************************************************
- */
-static atomic_t rm_uprobes_child_cnt = ATOMIC_INIT(0);
-
-static unsigned long cb_clean_child(void *data)
-{
- struct task_struct *parent = current;
- struct task_struct *child = *(struct task_struct **)data;
-
- hh_clean_task(parent, child);
-
- atomic_dec(&rm_uprobes_child_cnt);
- return 0;
-}
-
-static void rm_uprobes_child(struct kretprobe_instance *ri,
- struct pt_regs *regs, struct task_struct *child)
-{
- int ret;
-
- if (!sspt_proc_by_task(current))
- return;
-
- /* set jumper */
- ret = set_jump_cb((unsigned long)ri->ret_addr, regs,
- cb_clean_child, &child, sizeof(child));
- if (ret == 0) {
- atomic_inc(&rm_uprobes_child_cnt);
- ri->ret_addr = (unsigned long *)get_jump_addr();
- } else {
- WARN_ON(1);
- }
-}
-
-
-static int pre_handler_cp(struct kprobe *p, struct pt_regs *regs)
-{
- if (is_kthread(current))
- goto out;
-
- if (atomic_read(&stop_flag))
- call_mm_release(current);
-
-out:
- return 0;
-}
-
-static atomic_t copy_process_cnt = ATOMIC_INIT(0);
-
-static int entry_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- atomic_inc(©_process_cnt);
-
- return 0;
-}
-
-/* Delete uprobs in children at fork */
-static int ret_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct task_struct *task;
-
- task = (struct task_struct *)regs_return_value(regs);
- if (IS_ERR(task))
- goto out;
-
- if (task->mm != current->mm) /* check flags CLONE_VM */
- rm_uprobes_child(ri, regs, task);
-out:
- atomic_dec(©_process_cnt);
- return 0;
-}
-
-static struct kretprobe cp_kretprobe = {
- .entry_handler = entry_handler_cp,
- .handler = ret_handler_cp,
-};
-
-static struct kprobe cp_kprobe = {
- .pre_handler = pre_handler_cp
-};
-
-static int register_cp(void)
-{
- int ret;
-
-
- ret = swap_register_kprobe(&cp_kprobe);
- if (ret)
- pr_err("swap_register_kprobe(copy_process) ret=%d!\n", ret);
-
- ret = swap_register_kretprobe(&cp_kretprobe);
- if (ret) {
- pr_err("swap_register_kretprobe(copy_process) ret=%d!\n", ret);
- swap_unregister_kprobe(&cp_kprobe);
- }
-
- return ret;
-}
-
-static void unregister_cp(void)
-{
- swap_unregister_kretprobe_top(&cp_kretprobe, 0);
- do {
- synchronize_sched();
- } while (atomic_read(©_process_cnt));
- swap_unregister_kretprobe_bottom(&cp_kretprobe);
- swap_unregister_kprobe(&cp_kprobe);
-
- do {
- synchronize_sched();
- } while (atomic_read(&rm_uprobes_child_cnt));
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * mm_release() *
- ******************************************************************************
- */
-
-/* Detects when target process removes IPs. */
-static int mr_pre_handler(struct kprobe *p, struct pt_regs *regs)
-{
- struct task_struct *task = (struct task_struct *)swap_get_karg(regs, 0);
-
- hh_mm_release(task);
-
- return 0;
-}
-
-static struct kprobe mr_kprobe = {
- .pre_handler = mr_pre_handler
-};
-
-static int register_mr(void)
-{
- int ret;
-
- ret = swap_register_kprobe(&mr_kprobe);
- if (ret)
- pr_err("swap_register_kprobe(mm_release) ret=%d!\n", ret);
-
- return ret;
-}
-
-static void unregister_mr(void)
-{
- swap_unregister_kprobe(&mr_kprobe);
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * do_munmap() *
- ******************************************************************************
- */
-struct unmap_data {
- unsigned long start;
- size_t len;
-};
-
-static atomic_t unmap_cnt = ATOMIC_INIT(0);
-
-static unsigned long cb_munmap(void *data)
-{
- struct unmap_data *umd = (struct unmap_data *)data;
-
- hh_munmap(umd->start, umd->start + umd->len);
-
- atomic_dec(&unmap_cnt);
- return 0;
-}
-
-static int entry_handler_unmap(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct unmap_data *data = (struct unmap_data *)ri->data;
-
- data->start = swap_get_karg(regs, 1);
- data->len = (size_t)PAGE_ALIGN(swap_get_karg(regs, 2));
-
- atomic_inc(&unmap_cnt);
- return 0;
-}
-
-static int ret_handler_unmap(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int ret;
-
- if (regs_return_value(regs)) {
- atomic_dec(&unmap_cnt);
- return 0;
- }
-
- ret = set_jump_cb((unsigned long)ri->ret_addr, regs, cb_munmap,
- (struct unmap_data *)ri->data,
- sizeof(struct unmap_data));
- if (ret == 0) {
- ri->ret_addr = (unsigned long *)get_jump_addr();
- } else {
- WARN_ON(1);
- atomic_dec(&unmap_cnt);
- }
-
- return 0;
-}
-
-static struct kretprobe unmap_kretprobe = {
- .entry_handler = entry_handler_unmap,
- .handler = ret_handler_unmap,
- .data_size = sizeof(struct unmap_data)
-};
-
-static int register_unmap(void)
-{
- int ret;
-
- ret = swap_register_kretprobe(&unmap_kretprobe);
- if (ret)
- pr_err("swap_register_kprobe(do_munmap) ret=%d!\n", ret);
-
- return ret;
-}
-
-static void unregister_unmap(void)
-{
- swap_unregister_kretprobe_top(&unmap_kretprobe, 0);
- do {
- synchronize_sched();
- } while (atomic_read(&unmap_cnt));
- swap_unregister_kretprobe_bottom(&unmap_kretprobe);
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * do_mmap_pgoff() *
- ******************************************************************************
- */
-static int ret_handler_mmap(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- unsigned long addr = regs_return_value(regs);
-
- hh_mmap(NULL, addr);
-
- return 0;
-}
-
-static struct kretprobe mmap_kretprobe = {
- .handler = ret_handler_mmap
-};
-
-static int register_mmap(void)
-{
- int ret;
-
- ret = swap_register_kretprobe(&mmap_kretprobe);
- if (ret)
- pr_err("swap_register_kretprobe(do_mmap_pgoff) ret=%d!\n", ret);
-
- return ret;
-}
-
-static void unregister_mmap(void)
-{
- swap_unregister_kretprobe(&mmap_kretprobe);
-}
-
-
-
-
-
-/*
- ******************************************************************************
- * set_task_comm() *
- ******************************************************************************
- */
-struct comm_data {
- struct task_struct *task;
-};
-
-static unsigned long cb_check_and_install(void *data)
-{
- hh_set_comm(current);
-
- return 0;
-}
-
-static int entry_handler_comm(struct kretprobe_instance *ri,
- struct pt_regs *regs)
-{
- struct comm_data *data = (struct comm_data *)ri->data;
-
- data->task = (struct task_struct *)swap_get_karg(regs, 0);
-
- return 0;
-}
-
-static int ret_handler_comm(struct kretprobe_instance *ri, struct pt_regs *regs)
-{
- struct task_struct *task;
- int ret;
-
- if (is_kthread(current))
- return 0;
-
- task = ((struct comm_data *)ri->data)->task;
- if (task != current)
- return 0;
-
- ret = set_jump_cb((unsigned long)ri->ret_addr, regs,
- cb_check_and_install, NULL, 0);
- if (ret == 0)
- ri->ret_addr = (unsigned long *)get_jump_addr();
-
- return 0;
-}
-
-static struct kretprobe comm_kretprobe = {
- .entry_handler = entry_handler_comm,
- .handler = ret_handler_comm,
- .data_size = sizeof(struct comm_data)
-};
-
-static int register_comm(void)
-{
- int ret;
-
- ret = swap_register_kretprobe(&comm_kretprobe);
- if (ret)
- pr_err("swap_register_kretprobe(set_task_comm) ret=%d!\n", ret);
-
- return ret;
-}
-
-static void unregister_comm(void)
-{
- swap_unregister_kretprobe(&comm_kretprobe);
-}
-
-
-
-
-/*
- ******************************************************************************
- * release_task() *
- ******************************************************************************
- */
-static int release_task_h(struct kprobe *p, struct pt_regs *regs)
-{
- struct task_struct *task = (struct task_struct *)swap_get_karg(regs, 0);
- struct task_struct *cur = current;
-
- if (cur->flags & PF_KTHREAD)
- return 0;
-
- /* EXEC: change group leader */
- if (cur != task && task->pid == cur->pid)
- hh_change_leader(task, cur);
-
- return 0;
-}
-
-struct kprobe release_task_kp = {
- .pre_handler = release_task_h,
-};
-
-static int reg_release_task(void)
-{
- return swap_register_kprobe(&release_task_kp);
-}
-
-static void unreg_release_task(void)
-{
- swap_unregister_kprobe(&release_task_kp);
-}
-
-
-
-
-
-/**
- * @brief Registration of helper
- *
- * @return Error code
- */
-int helper_reg(void)
-{
- int ret = 0;
-
- atomic_set(&stop_flag, 0);
-
- /* tracking group leader changing */
- ret = reg_release_task();
- if (ret)
- return ret;
-
- /*
- * install probe on 'set_task_comm' to detect when field comm struct
- * task_struct changes
- */
- ret = register_comm();
- if (ret)
- goto unreg_rel_task;
-
- /* install probe on 'do_munmap' to detect when for remove US probes */
- ret = register_unmap();
- if (ret)
- goto unreg_comm;
-
- /* install probe on 'mm_release' to detect when for remove US probes */
- ret = register_mr();
- if (ret)
- goto unreg_unmap;
-
- /* install probe on 'copy_process' to disarm children process */
- ret = register_cp();
- if (ret)
- goto unreg_mr;
-
- /* install probe on 'do_mmap_pgoff' to detect when mapping file */
- ret = register_mmap();
- if (ret)
- goto unreg_cp;
-
- /*
- * install probe on 'handle_mm_fault' to detect when US pages will be
- * loaded
- */
- ret = register_mf();
- if (ret)
- goto unreg_mmap;
-
- return ret;
-
-unreg_mmap:
- unregister_mmap();
-
-unreg_cp:
- unregister_cp();
-
-unreg_mr:
- unregister_mr();
-
-unreg_unmap:
- unregister_unmap();
-
-unreg_comm:
- unregister_comm();
-
-unreg_rel_task:
- unreg_release_task();
-
- return ret;
-}
-
-/**
- * @brief Unegistration of helper bottom
- *
- * @return Void
- */
-void helper_unreg_top(void)
-{
- unregister_mf();
- atomic_set(&stop_flag, 1);
-}
-
-/**
- * @brief Unegistration of helper top
- *
- * @return Void
- */
-void helper_unreg_bottom(void)
-{
- unregister_mmap();
- unregister_cp();
- unregister_mr();
- unregister_unmap();
- unregister_comm();
- unreg_release_task();
-}
-
-/**
- * @brief Initialization of helper
- *
- * @return Error code
- */
-int helper_once(void)
-{
- const char *sym;
-
- sym = "do_page_fault";
- mf_kretprobe.kp.addr = swap_ksyms(sym);
- if (mf_kretprobe.kp.addr == 0)
- goto not_found;
-
- sym = "copy_process";
- cp_kretprobe.kp.addr = swap_ksyms_substr(sym);
- if (cp_kretprobe.kp.addr == 0)
- goto not_found;
- cp_kprobe.addr = cp_kretprobe.kp.addr;
-
- sym = "mm_release";
- mr_kprobe.addr = swap_ksyms(sym);
- if (mr_kprobe.addr == 0)
- goto not_found;
-
- sym = "do_munmap";
- unmap_kretprobe.kp.addr = swap_ksyms(sym);
- if (unmap_kretprobe.kp.addr == 0)
- goto not_found;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
- sym = "do_mmap";
-#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)) */
- sym = "do_mmap_pgoff";
-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)) */
- mmap_kretprobe.kp.addr = swap_ksyms(sym);
- if (mmap_kretprobe.kp.addr == 0)
- goto not_found;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
- sym = "__set_task_comm";
-#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) */
- sym = "set_task_comm";
-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) */
- comm_kretprobe.kp.addr = swap_ksyms(sym);
- if (comm_kretprobe.kp.addr == 0)
- goto not_found;
-
- sym = "release_task";
- release_task_kp.addr = swap_ksyms(sym);
- if (release_task_kp.addr == 0)
- goto not_found;
-
- return 0;
-
-not_found:
- pr_err("ERROR: symbol '%s' not found\n", sym);
- return -ESRCH;
-}
-
-int helper_init(void)
-{
- return 0;
-}
-
-void helper_uninit(void)
-{
-}
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/img/img_file.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include "img_file.h"
-#include "img_ip.h"
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/atomic.h>
-
-
-static atomic_t file_counter = ATOMIC_INIT(0);
-
-
-static void img_del_ip_by_list(struct img_ip *ip);
-
-/**
- * @brief Create img_file struct
- *
- * @param dentry Dentry of file
- * @return Pointer to the created img_file struct
- */
-struct img_file *img_file_create(struct dentry *dentry)
-{
- struct img_file *file;
-
- file = kmalloc(sizeof(*file), GFP_ATOMIC);
- if (file == NULL) {
- pr_err("%s: failed to allocate memory\n", __func__);
- return ERR_PTR(-ENOMEM);
- }
- atomic_inc(&file_counter);
-
- file->dentry = dentry;
- INIT_LIST_HEAD(&file->list);
- INIT_LIST_HEAD(&file->ips.head);
- mutex_init(&file->ips.mtx);
- atomic_set(&file->use, 1);
-
- return file;
-}
-
-/**
- * @brief Remove img_file struct
- *
- * @param file remove object
- * @return Void
- */
-static void img_file_free(struct img_file *file)
-{
- struct img_ip *ip, *tmp;
-
- list_for_each_entry_safe(ip, tmp, &file->ips.head, list) {
- img_del_ip_by_list(ip);
- img_ip_clean(ip);
- img_ip_put(ip);
- }
-
- atomic_dec(&file_counter);
- kfree(file);
-}
-
-/* called with mutex_[lock/unlock](&file->ips.mtx) */
-static void img_add_ip_by_list(struct img_file *file, struct img_ip *ip)
-{
- list_add(&ip->list, &file->ips.head);
-}
-
-/* called with mutex_[lock/unlock](&file->ips.mtx) */
-static void img_del_ip_by_list(struct img_ip *ip)
-{
- list_del(&ip->list);
-}
-
-void img_file_get(struct img_file *file)
-{
- WARN_ON(!atomic_read(&file->use));
- atomic_inc(&file->use);
-}
-
-void img_file_put(struct img_file *file)
-{
- if (atomic_dec_and_test(&file->use))
- img_file_free(file);
-}
-
-
-/**
- * @brief Add instrumentation pointer
- *
- * @param file Pointer to the img_file struct
- * @param addr Function address
- * @param probe_Pointer to a probe_info structure with an information about
- * the probe.
- * @return Error code
- */
-struct img_ip *img_file_add_ip(struct img_file *file, unsigned long addr,
- struct probe_desc *pd)
-{
- struct img_ip *ip;
-
- ip = img_ip_create(addr, pd, file);
- if (IS_ERR(ip))
- return ip;
-
- mutex_lock(&file->ips.mtx);
- img_add_ip_by_list(file, ip);
- mutex_unlock(&file->ips.mtx);
-
- return ip;
-}
-
-/**
- * @brief Delete img_ip struct from img_file struct
- *
- * @param file Pointer to the img_file struct
- * @param addr Function address
- * @return Error code
- */
-void img_file_del_ip(struct img_file *file, struct img_ip *ip)
-{
- mutex_lock(&file->ips.mtx);
- img_del_ip_by_list(ip);
- mutex_unlock(&file->ips.mtx);
-
- img_ip_clean(ip);
- img_ip_put(ip);
-}
-
-/**
- * @brief Check on absence img_ip structs in img_file struct
- *
- * @param file Pointer to the img_file struct
- * @return
- * - 0 - not empty
- * - 1 - empty
- */
-int img_file_empty(struct img_file *file)
-{
- return list_empty(&file->ips.head);
-}
-
-bool img_file_is_unloadable(void)
-{
- return !(atomic_read(&file_counter) + !img_ip_is_unloadable());
-}
-
-/**
- * @brief For debug
- *
- * @param file Pointer to the img_file struct
- * @return Void
- */
-
-/* debug */
-void img_file_print(struct img_file *file)
-{
- struct img_ip *ip;
-
- printk(KERN_INFO "### d_iname=%s\n", file->dentry->d_iname);
-
- mutex_lock(&file->ips.mtx);
- list_for_each_entry(ip, &file->ips.head, list) {
- img_ip_print(ip);
- }
- mutex_unlock(&file->ips.mtx);
-}
-/* debug */
+++ /dev/null
-/**
- * @file us_manager/img/img_file.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- */
-
-
-#ifndef _IMG_FILE_H
-#define _IMG_FILE_H
-
-#include <linux/types.h>
-#include <linux/mutex.h>
-
-struct probe_desc;
-
-/**
- * @struct img_file
- * @breaf Image of file
- */
-struct img_file {
- /* img_proc */
- struct list_head list; /**< List for img_proc */
-
- /* img_ip */
- struct {
- struct mutex mtx;
- struct list_head head; /**< Head for img_ip */
- } ips;
-
- struct dentry *dentry; /**< Dentry of file */
- atomic_t use;
-};
-
-struct img_file *img_file_create(struct dentry *dentry);
-void img_file_get(struct img_file *file);
-void img_file_put(struct img_file *file);
-
-struct img_ip *img_file_add_ip(struct img_file *file, unsigned long addr,
- struct probe_desc *pd);
-void img_file_del_ip(struct img_file *file, struct img_ip *ip);
-
-int img_file_empty(struct img_file *file);
-bool img_file_is_unloadable(void);
-
-/* debug */
-void img_file_print(struct img_file *file);
-/* debug */
-
-#endif /* _IMG_FILE_H */
-
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/img/img_ip.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include "img_ip.h"
-#include "img_file.h"
-#include <us_manager/probes/use_probes.h>
-#include <us_manager/sspt/sspt.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <linux/slab.h>
-#include <linux/atomic.h>
-
-
-static atomic_t ip_counter = ATOMIC_INIT(0);
-
-/**
- * @brief Create img_ip struct
- *
- * @param addr Function address
- * @param probe_i Pointer to the probe info data.
- * @return Pointer to the created img_ip struct
- */
-struct img_ip *img_ip_create(unsigned long addr, struct probe_desc *pd,
- struct img_file *file)
-{
- struct img_ip *ip;
-
- ip = kmalloc(sizeof(*ip), GFP_KERNEL);
- if (!ip)
- return ERR_PTR(-ENOMEM);
- atomic_inc(&ip_counter);
-
- INIT_LIST_HEAD(&ip->list);
- kref_init(&ip->ref);
- mutex_init(&ip->sspt.mtx);
- INIT_LIST_HEAD(&ip->sspt.head);
- ip->addr = addr;
- ip->desc = pd;
- ip->file = file;
-
- return ip;
-}
-
-static void img_ip_release(struct kref *ref)
-{
- struct img_ip *ip = container_of(ref, struct img_ip, ref);
-
- WARN_ON(!list_empty(&ip->sspt.head));
-
- atomic_dec(&ip_counter);
- kfree(ip);
-}
-
-void img_ip_clean(struct img_ip *ip)
-{
- struct sspt_ip *p;
-
- img_ip_lock(ip);
- while(!list_empty(&ip->sspt.head)) {
- p = list_first_entry(&ip->sspt.head, struct sspt_ip ,img_list);
- sspt_ip_get(p);
- img_ip_unlock(ip);
-
- if (sspt_page_is_installed_ip(p->page, p))
- sspt_unregister_usprobe(NULL, p, US_UNREGS_PROBE);
-
- sspt_ip_clean(p);
-
- img_ip_lock(ip);
- sspt_ip_put(p);
- }
- img_ip_unlock(ip);
-}
-
-void img_ip_get(struct img_ip *ip)
-{
- kref_get(&ip->ref);
-}
-
-void img_ip_put(struct img_ip *ip)
-{
- kref_put(&ip->ref, img_ip_release);
-}
-
-void img_ip_add_ip(struct img_ip *ip, struct sspt_ip *sspt_ip)
-{
- sspt_ip->img_ip = ip;
- list_add(&sspt_ip->img_list, &ip->sspt.head);
-}
-
-void img_ip_lock(struct img_ip *ip)
-{
- mutex_lock(&ip->sspt.mtx);
-}
-
-void img_ip_unlock(struct img_ip *ip)
-{
- mutex_unlock(&ip->sspt.mtx);
-}
-
-bool img_ip_is_unloadable(void)
-{
- return !atomic_read(&ip_counter);
-}
-
-/**
- * @brief For debug
- *
- * @param ip Pointer to the img_ip struct
- * @return Void
- */
-
-/* debug */
-void img_ip_print(struct img_ip *ip)
-{
- if (ip->desc->type == SWAP_RETPROBE)
- printk(KERN_INFO "### addr=8%lx, args=%s\n",
- ip->addr, ip->desc->info.rp_i.args);
-}
-/* debug */
+++ /dev/null
-/**
- * @file us_manager/img/img_ip.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- *
- */
-
-
-#ifndef _IMG_IP_H
-#define _IMG_IP_H
-
-#include <linux/types.h>
-#include <linux/kref.h>
-#include <linux/mutex.h>
-
-
-struct sspt_ip;
-struct img_file;
-struct probe_desc;
-
-/**
- * @struct img_ip
- * @breaf Image of instrumentation pointer
- */
-struct img_ip {
- /* img_file */
- struct list_head list; /**< List for img_file */
- struct img_file *file; /**< Pointer on the file (parent) */
-
- struct kref ref;
-
- /* sspt_ip */
- struct {
- struct mutex mtx;
- struct list_head head;
- } sspt;
-
- unsigned long addr; /**< Function address */
- struct probe_desc *desc; /**< Probe info */
-};
-
-struct img_ip *img_ip_create(unsigned long addr, struct probe_desc *info,
- struct img_file *file);
-void img_ip_clean(struct img_ip *ip);
-void img_ip_get(struct img_ip *ip);
-void img_ip_put(struct img_ip *ip);
-
-void img_ip_add_ip(struct img_ip *ip, struct sspt_ip *sspt_ip);
-void img_ip_lock(struct img_ip *ip);
-void img_ip_unlock(struct img_ip *ip);
-
-bool img_ip_is_unloadable(void);
-
-/* debug */
-void img_ip_print(struct img_ip *ip);
-/* debug */
-
-#endif /* _IMG_IP_H */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/img/img_proc.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/atomic.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include <us_manager/sspt/sspt_file.h>
-#include "img_ip.h"
-#include "img_proc.h"
-#include "img_file.h"
-
-
-struct img_proc {
- /* img_file */
- struct {
- struct list_head head;
- struct mutex mtx;
- } files;
-};
-
-
-static atomic_t proc_counter = ATOMIC_INIT(0);
-
-static void img_del_file_by_list(struct img_file *file);
-
-/**
- * @brief Create img_proc struct
- *
- * @return Pointer to the created img_proc struct
- */
-struct img_proc *img_proc_create(void)
-{
- struct img_proc *proc;
-
- proc = kmalloc(sizeof(*proc), GFP_ATOMIC);
- if (proc) {
- atomic_inc(&proc_counter);
- INIT_LIST_HEAD(&proc->files.head);
- mutex_init(&proc->files.mtx);
- }
-
- return proc;
-}
-
-/**
- * @brief Remove img_proc struct
- *
- * @param file remove object
- * @return Void
- */
-void img_proc_free(struct img_proc *proc)
-{
- struct img_file *file, *tmp;
-
- mutex_lock(&proc->files.mtx);
- list_for_each_entry_safe(file, tmp, &proc->files.head, list) {
- img_del_file_by_list(file);
- img_file_put(file);
- }
- mutex_unlock(&proc->files.mtx);
-
- atomic_dec(&proc_counter);
- kfree(proc);
-}
-
-/* called with mutex_[lock/unlock](&proc->files.mtx) */
-static void img_add_file_by_list(struct img_proc *proc, struct img_file *file)
-{
- list_add(&file->list, &proc->files.head);
-}
-
-/* called with mutex_[lock/unlock](&proc->files.mtx) */
-static void img_del_file_by_list(struct img_file *file)
-{
- list_del(&file->list);
-}
-
-/* called with mutex_[lock/unlock](&proc->files.mtx) */
-static struct img_file *img_file_find(struct img_proc *proc,
- struct dentry *dentry)
-{
- struct img_file *file;
-
- list_for_each_entry(file, &proc->files.head, list) {
- if (file->dentry == dentry)
- return file;
- }
-
- return NULL;
-}
-
-/**
- * @brief Add instrumentation pointer
- *
- * @param proc Pointer to the img_proc struct
- * @param dentry Dentry of file
- * @param addr Function address
- * @param probe_i Pointer to a probe_info struct related with the probe
- * @return Error code
- */
-struct img_ip *img_proc_add_ip(struct img_proc *proc, struct dentry *dentry,
- unsigned long addr, struct probe_desc *pd)
-{
- struct img_file *file;
-
- mutex_lock(&proc->files.mtx);
- file = img_file_find(proc, dentry);
- if (!file) {
- file = img_file_create(dentry);
- if (IS_ERR(file)) {
- mutex_unlock(&proc->files.mtx);
-
- /* handle type cast */
- return ERR_PTR(PTR_ERR(file));
- }
-
- img_add_file_by_list(proc, file);
- }
- mutex_unlock(&proc->files.mtx);
-
- return img_file_add_ip(file, addr, pd);
-}
-
-/**
- * @brief Remove instrumentation pointer
- *
- * @param proc Pointer to the img_proc struct
- * @param dentry Dentry of file
- * @param args Function address
- * @return Error code
- */
-void img_proc_del_ip(struct img_proc *proc, struct img_ip *ip)
-{
- struct img_file *file = ip->file;
-
- mutex_lock(&proc->files.mtx);
- img_file_del_ip(file, ip);
- if (img_file_empty(file)) {
- img_del_file_by_list(file);
- img_file_put(file);
- }
- mutex_unlock(&proc->files.mtx);
-}
-
-void img_proc_copy_to_sspt(struct img_proc *i_proc, struct sspt_proc *proc)
-{
- struct sspt_file *file;
- struct img_file *i_file;
-
- mutex_lock(&i_proc->files.mtx);
- list_for_each_entry(i_file, &i_proc->files.head, list) {
- file = sspt_proc_find_file_or_new(proc, i_file->dentry);
- if (file) {
- struct img_ip *i_ip;
-
- mutex_lock(&i_file->ips.mtx);
- list_for_each_entry(i_ip, &i_file->ips.head, list)
- sspt_file_add_ip(file, i_ip);
- mutex_unlock(&i_file->ips.mtx);
- }
- }
- mutex_unlock(&i_proc->files.mtx);
-}
-
-bool img_proc_is_unloadable(void)
-{
- return !(atomic_read(&proc_counter) + !img_file_is_unloadable());
-}
-
-/**
- * @brief For debug
- *
- * @param proc Pointer to the img_proc struct
- * @return Void
- */
-
-/* debug */
-void img_proc_print(struct img_proc *proc)
-{
- struct img_file *file;
-
- printk(KERN_INFO "### img_proc_print:\n");
-
- mutex_lock(&proc->files.mtx);
- list_for_each_entry(file, &proc->files.head, list) {
- img_file_print(file);
- }
- mutex_unlock(&proc->files.mtx);
-}
-/* debug */
+++ /dev/null
-/**
- * @file us_manager/img/img_proc.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENCE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-
-#ifndef _IMG_PROC_H
-#define _IMG_PROC_H
-
-#include <linux/types.h>
-
-struct dentry;
-struct sspt_proc;
-struct probe_desc;
-
-
-struct img_proc *img_proc_create(void);
-void img_proc_free(struct img_proc *proc);
-
-struct img_ip *img_proc_add_ip(struct img_proc *proc, struct dentry *dentry,
- unsigned long addr, struct probe_desc *pd);
-void img_proc_del_ip(struct img_proc *proc, struct img_ip *ip);
-
-void img_proc_copy_to_sspt(struct img_proc *i_proc, struct sspt_proc *proc);
-bool img_proc_is_unloadable(void);
-
-/* debug */
-void img_proc_print(struct img_proc *proc);
-/* debug */
-
-#endif /* _IMG_PROC_H */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/pf/pf_group.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/namei.h>
-#include <linux/mman.h>
-#include <linux/atomic.h>
-#include <linux/spinlock.h>
-#include "pf_group.h"
-#include "proc_filters.h"
-#include "../sspt/sspt_filter.h"
-#include "../us_manager_common.h"
-#include <us_manager/img/img_proc.h>
-#include <us_manager/img/img_file.h>
-#include <us_manager/img/img_ip.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include <us_manager/helper.h>
-#include <us_manager/us_common_file.h>
-#include <task_ctx/task_ctx.h>
-
-
-struct pf_group {
- struct list_head list;
- struct img_proc *i_proc;
- struct proc_filter filter;
- struct pfg_msg_cb *msg_cb;
- atomic_t usage;
-
- spinlock_t pl_lock; /* for proc_list */
- struct list_head proc_list;
-};
-
-struct pl_struct {
- struct list_head list;
- struct sspt_proc *proc;
-};
-
-
-static atomic_t pfg_counter = ATOMIC_INIT(0);
-
-static LIST_HEAD(pfg_list);
-static DECLARE_RWSEM(pfg_list_sem);
-static DECLARE_RWSEM(uninstall_sem);
-
-static void pfg_list_rlock(void)
-{
- down_read(&pfg_list_sem);
-}
-
-static void pfg_list_runlock(void)
-{
- up_read(&pfg_list_sem);
-}
-
-static void pfg_list_wlock(void)
-{
- down_write(&pfg_list_sem);
-}
-
-static void pfg_list_wunlock(void)
-{
- up_write(&pfg_list_sem);
-}
-
-
-/* struct pl_struct */
-static struct pl_struct *create_pl_struct(struct sspt_proc *proc)
-{
- struct pl_struct *pls = kmalloc(sizeof(*pls), GFP_ATOMIC);
-
- if (pls) {
- INIT_LIST_HEAD(&pls->list);
- pls->proc = sspt_proc_get(proc);
- }
-
- return pls;
-}
-
-static void free_pl_struct(struct pl_struct *pls)
-{
- sspt_proc_put(pls->proc);
- kfree(pls);
-}
-/* struct pl_struct */
-
-static struct pf_group *pfg_create(void)
-{
- struct pf_group *pfg = kmalloc(sizeof(*pfg), GFP_ATOMIC);
-
- if (pfg == NULL)
- return NULL;
-
- pfg->i_proc = img_proc_create();
- if (pfg->i_proc == NULL)
- goto create_pfg_fail;
-
- INIT_LIST_HEAD(&pfg->list);
- memset(&pfg->filter, 0, sizeof(pfg->filter));
- spin_lock_init(&pfg->pl_lock);
- INIT_LIST_HEAD(&pfg->proc_list);
- pfg->msg_cb = NULL;
- atomic_set(&pfg->usage, 1);
-
- atomic_inc(&pfg_counter);
- return pfg;
-
-create_pfg_fail:
-
- kfree(pfg);
-
- return NULL;
-}
-
-static void pfg_free(struct pf_group *pfg)
-{
- struct pl_struct *pl, *n;
-
- img_proc_free(pfg->i_proc);
- free_pf(&pfg->filter);
- list_for_each_entry_safe(pl, n, &pfg->proc_list, list) {
- sspt_proc_del_filter(pl->proc, pfg);
- free_pl_struct(pl);
- }
-
- atomic_dec(&pfg_counter);
- kfree(pfg);
-}
-
-bool pfg_is_unloadable(void)
-{
- return !(atomic_read(&pfg_counter) + !img_proc_is_unloadable());
-}
-
-static int pfg_add_proc(struct pf_group *pfg, struct sspt_proc *proc)
-{
- struct pl_struct *pls;
-
- pls = create_pl_struct(proc);
- if (pls == NULL)
- return -ENOMEM;
-
- spin_lock(&pfg->pl_lock);
- list_add(&pls->list, &pfg->proc_list);
- spin_unlock(&pfg->pl_lock);
-
- return 0;
-}
-
-static int pfg_del_proc(struct pf_group *pfg, struct sspt_proc *proc)
-{
- struct pl_struct *pls, *pls_free = NULL;
-
- spin_lock(&pfg->pl_lock);
- list_for_each_entry(pls, &pfg->proc_list, list) {
- if (pls->proc == proc) {
- list_del(&pls->list);
- pls_free = pls;
- break;
- }
- }
- spin_unlock(&pfg->pl_lock);
-
- if (pls_free)
- free_pl_struct(pls_free);
-
- return !!pls_free;
-}
-
-
-/* called with pfg_list_lock held */
-static void pfg_add_to_list(struct pf_group *pfg)
-{
- list_add(&pfg->list, &pfg_list);
-}
-
-/* called with pfg_list_lock held */
-static void pfg_del_from_list(struct pf_group *pfg)
-{
- list_del(&pfg->list);
-}
-
-
-static void msg_info(struct sspt_filter *f, void *data)
-{
- if (f->pfg_is_inst == false) {
- struct pfg_msg_cb *cb;
-
- f->pfg_is_inst = true;
-
- cb = pfg_msg_cb_get(f->pfg);
- if (cb) {
- struct dentry *dentry;
-
- dentry = (struct dentry *)f->pfg->filter.priv;
-
- if (cb->msg_info)
- cb->msg_info(f->proc->leader, dentry);
-
- if (cb->msg_status_info)
- cb->msg_status_info(f->proc->leader);
- }
- }
-}
-
-static void first_install(struct task_struct *task, struct sspt_proc *proc)
-{
- down_write(&task->mm->mmap_sem);
- sspt_proc_on_each_filter(proc, msg_info, NULL);
- sspt_proc_install(proc);
- up_write(&task->mm->mmap_sem);
-}
-
-static void subsequent_install(struct task_struct *task,
- struct sspt_proc *proc, unsigned long page_addr)
-{
- down_write(&task->mm->mmap_sem);
- sspt_proc_install_page(proc, page_addr);
- up_write(&task->mm->mmap_sem);
-}
-
-
-/**
- * @brief Get dentry struct by path
- *
- * @param path Path to file
- * @return Pointer on dentry struct on NULL
- */
-struct dentry *dentry_by_path(const char *path)
-{
- struct dentry *d;
-
- d = swap_get_dentry(path);
- if (d)
- dput(d);
-
- return d;
-}
-EXPORT_SYMBOL_GPL(dentry_by_path);
-
-
-int pfg_msg_cb_set(struct pf_group *pfg, struct pfg_msg_cb *msg_cb)
-{
- if (pfg->msg_cb)
- return -EBUSY;
-
- pfg->msg_cb = msg_cb;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pfg_msg_cb_set);
-
-void pfg_msg_cb_reset(struct pf_group *pfg)
-{
- pfg->msg_cb = NULL;
-}
-EXPORT_SYMBOL_GPL(pfg_msg_cb_reset);
-
-struct pfg_msg_cb *pfg_msg_cb_get(struct pf_group *pfg)
-{
- return pfg->msg_cb;
-}
-
-/**
- * @brief Get pf_group struct by dentry
- *
- * @param dentry Dentry of file
- * @param priv Private data
- * @return Pointer on pf_group struct
- */
-struct pf_group *get_pf_group_by_dentry(struct dentry *dentry, void *priv)
-{
- struct pf_group *pfg;
-
- pfg_list_wlock();
- list_for_each_entry(pfg, &pfg_list, list) {
- if (check_pf_by_dentry(&pfg->filter, dentry)) {
- atomic_inc(&pfg->usage);
- goto unlock;
- }
- }
-
- pfg = pfg_create();
- if (pfg == NULL)
- goto unlock;
-
- set_pf_by_dentry(&pfg->filter, dentry, priv);
-
- pfg_add_to_list(pfg);
-
-unlock:
- pfg_list_wunlock();
- return pfg;
-}
-EXPORT_SYMBOL_GPL(get_pf_group_by_dentry);
-
-/**
- * @brief Get pf_group struct by TGID
- *
- * @param tgid Thread group ID
- * @param priv Private data
- * @return Pointer on pf_group struct
- */
-struct pf_group *get_pf_group_by_tgid(pid_t tgid, void *priv)
-{
- struct pf_group *pfg;
-
- pfg_list_wlock();
- list_for_each_entry(pfg, &pfg_list, list) {
- if (check_pf_by_tgid(&pfg->filter, tgid)) {
- atomic_inc(&pfg->usage);
- goto unlock;
- }
- }
-
- pfg = pfg_create();
- if (pfg == NULL)
- goto unlock;
-
- set_pf_by_tgid(&pfg->filter, tgid, priv);
-
- pfg_add_to_list(pfg);
-
-unlock:
- pfg_list_wunlock();
- return pfg;
-}
-EXPORT_SYMBOL_GPL(get_pf_group_by_tgid);
-
-/**
- * @brief Get pf_group struct by comm
- *
- * @param comm Task comm
- * @param priv Private data
- * @return Pointer on pf_group struct
- */
-struct pf_group *get_pf_group_by_comm(char *comm, void *priv)
-{
- int ret;
- struct pf_group *pfg;
-
- pfg_list_wlock();
- list_for_each_entry(pfg, &pfg_list, list) {
- if (check_pf_by_comm(&pfg->filter, comm)) {
- atomic_inc(&pfg->usage);
- goto unlock;
- }
- }
-
- pfg = pfg_create();
- if (pfg == NULL)
- goto unlock;
-
- ret = set_pf_by_comm(&pfg->filter, comm, priv);
- if (ret) {
- printk(KERN_ERR "ERROR: set_pf_by_comm, ret=%d\n", ret);
- pfg_free(pfg);
- pfg = NULL;
- goto unlock;
- }
-
- pfg_add_to_list(pfg);
-unlock:
- pfg_list_wunlock();
- return pfg;
-}
-EXPORT_SYMBOL_GPL(get_pf_group_by_comm);
-
-/**
- * @brief Get pf_group struct for each process
- *
- * @param priv Private data
- * @return Pointer on pf_group struct
- */
-struct pf_group *get_pf_group_dumb(void *priv)
-{
- struct pf_group *pfg;
-
- pfg_list_wlock();
- list_for_each_entry(pfg, &pfg_list, list) {
- if (check_pf_dumb(&pfg->filter)) {
- atomic_inc(&pfg->usage);
- goto unlock;
- }
- }
-
- pfg = pfg_create();
- if (pfg == NULL)
- goto unlock;
-
- set_pf_dumb(&pfg->filter, priv);
-
- pfg_add_to_list(pfg);
-
-unlock:
- pfg_list_wunlock();
- return pfg;
-}
-EXPORT_SYMBOL_GPL(get_pf_group_dumb);
-
-/**
- * @brief Put pf_group struct
- *
- * @param pfg Pointer to the pf_group struct
- * @return Void
- */
-void put_pf_group(struct pf_group *pfg)
-{
- if (atomic_dec_and_test(&pfg->usage)) {
- pfg_list_wlock();
- pfg_del_from_list(pfg);
- pfg_list_wunlock();
-
- pfg_free(pfg);
- }
-}
-EXPORT_SYMBOL_GPL(put_pf_group);
-
-/**
- * @brief Register prober for pf_grpup struct
- *
- * @param pfg Pointer to the pf_group struct
- * @param dentry Dentry of file
- * @param offset Function offset
- * @param probe_info Pointer to the related probe_info struct
- * @return pointer to the img_ip struct or error
- */
-struct img_ip *pf_register_probe(struct pf_group *pfg, struct dentry *dentry,
- unsigned long offset, struct probe_desc *pd)
-{
- return img_proc_add_ip(pfg->i_proc, dentry, offset, pd);
-}
-EXPORT_SYMBOL_GPL(pf_register_probe);
-
-/**
- * @brief Unregister prober from pf_grpup struct
- *
- * @param pfg Pointer to the pf_group struct
- * @param ip Pointer to the img_ip struct
- * @return Void
- */
-void pf_unregister_probe(struct pf_group *pfg, struct img_ip *ip)
-{
- WARN(IS_ERR_OR_NULL(ip), "invalid img_ip");
- img_proc_del_ip(pfg->i_proc, ip);
-}
-EXPORT_SYMBOL_GPL(pf_unregister_probe);
-
-static int check_task_on_filters(struct task_struct *task)
-{
- int ret = 0;
- struct pf_group *pfg;
-
- pfg_list_rlock();
- list_for_each_entry(pfg, &pfg_list, list) {
- if (check_task_f(&pfg->filter, task)) {
- ret = 1;
- goto unlock;
- }
- }
-
-unlock:
- pfg_list_runlock();
- return ret;
-}
-
-enum pf_inst_flag {
- PIF_NONE,
- PIF_FIRST,
- PIF_SECOND,
- PIF_ADD_PFG
-};
-
-static enum pf_inst_flag pfg_check_task(struct task_struct *task)
-{
- struct pf_group *pfg;
- struct sspt_proc *proc = NULL;
- enum pf_inst_flag flag = PIF_NONE;
-
- pfg_list_rlock();
- list_for_each_entry(pfg, &pfg_list, list) {
- bool put_flag = false;
-
- if (check_task_f(&pfg->filter, task) == NULL)
- continue;
-
- if (proc == NULL) {
- proc = sspt_proc_get_by_task(task);
- put_flag = !!proc;
- }
-
- if (proc) {
- flag = flag == PIF_NONE ? PIF_SECOND : flag;
- } else if (task->tgid == task->pid) {
- proc = sspt_proc_get_by_task_or_new(task);
- if (proc == NULL) {
- printk(KERN_ERR "cannot create sspt_proc\n");
- break;
- }
- put_flag = true;
- flag = PIF_FIRST;
- }
-
- if (proc) {
- mutex_lock(&proc->filters.mtx);
- if (sspt_proc_is_filter_new(proc, pfg)) {
- img_proc_copy_to_sspt(pfg->i_proc, proc);
- sspt_proc_add_filter(proc, pfg);
- pfg_add_proc(pfg, proc);
- flag = flag == PIF_FIRST ? flag : PIF_ADD_PFG;
- }
- mutex_unlock(&proc->filters.mtx);
- if (put_flag)
- sspt_proc_put(proc);
- }
- }
- pfg_list_runlock();
-
- return flag;
-}
-
-static void pfg_all_del_proc(struct sspt_proc *proc)
-{
- struct pf_group *pfg;
-
- pfg_list_rlock();
- list_for_each_entry(pfg, &pfg_list, list)
- pfg_del_proc(pfg, proc);
- pfg_list_runlock();
-}
-
-/**
- * @brief Check task and install probes on demand
- *
- * @prarm task Pointer on the task_struct struct
- * @return Void
- */
-void check_task_and_install(struct task_struct *task)
-{
- struct sspt_proc *proc;
- enum pf_inst_flag flag;
-
- flag = pfg_check_task(task);
- switch (flag) {
- case PIF_FIRST:
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- sspt_proc_priv_create(proc);
- first_install(task, proc);
- sspt_proc_put(proc);
- }
- break;
- case PIF_ADD_PFG:
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- first_install(task, proc);
- sspt_proc_put(proc);
- }
- break;
-
- case PIF_NONE:
- case PIF_SECOND:
- break;
- }
-}
-
-/**
- * @brief Check task and install probes on demand
- *
- * @prarm task Pointer on the task_struct struct
- * @param page_addr Page fault address
- * @return Void
- */
-void call_page_fault(struct task_struct *task, unsigned long page_addr)
-{
- struct sspt_proc *proc;
- enum pf_inst_flag flag;
-
- flag = pfg_check_task(task);
- switch (flag) {
- case PIF_FIRST:
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- sspt_proc_priv_create(proc);
- first_install(task, proc);
- sspt_proc_put(proc);
- }
- break;
- case PIF_ADD_PFG:
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- first_install(task, proc);
- sspt_proc_put(proc);
- }
- break;
-
- case PIF_SECOND:
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- subsequent_install(task, proc, page_addr);
- sspt_proc_put(proc);
- }
- break;
-
- case PIF_NONE:
- break;
- }
-}
-
-/**
- * @brief Uninstall probes from the sspt_proc struct
- *
- * @prarm proc Pointer on the sspt_proc struct
- * @return Void
- */
-
-/* called with sspt_proc_write_lock() */
-void uninstall_proc(struct sspt_proc *proc)
-{
- struct task_struct *task = proc->leader;
-
- sspt_proc_uninstall(proc, task, US_UNREGS_PROBE);
- sspt_proc_cleanup(proc);
-}
-
-
-static void mmr_from_exit(struct sspt_proc *proc)
-{
- BUG_ON(proc->leader != current);
-
- sspt_proc_write_lock();
- list_del(&proc->list);
- sspt_proc_write_unlock();
-
- uninstall_proc(proc);
-
- pfg_all_del_proc(proc);
-}
-
-static void mmr_from_exec(struct sspt_proc *proc)
-{
- BUG_ON(proc->leader != current);
-
- if (proc->suspect.after_exec) {
- sspt_proc_uninstall(proc, proc->leader, US_UNREGS_PROBE);
- } else {
- mmr_from_exit(proc);
- }
-}
-
-/**
- * @brief Remove probes from the task on demand
- *
- * @prarm task Pointer on the task_struct struct
- * @return Void
- */
-void call_mm_release(struct task_struct *task)
-{
- struct sspt_proc *proc;
-
- down_read(&uninstall_sem);
- proc = sspt_proc_get_by_task(task);
- if (proc) {
- if (task->flags & PF_EXITING)
- mmr_from_exit(proc);
- else
- mmr_from_exec(proc);
- sspt_proc_put(proc);
- }
- up_read(&uninstall_sem);
-}
-
-/**
- * @brief Legacy code, it is need remove
- *
- * @param addr Page address
- * @return Void
- */
-void uninstall_page(unsigned long addr)
-{
-
-}
-
-
-static void install_cb(void *unused)
-{
- check_task_and_install(current);
-}
-
-
-
-
-struct task_item {
- struct list_head list;
- struct task_struct *task;
-};
-
-static void tasks_get(struct list_head *head)
-{
- struct task_item *item;
- struct task_struct *task;
-
- rcu_read_lock();
- for_each_process(task) {
- if (task->flags & PF_KTHREAD)
- continue;
-
- if (sspt_proc_by_task(task))
- continue;
-
- /* TODO: get rid of GFP_ATOMIC */
- item = kmalloc(sizeof(*item), GFP_ATOMIC);
- if (item == NULL) {
- WARN(1, "out of memory\n");
- goto unlock;
- }
-
- get_task_struct(task);
- item->task = task;
- list_add(&item->list, head);
- }
-
-unlock:
- rcu_read_unlock();
-}
-
-static void tasks_install_and_put(struct list_head *head)
-{
- struct task_item *item, *n;
-
- list_for_each_entry_safe(item, n, head, list) {
- int ret;
- struct task_struct *task;
-
- task = item->task;
- if (!check_task_on_filters(task))
- goto put_task;
-
- ret = taskctx_run(task, install_cb, NULL);
- if (ret) {
- pr_err("cannot tracking task[%u %u %s] ret=%d\n",
- task->tgid, task->pid, task->comm, ret);
- }
-
-put_task:
- put_task_struct(task);
- list_del(&item->list);
- kfree(item);
- }
-}
-
-static void do_install_all(void)
-{
- LIST_HEAD(head);
-
- tasks_get(&head);
- tasks_install_and_put(&head);
-}
-
-/**
- * @brief Install probes on running processes
- *
- * @return Void
- */
-void install_all(void)
-{
- int ret;
-
- ret = taskctx_get();
- if (!ret) {
- do_install_all();
- taskctx_put();
- } else {
- pr_err("taskctx_get ret=%d\n", ret);
- }
-}
-
-/**
- * @brief Uninstall probes from all processes
- *
- * @return Void
- */
-void uninstall_all(void)
-{
- struct list_head *proc_list = sspt_proc_list();
-
- down_write(&uninstall_sem);
- sspt_proc_write_lock();
- while (!list_empty(proc_list)) {
- struct sspt_proc *proc;
- proc = list_first_entry(proc_list, struct sspt_proc, list);
-
- list_del(&proc->list);
-
- sspt_proc_write_unlock();
- uninstall_proc(proc);
- sspt_proc_write_lock();
- }
- sspt_proc_write_unlock();
- up_write(&uninstall_sem);
-}
-
-static void __do_get_proc(struct sspt_proc *proc, void *data)
-{
- struct task_struct *task = proc->leader;
-
- get_task_struct(task);
- proc->__task = task;
- proc->__mm = get_task_mm(task);
-}
-
-static void __do_put_proc(struct sspt_proc *proc, void *data)
-{
- if (proc->__mm) {
- mmput(proc->__mm);
- proc->__mm = NULL;
- }
-
- if (proc->__task) {
- put_task_struct(proc->__task);
- proc->__task = NULL;
- }
-}
-
-void get_all_procs(void)
-{
- sspt_proc_read_lock();
- on_each_proc_no_lock(__do_get_proc, NULL);
- sspt_proc_read_unlock();
-}
-
-void put_all_procs(void)
-{
- sspt_proc_read_lock();
- on_each_proc_no_lock(__do_put_proc, NULL);
- sspt_proc_read_unlock();
-}
-
-/**
- * @brief For debug
- *
- * @param pfg Pointer to the pf_group struct
- * @return Void
- */
-
-/* debug */
-void pfg_print(struct pf_group *pfg)
-{
- img_proc_print(pfg->i_proc);
-}
-EXPORT_SYMBOL_GPL(pfg_print);
-/* debug */
+++ /dev/null
-/**
- * @file us_manager/pf/pf_group.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-
-#ifndef _PF_GROUP_H
-#define _PF_GROUP_H
-
-#include <linux/types.h>
-
-struct img_ip;
-struct dentry;
-struct pf_group;
-struct sspt_proc;
-struct probe_desc;
-
-
-struct pfg_msg_cb {
- void (*msg_info)(struct task_struct *task, struct dentry *dentry);
- void (*msg_status_info)(struct task_struct *task);
- void (*msg_term)(struct task_struct *task);
- void (*msg_map)(struct vm_area_struct *vma);
- void (*msg_unmap)(unsigned long start, unsigned long end);
-};
-
-
-/* FIXME: use swap_get_dentry() and swap_put_dentry() */
-struct dentry *dentry_by_path(const char *path);
-
-struct pf_group *get_pf_group_by_dentry(struct dentry *dentry, void *priv);
-struct pf_group *get_pf_group_by_tgid(pid_t tgid, void *priv);
-struct pf_group *get_pf_group_by_comm(char *comm, void *priv);
-struct pf_group *get_pf_group_dumb(void *priv);
-void put_pf_group(struct pf_group *pfg);
-bool pfg_is_unloadable(void);
-
-int pfg_msg_cb_set(struct pf_group *pfg, struct pfg_msg_cb *msg_cb);
-void pfg_msg_cb_reset(struct pf_group *pfg);
-struct pfg_msg_cb *pfg_msg_cb_get(struct pf_group *pfg);
-
-struct img_ip *pf_register_probe(struct pf_group *pfg, struct dentry *dentry,
- unsigned long offset, struct probe_desc *pd);
-void pf_unregister_probe(struct pf_group *pfg, struct img_ip *ip);
-
-void install_all(void);
-void uninstall_all(void);
-
-void get_all_procs(void);
-void put_all_procs(void);
-
-void call_page_fault(struct task_struct *task, unsigned long page_addr);
-void call_mm_release(struct task_struct *task);
-void check_task_and_install(struct task_struct *task);
-void uninstall_proc(struct sspt_proc *proc);
-
-void uninstall_page(unsigned long addr);
-
-/* debug */
-void pfg_print(struct pf_group *pfg);
-/* debug */
-
-#endif /* _PF_GROUP_H */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/pf/proc_filters.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/mm_types.h>
-#include <linux/string.h>
-#include <linux/fs.h>
-#include "proc_filters.h"
-#include <us_manager/sspt/sspt.h>
-
-
-#define VOIDP2PID(x) ((pid_t)(unsigned long)(x))
-#define PID2VOIDP(x) ((void *)(unsigned long)(x))
-
-static int check_dentry(struct task_struct *task, struct dentry *dentry)
-{
- struct vm_area_struct *vma;
- struct mm_struct *mm = task->mm;
-
- if (mm == NULL)
- return 0;
-
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
- if (check_vma(vma) && vma->vm_file->f_path.dentry == dentry)
- return 1;
- }
-
- return 0;
-}
-
-static struct task_struct *call_by_dentry(struct proc_filter *self,
- struct task_struct *task)
-{
- struct dentry *dentry = (struct dentry *)self->data;
-
- if (!dentry || check_dentry(task, dentry))
- return task;
-
- return NULL;
-}
-
-static inline void free_by_dentry(struct proc_filter *self)
-{
- return;
-}
-
-static struct task_struct *call_by_tgid(struct proc_filter *self,
- struct task_struct *task)
-{
- pid_t tgid = VOIDP2PID(self->data);
-
- if (task->tgid == tgid)
- return task;
-
- return NULL;
-}
-
-static inline void free_by_tgid(struct proc_filter *self)
-{
- return;
-}
-
-static struct task_struct *call_by_comm(struct proc_filter *self,
- struct task_struct *task)
-{
- struct task_struct *parent;
- char *comm = (char *)self->data;
- size_t len = strnlen(comm, TASK_COMM_LEN);
-
- if (!strncmp(comm, task->group_leader->comm, len))
- return task;
-
- parent = task->parent;
- if (parent && !strncmp(comm, parent->comm, len))
- return task;
-
- return NULL;
-}
-
-static inline void free_by_comm(struct proc_filter *self)
-{
- kfree(self->data);
-}
-
-/* Dumb call. Each task is exactly what we are looking for :) */
-static struct task_struct *call_dumb(struct proc_filter *self,
- struct task_struct *task)
-{
- return task;
-}
-
-/**
- * @brief Filling pf_group struct by dentry
- *
- * @param pf Pointer to the proc_filter struct
- * @param dentry Dentry
- * @param priv Private data
- * @return Void
- */
-void set_pf_by_dentry(struct proc_filter *pf, struct dentry *dentry, void *priv)
-{
- pf->call = &call_by_dentry;
- pf->data = (void *)dentry;
- pf->priv = priv;
-}
-
-/**
- * @brief Filling pf_group struct by TGID
- *
- * @param pf Pointer to the proc_filter struct
- * @param tgid Thread group ID
- * @param priv Private data
- * @return Void
- */
-void set_pf_by_tgid(struct proc_filter *pf, pid_t tgid, void *priv)
-{
- pf->call = &call_by_tgid;
- pf->data = PID2VOIDP(tgid);
- pf->priv = priv;
-}
-
-/**
- * @brief Fill proc_filter struct for given comm
- *
- * @param pf Pointer to the proc_filter struct
- * @param comm Task comm
- * @param priv Private data
- * @return 0 on suceess, error code on error.
- */
-int set_pf_by_comm(struct proc_filter *pf, char *comm, void *priv)
-{
- size_t len = strnlen(comm, TASK_COMM_LEN);
- char *new_comm = kmalloc(len, GFP_KERNEL);
-
- if (new_comm == NULL)
- return -ENOMEM;
-
- /* copy comm */
- memcpy(new_comm, comm, len - 1);
- new_comm[len - 1] = '\0';
-
- pf->call = &call_by_comm;
- pf->data = new_comm;
- pf->priv = priv;
-
- return 0;
-}
-
-/**
- * @brief Filling pf_group struct for each process
- *
- * @param pf Pointer to the proc_filter struct
- * @param priv Private data
- * @return Void
- */
-void set_pf_dumb(struct proc_filter *pf, void *priv)
-{
- pf->call = &call_dumb;
- pf->data = NULL;
- pf->priv = priv;
-}
-
-/**
- * @brief Free proc_filter struct
- *
- * @param filter Pointer to the proc_filter struct
- * @return Void
- */
-void free_pf(struct proc_filter *filter)
-{
- if (filter->call == &call_by_dentry)
- free_by_dentry(filter);
- else if (filter->call == &call_by_tgid)
- free_by_tgid(filter);
- else if (filter->call == &call_by_comm)
- free_by_comm(filter);
-}
-
-/**
- * @brief Check pf_group struct by dentry
- *
- * @param filter Pointer to the proc_filter struct
- * @param dentry Dentry
- * @return
- * - 0 - false
- * - 1 - true
- */
-int check_pf_by_dentry(struct proc_filter *filter, struct dentry *dentry)
-{
- return filter->data == (void *)dentry &&
- filter->call == &call_by_dentry;
-}
-
-/**
- * @brief Check pf_group struct by TGID
- *
- * @param filter Pointer to the proc_filter struct
- * @param tgid Thread group ID
- * @return
- * - 0 - false
- * - 1 - true
- */
-int check_pf_by_tgid(struct proc_filter *filter, pid_t tgid)
-{
- return filter->data == PID2VOIDP(tgid)
- && filter->call == &call_by_tgid;
-}
-
-/**
- * @brief Check proc_filter struct by comm
- *
- * @param filter Pointer to the proc_filter struct
- * @param comm Task comm
- * @return
- * - 0 - false
- * - 1 - true
- */
-int check_pf_by_comm(struct proc_filter *filter, char *comm)
-{
- return ((filter->call == &call_by_comm) && (filter->data != NULL) &&
- (!strncmp(filter->data, comm, TASK_COMM_LEN - 1)));
-}
-
-/**
- * @brief Dumb check always true if filter is a dumb one
- *
- * @param filter Pointer to the proc_filter struct
- * @return
- * - 0 - false
- * - 1 - true
- */
-int check_pf_dumb(struct proc_filter *filter)
-{
- return filter->call == &call_dumb;
-}
-
-/**
- * @brief Get priv from pf_group struct
- *
- * @param filter Pointer to the proc_filter struct
- * @return Pointer to the priv
- */
-void *get_pf_priv(struct proc_filter *filter)
-{
- return filter->priv;
-}
-
-/* Check function for call_page_fault() and other frequently called
-filter-check functions. It is used to call event-oriented and long-term filters
-only on specified events, but not every time memory map is changed. When
-iteraiting over the filters list, call this function on each step passing here
-pointer on filter. If it returns 1 then the filter should not be called. */
-int ignore_pf(struct proc_filter *filter)
-{
- return filter->call == &call_by_comm;
-}
+++ /dev/null
-/**
- * @file us_manager/pf/proc_filters.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-
-#ifndef _PROC_FILTERS_H
-#define _PROC_FILTERS_H
-
-#include <linux/types.h>
-
-struct dentry;
-struct task_struct;
-
-/**
- * @struct proc_filter
- * @breaf Filter for process
- */
-struct proc_filter {
- /** Callback for filtering */
- struct task_struct *(*call)(struct proc_filter *self,
- struct task_struct *task);
- void *data; /**< Data of callback */
- void *priv; /**< Private data */
-};
-
-/**
- * @def check_task_f @hideinitializer
- * Call filter on the task
- *
- * @param filter Pointer to the proc_filter struct
- * @param task Pointer to the task_struct struct
- */
-#define check_task_f(filter, task) ((filter)->call(filter, task))
-
-void set_pf_by_dentry(struct proc_filter *pf, struct dentry *dentry,
- void *priv);
-void set_pf_by_tgid(struct proc_filter *pf, pid_t tgid, void *priv);
-int set_pf_by_comm(struct proc_filter *pf, char *comm, void *priv);
-void set_pf_dumb(struct proc_filter *pf, void *priv);
-
-
-int check_pf_by_dentry(struct proc_filter *filter, struct dentry *dentry);
-int check_pf_by_tgid(struct proc_filter *filter, pid_t tgid);
-int check_pf_by_comm(struct proc_filter *filter, char *comm);
-int check_pf_dumb(struct proc_filter *filter);
-void *get_pf_priv(struct proc_filter *filter);
-
-void free_pf(struct proc_filter *filter);
-
-int ignore_pf(struct proc_filter *filter);
-
-#endif /* _PROC_FILTERS_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <us_manager/sspt/sspt_ip.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/sspt/sspt_proc.h>
-#include "probes.h"
-#include "probe_info_new.h"
-#include "register_probes.h"
-
-
-/*
- * handlers
- */
-static int urp_entry_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct uretprobe *rp = ri->rp;
-
- if (rp) {
- struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
- struct probe_desc *pd = NULL;
-
- pd = ip->desc;
- if (pd && pd->u.rp.entry_handler)
- return pd->u.rp.entry_handler(ri, regs);
-
- }
-
- return 0;
-}
-
-static int urp_ret_handler(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- struct uretprobe *rp = ri->rp;
-
- if (rp) {
- struct sspt_ip *ip = container_of(rp, struct sspt_ip, retprobe);
- struct probe_desc *pd = NULL;
-
- pd = ip->desc;
- if (pd && pd->u.rp.ret_handler)
- return pd->u.rp.ret_handler(ri, regs);
- }
-
- return 0;
-}
-
-static int uprobe_handler(struct uprobe *p, struct pt_regs *regs)
-{
- struct sspt_ip *ip = container_of(p, struct sspt_ip, uprobe);
- struct probe_desc *pd = NULL;
-
- pd = ip->desc;
- if (pd && pd->u.p.handler)
- return pd->u.p.handler(p, regs);
-
- return 0;
-}
-
-/*
- * register/unregister interface
- */
-int pin_register(struct probe_new *probe, struct pf_group *pfg,
- struct dentry *dentry)
-{
- struct img_ip *ip;
-
- ip = pf_register_probe(pfg, dentry, probe->offset, probe->desc);
- if (IS_ERR(ip)) {
- pr_err("%s: register probe failed\n", __func__);
- return PTR_ERR(ip);
- }
-
- probe->priv = ip;
- return 0;
-}
-EXPORT_SYMBOL_GPL(pin_register);
-
-void pin_unregister(struct probe_new *probe, struct pf_group *pfg)
-{
- struct img_ip *ip = probe->priv;
-
- pf_unregister_probe(pfg, ip);
-}
-EXPORT_SYMBOL_GPL(pin_unregister);
-
-
-
-
-
-/*
- * SWAP_NEW_UP
- */
-static int up_copy(struct probe_info *dst, const struct probe_info *src)
-{
- return 0;
-}
-
-static void up_cleanup(struct probe_info *probe_i)
-{
-}
-
-static struct uprobe *up_get_uprobe(struct sspt_ip *ip)
-{
- return &ip->uprobe;
-}
-
-static int up_register_probe(struct sspt_ip *ip)
-{
- return swap_register_uprobe(&ip->uprobe);
-}
-
-static void up_unregister_probe(struct sspt_ip *ip, int disarm)
-{
- __swap_unregister_uprobe(&ip->uprobe, disarm);
-}
-
-static void up_init(struct sspt_ip *ip)
-{
- ip->uprobe.pre_handler = uprobe_handler;
-}
-
-static void up_uninit(struct sspt_ip *ip)
-{
-}
-
-static struct probe_iface up_iface = {
- .init = up_init,
- .uninit = up_uninit,
- .reg = up_register_probe,
- .unreg = up_unregister_probe,
- .get_uprobe = up_get_uprobe,
- .copy = up_copy,
- .cleanup = up_cleanup
-};
-
-
-
-
-
-/*
- * SWAP_NEW_URP
- */
-static int urp_copy(struct probe_info *dst, const struct probe_info *src)
-{
- return 0;
-}
-
-static void urp_cleanup(struct probe_info *probe_i)
-{
-}
-
-static struct uprobe *urp_get_uprobe(struct sspt_ip *ip)
-{
- return &ip->retprobe.up;
-}
-
-static int urp_register_probe(struct sspt_ip *ip)
-{
- return swap_register_uretprobe(&ip->retprobe);
-}
-
-static void urp_unregister_probe(struct sspt_ip *ip, int disarm)
-{
- __swap_unregister_uretprobe(&ip->retprobe, disarm);
-}
-
-static void urp_init(struct sspt_ip *ip)
-{
- ip->retprobe.entry_handler = urp_entry_handler;
- ip->retprobe.handler = urp_ret_handler;
- ip->retprobe.maxactive = 0;
- /* FIXME: make dynamic size field 'data_size' */
-#ifdef CONFIG_ARM64
- /*
- * Loader module use field uretprobe_instance.data for storing
- * 'struct us_priv'. For ARM64 it requires much more space.
- */
- ip->retprobe.data_size = 512 - sizeof(struct uretprobe_instance);
-#else /* CONFIG_ARM64 */
- ip->retprobe.data_size = 128;
-#endif /* CONFIG_ARM64 */
-}
-
-static void urp_uninit(struct sspt_ip *ip)
-{
-}
-
-static struct probe_iface urp_iface = {
- .init = urp_init,
- .uninit = urp_uninit,
- .reg = urp_register_probe,
- .unreg = urp_unregister_probe,
- .get_uprobe = urp_get_uprobe,
- .copy = urp_copy,
- .cleanup = urp_cleanup
-};
-
-
-
-
-/*
- * init/exit()
- */
-int pin_init(void)
-{
- int ret;
-
- ret = swap_register_probe_type(SWAP_NEW_UP, &up_iface);
- if (ret)
- return ret;
-
- ret = swap_register_probe_type(SWAP_NEW_URP, &urp_iface);
- if (ret)
- swap_unregister_probe_type(SWAP_NEW_UP);
-
- return ret;
-}
-
-void pin_exit(void)
-{
- swap_unregister_probe_type(SWAP_NEW_URP);
- swap_unregister_probe_type(SWAP_NEW_UP);
-}
+++ /dev/null
-#ifndef _PROBE_INFO_NEW_H
-#define _PROBE_INFO_NEW_H
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <uprobe/swap_uprobes.h>
-#include "probes.h"
-
-
-struct dentry;
-struct pf_group;
-
-
-struct probe_info_new {
- enum probe_t type;
- union {
- struct {
- uprobe_pre_handler_t handler;
- } p;
-
- struct {
- uretprobe_handler_t entry_handler;
- uretprobe_handler_t ret_handler;
- /*
- * FIXME: make dynamic size,
- * currently data_size = sizeof(void *)
- */
- size_t data_size;
- } rp;
- } u;
-
- /* private */
- struct probe_info info;
-};
-
-struct probe_new {
- /* reg data */
- unsigned long offset;
- struct probe_desc *desc;
-
- /* unreg data */
- void *priv;
-};
-
-
-#define MAKE_UPROBE(_handler) \
- { \
- .type = SWAP_NEW_UP, \
- .u.p.handler = _handler \
- }
-
-#define MAKE_URPROBE(_entry, _ret, _size) \
- { \
- .type = SWAP_NEW_URP, \
- .u.rp.entry_handler = _entry, \
- .u.rp.ret_handler = _ret, \
- .u.rp.data_size = _size \
- }
-
-struct probe_info_otg {
- struct probe_info info;
- struct probe_info_new *data; /* field 'data[0]' in probe_info struct */
-};
-
-int pin_register(struct probe_new *probe, struct pf_group *pfg,
- struct dentry *dentry);
-void pin_unregister(struct probe_new *probe, struct pf_group *pfg);
-
-
-int pin_init(void);
-void pin_exit(void);
-
-
-#endif /* _PROBE_INFO_NEW_H */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/probes/probes.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov: Probes interface implement
- *
- */
-
-#include "probes.h"
-#include "register_probes.h"
-#include "use_probes.h"
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-
-static struct probe_iface *probes_methods[SWAP_PROBE_MAX_VAL] = { NULL };
-
-/* 1 - correct probe type
- 0 - wrong probe type
-*/
-static inline int correct_probe_type(enum probe_t probe_type)
-{
- if (probe_type >= SWAP_PROBE_MAX_VAL)
- return 0;
-
- return 1;
-}
-
-static inline int methods_exist(enum probe_t probe_type)
-{
- if (!correct_probe_type(probe_type) ||
- (probes_methods[probe_type] == NULL)) {
- printk(KERN_WARNING "SWAP US_MANAGER: Wrong probe type!\n");
- return 0;
- }
-
- return 1;
-}
-
-/**
- * @brief Calls specified probe type init method.
- *
- * @param pi Pointer to the probe_info.
- * @param ip Pointer to the probe us_ip struct.
- * @return Void.
- */
-void probe_info_init(enum probe_t type, struct sspt_ip *ip)
-{
- if (!methods_exist(type)) {
- return;
- }
-
- probes_methods[type]->init(ip);
-}
-
-/**
- * @brief Calls specified probe type uninit method.
- *
- * @param pi Pointer to the probe_info.
- * @param ip Pointer to the probe us_ip struct.
- * @return Void.
- */
-void probe_info_uninit(enum probe_t type, struct sspt_ip *ip)
-{
- if (!methods_exist(type)) {
- return;
- }
-
- probes_methods[type]->uninit(ip);
-}
-
-/**
- * @brief Calls specified probe type register method.
- *
- * @param pi Pointer to the probe_info.
- * @param ip Pointer to the probe us_ip struct.
- * @return -EINVAL on wrong probe type, method result otherwise.
- */
-int probe_info_register(enum probe_t type, struct sspt_ip *ip)
-{
- if (!methods_exist(type)) {
- return -EINVAL;
- }
-
- return probes_methods[type]->reg(ip);
-}
-
-/**
- * @brief Calls specified probe type unregister method.
- *
- * @param pi Pointer to the probe_info.
- * @param ip Pointer to the probe us_ip struct.
- * @param disarm Disarm flag.
- * @return Void.
- */
-void probe_info_unregister(enum probe_t type, struct sspt_ip *ip, int disarm)
-{
- if (!methods_exist(type)) {
- return;
- }
-
- probes_methods[type]->unreg(ip, disarm);
-}
-
-/**
- * @brief Calls specified probe type get underlying uprobe method.
- *
- * @param pi Pointer to the probe_info.
- * @param ip Pointer to the probe us_ip struct.
- * @return Pointer to the uprobe struct, NULL on error.
- */
-struct uprobe *probe_info_get_uprobe(enum probe_t type, struct sspt_ip *ip)
-{
- if (!methods_exist(type)) {
- return NULL;
- }
-
- return probes_methods[type]->get_uprobe(ip);
-}
-
-/**
- * @brief Registers probe type.
- *
- * @param probe_type Number, associated with this probe type.
- * @param pi Pointer to the probe interface structure
- * @return 0 on succes, error code on error.
- */
-int swap_register_probe_type(enum probe_t probe_type, struct probe_iface *pi)
-{
- if (!correct_probe_type(probe_type)) {
- printk(KERN_ERR "SWAP US_MANAGER: incorrect probe type!\n");
- return -EINVAL;
- }
-
- if (probes_methods[probe_type] != NULL)
- printk(KERN_WARNING "SWAP US_MANAGER: Re-registering probe %d\n",
- probe_type);
-
- probes_methods[probe_type] = pi;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(swap_register_probe_type);
-
-/**
- * @brief Unregisters probe type.
- *
- * @param probe_type Probe type that should be unregistered.
- * @return Void.
- */
-void swap_unregister_probe_type(enum probe_t probe_type)
-{
- if (!correct_probe_type(probe_type)) {
- printk(KERN_ERR "SWAP US_MANAGER: incorrect probe type!\n");
- return;
- }
-
- probes_methods[probe_type] = NULL;
-}
-EXPORT_SYMBOL_GPL(swap_unregister_probe_type);
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/probes/probes.h
- *
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2014
- *
- * 2014 Alexander Aksenov : Probes interface implement
- * 2014 Vitaliy Cherepanov: Portage
- *
- */
-
-
-#ifndef __PROBES_H__
-#define __PROBES_H__
-
-#include <linux/types.h>
-#include <uprobe/swap_uprobes.h>
-
-#include <preload/preload_probe.h> /* TODO Remove */
-#include <retprobe/retprobe.h> /* TODO Remove */
-#include <fbiprobe/fbiprobe.h> /* TODO Remove */
-
-
-
-/* All probe types. Only us_manager should know about them - it is its own
- * business to install proper probes on proper places.
- */
-enum probe_t {
- SWAP_RETPROBE = 0, /* Retprobe */
- SWAP_FBIPROBE = 1, /* FBI probe */
- SWAP_PRELOAD_PROBE = 2, /* Preload probe */
- SWAP_GET_CALLER = 4, /* Get caller probe. Supports preload */
- SWAP_GET_CALL_TYPE = 5, /* Get call type probe. Supports preload */
- SWAP_WRITE_MSG = 6, /* Write messages from user space directly to
- * kernel. Supports preload */
- SWAP_NEW_UP,
- SWAP_NEW_URP,
- SWAP_PROBE_MAX_VAL /* Probes max value. */
-};
-
-/* Probe info stuct. It contains the whole information about probe. */
-struct probe_info {
- /* Union of all SWAP supported probe types */
- union {
- struct retprobe_info rp_i;
- struct fbi_info fbi_i;
- struct preload_info pl_i;
- struct get_caller_info gc_i;
- struct get_call_type_info gct_i;
- struct write_msg_info wm_i;
- };
-};
-
-struct probe_desc {
- enum probe_t type;
-
- union {
- struct {
- uprobe_pre_handler_t handler;
- } p;
-
- struct {
- uretprobe_handler_t entry_handler;
- uretprobe_handler_t ret_handler;
- /*
- * FIXME: make dynamic size,
- * currently data_size = sizeof(void *)
- */
- size_t data_size;
- } rp;
- } u;
-
- struct probe_info info;
-};
-
-#endif /* __PROBES_H__ */
+++ /dev/null
-#ifndef __REGISTER_PROBES_H__
-#define __REGISTER_PROBES_H__
-
-#include "probes.h"
-
-struct sspt_ip;
-
-struct probe_iface {
- void (*init)(struct sspt_ip *);
- void (*uninit)(struct sspt_ip *);
- int (*reg)(struct sspt_ip *);
- void (*unreg)(struct sspt_ip *, int);
- struct uprobe *(*get_uprobe)(struct sspt_ip *);
- int (*copy)(struct probe_info *, const struct probe_info *);
- void (*cleanup)(struct probe_info *);
-};
-
-int swap_register_probe_type(enum probe_t probe_type, struct probe_iface *pi);
-void swap_unregister_probe_type(enum probe_t probe_type);
-
-#endif /* __REGISTER_PROBES_H__ */
+++ /dev/null
-#ifndef __USE_PROBES_H__
-#define __USE_PROBES_H__
-
-#include "probes.h"
-
-struct sspt_ip;
-
-void probe_info_init(enum probe_t type, struct sspt_ip *ip);
-void probe_info_uninit(enum probe_t type, struct sspt_ip *ip);
-int probe_info_register(enum probe_t type, struct sspt_ip *ip);
-void probe_info_unregister(enum probe_t type, struct sspt_ip *ip, int disarm);
-struct uprobe *probe_info_get_uprobe(enum probe_t type, struct sspt_ip *ip);
-int probe_info_copy(const struct probe_info *pi, struct probe_info *dest);
-void probe_info_cleanup(struct probe_info *pi);
-
-#endif /* __USE_PROBES_H__ */
+++ /dev/null
-#ifndef __SSPT__
-#define __SSPT__
-
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/driver/sspt/sspt.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include "sspt_ip.h"
-#include "sspt_page.h"
-#include "sspt_file.h"
-#include "sspt_proc.h"
-#include "sspt_debug.h"
-#include <uprobe/swap_uprobes.h>
-
-
-#include <us_manager/us_manager.h>
-#include <us_manager/pf/pf_group.h>
-#include <us_manager/probes/use_probes.h>
-
-
-static inline int check_vma(struct vm_area_struct *vma)
-{
- return vma->vm_file &&
- !(vma->vm_pgoff != 0 ||
- !(vma->vm_flags & VM_EXEC) ||
- !(vma->vm_flags & (VM_READ | VM_MAYREAD)));
-}
-
-static inline int sspt_register_usprobe(struct sspt_ip *ip)
-{
- int ret;
- struct uprobe *up = NULL;
-
- up = probe_info_get_uprobe(ip->desc->type, ip);
-
- if (!up) {
- printk(KERN_INFO "SWAP US_MANAGER: failed getting uprobe!\n");
- return -EINVAL;
- }
-
- up->addr = (uprobe_opcode_t *)ip->orig_addr;
- up->task = ip->page->file->proc->leader;
- up->sm = ip->page->file->proc->sm;
-
- ret = probe_info_register(ip->desc->type, ip);
- if (ret) {
- struct sspt_file *file = ip->page->file;
- char *name = file->dentry->d_iname;
- unsigned long addr = (unsigned long)up->addr;
- unsigned long offset = addr - file->vm_start;
-
- printk(KERN_ERR "probe_info_register failed %d (%s:%lx|%lx)\n",
- ret, name, offset,
- (unsigned long)ip->retprobe.up.opcode);
- }
-
- return ret;
-}
-
-static inline int sspt_unregister_usprobe(struct task_struct *task,
- struct sspt_ip *ip,
- enum US_FLAGS flag)
-{
- struct uprobe *up = NULL;
-
- switch (flag) {
- case US_UNREGS_PROBE:
- probe_info_unregister(ip->desc->type, ip, 1);
- break;
- case US_DISARM:
- up = probe_info_get_uprobe(ip->desc->type, ip);
- if (up)
- disarm_uprobe(up, task);
- break;
- case US_UNINSTALL:
- probe_info_unregister(ip->desc->type, ip, 0);
- break;
- default:
- panic("incorrect value flag=%d", flag);
- }
-
- return 0;
-}
-
-#endif /* __SSPT__ */
+++ /dev/null
-#ifndef __SSPT_DEBUG__
-#define __SSPT_DEBUG__
-
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/driver/sspt/sspt_debug.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include <kprobe/swap_kprobes_deps.h>
-#include <us_manager/probes/probes.h>
-
-
-static inline void print_retprobe(struct uretprobe *rp)
-{
- printk(KERN_INFO "### RP: handler=%lx\n",
- (unsigned long)rp->handler);
-}
-
-static inline void print_ip(struct sspt_ip *ip, int i)
-{
- if (ip->desc->type == SWAP_RETPROBE) {
- struct uretprobe *rp = &ip->retprobe;
-
- printk(KERN_INFO "### addr[%2d]=%lx, R_addr=%lx\n",
- i, (unsigned long)ip->offset,
- (unsigned long)rp->up.addr);
- print_retprobe(rp);
- }
-}
-
-static inline void print_page_probes(const struct sspt_page *page)
-{
- int i = 0;
- struct sspt_ip *ip;
-
- printk(KERN_INFO "### offset=%lx\n", page->offset);
- printk(KERN_INFO "### no install:\n");
- list_for_each_entry(ip, &page->ip_list.not_inst, list) {
- print_ip(ip, i);
- ++i;
- }
-
- printk(KERN_INFO "### install:\n");
- list_for_each_entry(ip, &page->ip_list.inst, list) {
- print_ip(ip, i);
- ++i;
- }
-}
-
-static inline void print_file_probes(struct sspt_file *file)
-{
- int i;
- unsigned long table_size;
- struct sspt_page *page = NULL;
- struct hlist_head *head = NULL;
- static unsigned char *NA = "N/A";
- unsigned char *name;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- if (file == NULL) {
- printk(KERN_INFO "### file_p == NULL\n");
- return;
- }
-
- table_size = (1 << file->htable.bits);
- name = (file->dentry) ? file->dentry->d_iname : NA;
-
- printk(KERN_INFO "### print_file_probes: path=%s, d_iname=%s, "
- "table_size=%lu, vm_start=%lx\n",
- file->dentry->d_iname, name, table_size, file->vm_start);
-
- down_read(&file->htable.sem);
- for (i = 0; i < table_size; ++i) {
- head = &file->htable.heads[i];
- swap_hlist_for_each_entry_rcu(page, node, head, hlist) {
- print_page_probes(page);
- }
- }
- up_read(&file->htable.sem);
-}
-
-static inline void print_proc_probes(struct sspt_proc *proc)
-{
- struct sspt_file *file;
-
- printk(KERN_INFO "### print_proc_probes\n");
- down_read(&proc->files.sem);
- list_for_each_entry(file, &proc->files.head, list) {
- print_file_probes(file);
- }
- up_read(&proc->files.sem);
- printk(KERN_INFO "### print_proc_probes\n");
-}
-
-
-#endif /* __SSPT_DEBUG__ */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/us_manager/sspt/sspt_feature.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include "sspt_feature.h"
-#include "sspt_proc.h"
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-
-
-struct sspt_feature {
- struct list_head feature_list;
-};
-
-struct sspt_feature_img {
- struct list_head list;
-
- void *(*alloc)(void);
- void (*free)(void *data);
-};
-
-struct sspt_feature_data {
- struct list_head list;
-
- struct sspt_feature_img *img;
- void *data;
-};
-
-static DEFINE_SPINLOCK(feature_img_lock);
-static LIST_HEAD(feature_img_list);
-
-static struct sspt_feature_data *create_feature_data(
- struct sspt_feature_img *img)
-{
- struct sspt_feature_data *fd;
-
- fd = kmalloc(sizeof(*fd), GFP_ATOMIC);
- if (fd) {
- INIT_LIST_HEAD(&fd->list);
- fd->img = img;
- fd->data = img->alloc();
- }
-
- return fd;
-}
-
-static void destroy_feature_data(struct sspt_feature_data *fd)
-{
- fd->img->free(fd->data);
- kfree(fd);
-}
-
-/**
- * @brief Create sspt_feature struct
- *
- * @return Pointer to the created sspt_feature struct
- */
-struct sspt_feature *sspt_create_feature(void)
-{
- struct sspt_feature *f;
-
- f = kmalloc(sizeof(*f), GFP_ATOMIC);
- if (f) {
- struct sspt_feature_data *fd;
- struct sspt_feature_img *fi;
- unsigned long flags;
-
- INIT_LIST_HEAD(&f->feature_list);
-
- spin_lock_irqsave(&feature_img_lock, flags);
- list_for_each_entry(fi, &feature_img_list, list) {
- fd = create_feature_data(fi);
- if (fd) /* add to list */
- list_add(&fd->list, &f->feature_list);
- }
- spin_unlock_irqrestore(&feature_img_lock, flags);
- }
-
- return f;
-}
-
-/**
- * @brief Destroy sspt_feature struct
- *
- * @param f remove object
- * @return Void
- */
-void sspt_destroy_feature(struct sspt_feature *f)
-{
- struct sspt_feature_data *fd, *n;
-
- list_for_each_entry_safe(fd, n, &f->feature_list, list) {
- /* delete from list */
- list_del(&fd->list);
- destroy_feature_data(fd);
- }
-
- kfree(f);
-}
-
-static void add_feature_img_to_list(struct sspt_feature_img *fi)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&feature_img_lock, flags);
- list_add(&fi->list, &feature_img_list);
- spin_unlock_irqrestore(&feature_img_lock, flags);
-}
-
-static void del_feature_img_from_list(struct sspt_feature_img *fi)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&feature_img_lock, flags);
- list_del(&fi->list);
- spin_unlock_irqrestore(&feature_img_lock, flags);
-}
-
-static struct sspt_feature_img *create_feature_img(void *(*alloc)(void),
- void (*free)(void *data))
-{
- struct sspt_feature_img *fi;
-
- fi = kmalloc(sizeof(*fi), GFP_ATOMIC);
- if (fi) {
- INIT_LIST_HEAD(&fi->list);
- fi->alloc = alloc;
- fi->free = free;
-
- add_feature_img_to_list(fi);
- }
-
- return fi;
-}
-
-static void destroy_feature_img(struct sspt_feature_img *fi)
-{
- del_feature_img_from_list(fi);
-
- kfree(fi);
-}
-
-static void del_feature_by_img(struct sspt_feature *f,
- struct sspt_feature_img *img)
-{
- struct sspt_feature_data *fd;
-
- list_for_each_entry(fd, &f->feature_list, list) {
- if (img == fd->img) {
- /* delete from list */
- list_del(&fd->list);
- destroy_feature_data(fd);
- break;
- }
- }
-}
-
-static void del_feature_from_proc(struct sspt_proc *proc, void *data)
-{
- del_feature_by_img(proc->feature, (struct sspt_feature_img *)data);
-}
-
-/**
- * @brief Get data for feature
- *
- * @param f Pointer to the sspt_feature struct
- * @param id Feature ID
- * @return Pointer to the data
- */
-void *sspt_get_feature_data(struct sspt_feature *f, sspt_feature_id_t id)
-{
- struct sspt_feature_img *img = (struct sspt_feature_img *)id;
- struct sspt_feature_data *fd;
-
- list_for_each_entry(fd, &f->feature_list, list) {
- if (img == fd->img)
- return fd->data;
- }
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sspt_get_feature_data);
-
-/**
- * @brief Register sspt feature
- *
- * @param alloc Callback for allocating data
- * @param free Callback to release data
- * @return Feature ID
- */
-sspt_feature_id_t sspt_register_feature(void *(*alloc)(void),
- void (*free)(void *data))
-{
- struct sspt_feature_img *fi;
-
- fi = create_feature_img(alloc, free);
-
- /* TODO: add to already instrumentation process */
-
- return (sspt_feature_id_t)fi;
-}
-EXPORT_SYMBOL_GPL(sspt_register_feature);
-
-/**
- * @brief Unregister sspt feature
- *
- * @param id Feature ID
- * @return Void
- */
-void sspt_unregister_feature(sspt_feature_id_t id)
-{
- struct sspt_feature_img *fi = (struct sspt_feature_img *)id;
-
- on_each_proc(del_feature_from_proc, (void *)fi);
- destroy_feature_img(fi);
-}
-EXPORT_SYMBOL_GPL(sspt_unregister_feature);
+++ /dev/null
-#ifndef _SSPT_FEATUER_H
-#define _SSPT_FEATUER_H
-
-/**
- * @file us_manager/sspt/sspt_feature.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-struct sspt_feature;
-
-typedef void *sspt_feature_id_t; /**< @brief sspt feature ID type */
-#define SSPT_FEATURE_ID_BAD NULL /**< @def SSPT_FEATURE_ID_BAD */
-
-struct sspt_feature *sspt_create_feature(void);
-void sspt_destroy_feature(struct sspt_feature *f);
-
-void *sspt_get_feature_data(struct sspt_feature *f, sspt_feature_id_t id);
-sspt_feature_id_t sspt_register_feature(void *(*alloc)(void),
- void (*free)(void *data));
-void sspt_unregister_feature(sspt_feature_id_t id);
-
-#endif /* _SSPT_FEATUER_H */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/driver/sspt/sspt_file.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include "sspt.h"
-#include "sspt_file.h"
-#include "sspt_page.h"
-#include "sspt_proc.h"
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/hash.h>
-#include <linux/sched.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include <us_manager/probes/probes.h>
-#include <us_manager/img/img_ip.h>
-
-static int calculation_hash_bits(int cnt)
-{
- int bits;
- for (bits = 1; cnt >>= 1; ++bits)
- ;
-
- return bits;
-}
-
-static unsigned long htable_size(const struct sspt_file *file)
-{
- return 1 << file->htable.bits;
-}
-
-static struct hlist_head *htable_head_by_idx(const struct sspt_file *file,
- unsigned long idx)
-{
- return &file->htable.heads[idx];
-}
-
-static struct hlist_head *htable_head_by_key(const struct sspt_file *file,
- unsigned long offset)
-{
- unsigned long idx = hash_ptr((void *)offset, file->htable.bits);
-
- return htable_head_by_idx(file, idx);
-}
-
-/**
- * @brief Create sspt_file struct
- *
- * @param dentry Dentry of file
- * @param page_cnt Size of hash-table
- * @return Pointer to the created sspt_file struct
- */
-struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt)
-{
- int i, table_size;
- struct hlist_head *heads;
- struct sspt_file *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
-
- if (obj == NULL)
- return NULL;
-
- INIT_LIST_HEAD(&obj->list);
- obj->proc = NULL;
- obj->dentry = dentry;
- obj->loaded = 0;
- obj->vm_start = 0;
- obj->vm_end = 0;
-
- obj->htable.bits = calculation_hash_bits(page_cnt);
- table_size = htable_size(obj);
-
- heads = kmalloc(sizeof(*obj->htable.heads) * table_size, GFP_ATOMIC);
- if (heads == NULL)
- goto err;
-
- for (i = 0; i < table_size; ++i)
- INIT_HLIST_HEAD(&heads[i]);
-
- obj->htable.heads = heads;
- init_rwsem(&obj->htable.sem);
-
- return obj;
-
-err:
- kfree(obj);
- return NULL;
-}
-
-/**
- * @brief Remove sspt_file struct
- *
- * @param file remove object
- * @return Void
- */
-void sspt_file_free(struct sspt_file *file)
-{
- struct hlist_head *head;
- struct sspt_page *page;
- int i, table_size = htable_size(file);
- struct hlist_node *n;
- DECLARE_NODE_PTR_FOR_HLIST(p);
-
- down_write(&file->htable.sem);
- for (i = 0; i < table_size; ++i) {
- head = htable_head_by_idx(file, i);
- swap_hlist_for_each_entry_safe(page, p, n, head, hlist) {
- hlist_del(&page->hlist);
- sspt_page_clean(page);
- sspt_page_put(page);
- }
- }
- up_write(&file->htable.sem);
-
- kfree(file->htable.heads);
- kfree(file);
-}
-
-static void sspt_add_page(struct sspt_file *file, struct sspt_page *page)
-{
- page->file = file;
- hlist_add_head(&page->hlist, htable_head_by_key(file, page->offset));
-}
-
-static struct sspt_page *sspt_find_page(struct sspt_file *file,
- unsigned long offset)
-{
- struct hlist_head *head = htable_head_by_key(file, offset);
- struct sspt_page *page;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- swap_hlist_for_each_entry(page, node, head, hlist) {
- if (page->offset == offset)
- return page;
- }
-
- return NULL;
-}
-
-static struct sspt_page *sspt_find_page_or_new(struct sspt_file *file,
- unsigned long offset)
-{
- struct sspt_page *page;
-
- down_write(&file->htable.sem);
- page = sspt_find_page(file, offset);
- if (page == NULL) {
- page = sspt_page_create(offset);
- if (page)
- sspt_add_page(file, page);
- }
- up_write(&file->htable.sem);
-
- return page;
-}
-
-/**
- * @brief Get sspt_page from sspt_file
- *
- * @param file Pointer to the sspt_file struct
- * @param page Page address
- * @return Pointer to the sspt_page struct
- */
-struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
- unsigned long page)
-{
- unsigned long offset;
- struct sspt_page *p;
-
- if (file->vm_start > page || file->vm_end < page) {
- /* TODO: or panic?! */
- printk(KERN_INFO "ERROR: file_p[vm_start..vm_end] <> page: "
- "file_p[vm_start=%lx, vm_end=%lx, "
- "d_iname=%s] page=%lx\n",
- file->vm_start, file->vm_end,
- file->dentry->d_iname, page);
- return NULL;
- }
-
- offset = page - file->vm_start;
-
- down_read(&file->htable.sem);
- p = sspt_find_page(file, offset);
- up_read(&file->htable.sem);
-
- return p;
-}
-
-/**
- * @brief Add instruction pointer to sspt_file
- *
- * @param file Pointer to the sspt_file struct
- * @param offset File offset
- * @param args Function arguments
- * @param ret_type Return type
- * @return Void
- */
-void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip)
-{
- unsigned long offset = 0;
- struct sspt_page *page = NULL;
- struct sspt_ip *ip = NULL;
-
- offset = img_ip->addr & PAGE_MASK;
- page = sspt_find_page_or_new(file, offset);
- if (!page)
- return;
-
- ip = sspt_ip_create(img_ip, page);
- if (!ip)
- return;
-
- probe_info_init(ip->desc->type, ip);
-}
-
-void sspt_file_on_each_ip(struct sspt_file *file,
- void (*func)(struct sspt_ip *, void *), void *data)
-{
- int i;
- const int table_size = htable_size(file);
- struct sspt_page *page;
- struct hlist_head *head;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- down_read(&file->htable.sem);
- for (i = 0; i < table_size; ++i) {
- head = htable_head_by_idx(file, i);
- swap_hlist_for_each_entry(page, node, head, hlist)
- sspt_page_on_each_ip(page, func, data);
- }
- up_read(&file->htable.sem);
-}
-
-/**
- * @brief Check install sspt_file (legacy code, it is need remove)
- *
- * @param file Pointer to the sspt_file struct
- * @return
- * - 0 - false
- * - 1 - true
- */
-int sspt_file_check_install_pages(struct sspt_file *file)
-{
- int ret = 0;
- int i, table_size;
- struct sspt_page *page;
- struct hlist_head *head;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- table_size = htable_size(file);
-
- down_read(&file->htable.sem);
- for (i = 0; i < table_size; ++i) {
- head = htable_head_by_idx(file, i);
- swap_hlist_for_each_entry(page, node, head, hlist) {
- if (sspt_page_is_installed(page)) {
- ret = 1;
- goto unlock;
- }
- }
- }
-
-unlock:
- up_read(&file->htable.sem);
- return ret;
-}
-
-/**
- * @brief Install sspt_file
- *
- * @param file Pointer to the sspt_file struct
- * @return Void
- */
-void sspt_file_install(struct sspt_file *file)
-{
- struct sspt_page *page = NULL;
- struct hlist_head *head = NULL;
- int i, table_size = htable_size(file);
- unsigned long page_addr;
- struct mm_struct *mm;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- down_read(&file->htable.sem);
- for (i = 0; i < table_size; ++i) {
- head = htable_head_by_idx(file, i);
- swap_hlist_for_each_entry(page, node, head, hlist) {
- unsigned long offset = page->offset;
-
-#ifdef CONFIG_64BIT
- /* Reset most significant bit for only 64-bit */
- offset &= (~(1UL << 63));
-#endif /* CONFIG_64BIT */
-
- page_addr = file->vm_start + offset;
- if (page_addr < file->vm_start ||
- page_addr >= file->vm_end)
- continue;
-
- mm = page->file->proc->leader->mm;
- if (page_present(mm, page_addr))
- sspt_register_page(page, file);
- }
- }
- up_read(&file->htable.sem);
-}
-
-/**
- * @brief Uninstall sspt_file
- *
- * @param file Pointer to the sspt_file struct
- * @param task Pointer to the task_stract struct
- * @param flag Action for probes
- * @return Void
- */
-int sspt_file_uninstall(struct sspt_file *file,
- struct task_struct *task,
- enum US_FLAGS flag)
-{
- int i, err = 0;
- int table_size = htable_size(file);
- struct sspt_page *page;
- struct hlist_head *head;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- down_read(&file->htable.sem);
- for (i = 0; i < table_size; ++i) {
- head = htable_head_by_idx(file, i);
- swap_hlist_for_each_entry(page, node, head, hlist) {
- err = sspt_unregister_page(page, flag, task);
- if (err != 0) {
- printk(KERN_INFO "ERROR sspt_file_uninstall: "
- "err=%d\n", err);
- up_read(&file->htable.sem);
- return err;
- }
- }
- }
- up_read(&file->htable.sem);
-
- if (flag != US_DISARM) {
- file->loaded = 0;
- file->vm_start = 0;
- file->vm_end = 0;
- }
-
- return err;
-}
-
-/**
- * @brief Set mapping for sspt_file
- *
- * @param file Pointer to the sspt_file struct
- * @param vma Pointer to the vm_area_struct struct
- * @return Void
- */
-void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma)
-{
- if (file->loaded == 0) {
- file->loaded = 1;
- file->vm_start = vma->vm_start;
- file->vm_end = vma->vm_end;
- }
-}
+++ /dev/null
-#ifndef __SSPT_FILE__
-#define __SSPT_FILE__
-
-/**
- * @file us_manager/sspt/sspt_file.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-#include "sspt_ip.h"
-#include <linux/types.h>
-#include <linux/rwsem.h>
-
-enum US_FLAGS;
-struct vm_area_struct;
-
-/**
- * @struct sspt_file
- * @breaf Image of file for specified process
- */
-struct sspt_file {
- /* sspt_proc */
- struct list_head list; /**< For sspt_proc */
- struct sspt_proc *proc; /**< Pointer to the proc (parent) */
-
- /* sspt_page */
- struct {
- struct rw_semaphore sem; /**< Semaphore for hash-table */
- unsigned long bits; /**< Hash-table size */
- struct hlist_head *heads; /**< Heads for pages */
- } htable;
-
- struct dentry *dentry; /**< Dentry of file */
- unsigned long vm_start; /**< VM start */
- unsigned long vm_end; /**< VM end */
- unsigned loaded:1; /**< Flag of loading */
-};
-
-
-struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt);
-void sspt_file_free(struct sspt_file *file);
-
-struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
- unsigned long page);
-void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip);
-
-void sspt_file_on_each_ip(struct sspt_file *file,
- void (*func)(struct sspt_ip *, void *), void *data);
-
-int sspt_file_check_install_pages(struct sspt_file *file);
-void sspt_file_install(struct sspt_file *file);
-int sspt_file_uninstall(struct sspt_file *file,
- struct task_struct *task,
- enum US_FLAGS flag);
-void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma);
-
-#endif /* __SSPT_FILE__ */
+++ /dev/null
-#include <linux/list.h>
-#include <linux/slab.h>
-#include "sspt_filter.h"
-#include "sspt_proc.h"
-#include "../pf/pf_group.h"
-
-
-struct sspt_filter *sspt_filter_create(struct sspt_proc *proc,
- struct pf_group *pfg)
-{
- struct sspt_filter *fl;
-
- fl = kmalloc(sizeof(*fl), GFP_ATOMIC);
- if (fl == NULL)
- return NULL;
-
- INIT_LIST_HEAD(&fl->list);
-
- fl->proc = proc;
- fl->pfg = pfg;
- fl->pfg_is_inst = false;
-
- return fl;
-}
-
-void sspt_filter_free(struct sspt_filter *fl)
-{
- if (fl->pfg_is_inst) {
- struct pfg_msg_cb *cb = pfg_msg_cb_get(fl->pfg);
-
- if (cb && cb->msg_term)
- cb->msg_term(fl->proc->leader);
- }
-
- kfree(fl);
-}
+++ /dev/null
-#ifndef __SSPT_FILTER_H__
-#define __SSPT_FILTER_H__
-
-#include <linux/types.h>
-
-struct pf_group;
-struct sspt_proc;
-
-struct sspt_filter {
- struct list_head list;
- struct sspt_proc *proc;
- struct pf_group *pfg;
- bool pfg_is_inst;
-};
-
-struct sspt_filter *sspt_filter_create(struct sspt_proc *proc,
- struct pf_group *pfg);
-void sspt_filter_free(struct sspt_filter *fl);
-
-#endif /* __SSPT_FILTER_H__ */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/driver/sspt/ip.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include "sspt_ip.h"
-#include "sspt_page.h"
-#include "sspt_file.h"
-#include <us_manager/probes/use_probes.h>
-#include <us_manager/img/img_ip.h>
-
-/**
- * @brief Create us_ip struct
- *
- * @param page User page
- * @param offset Function offset from the beginning of the page
- * @param probe_i Pointer to the probe data.
- * @param page Pointer to the parent sspt_page struct
- * @return Pointer to the created us_ip struct
- */
-struct sspt_ip *sspt_ip_create(struct img_ip *img_ip, struct sspt_page *page)
-{
- struct sspt_ip *ip;
-
- ip = kmalloc(sizeof(*ip), GFP_ATOMIC);
- if (!ip)
- return NULL;
-
- memset(ip, 0, sizeof(*ip));
- INIT_LIST_HEAD(&ip->list);
- INIT_LIST_HEAD(&ip->img_list);
- ip->offset = img_ip->addr & ~PAGE_MASK;
- ip->desc = img_ip->desc;
- atomic_set(&ip->usage, 2); /* for 'img_ip' and 'page' */
-
- /* add to img_ip list */
- img_ip_get(img_ip);
- img_ip_lock(img_ip);
- img_ip_add_ip(img_ip, ip);
- img_ip_unlock(img_ip);
-
- /* add to page list */
- sspt_page_get(page);
- sspt_page_lock(page);
- sspt_page_add_ip(page, ip);
- sspt_page_unlock(page);
-
- return ip;
-}
-
-static void sspt_ip_free(struct sspt_ip *ip)
-{
- WARN_ON(!list_empty(&ip->list) || !list_empty(&ip->img_list));
-
- kfree(ip);
-}
-
-void sspt_ip_get(struct sspt_ip *ip)
-{
- atomic_inc(&ip->usage);
-}
-
-void sspt_ip_put(struct sspt_ip *ip)
-{
- if (atomic_dec_and_test(&ip->usage))
- sspt_ip_free(ip);
-}
-
-void sspt_ip_clean(struct sspt_ip *ip)
-{
- bool put_page = false;
- bool put_ip = false;
-
- /* remove from page */
- sspt_page_lock(ip->page);
- if (!list_empty(&ip->list)) {
- list_del_init(&ip->list);
- put_page = true;
- }
- sspt_page_unlock(ip->page);
- if (put_page) {
- sspt_page_put(ip->page);
- sspt_ip_put(ip);
- }
-
- /* remove from img_ip */
- img_ip_lock(ip->img_ip);
- if (!list_empty(&ip->img_list)) {
- list_del_init(&ip->img_list);
- put_ip = true;
- }
- img_ip_unlock(ip->img_ip);
- if (put_ip) {
- img_ip_put(ip->img_ip);
- sspt_ip_put(ip);
- }
-}
+++ /dev/null
-#ifndef _SSPT_IP
-#define _SSPT_IP
-
-/**
- * @file us_manager/sspt/ip.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-#include <linux/list.h>
-#include <linux/atomic.h>
-#include <uprobe/swap_uprobes.h>
-#include <us_manager/probes/probes.h>
-
-struct sspt_page;
-
-/**
- * @struct sspt_ip
- * @breaf Image of instrumentation pointer for specified process
- */
-struct sspt_ip {
- /* sspt_page */
- struct list_head list; /**< For sspt_page */
- struct sspt_page *page; /**< Pointer on the page (parent) */
-
- /* img_ip */
- struct img_ip *img_ip; /**< Pointer on the img_ip (parent) */
- struct list_head img_list; /**< For img_ip */
-
- atomic_t usage;
-
- unsigned long orig_addr; /**< Function address */
- unsigned long offset; /**< Page offset */
-
- struct probe_desc *desc; /**< Probe's data */
-
- union {
- struct uretprobe retprobe;
- struct uprobe uprobe;
- };
-};
-
-
-struct sspt_ip *sspt_ip_create(struct img_ip *img_ip, struct sspt_page *page);
-void sspt_ip_clean(struct sspt_ip *ip);
-void sspt_ip_get(struct sspt_ip *ip);
-void sspt_ip_put(struct sspt_ip *ip);
-
-
-#endif /* _SSPT_IP */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/driver/sspt/sspt_page.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include "sspt.h"
-#include "sspt_page.h"
-#include "sspt_file.h"
-#include "sspt_ip.h"
-#include <us_manager/probes/use_probes.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-
-/**
- * @brief Create sspt_page struct
- *
- * @param offset File ofset
- * @return Pointer to the created sspt_page struct
- */
-struct sspt_page *sspt_page_create(unsigned long offset)
-{
- struct sspt_page *obj = kmalloc(sizeof(*obj), GFP_KERNEL);
- if (obj) {
- INIT_HLIST_NODE(&obj->hlist);
- mutex_init(&obj->ip_list.mtx);
- INIT_LIST_HEAD(&obj->ip_list.inst);
- INIT_LIST_HEAD(&obj->ip_list.not_inst);
- obj->offset = offset;
- obj->file = NULL;
- kref_init(&obj->ref);
- }
-
- return obj;
-}
-
-void sspt_page_clean(struct sspt_page *page)
-{
- struct sspt_ip *ip, *n;
- LIST_HEAD(head);
-
- sspt_page_lock(page);
- WARN_ON(!list_empty(&page->ip_list.inst));
- list_for_each_entry_safe(ip, n, &page->ip_list.inst, list) {
- sspt_ip_get(ip);
- list_move(&ip->list, &head);
- }
-
- list_for_each_entry_safe(ip, n, &page->ip_list.not_inst, list) {
- sspt_ip_get(ip);
- list_move(&ip->list, &head);
- }
-
- while (!list_empty(&head)) {
- ip = list_first_entry(&head, struct sspt_ip ,list);
- sspt_page_unlock(page);
-
- sspt_ip_clean(ip);
-
- sspt_page_lock(page);
- sspt_ip_put(ip);
- }
- sspt_page_unlock(page);
-}
-
-static void sspt_page_release(struct kref *ref)
-{
- struct sspt_page *page = container_of(ref, struct sspt_page, ref);
-
- WARN_ON(!list_empty(&page->ip_list.inst) ||
- !list_empty(&page->ip_list.not_inst));
-
- kfree(page);
-}
-
-void sspt_page_get(struct sspt_page *page)
-{
- kref_get(&page->ref);
-}
-
-void sspt_page_put(struct sspt_page *page)
-{
- kref_put(&page->ref, sspt_page_release);
-}
-
-/**
- * @brief Add instruction pointer to sspt_page
- *
- * @param page Pointer to the sspt_page struct
- * @param ip Pointer to the us_ip struct
- * @return Void
- */
-void sspt_page_add_ip(struct sspt_page *page, struct sspt_ip *ip)
-{
- ip->page = page;
- list_add(&ip->list, &page->ip_list.not_inst);
-}
-
-void sspt_page_lock(struct sspt_page *page)
-{
- mutex_lock(&page->ip_list.mtx);
-}
-
-void sspt_page_unlock(struct sspt_page *page)
-{
- mutex_unlock(&page->ip_list.mtx);
-}
-
-/**
- * @brief Check if probes are set on the page
- *
- * @param page Pointer to the sspt_page struct
- * @return Boolean
- */
-bool sspt_page_is_installed(struct sspt_page *page)
-{
- return !list_empty(&page->ip_list.inst);
-}
-
-bool sspt_page_is_installed_ip(struct sspt_page *page, struct sspt_ip *ip)
-{
- bool result = false; /* not installed by default */
- struct sspt_ip *p;
-
- sspt_page_lock(page);
- list_for_each_entry(p, &page->ip_list.inst, list) {
- if (p == ip) {
- result = true;
- goto unlock;
- }
- }
-
-unlock:
- sspt_page_unlock(page);
-
- return result;
-}
-
-/**
- * @brief Install probes on the page
- *
- * @param page Pointer to the sspt_page struct
- * @param file Pointer to the sspt_file struct
- * @return Error code
- */
-int sspt_register_page(struct sspt_page *page, struct sspt_file *file)
-{
- int err = 0;
- struct sspt_ip *ip, *n;
- LIST_HEAD(not_inst_head);
-
- mutex_lock(&page->ip_list.mtx);
- if (list_empty(&page->ip_list.not_inst))
- goto unlock;
-
- list_for_each_entry_safe(ip, n, &page->ip_list.not_inst, list) {
- /* set virtual address */
- ip->orig_addr = file->vm_start + page->offset + ip->offset;
-
- err = sspt_register_usprobe(ip);
- if (err) {
- sspt_ip_get(ip);
- list_move(&ip->list, ¬_inst_head);
- continue;
- }
- }
-
- list_splice_init(&page->ip_list.not_inst, &page->ip_list.inst);
-
-unlock:
- mutex_unlock(&page->ip_list.mtx);
-
- list_for_each_entry_safe(ip, n, ¬_inst_head, list) {
- sspt_ip_clean(ip);
- sspt_ip_put(ip);
- }
-
- return 0;
-}
-
-/**
- * @brief Uninstall probes on the page
- *
- * @param page Pointer to the sspt_page struct
- * @param flag Action for probes
- * @param task Pointer to the task_struct struct
- * @return Error code
- */
-int sspt_unregister_page(struct sspt_page *page,
- enum US_FLAGS flag,
- struct task_struct *task)
-{
- int err = 0;
- struct sspt_ip *ip;
-
- mutex_lock(&page->ip_list.mtx);
- if (list_empty(&page->ip_list.inst))
- goto unlock;
-
- list_for_each_entry(ip, &page->ip_list.inst, list) {
- err = sspt_unregister_usprobe(task, ip, flag);
- if (err != 0) {
- WARN_ON(1);
- break;
- }
- }
-
- if (flag != US_DISARM)
- list_splice_init(&page->ip_list.inst, &page->ip_list.not_inst);
-
-unlock:
- mutex_unlock(&page->ip_list.mtx);
- return err;
-}
-
-void sspt_page_on_each_ip(struct sspt_page *page,
- void (*func)(struct sspt_ip *, void *), void *data)
-{
- struct sspt_ip *ip;
-
- mutex_lock(&page->ip_list.mtx);
- list_for_each_entry(ip, &page->ip_list.inst, list)
- func(ip, data);
- mutex_unlock(&page->ip_list.mtx);
-}
+++ /dev/null
-#ifndef __SSPT_PAGE__
-#define __SSPT_PAGE__
-
-/**
- * @file us_manager/sspt/sspt_page.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-#include <linux/types.h>
-#include <linux/mutex.h>
-#include <linux/kref.h>
-
-struct sspt_ip;
-struct sspt_file;
-struct task_struct;
-enum US_FLAGS;
-
-/**
- * @struct sspt_page
- * @breaf Image of page for specified process
- */
-struct sspt_page {
- /* sspt_file */
- struct hlist_node hlist; /**< For sspt_file */
- struct sspt_file *file; /**< Ptr to the file (parent) */
-
- /* sspt_ip */
- struct {
- struct mutex mtx; /**< Lock page */
- struct list_head inst; /**< For installed ip */
- struct list_head not_inst; /**< For don'tinstalled ip */
- } ip_list;
-
- unsigned long offset; /**< File offset */
-
- struct kref ref;
-};
-
-struct sspt_page *sspt_page_create(unsigned long offset);
-void sspt_page_clean(struct sspt_page *page);
-void sspt_page_get(struct sspt_page *page);
-void sspt_page_put(struct sspt_page *page);
-
-bool sspt_page_is_installed_ip(struct sspt_page *page, struct sspt_ip *ip);
-void sspt_page_add_ip(struct sspt_page *page, struct sspt_ip *ip);
-void sspt_page_lock(struct sspt_page *page);
-void sspt_page_unlock(struct sspt_page *page);
-
-bool sspt_page_is_installed(struct sspt_page *page);
-
-int sspt_register_page(struct sspt_page *page, struct sspt_file *file);
-
-int sspt_unregister_page(struct sspt_page *page,
- enum US_FLAGS flag,
- struct task_struct *task);
-
-void sspt_page_on_each_ip(struct sspt_page *page,
- void (*func)(struct sspt_ip *, void *), void *data);
-
-#endif /* __SSPT_PAGE__ */
+++ /dev/null
-/*
- * Dynamic Binary Instrumentation Module based on KProbes
- * modules/driver/sspt/sspt_proc.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-#include "sspt.h"
-#include "sspt_proc.h"
-#include "sspt_page.h"
-#include "sspt_feature.h"
-#include "sspt_filter.h"
-#include "../pf/proc_filters.h"
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <kprobe/swap_ktd.h>
-#include <us_manager/us_slot_manager.h>
-
-static LIST_HEAD(proc_probes_list);
-static DEFINE_RWLOCK(sspt_proc_rwlock);
-
-
-struct list_head *sspt_proc_list()
-{
- return &proc_probes_list;
-}
-
-/**
- * @brief Global read lock for sspt_proc
- *
- * @return Void
- */
-void sspt_proc_read_lock(void)
-{
- read_lock(&sspt_proc_rwlock);
-}
-
-/**
- * @brief Global read unlock for sspt_proc
- *
- * @return Void
- */
-void sspt_proc_read_unlock(void)
-{
- read_unlock(&sspt_proc_rwlock);
-}
-
-/**
- * @brief Global write lock for sspt_proc
- *
- * @return Void
- */
-void sspt_proc_write_lock(void)
-{
- write_lock(&sspt_proc_rwlock);
-}
-
-/**
- * @brief Global write unlock for sspt_proc
- *
- * @return Void
- */
-void sspt_proc_write_unlock(void)
-{
- write_unlock(&sspt_proc_rwlock);
-}
-
-struct ktd_proc {
- struct sspt_proc *proc;
- spinlock_t lock;
-};
-
-static void ktd_init(struct task_struct *task, void *data)
-{
- struct ktd_proc *kproc = (struct ktd_proc *)data;
-
- kproc->proc = NULL;
- spin_lock_init(&kproc->lock);
-}
-
-static void ktd_exit(struct task_struct *task, void *data)
-{
- struct ktd_proc *kproc = (struct ktd_proc *)data;
-
- WARN_ON(kproc->proc);
-}
-
-static struct ktask_data ktd = {
- .init = ktd_init,
- .exit = ktd_exit,
- .size = sizeof(struct ktd_proc),
-};
-
-static struct ktd_proc *kproc_by_task(struct task_struct *task)
-{
- return (struct ktd_proc *)swap_ktd(&ktd, task);
-}
-
-int sspt_proc_init(void)
-{
- return swap_ktd_reg(&ktd);
-}
-
-void sspt_proc_uninit(void)
-{
- swap_ktd_unreg(&ktd);
-}
-
-void sspt_change_leader(struct task_struct *prev, struct task_struct *next)
-{
- struct ktd_proc *prev_kproc;
-
- prev_kproc = kproc_by_task(prev);
- spin_lock(&prev_kproc->lock);
- if (prev_kproc->proc) {
- struct ktd_proc *next_kproc;
-
- next_kproc = kproc_by_task(next);
- get_task_struct(next);
-
- /* Change the keeper sspt_proc */
- BUG_ON(next_kproc->proc);
-
- spin_lock(&next_kproc->lock);
- next_kproc->proc = prev_kproc->proc;
- prev_kproc->proc = NULL;
- spin_unlock(&next_kproc->lock);
-
- /* Set new the task leader to sspt_proc */
- next_kproc->proc->leader = next;
-
- put_task_struct(prev);
- }
- spin_unlock(&prev_kproc->lock);
-}
-
-static void sspt_reset_proc(struct task_struct *task)
-{
- struct ktd_proc *kproc;
-
- kproc = kproc_by_task(task->group_leader);
- spin_lock(&kproc->lock);
- kproc->proc = NULL;
- spin_unlock(&kproc->lock);
-}
-
-
-
-
-
-static struct sspt_proc *sspt_proc_create(struct task_struct *leader)
-{
- struct sspt_proc *proc = kzalloc(sizeof(*proc), GFP_KERNEL);
-
- if (proc) {
- proc->feature = sspt_create_feature();
- if (proc->feature == NULL) {
- kfree(proc);
- return NULL;
- }
-
- INIT_LIST_HEAD(&proc->list);
- INIT_LIST_HEAD(&proc->files.head);
- init_rwsem(&proc->files.sem);
- proc->tgid = leader->tgid;
- proc->leader = leader;
- /* FIXME: change the task leader */
- proc->sm = create_sm_us(leader);
- mutex_init(&proc->filters.mtx);
- INIT_LIST_HEAD(&proc->filters.head);
- atomic_set(&proc->usage, 1);
-
- get_task_struct(proc->leader);
-
- proc->suspect.after_exec = 1;
- proc->suspect.after_fork = 0;
- }
-
- return proc;
-}
-
-static void sspt_proc_free(struct sspt_proc *proc)
-{
- put_task_struct(proc->leader);
- free_sm_us(proc->sm);
- sspt_destroy_feature(proc->feature);
- kfree(proc);
-}
-
-/**
- * @brief Remove sspt_proc struct
- *
- * @param proc remove object
- * @return Void
- */
-
-/* called with sspt_proc_write_lock() */
-void sspt_proc_cleanup(struct sspt_proc *proc)
-{
- struct sspt_file *file, *n;
-
- sspt_proc_del_all_filters(proc);
-
- down_write(&proc->files.sem);
- list_for_each_entry_safe(file, n, &proc->files.head, list) {
- list_del(&file->list);
- sspt_file_free(file);
- }
- up_write(&proc->files.sem);
-
- sspt_destroy_feature(proc->feature);
-
- free_sm_us(proc->sm);
- sspt_reset_proc(proc->leader);
- sspt_proc_put(proc);
-}
-
-struct sspt_proc *sspt_proc_get(struct sspt_proc *proc)
-{
- atomic_inc(&proc->usage);
-
- return proc;
-}
-
-void sspt_proc_put(struct sspt_proc *proc)
-{
- if (atomic_dec_and_test(&proc->usage)) {
- if (proc->__mm) {
- mmput(proc->__mm);
- proc->__mm = NULL;
- }
- if (proc->__task) {
- put_task_struct(proc->__task);
- proc->__task = NULL;
- }
-
- WARN_ON(kproc_by_task(proc->leader)->proc);
-
- put_task_struct(proc->leader);
- kfree(proc);
- }
-}
-EXPORT_SYMBOL_GPL(sspt_proc_put);
-
-struct sspt_proc *sspt_proc_by_task(struct task_struct *task)
-{
- return kproc_by_task(task->group_leader)->proc;
-}
-EXPORT_SYMBOL_GPL(sspt_proc_by_task);
-
-struct sspt_proc *sspt_proc_get_by_task(struct task_struct *task)
-{
- struct ktd_proc *kproc = kproc_by_task(task->group_leader);
- struct sspt_proc *proc;
-
- spin_lock(&kproc->lock);
- proc = kproc->proc;
- if (proc)
- sspt_proc_get(proc);
- spin_unlock(&kproc->lock);
-
- return proc;
-}
-EXPORT_SYMBOL_GPL(sspt_proc_get_by_task);
-
-/**
- * @brief Call func() on each proc (no lock)
- *
- * @param func Callback
- * @param data Data for callback
- * @return Void
- */
-void on_each_proc_no_lock(void (*func)(struct sspt_proc *, void *), void *data)
-{
- struct sspt_proc *proc, *tmp;
-
- list_for_each_entry_safe(proc, tmp, &proc_probes_list, list) {
- func(proc, data);
- }
-}
-
-/**
- * @brief Call func() on each proc
- *
- * @param func Callback
- * @param data Data for callback
- * @return Void
- */
-void on_each_proc(void (*func)(struct sspt_proc *, void *), void *data)
-{
- sspt_proc_read_lock();
- on_each_proc_no_lock(func, data);
- sspt_proc_read_unlock();
-}
-EXPORT_SYMBOL_GPL(on_each_proc);
-
-/**
- * @brief Get sspt_proc by task or create sspt_proc
- *
- * @param task Pointer on the task_struct struct
- * @param priv Private data
- * @return Pointer on the sspt_proc struct
- */
-struct sspt_proc *sspt_proc_get_by_task_or_new(struct task_struct *task)
-{
- static DEFINE_MUTEX(local_mutex);
- struct ktd_proc *kproc;
- struct sspt_proc *proc;
- struct task_struct *leader = task->group_leader;
-
- kproc = kproc_by_task(leader);
- if (kproc->proc)
- goto out;
-
- proc = sspt_proc_create(leader);
-
- spin_lock(&kproc->lock);
- if (kproc->proc == NULL) {
- sspt_proc_get(proc);
- kproc->proc = proc;
- proc = NULL;
-
- sspt_proc_write_lock();
- list_add(&kproc->proc->list, &proc_probes_list);
- sspt_proc_write_unlock();
- }
- spin_unlock(&kproc->lock);
-
- if (proc)
- sspt_proc_free(proc);
-
-out:
- return kproc->proc;
-}
-
-/**
- * @brief Check sspt_proc on empty
- *
- * @return Pointer on the sspt_proc struct
- */
-void sspt_proc_check_empty(void)
-{
- WARN_ON(!list_empty(&proc_probes_list));
-}
-
-static void sspt_proc_add_file(struct sspt_proc *proc, struct sspt_file *file)
-{
- down_write(&proc->files.sem);
- list_add(&file->list, &proc->files.head);
- file->proc = proc;
- up_write(&proc->files.sem);
-}
-
-/**
- * @brief Get sspt_file from sspt_proc by dentry or new
- *
- * @param proc Pointer on the sspt_proc struct
- * @param dentry Dentry of file
- * @return Pointer on the sspt_file struct
- */
-struct sspt_file *sspt_proc_find_file_or_new(struct sspt_proc *proc,
- struct dentry *dentry)
-{
- struct sspt_file *file;
-
- file = sspt_proc_find_file(proc, dentry);
- if (file == NULL) {
- file = sspt_file_create(dentry, 10);
- if (file)
- sspt_proc_add_file(proc, file);
- }
-
- return file;
-}
-
-/**
- * @brief Get sspt_file from sspt_proc by dentry
- *
- * @param proc Pointer on the sspt_proc struct
- * @param dentry Dentry of file
- * @return Pointer on the sspt_file struct
- */
-struct sspt_file *sspt_proc_find_file(struct sspt_proc *proc,
- struct dentry *dentry)
-{
- struct sspt_file *file;
-
- down_read(&proc->files.sem);
- list_for_each_entry(file, &proc->files.head, list) {
- if (dentry == file->dentry)
- goto unlock;
- }
- file = NULL;
-
-unlock:
- up_read(&proc->files.sem);
-
- return file;
-}
-
-/**
- * @brief Install probes on the page to monitored process
- *
- * @param proc Pointer on the sspt_proc struct
- * @param page_addr Page address
- * @return Void
- */
-void sspt_proc_install_page(struct sspt_proc *proc, unsigned long page_addr)
-{
- struct mm_struct *mm = proc->leader->mm;
- struct vm_area_struct *vma;
-
- vma = find_vma_intersection(mm, page_addr, page_addr + 1);
- if (vma && check_vma(vma)) {
- struct dentry *dentry = vma->vm_file->f_path.dentry;
- struct sspt_file *file = sspt_proc_find_file(proc, dentry);
- if (file) {
- sspt_file_set_mapping(file, vma);
- sspt_file_install(file);
- }
- }
-}
-
-/**
- * @brief Install probes to monitored process
- *
- * @param proc Pointer on the sspt_proc struct
- * @return Void
- */
-void sspt_proc_install(struct sspt_proc *proc)
-{
- struct vm_area_struct *vma;
- struct mm_struct *mm = proc->leader->mm;
-
- proc->first_install = 1;
-
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
- if (check_vma(vma)) {
- struct dentry *dentry = vma->vm_file->f_path.dentry;
- struct sspt_file *file =
- sspt_proc_find_file(proc, dentry);
- if (file) {
- sspt_file_set_mapping(file, vma);
- sspt_file_install(file);
- }
- }
- }
-}
-
-/**
- * @brief Uninstall probes to monitored process
- *
- * @param proc Pointer on the sspt_proc struct
- * @param task Pointer on the task_struct struct
- * @param flag Action for probes
- * @return Error code
- */
-int sspt_proc_uninstall(struct sspt_proc *proc,
- struct task_struct *task,
- enum US_FLAGS flag)
-{
- int err = 0;
- struct sspt_file *file;
-
- down_read(&proc->files.sem);
- list_for_each_entry(file, &proc->files.head, list) {
- err = sspt_file_uninstall(file, task, flag);
- if (err != 0) {
- printk(KERN_INFO "ERROR sspt_proc_uninstall: err=%d\n",
- err);
- break;
- }
- }
- up_read(&proc->files.sem);
-
- return err;
-}
-
-static int intersection(unsigned long start_a, unsigned long end_a,
- unsigned long start_b, unsigned long end_b)
-{
- return start_a < start_b ?
- end_a > start_b :
- start_a < end_b;
-}
-
-/**
- * @brief Get sspt_file list by region (remove sspt_file from sspt_proc list)
- *
- * @param proc Pointer on the sspt_proc struct
- * @param head[out] Pointer on the head list
- * @param start Region start
- * @param len Region length
- * @return Error code
- */
-int sspt_proc_get_files_by_region(struct sspt_proc *proc,
- struct list_head *head,
- unsigned long start, unsigned long end)
-{
- int ret = 0;
- struct sspt_file *file, *n;
-
- down_write(&proc->files.sem);
- list_for_each_entry_safe(file, n, &proc->files.head, list) {
- if (intersection(file->vm_start, file->vm_end, start, end)) {
- ret = 1;
- list_move(&file->list, head);
- }
- }
- up_write(&proc->files.sem);
-
- return ret;
-}
-
-/**
- * @brief Insert sspt_file to sspt_proc list
- *
- * @param proc Pointer on the sspt_proc struct
- * @param head Pointer on the head list
- * @return Void
- */
-void sspt_proc_insert_files(struct sspt_proc *proc, struct list_head *head)
-{
- down_write(&proc->files.sem);
- list_splice(head, &proc->files.head);
- up_write(&proc->files.sem);
-}
-
-/**
- * @brief Add sspt_filter to sspt_proc list
- *
- * @param proc Pointer to sspt_proc struct
- * @param pfg Pointer to pf_group struct
- * @return Void
- */
-void sspt_proc_add_filter(struct sspt_proc *proc, struct pf_group *pfg)
-{
- struct sspt_filter *f;
-
- f = sspt_filter_create(proc, pfg);
- if (f)
- list_add(&f->list, &proc->filters.head);
-}
-
-/**
- * @brief Remove sspt_filter from sspt_proc list
- *
- * @param proc Pointer to sspt_proc struct
- * @param pfg Pointer to pf_group struct
- * @return Void
- */
-void sspt_proc_del_filter(struct sspt_proc *proc, struct pf_group *pfg)
-{
- struct sspt_filter *fl, *tmp;
-
- mutex_lock(&proc->filters.mtx);
- list_for_each_entry_safe(fl, tmp, &proc->filters.head, list) {
- if (fl->pfg == pfg) {
- list_del(&fl->list);
- sspt_filter_free(fl);
- }
- }
- mutex_unlock(&proc->filters.mtx);
-}
-
-/**
- * @brief Remove all sspt_filters from sspt_proc list
- *
- * @param proc Pointer to sspt_proc struct
- * @return Void
- */
-void sspt_proc_del_all_filters(struct sspt_proc *proc)
-{
- struct sspt_filter *fl, *tmp;
-
- mutex_lock(&proc->filters.mtx);
- list_for_each_entry_safe(fl, tmp, &proc->filters.head, list) {
- list_del(&fl->list);
- sspt_filter_free(fl);
- }
- mutex_unlock(&proc->filters.mtx);
-}
-
-/**
- * @brief Check if sspt_filter is already in sspt_proc list
- *
- * @param proc Pointer to sspt_proc struct
- * @param pfg Pointer to pf_group struct
- * @return Boolean
- */
-bool sspt_proc_is_filter_new(struct sspt_proc *proc, struct pf_group *pfg)
-{
- struct sspt_filter *fl;
-
- list_for_each_entry(fl, &proc->filters.head, list)
- if (fl->pfg == pfg)
- return false;
-
- return true;
-}
-
-void sspt_proc_on_each_filter(struct sspt_proc *proc,
- void (*func)(struct sspt_filter *, void *),
- void *data)
-{
- struct sspt_filter *fl;
-
- list_for_each_entry(fl, &proc->filters.head, list)
- func(fl, data);
-}
-
-void sspt_proc_on_each_ip(struct sspt_proc *proc,
- void (*func)(struct sspt_ip *, void *), void *data)
-{
- struct sspt_file *file;
-
- down_read(&proc->files.sem);
- list_for_each_entry(file, &proc->files.head, list)
- sspt_file_on_each_ip(file, func, data);
- up_read(&proc->files.sem);
-}
-
-static void is_send_event(struct sspt_filter *f, void *data)
-{
- bool *is_send = (bool *)data;
-
- if (!*is_send && f->pfg_is_inst)
- *is_send = !!pfg_msg_cb_get(f->pfg);
-}
-
-bool sspt_proc_is_send_event(struct sspt_proc *proc)
-{
- bool is_send = false;
-
- /* FIXME: add read lock (deadlock in sampler) */
- sspt_proc_on_each_filter(proc, is_send_event, (void *)&is_send);
-
- return is_send;
-}
-
-
-static struct sspt_proc_cb *proc_cb;
-
-int sspt_proc_cb_set(struct sspt_proc_cb *cb)
-{
- if (cb && proc_cb)
- return -EBUSY;
-
- proc_cb = cb;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sspt_proc_cb_set);
-
-void sspt_proc_priv_create(struct sspt_proc *proc)
-{
- if (proc_cb && proc_cb->priv_create)
- proc->private_data = proc_cb->priv_create(proc);
-}
-
-void sspt_proc_priv_destroy(struct sspt_proc *proc)
-{
- if (proc->first_install && proc_cb && proc_cb->priv_destroy)
- proc_cb->priv_destroy(proc, proc->private_data);
-}
+++ /dev/null
-#ifndef __SSPT_PROC__
-#define __SSPT_PROC__
-
-/**
- * @file us_manager/sspt/sspt_proc.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-#include <linux/types.h>
-#include <linux/mutex.h>
-#include <linux/rwsem.h>
-#include "sspt_file.h"
-
-struct slot_manager;
-struct task_struct;
-struct pf_group;
-struct sspt_filter;
-struct sspt_ip;
-
-/** Flags for sspt_*_uninstall() */
-enum US_FLAGS {
- US_UNREGS_PROBE, /**< probes remove and disarm */
- US_DISARM, /**< probes disarm */
- US_UNINSTALL /**< probes remove from list install */
-};
-
-/**
- * @struct sspt_proc
- * @breaf Image of process for specified process
- */
-struct sspt_proc {
- struct list_head list; /**< For global process list */
-
- /* sspt_file */
- struct {
- struct rw_semaphore sem;/**< Semaphore for files list */
- struct list_head head; /**< For sspt_file */
- } files;
-
- pid_t tgid; /**< Thread group ID */
- struct task_struct *leader; /**< Ptr to the task leader */
- struct mm_struct *__mm;
- struct task_struct *__task;
- struct slot_manager *sm; /**< Ptr to the manager slot */
-
- struct {
- unsigned after_exec:1;
- unsigned after_fork:1;
- } suspect;
-
- struct {
- struct mutex mtx; /**< Mutex for filter list */
- struct list_head head; /**< Filter head */
- } filters;
-
- unsigned first_install:1; /**< Install flag */
- struct sspt_feature *feature; /**< Ptr to the feature */
- atomic_t usage;
-
- /* FIXME: for preload (remove those fields) */
- unsigned long r_state_addr; /**< address of r_state */
- void *private_data; /**< Process private data */
-};
-
-struct sspt_proc_cb {
- void *(*priv_create)(struct sspt_proc *);
- void (*priv_destroy)(struct sspt_proc *, void *);
-};
-
-
-struct list_head *sspt_proc_list(void);
-
-struct sspt_proc *sspt_proc_by_task(struct task_struct *task);
-struct sspt_proc *sspt_proc_get_by_task(struct task_struct *task);
-struct sspt_proc *sspt_proc_get_by_task_or_new(struct task_struct *task);
-struct sspt_proc *sspt_proc_get(struct sspt_proc *proc);
-void sspt_proc_put(struct sspt_proc *proc);
-void sspt_proc_cleanup(struct sspt_proc *proc);
-
-void on_each_proc_no_lock(void (*func)(struct sspt_proc *, void *),
- void *data);
-void on_each_proc(void (*func)(struct sspt_proc *, void *), void *data);
-
-void sspt_proc_check_empty(void);
-
-struct sspt_file *sspt_proc_find_file(struct sspt_proc *proc,
- struct dentry *dentry);
-struct sspt_file *sspt_proc_find_file_or_new(struct sspt_proc *proc,
- struct dentry *dentry);
-void sspt_proc_install_page(struct sspt_proc *proc, unsigned long page_addr);
-void sspt_proc_install(struct sspt_proc *proc);
-int sspt_proc_uninstall(struct sspt_proc *proc,
- struct task_struct *task,
- enum US_FLAGS flag);
-
-int sspt_proc_get_files_by_region(struct sspt_proc *proc,
- struct list_head *head,
- unsigned long start, unsigned long end);
-void sspt_proc_insert_files(struct sspt_proc *proc, struct list_head *head);
-
-void sspt_proc_read_lock(void);
-void sspt_proc_read_unlock(void);
-void sspt_proc_write_lock(void);
-void sspt_proc_write_unlock(void);
-
-void sspt_proc_add_filter(struct sspt_proc *proc, struct pf_group *pfg);
-void sspt_proc_del_filter(struct sspt_proc *proc, struct pf_group *pfg);
-void sspt_proc_del_all_filters(struct sspt_proc *proc);
-bool sspt_proc_is_filter_new(struct sspt_proc *proc, struct pf_group *pfg);
-
-void sspt_proc_on_each_filter(struct sspt_proc *proc,
- void (*func)(struct sspt_filter *, void *),
- void *data);
-
-void sspt_proc_on_each_ip(struct sspt_proc *proc,
- void (*func)(struct sspt_ip *, void *), void *data);
-
-bool sspt_proc_is_send_event(struct sspt_proc *proc);
-
-int sspt_proc_cb_set(struct sspt_proc_cb *cb);
-void sspt_proc_priv_create(struct sspt_proc *proc);
-void sspt_proc_priv_destroy(struct sspt_proc *proc);
-
-void sspt_change_leader(struct task_struct *prev, struct task_struct *next);
-int sspt_proc_init(void);
-void sspt_proc_uninit(void);
-
-
-#endif /* __SSPT_PROC__ */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/us_manager_common_file.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2017
- *
- * 2017 Alexander Aksenov: SWAP us_manager implement
- *
- */
-
-#ifndef __US_MANAGER_COMMON_FILE_H__
-#define __US_MANAGER_COMMON_FILE_H__
-
-#include <linux/dcache.h>
-#include <linux/namei.h>
-
-static inline struct dentry *swap_get_dentry(const char *filepath)
-{
- struct path path;
- struct dentry *dentry = NULL;
-
- if (kern_path(filepath, LOOKUP_FOLLOW, &path) == 0) {
- dentry = dget(path.dentry);
- path_put(&path);
- }
-
- return dentry;
-}
-
-static inline void swap_put_dentry(struct dentry *dentry)
-{
- dput(dentry);
-}
-
-#endif /* __US_MANAGER_COMMON_FILE_H__ */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/us_manager.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/stop_machine.h>
-#include "pf/pf_group.h"
-#include "sspt/sspt_proc.h"
-#include "probes/probe_info_new.h"
-#include "helper.h"
-#include "us_manager.h"
-#include "debugfs_us_manager.h"
-#include "callbacks.h"
-#include <writer/event_filter.h>
-#include <master/swap_initializer.h>
-
-
-static DEFINE_MUTEX(mutex_inst);
-static enum status_type status = ST_OFF;
-
-
-static int __do_usm_stop(void *data)
-{
- get_all_procs();
-
- return 0;
-}
-
-static void do_usm_stop(void)
-{
- int ret;
-
- exec_cbs(STOP_CB);
- helper_unreg_top();
-
- ret = stop_machine(__do_usm_stop, NULL, NULL);
- if (ret)
- printk("do_usm_stop failed: %d\n", ret);
-
- uninstall_all();
- helper_unreg_bottom();
- sspt_proc_check_empty();
-}
-
-static int do_usm_start(void)
-{
- int ret;
-
- ret = helper_reg();
- if (ret)
- return ret;
-
- install_all();
-
- exec_cbs(START_CB);
-
- return 0;
-}
-
-/**
- * @brief Get instrumentation status
- *
- * @return Instrumentation status
- */
-enum status_type usm_get_status(void)
-{
- mutex_lock(&mutex_inst);
- return status;
-}
-EXPORT_SYMBOL_GPL(usm_get_status);
-
-/**
- * @brief Put instrumentation status
- *
- * @param st Instrumentation status
- * @return Void
- */
-void usm_put_status(enum status_type st)
-{
- status = st;
- mutex_unlock(&mutex_inst);
-}
-EXPORT_SYMBOL_GPL(usm_put_status);
-
-/**
- * @brief Stop instrumentation
- *
- * @return Error code
- */
-int usm_stop(void)
-{
- int ret = 0;
-
- if (usm_get_status() == ST_OFF) {
- printk(KERN_INFO "US instrumentation is not running!\n");
- ret = -EINVAL;
- goto put;
- }
-
- do_usm_stop();
-
-put:
- usm_put_status(ST_OFF);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usm_stop);
-
-/**
- * @brief Start instrumentation
- *
- * @return Error code
- */
-int usm_start(void)
-{
- int ret = -EINVAL;
- enum status_type st;
-
- st = usm_get_status();
- if (st == ST_ON) {
- printk(KERN_INFO "US instrumentation is already run!\n");
- goto put;
- }
-
- ret = do_usm_start();
- if (ret == 0)
- st = ST_ON;
-
-put:
- usm_put_status(st);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usm_start);
-
-
-
-
-
-/* ============================================================================
- * === QUIET ===
- * ============================================================================
- */
-static enum quiet_type quiet = QT_ON;
-
-/**
- * @brief Set quiet mode
- *
- * @param q Quiet mode
- * @return Void
- */
-void set_quiet(enum quiet_type q)
-{
- quiet = q;
-}
-EXPORT_SYMBOL_GPL(set_quiet);
-
-/**
- * @brief Get quiet mode
- *
- * @return Quiet mode
- */
-enum quiet_type get_quiet(void)
-{
- return quiet;
-}
-EXPORT_SYMBOL_GPL(get_quiet);
-
-
-
-
-
-/* ============================================================================
- * === US_FILTER ===
- * ============================================================================
- */
-static int us_filter(struct task_struct *task)
-{
- struct sspt_proc *proc;
-
- /* FIXME: add read lock (deadlock in sampler) */
- proc = sspt_proc_by_task(task);
- if (proc)
- return sspt_proc_is_send_event(proc);
-
- return 0;
-}
-
-static struct ev_filter ev_us_filter = {
- .name = "traced_process_only",
- .filter = us_filter
-};
-
-static int init_us_filter(void)
-{
- int ret;
-
- ret = event_filter_register(&ev_us_filter);
- if (ret)
- return ret;
-
- return event_filter_set(ev_us_filter.name);
-}
-
-static void exit_us_filter(void)
-{
- event_filter_unregister(&ev_us_filter);
-}
-
-
-
-
-
-static int usm_once(void)
-{
- int ret;
-
- ret = helper_once();
-
- return ret;
-}
-
-static int init_us_manager(void)
-{
- int ret;
-
- ret = helper_init();
- if (ret)
- return ret;
-
- ret = sspt_proc_init();
- if (ret)
- goto uninit_helper;
-
- ret = pin_init();
- if (ret)
- goto uninit_proc;
-
- ret = init_us_filter();
- if (ret)
- goto uninit_pin;
-
- return 0;
-
-uninit_pin:
- pin_exit();
-uninit_proc:
- sspt_proc_uninit();
-uninit_helper:
- helper_uninit();
-
- return ret;
-}
-
-static void exit_us_manager(void)
-{
- if (status == ST_ON)
- BUG_ON(usm_stop());
-
- remove_all_cbs();
-
- exit_us_filter();
- pin_exit();
- sspt_proc_uninit();
- helper_uninit();
-
- WARN_ON(!pfg_is_unloadable());
-}
-
-SWAP_LIGHT_INIT_MODULE(usm_once, init_us_manager, exit_us_manager,
- init_debugfs_us_manager, exit_debugfs_us_manager);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/**
- * @file us_manager/us_manager.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-#ifndef _US_MANAGER_H
-#define _US_MANAGER_H
-
-
-/** Quiet mode */
-enum quiet_type {
- QT_ON, /**< Quiet mode - on */
- QT_OFF /**< Quiet mode - off */
-};
-
-/** Instrumentation status */
-enum status_type {
- ST_OFF, /**< Instrumentation status - off */
- ST_ON /**< Instrumentation status - on */
-};
-
-void set_quiet(enum quiet_type q);
-enum quiet_type get_quiet(void);
-
-enum status_type usm_get_status(void);
-void usm_put_status(enum status_type st);
-
-int usm_start(void);
-int usm_stop(void);
-
-#endif /* _US_MANAGER_H */
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/us_slot_manager.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Alexander Aksenov: SWAP us_manager implement
- *
- */
-
-
-#include <linux/mm.h>
-#include <linux/version.h>
-
-/*
- * TODO: move declaration and definition swap_do_mmap_pgoff()
- * from swap_kprobe.ko to swap_us_manager.ko
- */
-#include <kprobe/swap_kprobes_deps.h>
-
-
-static inline unsigned long __swap_do_mmap(struct file *filp,
- unsigned long addr,
- unsigned long len,
- unsigned long prot,
- unsigned long flag,
- unsigned long offset)
-{
-#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0)
- unsigned long populate;
-
- return swap_do_mmap(filp, addr, len, prot,
- flag, 0, offset, &populate);
-
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
- unsigned long populate;
-
- return swap_do_mmap_pgoff(filp, addr, len, prot,
- flag, offset, &populate);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
- return swap_do_mmap_pgoff(filp, addr, len, prot, flag, offset);
-#else
- return do_mmap(filp, addr, len, prot, flag, offset);
-#endif
-}
+++ /dev/null
-/*
- * SWAP uprobe manager
- * modules/us_manager/us_slot_manager.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * 2013 Vyacheslav Cherkashin: SWAP us_manager implement
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/hardirq.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/list.h>
-
-#include <kprobe/swap_slots.h>
-#include <swap-asm/swap_kprobes.h>
-#include <swap-asm/swap_uprobes.h>
-#include "us_manager_common.h"
-
-
-static void *sm_alloc_us(struct slot_manager *sm)
-{
- unsigned long addr;
-
- addr = __swap_do_mmap(NULL, 0, PAGE_SIZE,
- PROT_EXEC | PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0);
- return (void *)addr;
-}
-
-static void sm_free_us(struct slot_manager *sm, void *ptr)
-{
- /*
- * E. G.: This code provides kernel dump because of rescheduling while
- * atomic. As workaround, this code was commented. In this case we will
- * have memory leaks for instrumented process, but instrumentation
- * process should functionate correctly. Planned that good solution for
- * this problem will be done during redesigning KProbe for improving
- * supportability and performance.
- */
-#if 0
- struct task_struct *task = sm->data;
-
- mm = get_task_mm(task);
- if (mm) {
- down_write(&mm->mmap_sem);
- do_munmap(mm, (unsigned long)(ptr), PAGE_SIZE);
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
-#endif
- /* FIXME: implement the removal of memory for task */
-}
-
-/**
- * @brief Create slot_manager struct for US
- *
- * @param task Pointer to the task_struct struct
- * @return Pointer to the created slot_manager struct
- */
-struct slot_manager *create_sm_us(struct task_struct *task)
-{
- struct slot_manager *sm = kmalloc(sizeof(*sm), GFP_ATOMIC);
-
- if (sm == NULL)
- return NULL;
-
- sm->slot_size = UPROBES_TRAMP_LEN;
- sm->alloc = sm_alloc_us;
- sm->free = sm_free_us;
- INIT_HLIST_HEAD(&sm->page_list);
- sm->data = task;
-
- return sm;
-}
-
-/**
- * @brief Remove slot_manager struct for US
- *
- * @param sm remove object
- * @return Void
- */
-void free_sm_us(struct slot_manager *sm)
-{
- if (sm == NULL)
- return;
-
- if (!hlist_empty(&sm->page_list)) {
- printk(KERN_WARNING "SWAP US_MANAGER: Error! Slot manager is "
- "not empty!\n");
- return;
- }
-
- kfree(sm);
-}
+++ /dev/null
-/**
- * @file us_manager/us_slot_manager.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- * Copyright (C) Samsung Electronics, 2013
- */
-
-
-#ifndef _US_SLOT_MANAGER_H
-#define _US_SLOT_MANAGER_H
-
-struct task_struct;
-struct slot_manager;
-
-struct slot_manager *create_sm_us(struct task_struct *task);
-void free_sm_us(struct slot_manager *sm);
-
-#endif /* _US_SLOT_MANAGER_H */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2017
- *
- * 2017 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/rwsem.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <kprobe/swap_kprobes_deps.h> // for swap_hlist_for_each_entry
-#include "usm_hook.h"
-
-
-static HLIST_HEAD(hook_head);
-static DECLARE_RWSEM(hook_sem);
-
-
-int usm_hook_reg(struct usm_hook *hook)
-{
- if (!try_module_get(hook->owner))
- return -ENODEV;
-
- INIT_HLIST_NODE(&hook->node);
-
- down_write(&hook_sem);
- hlist_add_head(&hook->node, &hook_head);
- up_write(&hook_sem);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(usm_hook_reg);
-
-void usm_hook_unreg(struct usm_hook *hook)
-{
- down_write(&hook_sem);
- hlist_del(&hook->node);
- up_write(&hook_sem);
-
- module_put(hook->owner);
-}
-EXPORT_SYMBOL_GPL(usm_hook_unreg);
-
-void usm_hook_mmap(struct sspt_proc *proc, struct vm_area_struct *vma)
-{
- struct usm_hook *hook;
- DECLARE_NODE_PTR_FOR_HLIST(node);
-
- down_read(&hook_sem);
- swap_hlist_for_each_entry(hook, node, &hook_head, node) {
- if (hook->mmap)
- hook->mmap(proc, vma);
- }
- up_read(&hook_sem);
-}
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2017
- *
- * 2017 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_USM_HOOK_H
-#define _SWAP_USM_HOOK_H
-
-
-#include <linux/list.h>
-
-
-struct module;
-struct sspt_proc;
-struct vm_area_struct;
-
-
-struct usm_hook {
- struct hlist_node node;
- struct module *owner;
-
- /*
- * mmap hook called only for vma which we can instrument
- * (e.g. vma->vm_file is already validate)
- */
- void (*mmap)(struct sspt_proc *proc, struct vm_area_struct *vma);
-};
-
-
-int usm_hook_reg(struct usm_hook *hook);
-void usm_hook_unreg(struct usm_hook *hook);
-
-
-/* private interface */
-void usm_hook_mmap(struct sspt_proc *proc, struct vm_area_struct *vma);
-
-
-#endif /* _SWAP_USM_HOOK_H */
-
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-KBUILD_EXTRA_SYMBOLS = $(src)/../buffer/Module.symvers \
- $(src)/../driver/Module.symvers
-
-obj-m := swap_writer.o
-swap_writer-y := swap_writer_module.o \
- debugfs_writer.o \
- event_filter.o \
- swap_msg.o
-
+++ /dev/null
-/**
- * writer/debugfs_writer.c
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Writer debugfs implementation.
- */
-
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <master/swap_debugfs.h>
-#include <master/swap_initializer.h>
-#include "swap_msg.h"
-#include "event_filter.h"
-
-
-/* ============================================================================
- * === BUFFER ===
- * ============================================================================
- */
-static char *common_buf;
-enum { subbuf_size = 8*1024 };
-enum { common_buf_size = subbuf_size * NR_CPUS };
-
-static int init_buffer(void)
-{
- common_buf = vmalloc(common_buf_size);
-
- return common_buf ? 0 : -ENOMEM;
-}
-
-static void exit_buffer(void)
-{
- vfree(common_buf);
- common_buf = NULL;
-}
-
-static void *get_current_buf(void)
-{
- return common_buf + subbuf_size * get_cpu();
-}
-
-static void put_current_buf(void)
-{
- put_cpu();
-}
-
-
-
-
-
-/* ============================================================================
- * === FOPS_RAW ===
- * ============================================================================
- */
-static ssize_t write_raw(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int ret;
- void *buf;
-
- if (count > subbuf_size)
- return -EINVAL;
-
- buf = get_current_buf();
- if (copy_from_user(buf, user_buf, count)) {
- ret = -EFAULT;
- goto put_buf;
- }
-
- ret = swap_msg_raw(buf, count);
-
-put_buf:
- put_current_buf();
- return ret;
-}
-
-static const struct file_operations fops_raw = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .write = write_raw,
- .llseek = default_llseek
-};
-
-
-
-
-
-/* ============================================================================
- * === FOPS_AVAILABLE_FILTERS ===
- * ============================================================================
- */
-struct read_buf {
- char *begin;
- char *ptr;
- char *end;
-};
-
-static void func_for_read(struct ev_filter *f, void *data)
-{
- struct read_buf *rbuf = (struct read_buf *)data;
- int len = strlen(f->name);
-
- if (rbuf->end - rbuf->ptr < len + 2)
- return;
-
- if (rbuf->ptr != rbuf->begin) {
- *rbuf->ptr = ' ';
- ++rbuf->ptr;
- }
-
- memcpy(rbuf->ptr, f->name, len);
- rbuf->ptr += len;
-}
-
-static ssize_t read_af(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- char buf[512];
- struct read_buf rbuf = {
- .begin = buf,
- .ptr = buf,
- .end = buf + sizeof(buf)
- };
-
- event_filter_on_each(func_for_read, (void *)&rbuf);
-
- *rbuf.ptr = '\n';
- ++rbuf.ptr;
-
- return simple_read_from_buffer(user_buf, count, ppos,
- rbuf.begin, rbuf.ptr - rbuf.begin);
-}
-
-static const struct file_operations fops_available_filters = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .read = read_af,
- .llseek = default_llseek
-};
-
-
-
-
-
-/* ============================================================================
- * === FOPS_FILTER ===
- * ============================================================================
- */
-static ssize_t read_filter(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- const char *name = event_filter_get();
- int len = strlen(name);
- char *buf;
- ssize_t ret;
-
- buf = kmalloc(len + 2, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- memcpy(buf, name, len);
- buf[len] = '\0';
- buf[len + 1] = '\n';
-
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, len + 2);
- kfree(buf);
-
- return ret;
-}
-
-static ssize_t write_filter(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- enum { len = 32 };
- char buf[len], name[len];
- size_t buf_size;
- ssize_t ret;
-
- buf_size = min(count, (size_t)(len - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[len - 1] = '\0';
- ret = sscanf(buf, "%31s", name);
- if (ret != 1)
- return -EINVAL;
-
- ret = event_filter_set(name);
- if (ret)
- return -EINVAL;
-
- return count;
-}
-
-static const struct file_operations fops_filter = {
- .owner = THIS_MODULE,
- .open = swap_init_simple_open,
- .release = swap_init_simple_release,
- .read = read_filter,
- .write = write_filter,
- .llseek = default_llseek
-};
-
-
-
-
-
-/* ============================================================================
- * === INIT/EXIT ===
- * ============================================================================
- */
-static struct dentry *writer_dir;
-
-/**
- * @brief Removes writer debugfs.
- *
- * @return Void.
- */
-void exit_debugfs_writer(void)
-{
- if (writer_dir)
- debugfs_remove_recursive(writer_dir);
-
- writer_dir = NULL;
-
- exit_buffer();
-}
-
-/**
- * @brief Initializes writer debugfs.
- *
- * @return 0 on success, error code on error.
- */
-int init_debugfs_writer(void)
-{
- int ret;
- struct dentry *swap_dir, *dentry;
-
- ret = init_buffer();
- if (ret)
- return ret;
-
- swap_dir = swap_debugfs_getdir();
- if (swap_dir == NULL)
- return -ENOENT;
-
- writer_dir = swap_debugfs_create_dir("writer", swap_dir);
- if (writer_dir == NULL)
- return -ENOMEM;
-
- dentry = swap_debugfs_create_file("raw", 0600, writer_dir,
- NULL, &fops_raw);
- if (dentry == NULL)
- goto fail;
-
- dentry = swap_debugfs_create_file("available_filters", 0600, writer_dir,
- NULL, &fops_available_filters);
- if (dentry == NULL)
- goto fail;
-
- dentry = swap_debugfs_create_file("filter", 0600, writer_dir,
- NULL, &fops_filter);
- if (dentry == NULL)
- goto fail;
-
- return 0;
-
-fail:
- exit_debugfs_writer();
- return -ENOMEM;
-}
+++ /dev/null
-/**
- * @file writer/debugfs_writer.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Writer debugfs interface declaration.
- */
-
-#ifndef _DEBUGFS_WRITER_H
-#define _DEBUGFS_WRITER_H
-
-int init_debugfs_writer(void);
-void exit_debugfs_writer(void);
-
-#endif /* _DEBUGFS_WRITER_H */
+++ /dev/null
-/**
- * writer/event_filter.c
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Events filter.
- */
-
-
-#include <linux/module.h>
-#include <linux/list.h>
-#include "event_filter.h"
-
-
-static LIST_HEAD(filter_list);
-
-static int func_none(struct task_struct *task)
-{
- return 1;
-}
-
-static struct ev_filter filter_none = {
- .name = "all",
- .filter = func_none
-};
-
-static struct ev_filter *filter_current = &filter_none;
-
-int check_event(struct task_struct *task)
-{
- return filter_current->filter(task);
-}
-EXPORT_SYMBOL_GPL(check_event);
-
-static struct ev_filter *event_filter_find(const char *name)
-{
- struct ev_filter *f, *tmp;
-
- list_for_each_entry_safe(f, tmp, &filter_list, list) {
- if (strcmp(f->name, name) == 0)
- return f;
- }
-
- return NULL;
-}
-
-/**
- * @brief Registers event filter.
- *
- * @param f Pointer to the event filter.
- * @return 0 on success, error code on error.
- */
-int event_filter_register(struct ev_filter *f)
-{
- if (event_filter_find(f->name))
- return -EINVAL;
-
- INIT_LIST_HEAD(&f->list);
- list_add(&f->list, &filter_list);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(event_filter_register);
-
-/**
- * @brief Unregisters event filter.
- *
- * @param f Pointer to the event filter.
- * @return Void.
- */
-void event_filter_unregister(struct ev_filter *f)
-{
- struct ev_filter *filter, *tmp;
-
- if (filter_current == f)
- filter_current = &filter_none;
-
- list_for_each_entry_safe(filter, tmp, &filter_list, list) {
- if (filter == f) {
- list_del(&filter->list);
- break;
- }
- }
-}
-EXPORT_SYMBOL_GPL(event_filter_unregister);
-
-/**
- * @brief Sets event filter by its name.
- *
- * @param name Filter name.
- * @return 0 on success, error code on error.
- */
-int event_filter_set(const char *name)
-{
- struct ev_filter *f;
-
- f = event_filter_find(name);
- if (f == NULL)
- return -EINVAL;
-
- filter_current = f;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(event_filter_set);
-
-/**
- * @brief Gets filter name.
- *
- * @return Pointer to the filter name string.
- */
-const char *event_filter_get(void)
-{
- return filter_current->name;
-}
-
-/**
- * @brief Runs specified callback for each filter in list.
- *
- * @param func Specified callback.
- * @param data Pointer to the data passed to the callback.
- * @return Void.
- */
-void event_filter_on_each(void (*func)(struct ev_filter *, void *),
- void *data)
-{
- struct ev_filter *f, *tmp;
-
- list_for_each_entry_safe(f, tmp, &filter_list, list)
- func(f, data);
-}
-
-/**
- * @brief Initializes event filter.
- *
- * @return Initialization result.
- */
-int event_filter_init(void)
-{
- return event_filter_register(&filter_none);
-}
-
-/**
- * @brief Uninitializes event filter.
- *
- * @return Void.
- */
-void event_filter_exit(void)
-{
- event_filter_unregister(&filter_none);
-}
+++ /dev/null
-/**
- * @file writer/event_filter.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Event filter interface declaration.
- */
-
-
-#ifndef _EVENT_FILTER_H
-#define _EVENT_FILTER_H
-
-
-#include <linux/list.h>
-
-struct task_struct;
-
-/**
- * @struct ev_filter
- * @bref Event filter structure.
- */
-struct ev_filter {
- struct list_head list; /**< Filter list head. */
- char *name; /**< Filter name. */
- int (*filter)(struct task_struct *); /**< Filter function. */
-};
-
-
-int check_event(struct task_struct *task);
-
-int event_filter_register(struct ev_filter *f);
-void event_filter_unregister(struct ev_filter *f);
-int event_filter_set(const char *name);
-const char *event_filter_get(void);
-
-void event_filter_on_each(void (*func)(struct ev_filter *, void *),
- void *data);
-
-int event_filter_init(void);
-void event_filter_exit(void);
-
-#endif /* _EVENT_FILTER_H */
+++ /dev/null
-/**
- * @file writer/kernel_operations.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Writer kernel operations.
- */
-
-/* Kernel functions wrap */
-
-#ifndef __KERNEL_OPERATIONS_H__
-#define __KERNEL_OPERATIONS_H__
-
-#include <linux/kernel.h>
-#include <linux/uaccess.h>
-#include <asm/ptrace.h>
-
-
-/* ARCH-DEPENDED OPERATIONS */
-
-
-/* Regs manipulations */
-#if defined(CONFIG_ARM)
-
-#define get_regs_ret_func(regs) (regs->ARM_lr) /**< Get lr reg. */
-#define get_regs_ret_val(regs) (regs->ARM_r0) /**< Get ret val. */
-#define get_regs_stack_ptr(regs) (regs->ARM_sp) /**< Get stack pointer. */
-
-#elif defined(CONFIG_X86_32)
-
-#define get_regs_ret_val(regs) (regs->ax) /**< Get ret val. */
-#define get_regs_stack_ptr(regs) (regs->sp) /**< Get stack pointer. */
-
-static inline u32 get_regs_ret_func(struct pt_regs *regs)
-{
- u32 *sp, addr = 0;
-
- if (user_mode(regs)) {
- sp = (u32 *)regs->sp;
- if (get_user(addr, sp))
- pr_info("failed to dereference a pointer, sp=%p, "
- "pc=%lx\n", sp, regs->ip - 1);
- } else {
- sp = (u32 *)kernel_stack_pointer(regs);
- addr = *sp;
- }
-
- return addr;
-}
-
-#elif defined(CONFIG_ARM64)
-
-static inline u64 get_regs_ret_func(struct pt_regs *regs)
-{
- if (compat_user_mode(regs))
- return regs->compat_lr;
- else
- return regs->regs[30];
-}
-
-#endif /* CONFIG_arch */
-
-#endif /* __KERNEL_OPERATIONS_H__ */
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/ctype.h>
-#include <linux/errno.h>
-#include <linux/atomic.h>
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <linux/spinlock.h>
-#include <buffer/swap_buffer_module.h>
-#include <swap-asm/swap_kprobes.h>
-#include <swap-asm/swap_uprobes.h>
-#include "swap_msg.h"
-
-
-#define MSG_PREFIX "[SWAP_MSG] "
-
-
-/* simple buffer */
-struct sb_struct {
- size_t subbuf_size;
-
- size_t count;
- void *data;
-};
-
-static int sb_init(struct sb_struct *sb, size_t count, size_t subbuf_size)
-{
- sb->data = vmalloc(count * subbuf_size);
- if (!sb->data)
- return -ENOMEM;
-
- sb->count = count;
- sb->subbuf_size = subbuf_size;
-
- return 0;
-}
-
-static void sb_uninit(struct sb_struct *sb)
-{
- vfree(sb->data);
-}
-
-static void *sb_data(struct sb_struct *sb, size_t idx)
-{
- return sb->data + sb->subbuf_size * idx;
-}
-
-static size_t sb_idx(struct sb_struct *sb, void *data)
-{
- return (data - sb->data) / sb->subbuf_size;
-}
-
-static bool sb_contains(struct sb_struct *sb, void *data)
-{
- void *begin = sb->data;
- void *end = sb->data + sb->count * sb->subbuf_size;
-
- return data >= begin && data < end;
-}
-
-static size_t sb_count(struct sb_struct *sb)
-{
- return sb->count;
-}
-
-
-/* pool buffer */
-struct pb_struct {
- spinlock_t lock;
- size_t free_first;
- size_t free_count;
-
- struct sb_struct buf;
-};
-
-static void *pb_data(struct pb_struct *pb, size_t idx)
-{
- return sb_data(&pb->buf, idx);
-}
-
-static size_t pb_idx(struct pb_struct *pb, void *data)
-{
- return sb_idx(&pb->buf, data);
-}
-
-static void pb_val_set(struct pb_struct *pb, size_t idx, size_t val)
-{
- *(size_t *)pb_data(pb, idx) = val;
-}
-
-static size_t pb_val_get(struct pb_struct *pb, size_t idx)
-{
- return *(size_t *)pb_data(pb, idx);
-}
-
-static int pb_init(struct pb_struct *pb, size_t count, size_t subbuf_size)
-{
- int ret;
- size_t idx;
-
- ret = sb_init(&pb->buf, count, subbuf_size);
- if (ret)
- return ret;
-
- spin_lock_init(&pb->lock);
- pb->free_first = 0;
- pb->free_count = count;
-
- for (idx = 0; idx < count; ++idx)
- pb_val_set(pb, idx, idx + 1);
-
- return 0;
-}
-
-static void pb_uninit(struct pb_struct *pb)
-{
- WARN(sb_count(&pb->buf) != pb->free_count,
- "count=%zu free_conut=%zu\n", sb_count(&pb->buf), pb->free_count);
-
- sb_uninit(&pb->buf);
-}
-
-static void *pb_buf_get(struct pb_struct *pb)
-{
- void *data;
- unsigned long flags;
-
- if (!pb->free_count)
- return NULL;
-
- spin_lock_irqsave(&pb->lock, flags);
- data = pb_data(pb, pb->free_first);
- pb->free_first = pb_val_get(pb, pb->free_first);
- --pb->free_count;
- spin_unlock_irqrestore(&pb->lock, flags);
-
- return data;
-}
-
-static void pb_buf_put(struct pb_struct *pb, void *data)
-{
- unsigned long flags;
- size_t idx = pb_idx(pb, data);
-
- spin_lock_irqsave(&pb->lock, flags);
- pb_val_set(pb, idx, pb->free_first);
- pb->free_first = idx;
- ++pb->free_count;
- spin_unlock_irqrestore(&pb->lock, flags);
-}
-
-
-struct swap_msg {
- u32 msg_id;
- u32 seq_num;
- u64 time;
- u32 len;
- char payload[0];
-} __packed;
-
-
-static struct sb_struct cpu_buf;
-static struct pb_struct pool_buffer;
-static atomic_t seq_num = ATOMIC_INIT(-1);
-static atomic_t discarded = ATOMIC_INIT(0);
-
-
-int swap_msg_init(void)
-{
- int ret;
-
- ret = sb_init(&cpu_buf, NR_CPUS, SWAP_MSG_BUF_SIZE);
- if (ret) {
- pr_err(MSG_PREFIX "Cannot init cpu_buf, ret=%d\n", ret);
- return ret;
- }
-
- ret = pb_init(&pool_buffer, NR_CPUS * 32, SWAP_MSG_BUF_SIZE);
- if (ret) {
- sb_uninit(&cpu_buf);
- pr_err(MSG_PREFIX "Cannot init ring_buffer, ret=%d\n", ret);
- }
-
- return ret;
-}
-
-void swap_msg_exit(void)
-{
- pb_uninit(&pool_buffer);
- sb_uninit(&cpu_buf);
-}
-
-void swap_msg_seq_num_reset(void)
-{
- atomic_set(&seq_num, -1);
-}
-EXPORT_SYMBOL_GPL(swap_msg_seq_num_reset);
-
-void swap_msg_discard_reset(void)
-{
- atomic_set(&discarded, 0);
-}
-EXPORT_SYMBOL_GPL(swap_msg_discard_reset);
-
-int swap_msg_discard_get(void)
-{
- return atomic_read(&discarded);
-}
-EXPORT_SYMBOL_GPL(swap_msg_discard_get);
-
-
-u64 swap_msg_timespec2time(struct timespec *ts)
-{
- return ((u64)ts->tv_nsec) << 32 | ts->tv_sec;
-}
-
-
-
-
-
-struct swap_msg *swap_msg_get(enum swap_msg_id id)
-{
- struct swap_msg *m;
-
- m = pb_buf_get(&pool_buffer);
- if (!m)
- m = sb_data(&cpu_buf, get_cpu());
-
- m->msg_id = (u32)id;
- m->seq_num = atomic_inc_return(&seq_num);
- m->time = swap_msg_current_time();
-
- return m;
-}
-EXPORT_SYMBOL_GPL(swap_msg_get);
-
-static int __swap_msg_flush(struct swap_msg *m, size_t size, bool wakeup)
-{
- if (unlikely(size >= SWAP_MSG_PAYLOAD_SIZE))
- return -ENOMEM;
-
- m->len = size;
-
- if (swap_buffer_write(m, SWAP_MSG_PRIV_DATA + size, wakeup) !=
- (SWAP_MSG_PRIV_DATA + size)) {
- atomic_inc(&discarded);
- return -EINVAL;
- }
-
- return 0;
-}
-
-int swap_msg_flush(struct swap_msg *m, size_t size)
-{
- return __swap_msg_flush(m, size, true);
-}
-EXPORT_SYMBOL_GPL(swap_msg_flush);
-
-int swap_msg_flush_wakeupoff(struct swap_msg *m, size_t size)
-{
- return __swap_msg_flush(m, size, false);
-}
-EXPORT_SYMBOL_GPL(swap_msg_flush_wakeupoff);
-
-void swap_msg_put(struct swap_msg *m)
-{
- if (unlikely(sb_contains(&cpu_buf, m)))
- put_cpu();
- else
- pb_buf_put(&pool_buffer, m);
-}
-EXPORT_SYMBOL_GPL(swap_msg_put);
-
-
-
-
-
-
-static unsigned long get_arg(struct pt_regs *regs, unsigned long n)
-{
- return user_mode(regs) ?
- swap_get_uarg(regs, n) : /* US argument */
- swap_get_sarg(regs, n); /* sys_call argument */
-}
-
-int swap_msg_pack_args(char *buf, int len,
- const char *fmt, struct pt_regs *regs)
-{
- char *buf_old = buf;
- u32 *tmp_u32;
- u64 *tmp_u64;
- int i, /* the index of the argument */
- fmt_i, /* format index */
- fmt_len; /* the number of parameters, in format */
-
- fmt_len = strlen(fmt);
-
- for (i = 0, fmt_i = 0; fmt_i < fmt_len; ++i, ++fmt_i) {
- if (len < 2)
- return -ENOMEM;
-
- *buf = fmt[fmt_i];
- buf += 1;
- len -= 1;
-
- switch (fmt[fmt_i]) {
- case 'b': /* 1 byte(bool) */
- *buf = (char)!!get_arg(regs, i);
- buf += 1;
- len -= 1;
- break;
- case 'c': /* 1 byte(char) */
- *buf = (char)get_arg(regs, i);
- buf += 1;
- len -= 1;
- break;
- case 'f': /* 4 byte(float) */
-#ifdef CONFIG_ARM64
- if (len < 4)
- return -ENOMEM;
-
- tmp_u32 = (u32 *)buf;
- *tmp_u32 = swap_get_float(regs, i);
- buf += 4;
- len -= 4;
- break;
-#endif /* CONFIG_ARM64 */
- /* For others case f == d */
- case 'd': /* 4 byte(int) */
- if (len < 4)
- return -ENOMEM;
- tmp_u32 = (u32 *)buf;
- *tmp_u32 = (u32)get_arg(regs, i);
- buf += 4;
- len -= 4;
- break;
- case 'x': /* 8 byte(long) */
- case 'p': /* 8 byte(pointer) */
- if (len < 8)
- return -ENOMEM;
- tmp_u64 = (u64 *)buf;
- *tmp_u64 = (u64)get_arg(regs, i);
- buf += 8;
- len -= 8;
- break;
- case 'w': /* 8 byte(double) */
- if (len < 8)
- return -ENOMEM;
- tmp_u64 = (u64 *)buf;
-#ifdef CONFIG_ARM64
- *tmp_u64 = swap_get_double(regs, i);
-#else /* CONFIG_ARM64 */
- *tmp_u64 = get_arg(regs, i);
- ++i;
- *tmp_u64 |= (u64)get_arg(regs, i) << 32;
-#endif /* CONFIG_ARM64 */
- buf += 8;
- len -= 8;
- break;
- case 's': /* string end with '\0' */
- {
- enum { max_str_len = 512 };
- const char __user *user_s;
- int len_s, ret;
-
- user_s = (const char __user *)get_arg(regs, i);
- len_s = strnlen_user(user_s, max_str_len);
- if (len < len_s)
- return -ENOMEM;
-
- ret = strncpy_from_user(buf, user_s, len_s);
- if (ret < 0)
- return -EFAULT;
-
- buf[ret] = '\0';
-
- buf += ret + 1;
- len -= ret + 1;
- }
- break;
- default:
- return -EINVAL;
- }
- }
-
- return buf - buf_old;
-}
-EXPORT_SYMBOL_GPL(swap_msg_pack_args);
-
-int swap_msg_pack_ret_val(char *buf, int len,
- char ret_type, struct pt_regs *regs)
-{
- const char *buf_old = buf;
- u32 *tmp_u32;
- u64 *tmp_u64;
-
- *buf = ret_type;
- ++buf;
-
- switch (ret_type) {
- case 'b': /* 1 byte(bool) */
- if (len < 1)
- return -ENOMEM;
- *buf = (char)!!regs_return_value(regs);
- ++buf;
- break;
- case 'c': /* 1 byte(char) */
- if (len < 1)
- return -ENOMEM;
- *buf = (char)regs_return_value(regs);
- ++buf;
- break;
- case 'd': /* 4 byte(int) */
- if (len < 4)
- return -ENOMEM;
- tmp_u32 = (u32 *)buf;
- *tmp_u32 = regs_return_value(regs);
- buf += 4;
- break;
- case 'x': /* 8 byte(long) */
- case 'p': /* 8 byte(pointer) */
- if (len < 8)
- return -ENOMEM;
- tmp_u64 = (u64 *)buf;
- *tmp_u64 = (u64)regs_return_value(regs);
- buf += 8;
- break;
- case 's': /* string end with '\0' */
- {
- enum { max_str_len = 512 };
- const char __user *user_s;
- int len_s, ret;
-
- user_s = (const char __user *)regs_return_value(regs);
- len_s = strnlen_user(user_s, max_str_len);
- if (len < len_s)
- return -ENOMEM;
-
- ret = strncpy_from_user(buf, user_s, len_s);
- if (ret < 0)
- return -EFAULT;
-
- buf[ret] = '\0';
- buf += ret + 1;
- }
- break;
- case 'n':
- case 'v':
- break;
- case 'f': /* 4 byte(float) */
- if (len < 4)
- return -ENOMEM;
- tmp_u32 = (u32 *)buf;
- *tmp_u32 = swap_get_urp_float(regs);
- buf += 4;
- break;
- case 'w': /* 8 byte(double) */
- if (len < 8)
- return -ENOMEM;
- tmp_u64 = (u64 *)buf;
- *tmp_u64 = swap_get_urp_double(regs);
- buf += 8;
- break;
- default:
- return -EINVAL;
- }
-
- return buf - buf_old;
-}
-EXPORT_SYMBOL_GPL(swap_msg_pack_ret_val);
-
-
-
-
-
-int swap_msg_raw(void *data, size_t size)
-{
- struct swap_msg *m = (struct swap_msg *)data;
-
- if (sizeof(*m) > size) {
- pr_err(MSG_PREFIX "ERROR: message RAW small size=%zu\n", size);
- return -EINVAL;
- }
-
- if (m->len + sizeof(*m) != size) {
- pr_err(MSG_PREFIX "ERROR: message RAW wrong format\n");
- return -EINVAL;
- }
-
- m->seq_num = atomic_inc_return(&seq_num);
-
- /* TODO: What should be returned?! When message was discarded. */
- if (swap_buffer_write(m, size, true) != size)
- atomic_inc(&discarded);
-
- return size;
-}
-EXPORT_SYMBOL_GPL(swap_msg_raw);
-
-void swap_msg_error(const char *fmt, ...)
-{
- int ret;
- struct swap_msg *m;
- void *p;
- size_t size;
- va_list args;
-
- m = swap_msg_get(MSG_ERROR);
- p = swap_msg_payload(m);
- size = swap_msg_size(m);
-
- va_start(args, fmt);
- ret = vsnprintf(p, size, fmt, args);
- va_end(args);
-
- if (ret <= 0) {
- pr_err(MSG_PREFIX "ERROR: msg error packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, ret + 1);
-
-put_msg:
- swap_msg_put(m);
-}
-EXPORT_SYMBOL_GPL(swap_msg_error);
+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * 2015 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- */
-
-
-#ifndef _SWAP_MSG_H
-#define _SWAP_MSG_H
-
-#include <linux/version.h>
-#include <linux/types.h>
-
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0)
-
-#include <linux/ktime.h> /* Needed by timekeeping.h */
-#include <linux/timekeeping.h> /* Now getnstimeofday() is here */
-
-#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
-
-#include <linux/time.h>
-
-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 16, 0) */
-
-enum swap_msg_id {
- MSG_PROC_INFO = 0x0001,
- MSG_TERMINATE = 0x0002,
- MSG_ERROR = 0x0003,
- MSG_SAMPLE = 0x0004,
- MSG_FUNCTION_ENTRY = 0x0008,
- MSG_FUNCTION_EXIT = 0x0009,
- MSG_SYSCALL_ENTRY = 0x000a,
- MSG_SYSCALL_EXIT = 0x000b,
- MSG_FILE_FUNCTION_ENTRY = 0x000c,
- MSG_FILE_FUNCTION_EXIT = 0x000d,
- MSG_PROCESS_STATUS_INFO = 0x000e,
- MSG_CONTEXT_SWITCH_ENTRY = 0x0010,
- MSG_CONTEXT_SWITCH_EXIT = 0x0011,
- MSG_PROC_MAP = 0x0012,
- MSG_PROC_UNMAP = 0x0013,
- MSG_PROC_COMM = 0x0014,
- MSG_WEB_PROFILING = 0x0015,
- MSG_NSP = 0x0019,
- MSG_WSP = 0x001a,
- MSG_FBI = 0x0020
-};
-
-enum {
- SWAP_MSG_PRIV_DATA = 20,
- SWAP_MSG_BUF_SIZE = 32 * 1024,
- SWAP_MSG_PAYLOAD_SIZE = SWAP_MSG_BUF_SIZE - SWAP_MSG_PRIV_DATA
-};
-
-
-struct swap_msg;
-
-
-static inline u64 swap_msg_spec2time(struct timespec *ts)
-{
- return ((u64)ts->tv_nsec) << 32 | ts->tv_sec;
-}
-
-static inline u64 swap_msg_current_time(void)
-{
- struct timespec ts;
- getnstimeofday(&ts);
- return swap_msg_spec2time(&ts);
-}
-
-struct swap_msg *swap_msg_get(enum swap_msg_id id);
-int swap_msg_flush(struct swap_msg *m, size_t size);
-int swap_msg_flush_wakeupoff(struct swap_msg *m, size_t size);
-void swap_msg_put(struct swap_msg *m);
-
-static inline void *swap_msg_payload(struct swap_msg *m)
-{
- return (void *)m + SWAP_MSG_PRIV_DATA;
-}
-
-static inline size_t swap_msg_size(struct swap_msg *m)
-{
- return (size_t)SWAP_MSG_PAYLOAD_SIZE;
-}
-
-
-int swap_msg_pack_args(char *buf, int len,
- const char *fmt, struct pt_regs *regs);
-int swap_msg_pack_ret_val(char *buf, int len,
- char ret_type, struct pt_regs *regs);
-
-
-int swap_msg_raw(void *buf, size_t size);
-void swap_msg_error(const char *fmt, ...);
-
-void swap_msg_seq_num_reset(void);
-void swap_msg_discard_reset(void);
-int swap_msg_discard_get(void);
-
-int swap_msg_init(void);
-void swap_msg_exit(void);
-
-
-#endif /* _SWAP_MSG_H */
+++ /dev/null
-/**
- * @file writer/swap_writer_errors.h
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Writer module error codes.
- */
-
-#ifndef __SWAP_WRITER_ERRORS_H__
-#define __SWAP_WRITER_ERRORS_H__
-
-/**
- * @enum _swap_writer_errors
- * Error codes.
- */
-enum _swap_writer_errors {
- E_SW_SUCCESS = 0 /**< Success. */
-};
-
-#endif /* __SWAP_WRITER_ERRORS_H__ */
+++ /dev/null
-/**
- * writer/swap_writer_module.c
- * @author Alexander Aksenov <a.aksenov@samsung.com>
- * @author Vitaliy Cherepanov <v.cherepanov@samsung.com>
- * @author Vyacheslav Cherkashin
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2013
- *
- * @section DESCRIPTION
- *
- * Packing and writing data.
- */
-
-
-#include <linux/module.h>
-#include <master/swap_initializer.h>
-#include "swap_msg.h"
-#include "event_filter.h"
-#include "debugfs_writer.h"
-
-
-static int core_init(void)
-{
- int ret;
-
- ret = swap_msg_init();
- if (ret)
- return ret;
-
- ret = event_filter_init();
- if (ret)
- swap_msg_exit();
-
- return ret;
-}
-
-static void core_exit(void)
-{
- event_filter_exit();
- swap_msg_exit();
-}
-
-SWAP_LIGHT_INIT_MODULE(NULL, core_init, core_exit,
- init_debugfs_writer, exit_debugfs_writer);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SWAP Writer module");
-MODULE_AUTHOR("Cherkashin V., Aksenov A.S.");
+++ /dev/null
-EXTRA_CFLAGS := $(extra_cflags)
-
-obj-m := swap_wsp.o
-swap_wsp-y := wsp_module.o \
- wsp_msg.o \
- wsp_debugfs.o \
- wsp.o \
- wsp_res.o
+++ /dev/null
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <linux/string.h>
-#include <us_manager/sspt/sspt.h>
-#include <us_manager/probes/probe_info_new.h>
-#include "wsp.h"
-#include "wsp_res.h"
-#include "wsp_msg.h"
-
-struct wsp_probe {
- const char *name;
- struct probe_new probe;
-};
-
-struct wsp_bin {
- const char *name;
- unsigned long cnt;
- struct wsp_probe *probe_array;
-};
-
-static char *webapp_path;
-static char *chromium_path;
-
-#define WSP_PROBE_MAKE(_name_, _offset_, _desc_) \
-{ \
- .name = (_name_), \
- .probe.offset = (_offset_), \
- .probe.desc = (_desc_) \
-}
-
-/*
- * res_request
- */
-/* blink::ResourceLoader.m_request.m_url */
-#define URL_OFFSET 84
-/* base::String.m_impl.m_ptr */
-#define URL_LEN_OFFSET 4
-#define URL_DATA_OFFSET 12
-
-static char *path_get_from_object(unsigned long ptr)
-{
- char *path;
- unsigned long url, len, ret;
-
- get_user(url, (unsigned long __user *)(ptr + URL_OFFSET));
- get_user(len, (unsigned long __user *)(url + URL_LEN_OFFSET));
- path = kzalloc(len + 1, GFP_KERNEL);
- if (!path)
- return NULL;
-
- ret = copy_from_user(path,
- (const void __user *)(url + URL_DATA_OFFSET),
- len);
- if (ret) {
- kfree(path);
- path = NULL;
- } else {
- path[len] = '\0';
- }
-
- return path;
-}
-
-static int res_request_handle(struct uprobe *p, struct pt_regs *regs)
-{
- unsigned long ptr;
- char *path;
-
- ptr = (unsigned long)swap_get_uarg(regs, 0);
- path = path_get_from_object(ptr);
- if (path) {
- int id = wsp_resource_data_add(ptr, path);
- if (id >= 0)
- wsp_msg(WSP_RES_LOAD_BEGIN, id, path);
- }
-
- return 0;
-}
-
-static struct probe_desc res_request = MAKE_UPROBE(res_request_handle);
-
-/*
- * res_finish
- */
-static int res_finish_ehandle(struct uretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int id;
- unsigned long ptr = (unsigned long)swap_get_uarg(regs, 0);
-
- id = wsp_resource_data_id(ptr);
- if (id >= 0) {
- *(unsigned long *)ri->data = ptr;
- wsp_msg(WSP_RES_PROC_BEGIN, id, NULL);
- }
-
- return 0;
-}
-
-static int res_finish_rhandle(struct uretprobe_instance *ri,
- struct pt_regs *regs)
-{
- int id;
- unsigned long ptr;
-
- ptr = *(unsigned long *)ri->data;
- id = wsp_resource_data_id(ptr);
- if (id >= 0) {
- wsp_msg(WSP_RES_PROC_END, id, NULL);
- wsp_msg(WSP_RES_LOAD_END, id, NULL);
- wsp_resource_data_del(ptr);
- }
-
- return 0;
-}
-
-static struct probe_desc res_finish =
- MAKE_URPROBE(res_finish_ehandle, res_finish_rhandle,
- sizeof(unsigned long));
-
-/*
- * redraw
- */
-static int redraw_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- wsp_msg(WSP_DRAW_BEGIN, 0, NULL);
-
- return 0;
-}
-
-static int redraw_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
- wsp_msg(WSP_DRAW_END, 0, NULL);
-
- return 0;
-}
-
-static struct probe_desc redraw = MAKE_URPROBE(redraw_eh, redraw_rh, 0);
-
-/* blink::ResourceLoader::start() */
-#define RES_REQ "_ZN5blink14ResourceLoader5startEv"
-/* blink::ResourceLoader::didFinishLoading(WebURLLoader*, double , int64_t) */
-#define RES_FINISH "_ZN5blink14ResourceLoader16didFinishLoadingEPNS_12WebURLLoaderEdx"
-
-/* content::RenderWidget::DidCommitAndDrawCompositorFrame */
-#define REDRAW "_ZN7content23CompositorOutputSurface11SwapBuffersEPN2cc15CompositorFrameE"
-
-static struct wsp_probe __probe_array[] = {
- /* res */
- WSP_PROBE_MAKE(RES_REQ, 0, &res_request),
- WSP_PROBE_MAKE(RES_FINISH, 0, &res_finish),
-
- /* redraw */
- WSP_PROBE_MAKE(REDRAW, 0, &redraw),
-};
-
-static struct wsp_bin chromium_bin = {
- .name = NULL,
- .probe_array = __probe_array,
- .cnt = ARRAY_SIZE(__probe_array)
-};
-
-/* check chromium_bin array on init address */
-static bool wsp_is_addr_init(void)
-{
- int i;
-
- for (i = 0; i < chromium_bin.cnt; ++i)
- if (chromium_bin.probe_array[i].probe.offset == 0)
- return false;
-
- return true;
-}
-
-static int wsp_bin_register(struct pf_group *pfg, struct wsp_bin *bin)
-{
- int i, ret;
- struct dentry *dentry;
-
- dentry = dentry_by_path(bin->name);
- if (!dentry) {
- pr_err("dentry not found (path='%s'\n", bin->name);
- return -EINVAL;
- }
-
- for (i = 0; i < bin->cnt; ++i) {
- struct wsp_probe *p = &bin->probe_array[i];
-
- ret = pin_register(&p->probe, pfg, dentry);
- if (ret) {
- pr_err("failed to register WSP probe (%lx:%d)\n",
- p->probe.offset, ret);
- return ret;
- }
- }
-
- return 0;
-}
-
-static void wsp_bin_unregister(struct pf_group *pfg, struct wsp_bin *bin)
-{
- int i;
- struct dentry *dentry;
-
- dentry = dentry_by_path(bin->name);
- if (!dentry) {
- pr_err("dentry not found (path='%s'\n", bin->name);
- return;
- }
-
- for (i = 0; i < bin->cnt; ++i) {
- struct wsp_probe *p = &bin->probe_array[i];
-
- pin_unregister(&p->probe, pfg);
- }
-}
-
-static char *do_set_path(char *path, size_t len)
-{
- char *p;
-
- p = kmalloc(len, GFP_KERNEL);
- if (!p)
- return NULL;
-
- strncpy(p, path, len);
- return p;
-}
-
-static void do_free_path(char **dest)
-{
- kfree(*dest);
- *dest = NULL;
-}
-
-static struct pf_group *g_pfg;
-
-static int wsp_app_register(void)
-{
- struct dentry *dentry;
-
- if (!webapp_path || !chromium_path) {
- pr_err("WSP: some required paths are not set!\n");
- return -EINVAL;
- }
-
- chromium_bin.name = chromium_path;
-
- dentry = dentry_by_path(webapp_path);
- if (!dentry) {
- pr_err("dentry not found (path='%s'\n", webapp_path);
- return -EINVAL;
- }
-
- g_pfg = get_pf_group_by_dentry(dentry, (void *)dentry);
- if (!g_pfg) {
- pr_err("WSP: g_pfg is NULL (by dentry=%p)\n", dentry);
- return -ENOMEM;
- }
-
- return wsp_bin_register(g_pfg, &chromium_bin);
-}
-
-static void wsp_app_unregister(void)
-{
- if (!chromium_bin.name) {
- pr_err("WSP: chromium path is not initialized\n");
- return;
- }
-
- wsp_bin_unregister(g_pfg, &chromium_bin);
- put_pf_group(g_pfg);
-}
-
-static int do_wsp_on(void)
-{
- int ret;
-
- ret = wsp_res_init();
- if (ret)
- return ret;
-
- ret = wsp_app_register();
- if (ret)
- wsp_res_exit();
-
- return ret;
-}
-
-static int do_wsp_off(void)
-{
- wsp_app_unregister();
- wsp_res_exit();
-
- return 0;
-}
-
-static enum wsp_mode g_mode = WSP_OFF;
-static DEFINE_MUTEX(g_mode_mutex);
-
-int wsp_set_addr(const char *name, unsigned long offset)
-{
- int i, ret = 0;
-
- if (mutex_trylock(&g_mode_mutex) == 0)
- return -EBUSY;
-
- for (i = 0; i < chromium_bin.cnt; ++i) {
- if (!strcmp(name, chromium_bin.probe_array[i].name)) {
- chromium_bin.probe_array[i].probe.offset = offset;
- goto unlock;
- }
- }
-
- ret = -EINVAL;
-
-unlock:
- mutex_unlock(&g_mode_mutex);
- return ret;
-}
-
-int wsp_set_mode(enum wsp_mode mode)
-{
- int ret = -EINVAL;
-
- if (g_mode == mode)
- return -EBUSY;
-
- mutex_lock(&g_mode_mutex);
- switch (mode) {
- case WSP_ON:
- ret = wsp_is_addr_init() ? do_wsp_on() : -EPERM;
- break;
- case WSP_OFF:
- ret = do_wsp_off();
- break;
- }
-
- if (!ret)
- g_mode = mode;
-
- mutex_unlock(&g_mode_mutex);
- return ret;
-}
-
-enum wsp_mode wsp_get_mode(void)
-{
- return g_mode;
-}
-
-void wsp_set_webapp_path(char *path, size_t len)
-{
- do_free_path(&webapp_path);
- webapp_path = do_set_path(path, len);
-}
-
-void wsp_set_chromium_path(char *path, size_t len)
-{
- do_free_path(&chromium_path);
- chromium_path = do_set_path(path, len);
-}
-
-int wsp_init(void)
-{
- return 0;
-}
-
-void wsp_exit(void)
-{
- wsp_set_mode(WSP_OFF);
- do_free_path(&webapp_path);
- do_free_path(&chromium_path);
-}
+++ /dev/null
-#ifndef _WSP_H
-#define _WSP_H
-
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-enum wsp_mode {
- WSP_ON,
- WSP_OFF
-};
-
-int wsp_set_addr(const char *name, unsigned long offset);
-
-int wsp_set_mode(enum wsp_mode mode);
-enum wsp_mode wsp_get_mode(void);
-
-void wsp_set_webapp_path(char *path, size_t len);
-void wsp_set_chromium_path(char *path, size_t len);
-
-int wsp_init(void);
-void wsp_exit(void);
-
-#endif /* _WSP_H */
+++ /dev/null
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <linux/slab.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
-#include <master/swap_debugfs.h>
-#include "wsp.h"
-#include "wsp_debugfs.h"
-
-static int do_write_cmd(const char *buf, size_t count)
-{
- int n, ret = 0;
- char *name;
- unsigned long offset;
-
- name = kmalloc(count, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
-
- n = sscanf(buf, "%lx %1024s", &offset, name);
- if (n != 2) {
- ret = -EINVAL;
- goto free_name;
- }
-
- ret = wsp_set_addr(name, offset);
-
-free_name:
- kfree(name);
- return ret;
-}
-
-/* ============================================================================
- * === DEBUGFS FOR CMD ===
- * ============================================================================
- */
-static ssize_t write_cmd(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- enum { max_count = 1024 };
- int ret;
- char *buf;
-
- if (count > max_count)
- return -ENOMEM;
-
- buf = kmalloc(count + 1, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, user_buf, count)) {
- ret = -EFAULT;
- goto free_buf;
- }
-
- buf[count] = '\0';
- ret = do_write_cmd(buf, count);
-
-free_buf:
- kfree(buf);
- return ret ? ret : count;
-}
-
-static const struct file_operations fops_cmd = {
- .write = write_cmd,
- .llseek = default_llseek,
-};
-
-/* ============================================================================
- * === DEBUGFS FOR ENABLE ===
- * ============================================================================
- */
-static ssize_t read_enabled(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- char buf[2];
-
- buf[0] = wsp_get_mode() == WSP_OFF ? '0' : '1';
- buf[1] = '\n';
-
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t write_enabled(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- int ret = 0;
- char buf[32];
- size_t buf_size;
-
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
- switch (buf[0]) {
- case '1':
- ret = wsp_set_mode(WSP_ON);
- break;
- case '0':
- ret = wsp_set_mode(WSP_OFF);
- break;
- default:
- return -EINVAL;
- }
-
- if (ret)
- return ret;
-
- return count;
-}
-
-static const struct file_operations fops_enabled = {
- .read = read_enabled,
- .write = write_enabled,
- .llseek = default_llseek,
-};
-
-/* ============================================================================
- * === DEBUGFS FOR WEBAPP_PATH ===
- * ============================================================================
- */
-static ssize_t write_webapp_path(struct file *file,
- const char __user *user_buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *path;
-
- path = kmalloc(len, GFP_KERNEL);
- if (!path) {
- ret = -ENOMEM;
- goto write_webapp_path_failed;
- }
-
- if (copy_from_user(path, user_buf, len)) {
- ret = -EINVAL;
- goto write_webapp_path_failed;
- }
-
- path[len - 1] = '\0';
- wsp_set_webapp_path(path, len);
-
- ret = len;
-
-write_webapp_path_failed:
- kfree(path);
-
- return ret;
-}
-
-static const struct file_operations fops_webapp_path = {
- .write = write_webapp_path
-};
-
-/* ============================================================================
- * === DEBUGFS FOR EWEBKIT_PATH ===
- * ============================================================================
- */
-static ssize_t write_ewebkit_path(struct file *file,
- const char __user *user_buf,
- size_t len, loff_t *ppos)
-{
- ssize_t ret;
- char *path;
-
- path = kmalloc(len, GFP_KERNEL);
- if (!path) {
- ret = -ENOMEM;
- goto write_ewebkit_path_failed;
- }
-
- if (copy_from_user(path, user_buf, len)) {
- ret = -EINVAL;
- goto write_ewebkit_path_failed;
- }
-
- path[len - 1] = '\0';
-
- wsp_set_chromium_path(path, len);
-
- ret = len;
-
-write_ewebkit_path_failed:
- kfree(path);
-
- return ret;
-}
-
-static const struct file_operations fops_ewebkit_path = {
- .write = write_ewebkit_path
-};
-
-static struct dentry *wsp_dir;
-
-void wsp_debugfs_exit(void)
-{
- debugfs_remove_recursive(wsp_dir);
- wsp_dir = NULL;
-}
-
-int wsp_debugfs_init(void)
-{
- struct dentry *dentry;
-
- dentry = swap_debugfs_getdir();
- if (!dentry)
- return -ENOENT;
-
- wsp_dir = swap_debugfs_create_dir("wsp", dentry);
- if (!wsp_dir)
- return -ENOMEM;
-
- dentry = swap_debugfs_create_file("enabled", 0600, wsp_dir, NULL,
- &fops_enabled);
- if (!dentry)
- goto fail;
-
- dentry = swap_debugfs_create_file("cmd", 0600, wsp_dir, NULL,
- &fops_cmd);
- if (!dentry)
- goto fail;
-
- dentry = swap_debugfs_create_file("webapp_path", 0600, wsp_dir, NULL,
- &fops_webapp_path);
- if (!dentry)
- goto fail;
-
- dentry = swap_debugfs_create_file("ewebkit_path", 0600, wsp_dir, NULL,
- &fops_ewebkit_path);
- if (!dentry)
- goto fail;
-
- return 0;
-
-fail:
- wsp_debugfs_exit();
- return -ENOMEM;
-}
+++ /dev/null
-#ifndef _WSP_DEBUGFS_H
-#define _WSP_DEBUGFS_H
-
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-int wsp_debugfs_init(void);
-void wsp_debugfs_exit(void);
-
-#endif /* _WSP_DEBUGFS_H */
+++ /dev/null
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <master/swap_initializer.h>
-#include "wsp.h"
-#include "wsp_debugfs.h"
-
-SWAP_LIGHT_INIT_MODULE(NULL, wsp_init, wsp_exit,
- wsp_debugfs_init, wsp_debugfs_exit);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * wsp/wsp_msg.c
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <writer/swap_msg.h>
-#include "wsp_msg.h"
-
-/*
- * MSG_WSP (payload):
- * +-------------+----------+----------+
- * | name | type | length |
- * +-------------+----------+----------+
- * | PID | int | 4 |
- * | wsp_id | int | 4 |
- * | wsp_payload | variable | variable |
- * +-------------+----------+----------+
-
- * wsp_id:
- * - WSP_RES_LOAD_BEGIN = 0x0001
- * - WSP_RES_LOAD_END = 0x0002
- * - WSP_RES_PROC_BEGIN = 0x0003
- * - WSP_RES_PROC_END = 0x0004
- * - WSP_DRAW_BEGIN = 0x0005
- * - WSP_DRAW_END = 0x0006
- *
- * wsp_payload:
- *
- * 1. WSP_RES_LOAD_BEGIN:
- * +--------+--------+----------+
- * | name | type | length |
- * +--------+--------+----------+
- * | res_id | int | 4 |
- * | path | string | variable |
- * +--------+--------+----------+
- *
- * 2. WSP_RES_LOAD_END, WSP_RES_PROC_BEGIN, WSP_RES_PROC_END:
- * +--------+--------+----------+
- * | name | type | length |
- * +--------+--------+----------+
- * | res_id | int | 4 |
- * +--------+--------+----------+
- *
- * 3. WSP_DRAW_BEGIN, WSP_DRAW_END:
- * no wsp_payload
- */
-
-static int pack_wsp_msg(void *data, size_t size, enum wsp_id id,
- u32 res_id, const char *path)
-{
- size_t len;
- const size_t old_size = size;
-
- /* write PID */
- *(u32 *)data = (u32)current->tgid;
- data += 4;
- size -= 4;
-
- /* write wsp_id */
- *(u32 *)data = (u32)id;
- data += 4;
- size -= 4;
-
- /* pack wsp_payload */
- switch (id) {
- case WSP_RES_LOAD_BEGIN:
- len = strlen(path) + 1;
- if (size < len + 4)
- return -ENOMEM;
-
- /* '+ 4' - skip space for res_id */
- memcpy(data + 4, path, len);
- size -= len;
- case WSP_RES_LOAD_END:
- case WSP_RES_PROC_BEGIN:
- case WSP_RES_PROC_END:
- /* write res_id */
- *(u32 *)data = res_id;
- size -= 4;
- break;
-
- case WSP_DRAW_BEGIN:
- case WSP_DRAW_END:
- break;
-
- default:
- pr_err("unknown wsp_id: id=%u\n", (unsigned int)id);
- return -EINVAL;
- }
-
- return old_size - size;
-}
-
-void wsp_msg(enum wsp_id id, u32 res_id, const char *path)
-{
- int ret;
- void *data;
- size_t size;
- struct swap_msg *m;
-
- m = swap_msg_get(MSG_WSP);
- data = swap_msg_payload(m);
- size = swap_msg_size(m);
- ret = pack_wsp_msg(data, size, id, res_id, path);
- if (ret < 0) {
- pr_err("error MSG_WSP packing, ret=%d\n", ret);
- goto put_msg;
- }
-
- swap_msg_flush(m, ret);
-
-put_msg:
- swap_msg_put(m);
-}
+++ /dev/null
-#ifndef _WSP_MSG_H
-#define _WSP_MSG_H
-
-/*
- * wsp/wsp_msg.h
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <linux/types.h>
-
-enum wsp_id {
- WSP_RES_LOAD_BEGIN = 0x0001,
- WSP_RES_LOAD_END = 0x0002,
- WSP_RES_PROC_BEGIN = 0x0003,
- WSP_RES_PROC_END = 0x0004,
- WSP_DRAW_BEGIN = 0x0005,
- WSP_DRAW_END = 0x0006
-};
-
-void wsp_msg(enum wsp_id id, u32 res_id, const char *path);
-
-#endif /* _WSP_MSG_H */
+++ /dev/null
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <linux/slab.h>
-#include <linux/atomic.h>
-#include <kprobe/swap_kprobes_deps.h>
-#include "wsp_res.h"
-
-static atomic_t __resource_id = ATOMIC_INIT(0);
-
-static inline int __wsp_resource_id(void)
-{
- return atomic_inc_return(&__resource_id);
-}
-
-struct wsp_resource_data {
- struct list_head list;
- int id;
- unsigned long addr;
- char *path;
-};
-
-static LIST_HEAD(__resources_list);
-static DEFINE_MUTEX(__resources_mutex);
-
-static struct wsp_resource_data *wsp_resource_data_alloc(void)
-{
- struct wsp_resource_data *p;
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- return NULL;
-
- INIT_LIST_HEAD(&p->list);
-
- return p;
-}
-
-static void wsp_resource_data_free(struct wsp_resource_data *p)
-{
- if (!p)
- return;
-
- kfree(p->path);
- kfree(p);
-}
-
-static struct wsp_resource_data *wsp_resource_data_find(unsigned long addr)
-{
- struct wsp_resource_data *p;
-
- list_for_each_entry(p, &__resources_list, list)
- if (p->addr == addr)
- return p;
-
- return NULL;
-}
-
-int wsp_resource_data_id(unsigned long addr)
-{
- int ret = -1;
- struct wsp_resource_data *p;
-
- mutex_lock(&__resources_mutex);
- p = wsp_resource_data_find(addr);
- if (p)
- ret = p->id;
- mutex_unlock(&__resources_mutex);
-
- return ret;
-}
-
-int wsp_resource_data_add(unsigned long addr, char *path)
-{
- int ret = -1;
- struct wsp_resource_data *p;
-
- mutex_lock(&__resources_mutex);
- p = wsp_resource_data_find(addr);
- if (p) {
- ret = p->id;
- goto out;
- }
- p = wsp_resource_data_alloc();
- if (p) {
- p->id = __wsp_resource_id();
- p->addr = addr;
- p->path = path;
- list_add_tail(&p->list, &__resources_list);
- ret = p->id;
- }
-
-out:
- mutex_unlock(&__resources_mutex);
-
- return ret;
-}
-
-void wsp_resource_data_del(unsigned long addr)
-{
- struct wsp_resource_data *p;
-
- mutex_lock(&__resources_mutex);
- p = wsp_resource_data_find(addr);
- if (p) {
- list_del(&p->list);
- wsp_resource_data_free(p);
- }
-
- mutex_unlock(&__resources_mutex);
-}
-
-/* ============================================================================
- * = init/exit() =
- * ============================================================================
- */
-int wsp_res_init(void)
-{
- return 0;
-}
-
-void wsp_res_exit(void)
-{
- struct wsp_resource_data *p, *tmp;
-
- mutex_lock(&__resources_mutex);
- list_for_each_entry_safe(p, tmp, &__resources_list, list) {
- list_del(&p->list);
- wsp_resource_data_free(p);
- }
- mutex_unlock(&__resources_mutex);
-}
+++ /dev/null
-#ifndef _WSP_TDATA_H
-#define _WSP_TDATA_H
-
-/*
- * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * @section COPYRIGHT
- *
- * Copyright (C) Samsung Electronics, 2015
- *
- * @section DESCRIPTION
- *
- * Web startup profiling
- */
-
-#include <linux/types.h>
-
-int wsp_resource_data_add(unsigned long addr, char *path);
-void wsp_resource_data_del(unsigned long addr);
-int wsp_resource_data_id(unsigned long addr);
-
-int wsp_res_init(void);
-void wsp_res_exit(void);
-
-#endif /* _WSP_TDATA_H */