# HG changeset patch # User Paul Boddie # Date 1660083944 -7200 # Node ID 77fbb52e0a14015a9b0987488d876db54f3a677a # Parent ba6c9728a9aec561953579c3f35bdde607fbcb1b# Parent bde6edfa486599da68a6f42cd1e99064ad41e6c1 Merged the mmap-region-flags branch into the default branch. diff -r ba6c9728a9ae -r 77fbb52e0a14 conf/dstest_exec.cfg --- a/conf/dstest_exec.cfg Sun Jun 12 17:10:30 2022 +0200 +++ b/conf/dstest_exec.cfg Wed Aug 10 00:25:44 2022 +0200 @@ -34,7 +34,7 @@ }, log = { "ext2svr", "y" }, }, - "rom/dstest_ext2_server", "blocksvr", "rom/e2test.fs", "10", "ext2svr"); + "rom/dstest_ext2_server", "blocksvr", "rom/e2test.fs", "20", "ext2svr"); -- Obtain user filesystems with umask 0022 (18). @@ -47,4 +47,4 @@ }, log = { "client", "g" }, }, - "rom/dstest_exec", "home/paulb/dstest_exec_payload", "hello", "world"); + "rom/dstest_exec", "home/paulb/exec_region_mapper", "home/paulb/dstest_exec_payload", "hello", "world"); diff -r ba6c9728a9ae -r 77fbb52e0a14 conf/dstest_file_mapping.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_mapping.cfg Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,23 @@ +-- vim:set ft=lua: + +local L4 = require("L4"); + +local l = L4.default_loader; + +local server = l:new_channel(); + +l:startv({ + caps = { + server = server:svr(), + }, + log = { "server", "r" }, + }, + "rom/dstest_host_server", "10"); + +l:startv({ + caps = { + server = server, + }, + log = { "client", "g" }, + }, + "rom/dstest_file_mapping", "rom/dstest_file_mapping.cfg", 1024, 1024); diff -r ba6c9728a9ae -r 77fbb52e0a14 conf/dstest_file_mapping.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_mapping.list Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,25 @@ +entry dstest_file_mapping +roottask moe rom/dstest_file_mapping.cfg +module dstest_file_mapping.cfg +module l4re +module ned +module dstest_file_mapping +module dstest_host_server +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libc_support_misc.so +module libdl.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module libld-l4.so +module libpthread.so +module libstdc++.so +module libsupc++.so +module libuc_c.so diff -r ba6c9728a9ae -r 77fbb52e0a14 conf/dstest_file_remove.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_remove.cfg Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,51 @@ +-- vim:set ft=lua: + +local L4 = require("L4"); + +local l = L4.default_loader; + +local pipe_server = l:new_channel(); + +l:startv({ + caps = { + server = pipe_server:svr(), + }, + log = { "pipes", "r" }, + }, + "rom/dstest_pipe_server", "10"); + +local block_server = l:new_channel(); + +l:startv({ + caps = { + server = block_server:svr(), + }, + log = { "blocksvr", "r" }, + }, + "rom/dstest_block_server", "10"); + +local ext2svr = l:new_channel(); + +l:startv({ + caps = { + blocksvr = block_server, + pipes = pipe_server, + ext2svr = ext2svr:svr(), + }, + log = { "ext2svr", "y" }, + }, + "rom/dstest_ext2_server", "blocksvr", "rom/e2test.fs", "10", "ext2svr"); + +-- Obtain user filesystems with umask 0022 (18). + +local open_for_user = 6; +local ext2svr_paulb = L4.cast(L4.Proto.Factory, ext2svr):create(open_for_user, 1000, 1000, 18); + +l:startv({ + caps = { + server = ext2svr_paulb, + }, + log = { "client", "g" }, + }, + -- program, file to create and remove + "rom/dstest_file_remove", "home/paulb/to_remove"); diff -r ba6c9728a9ae -r 77fbb52e0a14 conf/dstest_file_remove.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_remove.list Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,28 @@ +entry dstest_file_remove +roottask moe rom/dstest_file_remove.cfg +module dstest_file_remove.cfg +module e2test.fs +module l4re +module ned +module dstest_file_remove +module dstest_ext2_server +module dstest_block_server +module dstest_pipe_server +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libc_support_misc.so +module libdl.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module libld-l4.so +module libpthread.so +module libstdc++.so +module libsupc++.so +module libuc_c.so diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/ClientLibrary --- a/docs/wiki/ClientLibrary Sun Jun 12 17:10:30 2022 +0200 +++ b/docs/wiki/ClientLibrary Wed Aug 10 00:25:44 2022 +0200 @@ -15,6 +15,7 @@ The fields of the `file_t` data structure are as follows: || '''Field''' || '''Description''' || +|| `ref` || A reference to the component providing file content || || `memory` || The memory address of the exposed file region || || `start_pos` || The start position of the region in the file || || `end_pos` || The end position of the region in the file || @@ -24,6 +25,7 @@ || `object_flags` || Flags indicating support for certain file features || || `can_block` || Notification flags for blocking access to the file || || `notifications`|| Notification flags set for the file || +|| `flags` || The flags used to open the file || == Client Programming Interface == diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/Components --- a/docs/wiki/Components Sun Jun 12 17:10:30 2022 +0200 +++ b/docs/wiki/Components Wed Aug 10 00:25:44 2022 +0200 @@ -59,9 +59,9 @@ Object [label="MappedFile\nor\nDirectory"]; } - Client1 -> Client2 -> Client3 -> Client4 -> Client5 -> Client6 -> Client7 [arrowhead=none,style=dotted]; - Opener1 -> Opener2 [arrowhead=none,style=dotted]; - OpenerContext1 -> OpenerContext2 -> OpenerContext3 [arrowhead=none,style=dotted]; + Client1 -> Client2 -> Client3 -> Client4 -> Client5 -> Client6 -> Client7 [dir=none,style=dotted]; + Opener1 -> Opener2 [dir=none,style=dotted]; + OpenerContext1 -> OpenerContext2 -> OpenerContext3 [dir=none,style=dotted]; Client1 -> Filesystem [label="open_for_user(user)"]; Filesystem -> Opener1; @@ -129,8 +129,8 @@ } } - Client1 -> Client2 -> Client3 -> Client4 [arrowhead=none,style=dotted]; - Reader1 -> Reader2 -> Reader3 [arrowhead=none,style=dotted]; + Client1 -> Client2 -> Client3 -> Client4 [dir=none,style=dotted]; + Reader1 -> Reader2 -> Reader3 [dir=none,style=dotted]; Client1 -> Directory [label="opendir()"]; Directory -> Reader1; @@ -251,6 +251,7 @@ {{{ mmap(in offset_t position, in offset_t length, + in offset_t start_visible, in offset_t end_visible, out offset_t start_pos, out offset_t end_pos, out offset_t size) }}} diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/Directories --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/wiki/Directories Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,273 @@ += Directories = + +Filesystem directories contain collections of files, other directories, along +with other filesystem objects. Directories can be listed, permitting +filesystem clients to peruse the contents of each directory. However, the act +of listing a directory may impose certain constraints on the operations being +conducted on the directory during the listing process. + +Operations that affect directory contents include the following: + + * Creating a new object + * Removing an object + * Moving an object out of the directory + * Moving an object into the directory + +It is conceivable that for some filesystems, these operations will not be able +to rely on directly modifying the directory since a listing operation may be +in progress. Consequently, a number of rules would be imposed for such +filesystems. It appears that Ext2-based filesystems accessed via libext2fs do +not require such rules to be applied outside the library due to the design of +the filesystem structures and the behaviour of the library itself. + +== Providers == + +Providers of filesystem objects are created when at least one filesystem +client is accessing such an object, with the object regarded as being +'''open'''. By definition, a provider will exist until all clients have closed +or discarded their means of accessing the object. + +An object is regarded as '''accessible''' if it can still be opened by +filesystem clients. Where an object has been removed, it will no longer be +accessible since clients should not be able to see the object in the +filesystem any more. (The actual filesystem residing in storage may not have +been updated for such a removal, this being the eventual outcome of a removal +operation.) + +Providers can be regarded as '''accessible''' if they maintain access to +objects that are open and accessible. Clients opening objects will only gain +access to accessible providers. Providers can become inaccessible if an object +is removed even if the object (and its provider) is still open. + +Initially, providers will correspond to objects resident in the stored +filesystem. Thus, looking up an object using its path will involve the +filesystem, yielding a file identifier that can be used to map to a provider. + +{{{ +def get_provider_from_filesystem(path): + fileid = opening.get_fileid(path) + + if not fileid: + return error + + return find_provider(fileid) +}}} + +However, providers may not always immediately correspond to objects resident +in the stored filesystem. Where an object is created but cannot be immediately +registered in the contents of a directory, it must be registered separately +and attempts to open this object must consult this separate mapping of +filenames to providers. + +{{{ +def get_provider(path): + provider = find_created_provider(path) + + if provider: + return provider + + return get_provider_from_filesystem(path) +}}} + +Providers that are inaccessible need to be retained until they are closed. +However, since they are no longer accessible, they should not be available to +the provider lookup operations. + +When providers are closed, they are removed from any mapping in which they +could be found. Inaccessible providers that have been retained outside the +identifier or filename mappings will represent objects that should be removed +from their parent directory. Accessible providers retained by the mappings +will represent objects that should be created in their parent directory. + +Whether object removal or creation will occur depends on whether the parent +directory is able to safely perform these operations concurrently with other +operations (such as servicing a listing operation) or whether such operations +will need to be deferred until they can be safely performed. + +== Object Removal == + +Filesystem object removal involves obtaining any provider of the object. Where +a provider can be obtained, it will be made inaccessible and removal will be +requested. The actual object will not be removed at least until it is closed +because it may still receive and provide data. (Unlinking open files is a +feature of Unix systems.) + +Where a provider cannot be obtained, an attempt will be made to obtain the +parent directory provider, and if this succeeds, it indicates that the +directory is being accessed and must therefore be notified of the intention to +eventually remove the object concerned. + +Without any provider of the object, and where no directory provider can be +obtained, the object can be immediately removed from the filesystem. + +{{{ +def remove_object(path): + provider = get_provider(path) + + if provider: + return make_provider_inaccessible(provider) + + dirname, basename = split(path) + directory_provider = get_provider(dirname) + + if directory_provider: + return directory_provider.remove_pending(basename) + + return filesystem.remove_object(path) +}}} + +It should be noted that with no accessible provider, any attempt to create a +file with the same name as a removed file should cause a new "version" of the +file to be created. + +== Object Creation == + +Filesystem object creation occurs when no existing provider can be found for a +named object and where creation is requested. Any new object will be +accessible and remain so until or unless it is removed. + +Whether an object is created in the filesystem storage depends on whether the +parent directory is being used. + +If a parent directory is not being used, the object can be registered in its +proper location in the filesystem itself, yielding a file identifier. + +If a parent directory is being used, the object cannot be immediately +registered in its proper location, but since data may still need to be written +to storage, a temporary location is employed, yielding a file identifier. + +For an object created in a temporary location, a temporary mapping from the +path of the object to the provider is established, with the parent directory +being notified of the pending object creation. + +{{{ +def create_object(path): + provider = get_provider(path) + + if provider: + return error + + dirname, basename = split(path) + directory_provider = get_provider(dirname) + + if directory_provider: + provider = make_provider(get_temporary_location(path)) + directory_provider.create_pending(provider) + return register_created_provider(path, provider) + + provider = make_provider(path) + return register_provider(provider) +}}} + +== Created Providers == + +Created providers are retained by the registry until their files can be +committed to the filesystem in the desired location. + +The `register_created_provider` operation establishes a mapping from a path to +a provider that can be queried using the `find_created_provider` operation. + +Created providers are deregistered when they are closed. This will occur when +the parent directory of the file to be committed is closed. At that time, the +created file will be moved from its temporary location to the desired +location. + +== Inaccessible Providers == + +Inaccessible providers are retained by the registry until they are closed. + +Where the provided file resides in a non-temporary location, closure will +occur when the parent directory of the provided file is closed, this having +obstructed the removal of the file. At that time, the provided file will be +removed. + +Where the provided file resides in a temporary location, closure will not be +obstructed by any directory and will cause the file to be removed immediately. + +== Directory Provider Closure == + +A critical event that typically initiates housekeeping work for created and +removed files is the closure of the parent directory of those files. + +Directory providers support the `create_pending` and `remove_pending` +operations that register the details of affected files. When closure occurs, +the provider will contact the registry to initiate the necessary work to +relocate created files and to remove files. + + + + + Where an object provider is notified of pending removal, it will initiate + removal of the actual object upon closure, checking for an active parent + provider. + + Where a parent provider is notified of pending removal, it will initiate + removal of the actual object upon closure, checking for an active object + provider. + + Object opening logic would need to be adjusted. Consider the case where a + file is removed but still open. + + Any attempt to open a file with the same name must ignore the original and + open a new file of that name which would be stored elsewhere until such + time as the original is actually removed from its location. + + This new file might have a different identifier since the original file + would still exist and retain the identifier. + + Any such new, but uncommitted, file must be accessible by other clients + attempting to open a file of that name. However, removal operations must + also be possible, making this version of the open file also unaccessible + to new opening clients. + + Therefore, any providers used by opening clients must only refer to a + version of a file that is pending removal if no other version has been + created. Once a new version has been created, any provider pending removal + must be relegated to a separate collection. + + Storage of new versions of files could be confined to special directories + that are hidden from clients. + + The opening logic would be as follows: + + provider = find_provider(path) + + if provider: + if provider.pending_removal(): + relegate(provider) + provider = create_provider(path, hidden=True) + else: + provider = create_provider(path, hidden=False) + + # Have provider. + + return provider.make_resource() + + A provider would normally be obtained by consulting the filesystem + implementation. However, new versions of files replacing ones pending + removal will reside in locations that are different to the intended + location of the file. Consequently, the registry needs to maintain its own + mapping of paths to providers for such file versions. + + The opportunity to remove a file definitively arises when the following + conditions are satisfied: + + * The parent directory is not open in some way + * The original provider for the file is no longer open + + For new versions of a removed file that have themselves been marked as + pending removal, their closure is sufficient to remove their filesystem + resources. + + However, the registry must maintain a path entry for any active version + of a removed file until that version is closed. Thus, the following + conditions apply: + + * The parent directory (of the original file) is not open in some way + * The provider of the new version is no longer open + + It will not be generally possible to open the hidden directory containing + new file versions. Therefore, the transfer of the file to its intended + location will not risk interfering with any operations on the hidden + directory. + diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/FilesystemAccess --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/wiki/FilesystemAccess Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,611 @@ += Filesystem Access = + +Within the [[ServerLibrary|filesystem server library]], a number of different +abstractions and mechanisms are employed to provide access to filesystem +objects. An overview of these abstractions is presented below. + +######## A graph showing the relationships between filesystem access +######## components + +{{{#!graphviz +#format svg +#transform notugly +digraph components { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; + Opener; + + Opener_note -> Opener [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; + ResourceRegistry; + + ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Resource_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; + Resource [label="Resource\n(Pager)"]; + + Resource_note -> Resource [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; + Provider; + + Provider_note -> Provider [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Provides\npopulated\nfile pages"]; + PageMapper; + + PageMapper_note -> PageMapper [dir=none,style=dotted]; + } + + ProviderRegistry [shape=record,label=" ProviderRegistry | ... | { file-n | provider-n } | ... "]; + + subgraph { + rank=same; + + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; + FileOpening; + + FileOpening_note -> FileOpening [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Populates\nfile pages"]; + Accessor; + Accessor_note -> Accessor [dir=none,style=dotted]; + } + + /* Opening a file. */ + + Opener -> ResourceRegistry -> Resource; + ResourceRegistry -> FileOpening; + + Provider -> PageMapper; + FileOpening -> Accessor; + + /* Closing a file. */ + + Resource -> Provider -> ProviderRegistry:head; + + /* Open file management. */ + + ResourceRegistry -> ProviderRegistry:head; +} +}}} + +######## + +An `Opener` requests a `Resource` from a `ResourceRegistry`, each `Resource` +acting as a [[ServerLibrary#Pager|`Pager`]] and providing the actual access +mechanism to file content for a particular program. Since many programs may +access the same file simultaneously, a `Provider` object represents a file +opened in the system, retaining a `PageMapper` that coordinates the collective +access to the file (see the [[Paging|paging documentation]] for more +information). + +A `ProviderRegistry` maintains the record of opened files, yielding a new +`Provider` for an unopened file or an existing `Provider` for a file that is +already open. The `ProviderRegistry` monitors the usage of files, discarding +any `Provider` no longer in use. + +== Opening Files == + +Opening a file involves the identification of the filesystem object involved, +the acquisition of a `Provider` representing the file, and the creation of a +`Resource` to deliver file content to the requesting program. + +######## A graph showing the opening mechanism for an already-open file + +{{{#!graphviz +#format svg +#transform notugly +digraph opening_open { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; + Opener; + + Opener_note -> Opener [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; + ResourceRegistry; + + Resource_returned [label="Resource\n(Pager)"]; + Resource_returned_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; + + ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; + Resource_returned -> Resource_returned_note [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Resource [label="Resource\n(Pager)"]; + + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; + FileOpening; + + FileOpening_note -> FileOpening [dir=none,style=dotted]; + } + + subgraph { + rank=max; + + ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Provides\nopen files"]; + ProviderRegistry; + + Provider; + Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; + + ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; + Provider -> Provider_note [dir=none,style=dotted]; + } + + /* Obtaining a resource from the registry. */ + + Opener -> ResourceRegistry [label="get_resource"]; + ResourceRegistry -> Resource_returned [dir=none]; + Resource_returned -> Opener; + + /* Obtaining a provider. */ + + ResourceRegistry -> FileOpening [label="get_fileid"]; + ResourceRegistry -> ProviderRegistry [label="get(fileid)"]; + ProviderRegistry -> Provider; + + /* Obtaining the resource from the provider. */ + + ResourceRegistry -> Provider [label="make_resource"]; + Provider -> Resource [dir=none]; + Resource -> ResourceRegistry; +} +}}} + +######## + +Where a provider does not already exist, with the file not having been opened +already, some extra interactions occur: + +######## A graph showing opening mechanism details for an unopened file + +{{{#!graphviz +#format svg +#transform notugly +digraph opening_unopened { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + Opener; + + ResourceRegistry; + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Created\nand set in\nregistry"]; + Provider; + + Accessor; + PageMapper; + PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Created\nfor provider"]; + + Provider_note -> Provider [dir=none,style=dotted]; + PageMapper -> PageMapper_note [dir=none,style=dotted]; + } + + subgraph { + rank=max; + + ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen files"]; + ProviderRegistry; + FileOpening; + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; + + ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; + FileOpening -> FileOpening_note [dir=none,style=dotted]; + } + + Opener -> ResourceRegistry [dir=none,style=dashed]; + + /* Obtaining a new provider. */ + + ResourceRegistry -> FileOpening [label="make_accessor"]; + FileOpening -> Accessor [dir=none]; + Accessor -> PageMapper -> ResourceRegistry; + + ResourceRegistry -> Provider [dir=none]; + Provider -> ProviderRegistry [label="set(fileid)"]; +} +}}} + +######## + +== Notifications == + +Within the filesystem access architecture, users of files may act in ways that +may notify other users of the same file. To support such notifications, an +interface is provided for filesystem clients to subscribe and unsubscribe to +notifications. The notifications themselves occur when files are opened, +modified and closed. Pipes also generate notifications when a pipe reader +makes more space available for the writer. + +######## A graph showing subscription to notifications + +{{{#!graphviz +#format svg +#transform notugly +digraph notification_subscriptions { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Client1 [label="Client\nprogram"]; + Client2 [label="Client\nprogram"]; + } + + subgraph { + rank=same; + + Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Created for\nnotifications"]; + Notifier1 [label="Notifier"]; + Notifier2 [label="Notifier"]; + + Notifier1_note -> Notifier1 -> Notifier2 [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Resource1 [label="Resource\n(Pager)"]; + Resource2 [label="Resource\n(Pager)"]; + } + + subgraph { + rank=same; + + Notifier1_subscribe_note [shape=note,style=filled,fillcolor=gold,label="Propagated to\nprovider"]; + Notifier1_subscribe [label="Notifier"]; + Notifier2_subscribe [label="Notifier"]; + + Notifier1_subscribe_note -> Notifier1_subscribe -> Notifier2_subscribe [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Manages file\nsubscriptions"]; + Provider [label="Provider"]; + + Provider_note -> Provider [dir=none,style=dotted]; + } + + /* Subscribing. */ + + Client1 -> Notifier1 [dir=none]; + Notifier1 -> Resource1 [label="subscribe"]; + + Resource1 -> Notifier1_subscribe [dir=none]; + Notifier1_subscribe -> Provider [label="subscribe"]; + + Client2 -> Notifier2 [dir=none]; + Notifier2 -> Resource2 [label="subscribe"]; + + Resource2 -> Notifier2_subscribe [dir=none]; + Notifier2_subscribe -> Provider [label="subscribe"]; +} +}}} + +######## + +An example of a notification scenario is given below: + +######## A graph showing notification + +{{{#!graphviz +#format svg +#transform notugly +digraph notifications { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Client1 [label="Client\nprogram"]; + Client2 [label="Client\nprogram"]; + } + + subgraph { + rank=same; + + Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Registered for\nnotifications"]; + Notifier1 [label="Notifier"]; + + Resource2 [label="Resource\n(Pager)"]; + Resource2_note [shape=note,style=filled,fillcolor=gold,label="Generates\nnotification"]; + + Notifier1_note -> Notifier1 [dir=none,style=dotted]; + Resource2 -> Resource2_note [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Propagates\nnotifications"]; + Provider [label="Provider"]; + + Provider_note -> Provider [dir=none,style=dotted]; + } + + /* A notification scenario. */ + + Client2 -> Resource2 [label="resize"]; + Resource2 -> Provider [label="notify_others"]; + Provider -> Notifier1 [label="notify"]; + Client1 -> Notifier1 [label="wait"]; +} +}}} + +######## + +Notifications provide the basis for blocking reading and writing operations, +with pipe endpoints depending on such blocking for efficient use of pipes. The +interactions involved when transferring data through pipes are depicted below. + +######## A graph showing notifications employed by pipe endpoints + +{{{#!graphviz +#format svg +#transform notugly +digraph pipe_notifications { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=min; + + Client1_note [shape=note,style=filled,fillcolor=gold,label="Fill pipe\nthen request\nmore space"]; + Client1 [label="Client\nprogram\n(writer)"]; + Client1_wait_note [shape=note,style=filled,fillcolor=gold,label="Waiting after\nfilling pipe"]; + Client1_wait [label="Client\nprogram\n(writer)"]; + + Client1_note -> Client1 [dir=none,style=dotted]; + Client1_wait_note -> Client1_wait [dir=none,style=dotted]; + + Client1 -> Client1_wait_note [dir=none,style=invis]; + } + + subgraph { + rank=max; + + Client2_note [shape=note,style=filled,fillcolor=gold,label="Waiting for\nmore content\nbefore reading"]; + Client2 [label="Client\nprogram\n(reader)"]; + Client2_read_note [shape=note,style=filled,fillcolor=gold,label="Empty pipe\nthen request\nmore content"]; + Client2_read [label="Client\nprogram\n(reader)"]; + + Client2_note -> Client2 [dir=none,style=dotted]; + Client2_read_note -> Client2_read [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Notifier1 [label="Notifier"]; + Resource1 [label="Resource\n(PipePager)"]; + } + + subgraph { + rank=same; + + Notifier2 [label="Notifier"]; + Resource2 [label="Resource\n(PipePager)"]; + } + + subgraph { + rank=same; + + PipePaging [label="Provider\n(PipePaging)"]; + PipePaging_space [label="Provider\n(PipePaging)"]; + } + + /* Making content available. */ + + Client1 -> Resource1 [label="next_region"]; + Resource1 -> PipePaging [label="add_region"]; + PipePaging -> Notifier2 [label="notify"]; + Client2 -> Notifier2 [label="wait"]; + + /* Making space available. */ + + Client2_read -> Resource2 [label="next_region"]; + Resource2 -> PipePaging_space [label="next_region"]; + PipePaging_space -> Notifier1 [label="notify"]; + Client1_wait -> Notifier1 [label="wait"]; +} +}}} + +######## + +== Closing Files == + +Files are closed when client programs release their references to the resource +capabilities used to access files. The mechanism by which this is done +involves the registration of an IRQ object that is bound to the same thread as +the one used by resources to service file access requests. This IRQ object is +associated with the IPC gate through which such requests occur, and the IRQ +object is configured to deliver an interrupt message when the final reference +to the IPC gate has been released. + +When a client program terminates, its references to capabilities should be +released, and this should cause a reference counter associated with the IPC +gate to decrement. Upon the loss of the final reference and the delivery of +the interrupt, the server framework will call a `close` method on the +`Resource` object concerned, this implementing the +[[ServerLibrary#Accountable|`Accountable`]] interface. + +######## A graph showing the closing mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph closing { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + Client [label="Client\nprogram"]; + + subgraph { + rank=same; + + Gate_note [shape=note,style=filled,fillcolor=gold,label="Actual\nendpoint"]; + Gate [label="IPC gate",shape="octagon"]; + IRQ [shape="triangle"]; + ResourceServer; + ResourceServer_note [shape=note,style=filled,fillcolor=gold,label="Handles\nrequests"]; + + Gate_note -> Gate [dir=none,style=dotted]; + ResourceServer -> ResourceServer_note [dir=none,style=dotted]; + } + + Resource [label="Resource\n(Pager)"]; + + Provider; + + ProviderRegistry; + + Client -> Gate [style=dashed,label="(release)"]; + Gate -> IRQ -> ResourceServer; + ResourceServer -> Resource [label="close"]; + Resource -> Provider [label="notify_others\nunsubscribe"]; + Resource -> ProviderRegistry [label="detach"]; +} +}}} + +######## + +As a consequence of closing a file, an `unsubscribe` operation performed on +the `Provider` should cause any `Notifier` objects associated with the +`Resource` object to be released. Meanwhile, the `ResourceServer` will discard +the `Resource` object before the server thread terminates. + +== Removing Files == + +Unix filesystem semantics typically allow a file to be "unlinked" while still +being accessed by a program, with accesses still being directed to the file, +but with the file being removed from a directory and potentially being +replaced by another file. To support this, a mechanism is provided to defer +any removal of an open file. + +######## A graph showing the removal mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph removing { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Client1_note [shape=note,style=filled,fillcolor=gold,label="Removing\nopen file"]; + Client1 [label="Client\nprogram"]; + Client2 [label="Client\nprogram"]; + Client2_note [shape=note,style=filled,fillcolor=gold,label="Closes\nopen file"]; + + Client1_note -> Client1 [dir=none,style=dotted]; + Client2 -> Client2_note [dir=none,style=dotted]; + + Client1 -> Client2 [dir=none,style=invis]; + } + + subgraph { + rank=same; + + Resource [label="Resource\n(Pager)"]; + Opener; + } + + subgraph { + rank=same; + + Provider1 [label="Provider"]; + Provider1_note [shape=note,style=filled,fillcolor=gold,label="Maintains\nremoval\nstate"]; + Provider2 [label="Provider"]; + + Provider1 -> Provider1_note -> Provider2 [dir=none,style=dotted]; + } + + ProviderRegistry1 [label="ProviderRegistry"]; + ProviderRegistry2 [label="ProviderRegistry"]; + + subgraph { + rank=same; + + FileOpening; + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Performing\nfilesystem\noperations"]; + Accessor; + + FileOpening -> Accessor_note -> Accessor [dir=none,style=dotted]; + } + + /* Removing the file while it remains open. */ + + Client1 -> Opener [label="remove\n(via context)"]; + Opener -> ResourceRegistry [label="remove_provider"]; + ResourceRegistry -> FileOpening [label="get_fileid\nunlink_object"]; + ResourceRegistry -> ProviderRegistry1 [label="get(fileid)"]; + ResourceRegistry -> Provider1 [label="remove_pending"]; + + /* File closure and deferred removal. */ + + Client2 -> Resource [label="close"]; + Resource -> Provider2 [label="notify_others\nunsubscribe"]; + Resource -> ProviderRegistry2 [label="detach"]; + ProviderRegistry2 -> Provider2 [label="remove"]; + Provider2 -> Accessor [label="remove"]; +} +}}} + +######## diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/Filesystems --- a/docs/wiki/Filesystems Sun Jun 12 17:10:30 2022 +0200 +++ b/docs/wiki/Filesystems Wed Aug 10 00:25:44 2022 +0200 @@ -5,6 +5,15 @@ == Topics == - * [[Components]] and [[Mechanisms]] - * [[ClientLibrary|Client Library]] + * [[ClientLibrary|Client Library]] - convenience functions and structures to + access filesystem objects + * [[Components]] - server objects exposed at the system level that support + filesystem access + * [[FilesystemAccess|Filesystem Access]] - mechanisms within filesystem + servers to manage filesystem resources and content + * [[Paging]] - the mechanism by which filesystem content is provided for use + by client programs + * [[ProgramLoading|Program Loading]] - the mechanism by which programs are loaded + * [[ServerLibrary|Server Library]] - abstractions to expose and manage access + to filesystem objects * [[Users]] diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/Mechanisms --- a/docs/wiki/Mechanisms Sun Jun 12 17:10:30 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,299 +0,0 @@ -= Mechanisms = - -Within the filesystem server library, a number of different abstractions and -mechanisms are employed to provide access to filesystem objects. - -<> - -The abstractions or components used in the library are organised as follows. - -######## A graph showing the relationships between library components - -{{{#!graphviz -#format svg -#transform notugly -digraph components { - node [fontsize="12.0",fontname="sans-serif",shape=box]; - edge [fontsize="12.0",fontname="sans-serif"]; - rankdir=LR; - - subgraph { - rank=same; - - Resource -> DirectoryResource -> FilePager - [dir=none,style=dotted]; - } - - subgraph { - rank=same; - - Provider -> DirectoryProvider -> FileProvider - [dir=none,style=dotted]; - } - - subgraph { - rank=same; - - Accessor -> Ext2FileAccessor - [dir=none,style=dotted]; - } - - subgraph { - rank=same; - - DirectoryAccessor -> Ext2DirectoryAccessor - [dir=none,style=dotted]; - } - - subgraph { - node [shape=ellipse]; - rank=same; - - Directory [label="dir"]; - File [label="dir/file"]; - } - - DirectoryResource -> DirectoryProvider -> Ext2DirectoryAccessor -> Directory; - FilePager -> FileProvider -> Ext2FileAccessor -> File; -} -}}} - -######## - -This document uses C-style interface syntax since the components operating -within the described mechanisms will themselves be described using such -syntax. - -== Accountable == - -This interface provides the following operations: - -{{{ -void attach() -unsigned int detach() -}}} - -Its purpose is to provide reference counting for components, with the `attach` -operation incrementing the number of users of a component, and with the -`detach` operation decrementing the number of users and returning the -resulting number of users. When components no longer have any attached users, -they may perform operations to tidy up after themselves and permit their -deallocation. - -== Accessors == - -Accessors provide the means of accessing filesystem object data and metadata. -Conceptually, files and directories are both exposed by accessors, although -the interfaces and mechanisms may differ between these types of objects. - -=== Directory Accessors === - -Currently, directory accessors provide support for traversing directory -listings, obtaining the relevant filesystem metadata using the underlying -filesystem access library. - -=== File Accessors === - -File content is accessed through an interface with the following operations: - -{{{ -void close() -void fill(Flexpage *flexpage) -void flush(Flexpage *flexpage) -offset_t get_size() -void set_size(offset_t size) -}}} - -The operations need to be supported with actual filesystem operations. For -example, ext2-based filesystems employ a specific abstraction which invokes -library functions provided by the `libext2fs` package. - -== Providers == - -Providers encapsulate the essential functionality for accessing filesystem -objects. Implementing the `Accountable` interface, they are shared by -resources and discarded when no resources are using them. - -The following operations are supported by providers: - -{{{ -ProviderRegistry *registry() -long make_resource(offset_t *size, object_flags_t *object_flags, Resource **resource) -bool removal_pending() -void remove() -void remove_pending(bool remove) -}}} - -Providers are associated with filesystem objects in a registry which can be -obtained from each provider using the `registry` operation. - -Providers also support the creation of resources through which each user of a -provider exercises its use of that provider. The `make_resource` operation -performs the creation and returns size, flags and resource instance details. - -The removal of providers can be directed using the `remove_pending` and -`remove` operations. Where `remove_pending` has been called with a true value -as its parameter, the `removal_pending` operation will also return a true -value, and upon the provider being discarded, the `remove` operation will be -invoked. The `remove` operation performs the appropriate underlying operation -to remove the object being provided. - -Typically, removal of providers is managed by the provider registry when -resources are closed and detached from providers. - -######## A graph showing the removal mechanism - -{{{#!graphviz -#format svg -#transform notugly -digraph provider_removal { - node [fontsize="12.0",fontname="sans-serif",shape=box]; - edge [fontsize="12.0",fontname="sans-serif"]; - rankdir=LR; - - ResourceServer -> Resource [label="close"]; - Resource -> ProviderRegistry [label="detach"]; - ProviderRegistry -> Provider [label="remove"]; -} -}}} - -######## - -== Resources == - -Resources are objects accessed by clients that support a basic level of -accounting and management. - -The base interface of a resource is as follows: - -{{{ -void activate() -void close() -}}} - -Activation of a resource is an optional operation that performs any -initialisation before a resource is made available to its user. - -In practice, other operations are required to make resources useful. - -In some cases, resources provide the mechanism by which each user of a -filesystem object may access that object independently. They would then -effectively provide a session in which accesses can occur. - -=== Directory Resources === - -Directory resources primarily expose the contents of directories in the -filesystem to a user. They employ directory accessors which concern themselves -with the actual filesystem content. - -[[Components#Directories|Directory components]] are provided using directory -resources. - -=== Pagers or File Resources === - -Pagers are resources that support dataspace access operations, thus allowing -the resources to expose filesystem content in mapped memory regions to a -particular user of the object providing the content. - -[[Components#Files|File components]] and [[Components#Pipes|pipe components]] -are provided using pagers. - -=== Filesystem Resources === - -Filesystem resources provide the entry point for access to a filesystem by -other components or programs. Since filesystems often enforce identity-based -access controls, a filesystem resource will typically support the -`open_for_user` operation in various forms, with the result of this operation -being the instantiation of an `OpenerResource` configured for the indicated -user identity. - -== Registries == - -The basic mechanism for obtaining a resource involves a registry, as -illustrated by the following diagram. - -######## A graph showing the use of a registry in obtaining resources - -{{{#!graphviz -#format svg -#transform notugly -digraph registry { - node [fontsize="12.0",fontname="sans-serif",shape=box]; - edge [fontsize="12.0",fontname="sans-serif"]; - rankdir=LR; - - subgraph { - node [label="OpenerContextResource"]; - rank=same; - - OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; - } - - subgraph { - node [label="OpenerResource"]; - rank=same; - - OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; - } - - subgraph { - rank=same; - - ResourceRegistry -> Resource; - } - - subgraph { - rank=same; - - ProviderRegistry -> Provider; - } - - OpenerContextResource1 -> OpenerResource1 [label="open"]; - OpenerResource1 -> ResourceRegistry [label="get_resource"]; - ResourceRegistry -> ProviderRegistry [label="get/set"]; - - Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; -} -}}} - -######## - -The `ResourceRegistry` coordinates access to filesystem resources and, through -synchronisation, prevents conflicting operations from occurring concurrently. -For example, a removal operation on a file may not be allowed to occur while -an opening operation is in progress. - -To achieve this, a common lock is employed to serialise access, with any -underlying filesystem operations being initiated from the registry while the -lock is held. An object providing the `FileOpening` interface for a filesystem -provides such operations, and the following interaction pattern is thereby -employed: - -######## A graph showing the registry coordinating filesystem operations - -{{{#!graphviz -#format svg -#transform notugly -digraph registry_operations { - node [fontsize="12.0",fontname="sans-serif",shape=box]; - edge [fontsize="12.0",fontname="sans-serif"]; - rankdir=LR; - - OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; -} -}}} - -######## - -Since the `ResourceRegistry` functionality is generic, it could be specialised -for each filesystem or be configured with an appropriate reference to a -`FileOpening` object. The `OpenerResource` would then be generic, invoking the -registry which would, in turn, invoke the opening object. - -However, the chosen approach is to permit `OpenerResource` objects to -implement the `FileOpening` interface for each filesystem, meaning that the -`ResourceRegistry` will end up being called by the opener and then invoking -the opener in return. This is slightly more convenient than the alternative -since the opener can be configured with a given user identity, and such -identity details will ultimately be employed when accessing the underlying -filesystem itself. diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/Paging --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/wiki/Paging Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,336 @@ += Paging = + +Within filesystem servers, a number of abstractions are combined to support +[[FilesystemAccess|access to filesystem content]] through paging: the delivery +of content to clients in mapped memory. The general mechanism used to support +paging is depicted below. + +######## A graph showing the general paging mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph paging { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Pager_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; + Pager; + + Pager_note -> Pager [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Provides\npopulated\nfile pages"]; + PageMapper; + + PageMapper_note -> PageMapper [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Populates\nfile pages"]; + Accessor; + + Accessor_note -> Accessor [dir=none,style=dotted]; + } + + AccessMap [shape=record,label=" AccessMap | + { offset-0 | flexpage-0 } | ... | + { offset-n | flexpage-n } | ..."]; + + subgraph { + rank=same; + + Pages_note [shape=note,style=filled,fillcolor=gold,label="Provides\nmemory\npages"]; + Pages; + + Pages_note -> Pages [dir=none,style=dotted]; + } + + PageQueue [shape=record,label=" PageQueue | + { owner-0 | flexpage-0 } | ... | + { owner-N | flexpage-N } | ..."]; + + Flexpage_offset_n [shape=record,label=" Flexpage | + offset | size | region"]; + + subgraph { + rank=same; + + Memory_note [shape=note,style=filled,fillcolor=gold,label="Allocates\nmemory\nregions"]; + Memory; + + Memory_note -> Memory [dir=none,style=dotted]; + } + + Region; + + /* Relationships. */ + + PageMapper -> Accessor; + PageMapper -> AccessMap:head; + Pager -> PageMapper -> Pages; + Pages -> Memory; + Pages -> PageQueue:head; + AccessMap:fpn -> Flexpage_offset_n:head; + PageQueue:fpN -> Flexpage_offset_n:head; + Flexpage_offset_n:r -> Region; +} +}}} + +######## + +A [[ServerLibrary#Pager|`Pager`]], being a resource that provides filesystem +content to clients for a particular file, requests pages from the +`PageMapper`, itself having the role of supplying populated pages of content +from the given file. The `PageMapper` relies on the `AccessMap` to discover +existing pages for any accessed region of a file, involving the `Pages` object +(or page collection) to obtain new memory pages where existing pages are not +available or are no longer valid. It also requests the population of such +pages through use of the associated `Accessor` for the file concerned. + +== Obtaining and Queuing Pages == + +The `Pages` object may either obtain new memory pages from a `Memory` object +or reclaim (or recycle) existing pages from a `PageQueue`, this recording all +pages that have been populated and supplied to clients. Since it may be +desirable to limit the number of pages employed to satisfy file access +operations, the `PageQueue` provides a kind of lending mechanism: pages are +issued to clients and then added to the end of the queue; where no new page +can be supplied by a `Memory` object, the issued page at the head of the queue +is obtained for recycling; ultimately, an issued page will remain valid for +its user (the client accessing its contents) until all pages in front of it in +the queue are themselves recycled and it is then removed from the queue for +recycling. + +== Satisfying Requests for Pages == + +The general procedure for satisfying requests for pages is as follows: + + 1. Attempt to find an existing page for the affected file region. + 1. With an existing page, attempt to reserve the page for issuing. + 1. Without an existing or reserved page, obtain a fresh page for populating. + 1. Queue the page for eventual reuse. + +Since many files are likely to be in use, and since a fixed amount of memory +may be shared to provide access to file content, the interaction between +different `PageMapper` objects, operating on behalf of different files, must +be taken into consideration. The following diagram depicts the principal +mechanisms involved in securing access to pages so as to provide access to +file content. + +######## A graph showing concurrency considerations + +{{{#!graphviz +#format svg +#transform notugly +digraph paging_concurrency { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + /* First page mapper activities. */ + + subgraph { + rank=same; + + PageMapper1_note [shape=note,style=filled,fillcolor=gold,label="Find\nflexpage"]; + PageMapper1 [label="PageMapper"]; + + Accessor1 [label="Accessor"]; + + PageMapper1_reserve_note [shape=note,style=filled,fillcolor=gold,label="Reserve\nflexpage"]; + PageMapper1_reserve [label="PageMapper"]; + + PageMapper1_queue_note [shape=note,style=filled,fillcolor=gold,label="Queue\nflexpage"]; + PageMapper1_queue [label="PageMapper"]; + + PageMapper1_note -> PageMapper1 [dir=none,style=dotted]; + PageMapper1_reserve_note -> PageMapper1_reserve [dir=none,style=dotted]; + PageMapper1_queue_note -> PageMapper1_queue [dir=none,style=dotted]; + } + + /* Access map usage and flexpage acquisition. */ + + subgraph { + rank=same; + + AccessMap_note [shape=note,style=filled,fillcolor=gold,label="Provides\nflexpages\ntentatively"]; + AccessMap1 [label="AccessMap"]; + + Flexpage1 [label="Flexpage",shape=note,style=dashed]; + Flexpage1_note [shape=note,style=filled,fillcolor=gold,label="Needs reservation\nif found\nby mapper"]; + + Flexpage1_reserve [label="Flexpage",shape=note,style=dashed]; + Flexpage1_reserved [label="Flexpage",shape=note]; + Flexpage1_reserved_note [shape=note,style=filled,fillcolor=gold,label="Claimed if\nstill available"]; + + Flexpage1_queue [label="Flexpage",shape=note]; + + AccessMap_note -> AccessMap1 [dir=none,style=dotted]; + Flexpage1_note -> Flexpage1_reserve [dir=none,style=dotted]; + Flexpage1_reserved -> Flexpage1_reserved_note [dir=none,style=dotted]; + + Flexpage1_reserve -> Flexpage1_reserved [dir=none,style=invis]; + } + + subgraph { + rank=same; + + PageQueue_note [shape=note,style=filled,fillcolor=gold,label="Provides\nflexpages\ndefinitively"]; + PageQueue [label="PageQueue"]; + Flexpage [shape=note]; + Pages [label="Pages"]; + Pages_note [shape=note,style=filled,fillcolor=gold,label="Reclaim and\nrecycle\nflexpage"]; + + PageQueue_reserve_note [shape=note,style=filled,fillcolor=gold,label="Provides\nreserved\nflexpage"]; + PageQueue_reserve [label="PageQueue"]; + Flexpage_reserve [label="Flexpage",shape=note]; + Pages_reserve [label="Pages"]; + Pages_reserve_note [shape=note,style=filled,fillcolor=gold,label="Reserve\nflexpage\nif available"]; + + Pages_queue_note [shape=note,style=filled,fillcolor=gold,label="Queues\nissued\nflexpage"]; + Pages_queue [label="Pages"]; + Flexpage_queued [label="Flexpage",shape=note]; + PageQueue_queued [label="PageQueue"]; + + PageQueue_note -> PageQueue [dir=none,style=dotted]; + Pages -> Pages_note [dir=none,style=dotted]; + PageQueue_reserve_note -> PageQueue_reserve [dir=none,style=dotted]; + Pages_reserve -> Pages_reserve_note [dir=none,style=dotted]; + Pages_queue_note -> Pages_queue [dir=none,style=dotted]; + + Pages_note -> PageQueue_reserve_note [dir=none,style=invis]; + Pages_reserve_note -> Pages_queue_note [dir=none,style=invis]; + } + + subgraph { + rank=same; + + Flexpage2 [label="Flexpage",shape=note]; + Flexpage2_note [shape=note,style=filled,fillcolor=gold,label="Reserved\nwhen obtained"]; + + Flexpage2_reserved [label="Flexpage",shape=note]; + + AccessMap2 [label="AccessMap"]; + AccessMap2_note [shape=note,style=filled,fillcolor=gold,label="Records\nreserved\nflexpage"]; + + Flexpage2_queue [label="Flexpage",shape=note]; + + Flexpage2_note -> Flexpage2 [dir=none,style=dotted]; + AccessMap2 -> AccessMap2_note [dir=none,style=dotted]; + + Flexpage2 -> Flexpage2_reserved [dir=none,style=invis]; + } + + /* Second page mapper activities. */ + + subgraph { + rank=same; + + PageMapper2_note [shape=note,style=filled,fillcolor=gold,label="Obtain\nflexpage"]; + PageMapper2 [label="PageMapper"]; + + Accessor2 [label="Accessor"]; + + PageMapper2_record_note [shape=note,style=filled,fillcolor=gold,label="Record\nflexpage"]; + PageMapper2_record [label="PageMapper"]; + + PageMapper2_queue_note [shape=note,style=filled,fillcolor=gold,label="Queue\nflexpage"]; + PageMapper2_queue [label="PageMapper"]; + + PageMapper2_note -> PageMapper2 [dir=none,style=dotted]; + PageMapper2_record_note -> PageMapper2_record [dir=none,style=dotted]; + PageMapper2_queue_note -> PageMapper2_queue [dir=none,style=dotted]; + } + + /* First pager dataflow. */ + + PageMapper1 -> AccessMap1 [label="find"]; + AccessMap1 -> Flexpage1 [dir=none]; + Flexpage1 -> PageMapper1; + + PageMapper1_reserve -> Flexpage1_reserve [dir=none]; + Flexpage1_reserve -> Pages_reserve [label="reserve"]; + Pages_reserve -> Flexpage1_reserved [dir=none]; + Flexpage1_reserved -> PageMapper1_reserve; + + PageMapper1_queue -> Flexpage1_queue [dir=none]; + Flexpage1_queue -> Pages_queue [label="queue"]; + + /* Flexpage queuing. */ + + Pages_queue -> Flexpage_queued [dir=none]; + Flexpage_queued -> PageQueue_queued; + + /* Flexpage retrieval from the queue. */ + + PageQueue -> Flexpage [color="red",dir=none]; + Flexpage -> Pages [color="red"]; + + PageQueue_reserve -> Flexpage_reserve [dir=none]; + Flexpage_reserve -> Pages_reserve; + + /* Flexpage removal from the access map. */ + + Pages -> AccessMap1 [color="red",label="remove\n(via PageMapper)"]; + AccessMap1 -> PageMapper1 [color="red",label="flush"]; + PageMapper1 -> Accessor1 [color="red",label="flush"]; + + /* Second pager dataflow. */ + + PageMapper2 -> Pages [color="red",label="flexpage"]; + Pages -> Flexpage2 [color="red",dir=none]; + Flexpage2 -> PageMapper2 [color="red"]; + PageMapper2 -> Accessor2 [label="fill"]; + + PageMapper2_record -> Flexpage2_reserved [dir=none,label="insert"]; + Flexpage2_reserved -> AccessMap2; + + PageMapper2_queue -> Flexpage2_queue [dir=none]; + Flexpage2_queue -> Pages_queue [label="queue"]; +} +}}} + +######## + +== Reclaiming and Recycling Pages == + +When a `PageMapper` requests a page from the `Pages` object and when the +`Pages` object needs to reclaim such a previously issued page, the page +obtained from the head of the queue is removed from the `AccessMap` employed +by the `PageMapper` that currently owns it. The "owner" of such a page is +employing the page to satisfy requests for content in a region of a particular +file. If modified, the page's contents may be flushed to the underlying file +when it is removed from the owner. As a consequence of its removal, the +`AccessMap` will no longer be able to offer this page to satisfy a request for +data in the affected region to its `PageMapper`. A reclaimed page is then +returned to the `PageMapper` requiring it, and since the page will no longer +reside in the `PageQueue`, it will be exclusively available for that +`PageMapper`, being populated with data from the underlying file and then +issued to its client. + +== Reserving Available Pages == + +When an `AccessMap` is able to satisfy a request for a page providing access +to a region in a particular file, the `PageMapper` must secure exclusive +access to that page. Otherwise, the possibility exists that the page will be +reclaimed and recycled concurrently by another `PageMapper`. Thus, the +`PageMapper` must request that the page be reserved by the `Pages` object +which in turn removes the page from the `PageQueue`. Since no other party can +now obtain the page independently, it can be issued safely. + +== Queuing Issued Pages == + +Once a page has been issued to a client by the `Pager`, regardless of how it +was obtained, it must be made available for future reuse. This is achieved by +the `PageMapper` requesting the queuing of the page, with it being added to +the end of the `PageQueue`. diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/ProgramLoading --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/wiki/ProgramLoading Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,236 @@ += Program Loading = + +The provision of [[Paging|paging]] of file content leads to the possibility of +demand paging for programs, enabling them to be loaded into memory dynamically +and to have only the active portions of those programs resident. To achieve +this, programs must be appropriately initialised in new tasks, with a page +fault handler configured to provide program file content whenever a region of +the program payload is encountered that is not currently resident in memory. + +== Internal Page Fault Handlers == + +When satisfying page faults for a task, one approach involves situating the +page fault handler within the task itself, this managing the available memory +regions and employing receive windows when requesting memory pages. + +The general arrangement involving such internal page fault handlers for a +program in a task is as follows: + +######## A graph showing the internal paging mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph internal_paging { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + TaskMemory [shape=record,label="Task Memory | stack | ... | data | ... | code"]; + + subgraph { + rank=same; + + InternalPager; + InternalPager_note [shape=note,style=filled,fillcolor=gold,label="Resides in\nsame task and\ndefines scope of\nfault resolution"]; + + InternalPager -> InternalPager_note [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Regions [shape=record,label="Regions | + { stack | stack-dataspace } | + { data |
data-dataspace } | + { code | code-dataspace } |..."]; + + ReceiveFlexpage [shape=note,label="Flexpage\n(receive window)"]; + + Flexpage [shape=note]; + Flexpage_note [shape=note,style=filled,fillcolor=gold,label="Satisfies\nmemory\naccess"]; + + Flexpage -> Flexpage_note [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + ResourceD [label="Resource\n(Pager)"]; + ResourceD_note [shape=note,style=filled,fillcolor=gold,label="Provides access\nto file content"]; + + ResourceD -> ResourceD_note [dir=none,style=dotted]; + } + + ProgramFile [shape=record,label="Program File | ... | data | code | ..."]; + + /* Page fault handling. */ + + TaskMemory:d -> InternalPager [label="page fault"]; + + InternalPager -> Regions:d [label="find region"]; + + Regions:dd -> ResourceD [style=dashed]; + + InternalPager -> ReceiveFlexpage [dir=none]; + ReceiveFlexpage -> ResourceD [label="map"]; + + ResourceD -> ProgramFile:d [style=dashed]; + + ResourceD -> Flexpage [dir=none]; + Flexpage -> InternalPager; +} +}}} + +######## + +== External Page Fault Handlers == + +Another approach that may be used to support an internal page fault handler +deployed in a task is to employ an external page fault handler in the creating +task. When a page fault occurs, the external handler ensures that the +appropriate content has been brought into its own memory space. It then +returns a flexpage from the handler routine to resolve the fault. + +######## A graph showing the external paging mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph external_paging { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + TaskMemory [shape=record,label="Task Memory | stack | ... | data | ... | code"]; + + subgraph { + rank=same; + + ExternalPager_note [shape=note,style=filled,fillcolor=gold,label="Resides in\nseparate task"]; + ExternalPager; + + ExternalPager_note -> ExternalPager [dir=none,style=dotted]; + + MappedFlexpage [shape=note,label="Flexpage\n(positioned)"]; + MappedFlexpage_note [shape=note,style=filled,fillcolor=gold,label="Satisfies\nmemory\naccess"]; + + MappedFlexpage -> MappedFlexpage_note [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Regions [shape=record,label="Regions | + { stack | stack-dataspace } | + { data |
data-dataspace } | + { code | code-dataspace } |..."]; + + L4Re [shape=ellipse,label="L4Re paging\nfunctionality"]; + L4Re_note [shape=note,style=filled,fillcolor=gold,label="Supports normal\naccess to file content"]; + + L4Re -> L4Re_note [dir=none,style=dotted]; + + Flexpage [shape=note]; + } + + subgraph { + rank=same; + + ResourceD [label="Resource\n(Pager)"]; + ResourceD_note [shape=note,style=filled,fillcolor=gold,label="Provides access\nto file content"]; + + ResourceD -> ResourceD_note [dir=none,style=dotted]; + } + + ProgramFile [shape=record,label="Program File | ... | data | code | ..."]; + + /* Page fault handling. */ + + TaskMemory:d -> ExternalPager [label="page fault"]; + + ExternalPager -> Regions:d [label="find region"]; + + Regions:dd -> ResourceD [style=dashed]; + + ExternalPager -> L4Re; + L4Re -> ResourceD [label="map"]; + + ResourceD -> ProgramFile:d [style=dashed]; + + ResourceD -> Flexpage [dir=none]; + Flexpage -> ExternalPager; + + ExternalPager -> MappedFlexpage [dir=none]; + MappedFlexpage -> TaskMemory:d; +} +}}} + +######## + +== Configuring Programs == + +To provide an internal page fault handler alongside an actual program to be +run, the following arrangement is used: + +######## A graph showing the configuration arrangement + +{{{#!graphviz +#format svg +#transform notugly +digraph program_configuration { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=min; + + CreatingTask_note [shape=note,style=filled,fillcolor=gold,label="Responsible for\ncreating the\nnew task"]; + CreatingTask [label="Creating task"]; + + CreatingTask_note -> CreatingTask [dir=none,style=dotted]; + } + + subgraph { + rank=max; + + IPCGate_note [shape=note,style=filled,fillcolor=gold,label="Created for sharing\nbetween the tasks"]; + IPCGate [shape=octagon,label="IPC gate"]; + + IPCGate_note -> IPCGate [dir=none,style=dotted]; + } + + InitCaps [shape=record,label=" Initial capabilities | { \"server\" | capability }"]; + + subgraph { + rank=same; + + InternalPager; + InternalPager_note [shape=note,style=filled,fillcolor=gold,label="Starts and binds\nto IPC gate"]; + + InternalPager -> InternalPager_note [dir=none,style=dotted]; + + Program; + Program_note [shape=note,style=filled,fillcolor=gold,label="Starts and references\npager via IPC gate"]; + + Program -> Program_note [dir=none,style=dotted]; + } + + /* Create and transfer IPC gate for binding. */ + + CreatingTask -> IPCGate [label="create"]; + CreatingTask -> InitCaps:head [label="define"]; + InitCaps:c -> IPCGate; + + /* Thread initiation. */ + + CreatingTask -> InternalPager [label="start"]; + InternalPager -> InitCaps:s [label="bind"]; + + CreatingTask -> Program [label="start"]; + Program -> IPCGate -> InternalPager [style=dashed]; +} +}}} + +######## diff -r ba6c9728a9ae -r 77fbb52e0a14 docs/wiki/ServerLibrary --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/wiki/ServerLibrary Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,245 @@ += Server Library = + +Within the filesystem server library, a number of different abstractions and +mechanisms are employed to provide access to filesystem objects. + +<> + +This document uses C-style interface syntax since the components operating +within the described mechanisms will themselves be described using such +syntax. + +== Accountable == + +This interface provides the following operations: + +{{{ +void attach(); +unsigned int detach(); +}}} + +Its purpose is to provide reference counting for components, with the `attach` +operation incrementing the number of users of a component, and with the +`detach` operation decrementing the number of users and returning the +resulting number of users. When components no longer have any attached users, +they may perform operations to tidy up after themselves and permit their +deallocation. + +== Accessors == + +Accessors provide the means of accessing filesystem object data and metadata. +Conceptually, files and directories are both exposed by accessors, although +the interfaces and mechanisms may differ between these types of objects. + +=== Directory Accessors === + +Currently, directory accessors provide support for traversing directory +listings, obtaining the relevant filesystem metadata using the underlying +filesystem access library. + +=== File Accessors === + +File content is accessed through an interface with the following operations: + +{{{ +void close(); +void fill(Flexpage *flexpage); +void flush(Flexpage *flexpage); +offset_t get_size(); +void set_size(offset_t size); +}}} + +The operations need to be supported with actual filesystem operations. For +example, ext2-based filesystems employ a specific abstraction which invokes +library functions provided by the `libext2fs` package. + +== Providers == + +Providers encapsulate the essential functionality for accessing filesystem +objects. Implementing the `Accountable` interface, they are shared by +resources and discarded when no resources are using them. + +The following operations are supported by providers: + +{{{ +ProviderRegistry *registry(); +long make_resource(offset_t *size, object_flags_t *object_flags, Resource **resource); +bool removal_pending(); +void remove(); +void remove_pending(bool remove); +}}} + +Providers are associated with filesystem objects in a registry which can be +obtained from each provider using the `registry` operation. + +Providers also support the creation of resources through which each user of a +provider exercises its use of that provider. The `make_resource` operation +performs the creation and returns size, flags and resource instance details. + +The removal of providers can be directed using the `remove_pending` and +`remove` operations. Where `remove_pending` has been called with a true value +as its parameter, the `removal_pending` operation will also return a true +value, and upon the provider being discarded, the `remove` operation will be +invoked. The `remove` operation performs the appropriate underlying operation +to remove the object being provided. + +Typically, removal of providers is managed by the provider registry when +resources are closed and detached from providers. + +######## A graph showing the removal mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph provider_removal { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + ResourceServer -> Resource [label="close"]; + Resource -> ProviderRegistry [label="detach"]; + ProviderRegistry -> Provider [label="remove"]; +} +}}} + +######## + +== Resources == + +Resources are objects accessed by clients that support a basic level of +accounting and management. + +The base interface of a resource is as follows: + +{{{ +void activate(); +void close(); +}}} + +Activation of a resource is an optional operation that performs any +initialisation before a resource is made available to its user. + +In practice, other operations are required to make resources useful. + +In some cases, resources provide the mechanism by which each user of a +filesystem object may access that object independently. They would then +effectively provide a session in which accesses can occur. + +=== Directory Resources === + +Directory resources primarily expose the contents of directories in the +filesystem to a user. They employ directory accessors which concern themselves +with the actual filesystem content. + +[[Components#Directories|Directory components]] are provided using directory +resources. + +<> +=== Pagers or File Resources === + +Pagers are resources that support dataspace access operations, thus allowing +the resources to expose filesystem content in mapped memory regions to a +particular user of the object providing the content. + +[[Components#Files|File components]] and [[Components#Pipes|pipe components]] +are provided using pagers. + +=== Filesystem Resources === + +Filesystem resources provide the entry point for access to a filesystem by +other components or programs. Since filesystems often enforce identity-based +access controls, a filesystem resource will typically support the +`open_for_user` operation in various forms, with the result of this operation +being the instantiation of an `OpenerResource` configured for the indicated +user identity. + +== Registries == + +The basic mechanism for obtaining a resource involves a registry, as +illustrated by the following diagram. + +######## A graph showing the use of a registry in obtaining resources + +{{{#!graphviz +#format svg +#transform notugly +digraph registry { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + node [label="OpenerContextResource"]; + rank=same; + + OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; + } + + subgraph { + node [label="OpenerResource"]; + rank=same; + + OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + ResourceRegistry -> Resource; + } + + subgraph { + rank=same; + + ProviderRegistry -> Provider; + } + + OpenerContextResource1 -> OpenerResource1 [label="open"]; + OpenerResource1 -> ResourceRegistry [label="get_resource"]; + ResourceRegistry -> ProviderRegistry [label="get/set"]; + + Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; +} +}}} + +######## + +The `ResourceRegistry` coordinates access to filesystem resources and, through +synchronisation, prevents conflicting operations from occurring concurrently. +For example, a removal operation on a file may not be allowed to occur while +an opening operation is in progress. + +To achieve this, a common lock is employed to serialise access, with any +underlying filesystem operations being initiated from the registry while the +lock is held. An object providing the `FileOpening` interface for a filesystem +provides such operations, and the following interaction pattern is thereby +employed: + +######## A graph showing the registry coordinating filesystem operations + +{{{#!graphviz +#format svg +#transform notugly +digraph registry_operations { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; +} +}}} + +######## + +Since the `ResourceRegistry` functionality is generic, it could be specialised +for each filesystem or be configured with an appropriate reference to a +`FileOpening` object. The `OpenerResource` would then be generic, invoking the +registry which would, in turn, invoke the opening object. + +However, the chosen approach is to permit `OpenerResource` objects to +implement the `FileOpening` interface for each filesystem, meaning that the +`ResourceRegistry` will end up being called by the opener and then invoking +the opener in return. This is slightly more convenient than the alternative +since the opener can be configured with a given user identity, and such +identity details will ultimately be employed when accessing the underlying +filesystem itself. diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/Makefile --- a/libexec/Makefile Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -1,4 +1,10 @@ PKGDIR ?= . L4DIR ?= $(PKGDIR)/../../.. +TARGET = include lib rm + include $(L4DIR)/mk/subdir.mk + +# Internal dependencies. + +rm: lib diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/include/exec/common.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,58 @@ +/* + * Common structures and functions. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include + +#include + + + +EXTERN_C_BEGIN + +/* Auxiliary vector key-value pair. */ + +struct auxv_entry +{ + l4_umword_t key, value; +}; + +/* Region description. */ + +struct exec_region +{ + l4_addr_t start; + offset_t size; + l4_umword_t flags; + l4_cap_idx_t ds; +}; + + + +/* Utility functions. */ + +l4re_aux_t *exec_get_l4re_aux(int argc, char *argv[]); + +EXTERN_C_END + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/elf.h --- a/libexec/include/exec/elf.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/include/exec/elf.h Wed Aug 10 00:25:44 2022 +0200 @@ -19,27 +19,16 @@ * Boston, MA 02110-1301, USA */ +#pragma once + #include /* Generic program segment interface. */ -class ProgramSegment -{ -public: - virtual bool loadable() = 0; - virtual offset_t file_contents() = 0; - virtual offset_t file_offset() = 0; - virtual l4_addr_t region_address() = 0; - virtual offset_t region_size() = 0; - virtual l4re_rm_flags_t region_flags() = 0; - - Segment *segment(); -}; - template -class ProgramSegmentVariant : public ProgramSegment +class ProgramSegmentVariant : public Segment { protected: PROGRAM_HEADER *_header; @@ -55,18 +44,23 @@ l4re_rm_flags_t region_flags(); }; - + /* Generic interface for an ELF payload. */ class Payload { +protected: + Segment **_segments = NULL; + public: + virtual ~Payload(); + virtual l4_addr_t entry_point() = 0; virtual offset_t header_extent() = 0; virtual offset_t program_header_extent() = 0; - virtual unsigned int segments() = 0; - virtual ProgramSegment *segment(unsigned int i) = 0; + virtual unsigned int segments(); + virtual Segment *segment(unsigned int i) = 0; }; template @@ -82,14 +76,14 @@ offset_t header_extent(); offset_t program_header_extent(); unsigned int segments(); - ProgramSegment *segment(unsigned int i); + Segment *segment(unsigned int i); }; /* Return the appropriate payload object. */ -Payload *get_payload(char *buf); +Payload *exec_open_payload(char *buf); /* vim: tabstop=2 expandtab shiftwidth=2 */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/external_pager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/include/exec/external_pager.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,63 @@ +/* + * A system pager implementation residing in a separate task. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include + +#include +#include + + + +/* Collection types. */ + +typedef std::map MappedRegions; + + + +/* A simple system pager also acting as a region mapper. */ + +class ExternalPager : public ExecPager +{ +protected: + MappedRegions _regions; + +public: + virtual void add(MappedRegion ®ion); + + /* Notification methods, implementing PagerObject. */ + + virtual long exception(l4_exc_regs_t regs, + l4_snd_fpage_t *region); + + virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, + l4_snd_fpage_t *region); + + /* Region manager/mapper methods. */ + + virtual long attach(address_t *start, offset_t size, map_flags_t flags, + l4_cap_idx_t ds, address_t offset, unsigned char align); + +}; + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/internal_pager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/include/exec/internal_pager.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,64 @@ +/* + * A system pager implementation residing in the same task as a program. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include + +#include +#include + + + +/* Collection types. */ + +typedef std::map ExecRegions; +typedef struct exec_region ExecRegion; + + + +/* A simple system pager also acting as a region mapper. */ + +class InternalPager : public ExecPager +{ +protected: + ExecRegions _regions; + +public: + virtual void add(ExecRegion ®ion); + + /* Notification methods, implementing PagerObject. */ + + virtual long exception(l4_exc_regs_t regs, + l4_snd_fpage_t *region); + + virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, + l4_snd_fpage_t *region); + + /* Region manager/mapper methods. */ + + virtual long attach(address_t *start, offset_t size, map_flags_t flags, + l4_cap_idx_t ds, address_t offset, unsigned char align); + +}; + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/mapped_region.h --- a/libexec/include/exec/mapped_region.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/include/exec/mapped_region.h Wed Aug 10 00:25:44 2022 +0200 @@ -36,15 +36,17 @@ offset_t size; l4_umword_t flags; l4_addr_t map_start; + l4_cap_idx_t ds; explicit MappedRegion() - : start(0), size(0), flags(0), map_start(0) + : start(0), size(0), flags(0), map_start(0), ds(L4_INVALID_CAP) { } explicit MappedRegion(l4_addr_t start, l4_addr_t size, - l4_umword_t flags, l4_addr_t map_start) - : start(start), size(size), flags(flags), map_start(map_start) + l4_umword_t flags, l4_addr_t map_start, + l4_cap_idx_t ds = L4_INVALID_CAP) + : start(start), size(size), flags(flags), map_start(map_start), ds(ds) { } }; diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/memory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/include/exec/memory.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,34 @@ +/* + * Program memory initialisation support. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + + + +/* Initialisation functions. */ + +long exec_get_payload(const char *filename, Payload **payload, bool attach); + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/pager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/include/exec/pager.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,51 @@ +/* + * A system pager interface. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include "pager_object_interface.h" + + + +/* A simple system pager also acting as a region mapper. */ + +class ExecPager : public PagerObject +{ +public: + virtual ~ExecPager(); + + /* Notification methods. */ + + virtual long exception(l4_exc_regs_t regs, + l4_snd_fpage_t *region) = 0; + + virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, + l4_snd_fpage_t *region); + + /* Region manager/mapper methods. */ + + virtual long attach(address_t *start, offset_t size, map_flags_t flags, + l4_cap_idx_t ds, address_t offset, unsigned char align); + +}; + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/process.h --- a/libexec/include/exec/process.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/include/exec/process.h Wed Aug 10 00:25:44 2022 +0200 @@ -50,17 +50,18 @@ enum exec_task_caps { - L4_EXEC_PAGER_CAP = 0x10 << L4_CAP_SHIFT, - L4_EXEC_RM_CAP = 0x11 << L4_CAP_SHIFT, - L4_EXEC_MA_CAP = 0x12 << L4_CAP_SHIFT, - L4_EXEC_KIP_CAP = 0x14 << L4_CAP_SHIFT, + L4_EXEC_PAGER_CAP = 0x10UL << L4_CAP_SHIFT, + L4_EXEC_RM_CAP = 0x11UL << L4_CAP_SHIFT, + L4_EXEC_MA_CAP = 0x12UL << L4_CAP_SHIFT, + L4_EXEC_KIP_CAP = 0x14UL << L4_CAP_SHIFT, }; -/* The default first free capability index must follow those above. */ +/* The default first free capability index must follow those above. Any + additional initial capabilities must elevate the index below. */ enum exec_task_cap_indexes { - L4_EXEC_FIRST_FREE_CAP_INDEX = 0x15, + L4_EXEC_FIRST_FREE_CAP_INDEX = 0x15UL, }; @@ -76,7 +77,7 @@ l4_addr_t _utcb_start; - /* Common environment details. */ + /* Common and thread environment details. */ l4re_aux_t _aux; l4re_env_t _env; @@ -88,9 +89,16 @@ long create_thread(l4_cap_idx_t *thread); public: - explicit Process(); + explicit Process(int reserved_threads = 1); + + l4_cap_idx_t allocate_cap(); - long configure(l4_cap_idx_t server); + long configure_task(); + + long configure_thread(l4_cap_idx_t server, l4_cap_idx_t mapped_cap = L4_INVALID_CAP); + + long map_capabilities(struct ipc_mapped_cap mapped_caps[], + bool to_count = true); long thread_start(l4_addr_t program_start, Stack &st); }; diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/segment.h --- a/libexec/include/exec/segment.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/include/exec/segment.h Wed Aug 10 00:25:44 2022 +0200 @@ -26,45 +26,44 @@ #include +#include #include -/* Program segment abstraction. */ +/* Program segment abstractions. */ class Segment { protected: MappedRegion _region; + struct exec_region _exec_region; /* Allocated memory. */ char *_buf; l4re_ds_t _ds; - /* Segment base and corresponding region base. */ + /* Segment region base. */ - l4_addr_t _base, _region_base; + l4_addr_t _region_base; - /* Segment size and corresponding region size. */ + /* Segment region size. */ - offset_t _size, _region_size; + offset_t _region_allocated_size; /* Offset of segment content within the region. */ - offset_t _region_offset; - - /* Access flags. */ + offset_t _region_content_offset; - l4re_rm_flags_t _flags; + /* Common initialisation. */ - /* File access details. */ - - offset_t _file_offset, _file_contents; + void init(); public: - explicit Segment(l4_addr_t base, offset_t size, l4re_rm_flags_t flags, - offset_t file_offset = 0, offset_t file_contents = 0); + virtual ~Segment(); + + l4_cap_idx_t cap(); char *address(); @@ -72,9 +71,9 @@ /* Segment population methods. */ - long allocate(); + long allocate(bool attach); - long fill(file_t *file); + long fill(file_t *file, bool attach); /* Mapped region methods. */ @@ -86,8 +85,48 @@ /* Generic property access. */ + struct exec_region &exec_region(); + + virtual bool loadable() = 0; + virtual offset_t file_contents() = 0; + virtual offset_t file_offset() = 0; + virtual l4_addr_t region_address() = 0; + virtual offset_t region_size() = 0; + virtual l4re_rm_flags_t region_flags() = 0; +}; + + + +class ExplicitSegment : public Segment +{ +protected: + /* Segment base. */ + + l4_addr_t _base; + + /* Segment size. */ + + offset_t _size; + + /* Access flags. */ + + l4re_rm_flags_t _flags; + + /* File access details. */ + + offset_t _file_offset, _file_contents; + +public: + explicit ExplicitSegment(l4_addr_t base, offset_t size, l4re_rm_flags_t flags, + offset_t file_offset = 0, offset_t file_contents = 0); + + /* Generic property access. */ + + bool loadable(); offset_t file_contents(); - + offset_t file_offset(); + l4_addr_t region_address(); + offset_t region_size(); l4re_rm_flags_t region_flags(); }; diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/include/exec/stack.h --- a/libexec/include/exec/stack.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/include/exec/stack.h Wed Aug 10 00:25:44 2022 +0200 @@ -22,8 +22,8 @@ #pragma once #include -#include +#include #include @@ -34,13 +34,6 @@ { protected: - /* Auxiliary vector key-value pair. */ - - struct auxv_entry - { - l4_umword_t key, value; - }; - /* Program segment holding the stack contents. */ Segment &_segment; @@ -51,25 +44,30 @@ /* Stack section properties. */ - l4_addr_t _caps; + l4_addr_t _regions = 0; + l4_addr_t _caps = 0; + int _num_caps = 0; char *_arg_top, *_env_top; char **_argv; char *_auxv_end; - int _env_entries; + int _env_entries = 0; /* L4Re auxiliary and environment regions. */ l4re_aux_t *_aux; l4re_env_t *_env; + /* Any indicated initial capabilities and regions. */ + + l4re_env_cap_entry_t *_cap_entries = NULL; + struct exec_region *_region_entries = NULL; + /* Stack pointer start address. */ l4_addr_t _start; /* Internal stack population methods. */ - void push_cap_entries(l4re_env_cap_entry_t *entries); - void push_string(char *s); void push_env(char *envp[]); @@ -82,6 +80,10 @@ void push_auxv(); + void push_init_caps(); + + void push_regions(); + offset_t write_address(char *arg, char **addr, char *s); void push_envp(char *envp[]); @@ -93,6 +95,10 @@ public: explicit Stack(Segment &segment); + void set_init_caps(l4re_env_cap_entry_t *entries); + + void set_regions(struct exec_region *regions); + void set_l4re_aux(l4re_aux_t *aux); void set_l4re_env(l4re_env_t *env); diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/Makefile --- a/libexec/lib/src/Makefile Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/lib/src/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -3,10 +3,56 @@ TARGET = libexec.a libexec.so PC_FILENAME = libexec -SRC_CC = elf.cc process.cc segment.cc stack.cc -REQUIRES_LIBS = l4re_c-util libmem libipc libstdc++ libsystypes libfsclient + +# Locations for interface input and generated output. + +IDL_DIR = $(PKGDIR)/../libsystypes/idl +IDL_MK_DIR = $(L4DIR)/idl4re/mk +IDL_BUILD_DIR = . +IDL_EXPORT_DIR = . + +include $(IDL_MK_DIR)/idl.mk + +# Compound interfaces. + +pager_object_NAME = PagerObject +pager_object_INTERFACES = region_mapper system_pager + +COMP_INTERFACES_CC = pager_object + +# Individual interfaces. + +CLIENT_INTERFACES_CC = dataspace mapped_file + +SERVER_INTERFACES_CC = $(call common_interfaces,$(COMP_INTERFACES_CC)) -PRIVATE_INCDIR += $(PKGDIR)/include/exec +# Generated and plain source files. + +CLIENT_INTERFACES_SRC_CC = $(call interfaces_to_client_cc,$(CLIENT_INTERFACES_CC)) + +SERVER_INTERFACES_SRC_CC = $(call interfaces_to_server_cc,$(SERVER_INTERFACES_CC) $(COMP_INTERFACES_CC)) + +# Normal source files. + +PLAIN_SRC_CC = \ + common.cc elf.cc external_pager.cc \ + internal_pager.cc memory.cc pager.cc \ + process.cc segment.cc stack.cc + +# Normal definitions. + +SRC_CC = \ + $(CLIENT_INTERFACES_SRC_CC) \ + $(SERVER_INTERFACES_SRC_CC) \ + $(PLAIN_SRC_CC) + +REQUIRES_LIBS = l4re_c-util libmem libipc libstdc++ libsystypes libfsclient + +PRIVATE_INCDIR = $(PKGDIR)/include/exec $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR) + CONTRIB_INCDIR = libexec include $(L4DIR)/mk/lib.mk +include $(IDL_MK_DIR)/interface_rules.mk + +$(PLAIN_SRC_CC): $(CLIENT_INTERFACES_SRC_CC) $(SERVER_INTERFACES_SRC_CC) diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/common.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/lib/src/common.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,59 @@ +/* + * Common functionality. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include "common.h" + + + +/* Find the L4Re auxiliary structure from program argument details. */ + +l4re_aux_t *exec_get_l4re_aux(int argc, char *argv[]) +{ + /* Start after the final terminator (at argc) of the argument vector. */ + + l4_umword_t *arg = ((l4_umword_t *) &argv[argc]) + 1; + + /* The environment vector is traversed. */ + + while (*arg) + arg++; + + /* Skip the terminating null entry. */ + + arg++; + + /* Traverse the auxiliary vector. */ + + struct auxv_entry *entry = (struct auxv_entry *) arg; + + for (; entry->key; entry++) + { + if (entry->key == AT_L4_AUX) + return (l4re_aux_t *) entry->value; + } + + return NULL; +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/elf.cc --- a/libexec/lib/src/elf.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/lib/src/elf.cc Wed Aug 10 00:25:44 2022 +0200 @@ -25,26 +25,13 @@ -/* Generic segment creation from program segments. */ - -Segment *ProgramSegment::segment() -{ - return new Segment( - region_address(), - region_size(), - region_flags(), - file_offset(), - file_contents()); -} - - - /* Program segment construction for 32- or 64-bit variants. */ template ProgramSegmentVariant::ProgramSegmentVariant(PROGRAM_HEADER *header) : _header(header) { + init(); } template @@ -94,6 +81,24 @@ +/* Generic payload destruction. */ + +Payload::~Payload() +{ + if (_segments != NULL) + { + for (unsigned int i = 0; i < segments(); i++) + delete _segments[i]; + } +} + +unsigned int Payload::segments() +{ + return 0; +} + + + /* ELF payload construction for 32- or 64-bit variants. */ template @@ -127,21 +132,37 @@ } template -ProgramSegment *PayloadVariant::segment(unsigned int i) +Segment *PayloadVariant::segment(unsigned int i) { if (i >= segments()) return NULL; - return new ProgramSegmentVariant( - (PROGRAM_HEADER *) ((char *) _header + _header->e_phoff + - _header->e_phentsize * i)); + if (_segments == NULL) + { + _segments = new Segment *[segments()]; + + for (unsigned int j = 0; j < segments(); j++) + _segments[j] = NULL; + } + + Segment *segment = _segments[i]; + + if (segment == NULL) + { + segment = new ProgramSegmentVariant( + (PROGRAM_HEADER *) ((char *) _header + _header->e_phoff + + _header->e_phentsize * i)); + _segments[i] = segment; + } + + return segment; } /* Return the appropriate payload object. */ -Payload *get_payload(char *buf) +Payload *exec_open_payload(char *buf) { switch (buf[EI_CLASS]) { diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/external_pager.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/lib/src/external_pager.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,265 @@ +/* + * A system pager implementation residing in a separate task. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "external_pager.h" + + + +/* A simple system pager also acting as a region mapper. */ + +/* Add a region to the pager. */ + +void ExternalPager::add(MappedRegion ®ion) +{ + _regions[region.map_start] = region; +} + +/* Handle a general exception. */ + +long ExternalPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) +{ + (void) region; + + printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); + + printf("r15 = %lx\n", regs.r15); + printf("r14 = %lx\n", regs.r14); + printf("r13 = %lx\n", regs.r13); + printf("r12 = %lx\n", regs.r12); + printf("r11 = %lx\n", regs.r11); + printf("r10 = %lx\n", regs.r10); + printf("r9 = %lx\n", regs.r9); + printf("r8 = %lx\n", regs.r8); + printf("rdi = %lx\n", regs.rdi); + printf("rsi = %lx\n", regs.rsi); + printf("rbp = %lx\n", regs.rbp); + printf("pfa = %lx\n", regs.pfa); + printf("rbx = %lx\n", regs.rbx); + printf("rdx = %lx\n", regs.rdx); + printf("rcx = %lx\n", regs.rcx); + printf("rax = %lx\n", regs.rax); + printf("trapno = %lx\n", regs.trapno); + printf("err = %lx\n", regs.err); + printf("ip = %lx\n", regs.ip); + printf("flags = %lx\n", regs.flags); + printf("sp = %lx\n", regs.sp); + printf("ss = %lx\n", regs.ss); + printf("fs_base = %lx\n", regs.fs_base); + printf("gs_base = %lx\n", regs.gs_base); + + while (1) + l4_sleep_forever(); + + return L4_EOK; +} + +#define DEBUG 0 + +/* Handle a page fault using any configured regions. */ + +long ExternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) +{ + l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; + +#if DEBUG + printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); +#endif + + MappedRegions::iterator it = _regions.upper_bound(addr); + + if (it != _regions.begin()) + it--; + else + { + printf("not mapped!\n"); + return -L4_ENOMEM; + } + + MappedRegion &r = it->second; + + if ((addr >= r.map_start) && (addr < r.map_start + r.size)) + { + l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); + map_flags_t map_flags = (flags & 4 ? L4RE_DS_F_RX : 0) | (flags & 2 ? L4RE_DS_F_W : 0) | (flags & 1 ? L4RE_DS_F_R : 0); + + if (!map_flags) + map_flags = L4RE_DS_F_R; + + region->fpage = l4_fpage(r.start + (page_addr - r.map_start), L4_PAGESHIFT, map_flags & r.flags); + region->snd_base = page_addr; + +#if DEBUG + printf("%lx...%lx from %lx...%lx offset %lx size %d rights %x ds %lx\n", + r.map_start, region->snd_base, + r.start, l4_fpage_memaddr(region->fpage), + addr - r.map_start, + l4_fpage_size(region->fpage), + l4_fpage_rights(region->fpage), + r.ds); + + printf("%lx -> ", addr); + + for (unsigned int i = 0; i < sizeof(l4_umword_t); i++) + printf("%02x", *((unsigned char *)(r.start + (addr - r.map_start) + i))); + + printf("\n"); +#endif + + if (r.flags & L4RE_RM_F_W) + l4_touch_rw((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); + else + l4_touch_ro((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); + + return L4_EOK; + } + +#if DEBUG + printf("not mapped!\n"); +#endif + + return -L4_ENOMEM; +} + +/* Attach a region for provision when page faults occur. This is required in + the initialisation of a program by the C library which requires a region + mapper. */ + +long ExternalPager::attach(address_t *start, offset_t size, map_flags_t flags, + l4_cap_idx_t ds, address_t offset, + unsigned char align) +{ +#if DEBUG + printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); +#endif + + if (align < L4_PAGESHIFT) + align = L4_PAGESHIFT; + + offset_t increment = 1UL << align; + offset_t region_size = round(size, increment); + + /* Either attempt to find an address for the specified region, starting from + any indicated address. */ + + if (flags & L4RE_RM_F_SEARCH_ADDR) + { + address_t region_start = trunc(*start, increment); + MappedRegions::iterator it = _regions.upper_bound(*start); + + if (!region_start) + region_start += increment; + +#if DEBUG + printf("-> search from %lx -> %lx...\n", *start, region_start); +#endif + + /* Before last known region. */ + + while (it != _regions.end()) + { + MappedRegions::iterator next = it; + MappedRegion &r = it->second; + address_t start_limit; + address_t end_limit = r.map_start; + + /* Consider any preceding region. If no such region exists, choose an + address at the start of memory. */ + + if (it == _regions.begin()) + start_limit = L4_PAGESIZE; + else + { + it--; + MappedRegion &pr = it->second; + start_limit = pr.map_start + pr.size; + it = next; + } + + /* Test against the limits. */ + + if (region_start < start_limit) + region_start = round(start_limit, increment); + + /* Investigate subsequent regions if not enough space exists between the + preceding region (or start of memory) and the current region. */ + + if ((region_start + region_size) > end_limit) + { + it++; + if (it == _regions.end()) + return -L4_ENOMEM; + } + else + break; + } + + /* Attach the provided dataspace. + NOTE: This is only done in this implementation to support the paging + mechanism. In a region mapper residing within the actual task, the + dataspace's map operation would be invoked to obtain mappings. */ + + l4_addr_t ds_start; + + long err = ipc_attach_dataspace(ds, size, (void **) &ds_start); + + if (err) + return err; + + l4_touch_rw((const void *) ds_start, size); + +#if DEBUG + printf("-> added region at %lx size %ld (%d) from %lx ds %lx\n", region_start, region_size, page_order(region_size), ds_start, ds); +#endif + + MappedRegion r(ds_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, region_start, ds); + + add(r); + + *start = region_start; + return L4_EOK; + } + + /* Or attempt to add the specified region at a specific address. */ + + else + { + // NOTE: To be implemented. + +#if DEBUG + printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); +#endif + + return -L4_ENOMEM; + } +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/internal_pager.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/lib/src/internal_pager.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,250 @@ +/* + * A system pager implementation residing in the same task as a program. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "dataspace_client.h" +#include "internal_pager.h" + + + +/* A simple system pager also acting as a region mapper. */ + +/* Add a region to the pager. */ + +void InternalPager::add(ExecRegion ®ion) +{ + _regions[region.start] = region; +} + +/* Handle a general exception. */ + +long InternalPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) +{ + (void) region; + + printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); + + printf("r15 = %lx\n", regs.r15); + printf("r14 = %lx\n", regs.r14); + printf("r13 = %lx\n", regs.r13); + printf("r12 = %lx\n", regs.r12); + printf("r11 = %lx\n", regs.r11); + printf("r10 = %lx\n", regs.r10); + printf("r9 = %lx\n", regs.r9); + printf("r8 = %lx\n", regs.r8); + printf("rdi = %lx\n", regs.rdi); + printf("rsi = %lx\n", regs.rsi); + printf("rbp = %lx\n", regs.rbp); + printf("pfa = %lx\n", regs.pfa); + printf("rbx = %lx\n", regs.rbx); + printf("rdx = %lx\n", regs.rdx); + printf("rcx = %lx\n", regs.rcx); + printf("rax = %lx\n", regs.rax); + printf("trapno = %lx\n", regs.trapno); + printf("err = %lx\n", regs.err); + printf("ip = %lx\n", regs.ip); + printf("flags = %lx\n", regs.flags); + printf("sp = %lx\n", regs.sp); + printf("ss = %lx\n", regs.ss); + printf("fs_base = %lx\n", regs.fs_base); + printf("gs_base = %lx\n", regs.gs_base); + + while (1) + l4_sleep_forever(); + + return L4_EOK; +} + +#define DEBUG 0 + +/* Handle a page fault using any configured regions. */ + +long InternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) +{ + l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; + +#if DEBUG + printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); +#endif + + ExecRegions::iterator it = _regions.upper_bound(addr); + + if (it != _regions.begin()) + it--; + else + { + printf("not mapped!\n"); + return -L4_ENOMEM; + } + + ExecRegion &r = it->second; + + if ((addr >= r.start) && (addr < r.start + r.size)) + { + offset_t window_size = L4_PAGESIZE; + address_t window_base = trunc(addr, window_size); + offset_t offset = addr - r.start; + address_t page_addr = trunc(addr, L4_PAGESIZE); + address_t hot_spot = page_addr - window_base; + + /* Interact with the region's dataspace, specifying a receive window for a + map operation. Here, a single page is specified. */ + + client_Dataspace dataspace(r.ds); + l4_snd_fpage_t rw_region = {0, l4_fpage(window_base, L4_PAGESHIFT, 0)}; + map_flags_t map_flags = (flags & 4 ? L4RE_DS_F_RX : 0) | (flags & 2 ? L4RE_DS_F_W : 0) | (flags & 1 ? L4RE_DS_F_R : 0); + + if (!map_flags) + map_flags = L4RE_DS_F_R; + +#if DEBUG + printf("window_base = %lx; window_size = %lx\n", window_base, window_size); + printf("region = {%lx, {%lx, %d}}\n", rw_region.snd_base, l4_fpage_memaddr(rw_region.fpage), l4_fpage_size(rw_region.fpage)); + printf("map(%lx, %lx, %lx) -> %lx\n", offset, hot_spot, map_flags, r.ds); +#endif + + long err = dataspace.map(offset, hot_spot, map_flags & r.flags, &rw_region); + + /* Indicate an unspecified result, since the mapping should have taken + place. */ + + *region = {0, l4_fpage_invalid()}; + + return err; + } + +#if DEBUG + printf("not mapped!\n"); +#endif + + return -L4_ENOMEM; +} + +/* Attach a region for provision when page faults occur. This is required in + the initialisation of a program by the C library which requires a region + mapper. */ + +long InternalPager::attach(address_t *start, offset_t size, map_flags_t flags, + l4_cap_idx_t ds, address_t offset, + unsigned char align) +{ +#if DEBUG + printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); +#endif + + if (align < L4_PAGESHIFT) + align = L4_PAGESHIFT; + + offset_t increment = 1UL << align; + offset_t region_size = round(size, increment); + + /* Either attempt to find an address for the specified region, starting from + any indicated address. */ + + if (flags & L4RE_RM_F_SEARCH_ADDR) + { + address_t region_start = trunc(*start, increment); + ExecRegions::iterator it = _regions.upper_bound(*start); + + if (!region_start) + region_start += increment; + +#if DEBUG + printf("-> search from %lx -> %lx...\n", *start, region_start); +#endif + + /* Before last known region. */ + + while (it != _regions.end()) + { + ExecRegions::iterator next = it; + ExecRegion &r = it->second; + address_t start_limit; + address_t end_limit = r.start; + + /* Consider any preceding region. If no such region exists, choose an + address at the start of memory. */ + + if (it == _regions.begin()) + start_limit = L4_PAGESIZE; + else + { + it--; + ExecRegion &pr = it->second; + start_limit = pr.start + pr.size; + it = next; + } + + /* Test against the limits. */ + + if (region_start < start_limit) + region_start = round(start_limit, increment); + + /* Investigate subsequent regions if not enough space exists between the + preceding region (or start of memory) and the current region. */ + + if ((region_start + region_size) > end_limit) + { + it++; + if (it == _regions.end()) + return -L4_ENOMEM; + } + else + break; + } + +#if DEBUG + printf("-> added region at %lx size %ld (%d) ds %lx\n", region_start, region_size, page_order(region_size), ds); +#endif + + ExecRegion r = (ExecRegion) {region_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, ds}; + + add(r); + + *start = region_start; + return L4_EOK; + } + + /* Or attempt to add the specified region at a specific address. */ + + else + { + // NOTE: To be implemented. + +#if DEBUG + printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); +#endif + + return -L4_ENOMEM; + } +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/memory.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/lib/src/memory.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,106 @@ +/* + * Support for initialising program memory regions in new tasks. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include +#include + +#include + +#include "memory.h" + + + +/* Obtain the payload as a dataspace. */ + +long exec_get_payload(const char *filename, Payload **payload, bool attach) +{ + file_t *file = client_open(filename, O_RDONLY); + + if (file == NULL) + return -L4_EIO; + + /* Obtain metadata from the file. */ + + char *buf = (char *) client_mmap(file, 0, file->size, 0, 0, L4RE_DS_F_R); + + /* Test the file type indicator. */ + + if ((file->size < EI_NIDENT) || memcmp(buf, "\x7f" "ELF", 4)) + return -L4_EINVAL; + + /* Attempt to get a payload object appropriate for a particular object size + variant. */ + + *payload = exec_open_payload(buf); + + if (*payload == NULL) + return -L4_ERANGE; + + if ((file->size < (*payload)->header_extent()) || + (file->size < (*payload)->program_header_extent())) + { + delete *payload; + *payload = NULL; + return -L4_EIO; + } + + /* Obtain all loadable segments. */ + + for (unsigned int i = 0; i < (*payload)->segments(); i++) + { + Segment *segment = (*payload)->segment(i); + long err; + + if (!segment->loadable()) + continue; + + if (segment->file_contents()) + { + file_t *rfile = client_open(filename, file_opening_flags(segment->region_flags())); + + if (rfile == NULL) + { + delete *payload; + *payload = NULL; + return -L4_EIO; + } + + err = segment->fill(rfile, attach); + } + else + err = segment->allocate(attach); + + if (err) + { + delete *payload; + *payload = NULL; + return err; + } + } + + return L4_EOK; +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/pager.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/lib/src/pager.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,33 @@ +/* + * A system pager interface. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include "pager.h" + + + +/* Necessary virtual destructor. */ + +ExecPager::~ExecPager() +{ +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/process.cc --- a/libexec/lib/src/process.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/lib/src/process.cc Wed Aug 10 00:25:44 2022 +0200 @@ -50,7 +50,7 @@ /* Initialise a new process, this being an abstraction for a new task with some threads. */ -Process::Process() +Process::Process(int reserved_threads) { /* Obtain UTCB area details for the task. */ @@ -61,14 +61,17 @@ /* Populate the common initial environment for the threads. */ _env.factory = L4_BASE_FACTORY_CAP; - _env.main_thread = L4_BASE_THREAD_CAP; _env.log = L4_BASE_LOG_CAP; _env.scheduler = L4_BASE_SCHEDULER_CAP; + _env.mem_alloc = L4_EXEC_MA_CAP; + _env.utcb_area = utcb_fpage; + _env.first_free_utcb = l4_fpage_memaddr(utcb_fpage) + reserved_threads * L4_UTCB_OFFSET; + + /* Capability details that are updated for each thread. */ + + _env.main_thread = L4_BASE_THREAD_CAP; _env.rm = L4_EXEC_RM_CAP; - _env.mem_alloc = L4_EXEC_MA_CAP; _env.first_free_cap = L4_EXEC_FIRST_FREE_CAP_INDEX; - _env.utcb_area = utcb_fpage; - _env.first_free_utcb = l4_fpage_memaddr(utcb_fpage) + L4_UTCB_OFFSET; /* Populate auxiliary information. */ @@ -77,6 +80,13 @@ _aux.ldr_flags = 0; } +/* Capability index allocation. */ + +l4_cap_idx_t Process::allocate_cap() +{ + return (_env.first_free_cap++ << L4_CAP_SHIFT); +} + /* Task and thread initialisation. */ long Process::create_task() @@ -101,7 +111,7 @@ /* Configure the task environment. */ -long Process::configure(l4_cap_idx_t server) +long Process::configure_task() { long err = create_task(); @@ -122,17 +132,48 @@ /* Define capability mappings for the new task. */ struct ipc_mapped_cap mapped_caps[] = { - {L4_BASE_TASK_CAP, _task, L4_CAP_FPAGE_RWS}, - {L4_EXEC_PAGER_CAP, server, L4_CAP_FPAGE_RWS}, - {_env.rm, server, L4_CAP_FPAGE_RWS}, - {_env.factory, l4re_env()->factory, L4_CAP_FPAGE_RWS}, - {_env.log, l4re_env()->log, L4_CAP_FPAGE_RWS}, - {_env.scheduler, l4re_env()->scheduler, L4_CAP_FPAGE_RWS}, - {_env.mem_alloc, l4re_env()->mem_alloc, L4_CAP_FPAGE_RWS}, - {0, L4_INVALID_CAP, 0}, + {L4_BASE_TASK_CAP, _task, L4_CAP_FPAGE_RWS, 0}, + {_env.factory, l4re_env()->factory, L4_CAP_FPAGE_RWS, 0}, + {_env.log, l4re_env()->log, L4_CAP_FPAGE_RWS, 0}, + {_env.scheduler, l4re_env()->scheduler, L4_CAP_FPAGE_RWS, 0}, + {_env.mem_alloc, l4re_env()->mem_alloc, L4_CAP_FPAGE_RWS, 0}, + {0, L4_INVALID_CAP, 0, 0}, }; - return ipc_map_capabilities(_task, mapped_caps); + return map_capabilities(mapped_caps, false); +} + +/* Configure the thread environment. */ + +long Process::configure_thread(l4_cap_idx_t server, l4_cap_idx_t mapped_cap) +{ + /* Employ a distinct region mapper for each thread's environment, this acting + as pager. */ + + if (l4_is_valid_cap(mapped_cap)) + { + _env.rm = mapped_cap; + return L4_EOK; + } + else + { + _env.rm = allocate_cap(); + return ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.rm, server, L4_CAP_FPAGE_RWS, 0}); + } +} + +/* Map capabilities into the task, counting them if indicated. */ + +long Process::map_capabilities(struct ipc_mapped_cap mapped_caps[], + bool to_count) +{ + unsigned int num_mapped_caps; + long err = ipc_map_capabilities(_task, mapped_caps, to_count ? &num_mapped_caps : NULL); + + if (to_count) + _env.first_free_cap += num_mapped_caps; + + return err; } /* Create, initialise and start a thread. */ @@ -150,8 +191,8 @@ /* Initialise the thread with pager, UTCB and task details. */ l4_thread_control_start(); - l4_thread_control_pager(L4_EXEC_PAGER_CAP); - l4_thread_control_exc_handler(L4_EXEC_PAGER_CAP); + l4_thread_control_pager(_env.rm); + l4_thread_control_exc_handler(_env.rm); l4_thread_control_bind((l4_utcb_t *) _utcb_start, _task); err = l4_error(l4_thread_control_commit(thread)); @@ -162,15 +203,24 @@ return err; } - /* Map the thread capability to the task. */ + /* Map the thread capability to the task using a distinct capability index. */ - ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.main_thread, thread, L4_CAP_FPAGE_RWS}); + _env.main_thread = allocate_cap(); + + ipc_map_capability(_task, (struct ipc_mapped_cap) {_env.main_thread, thread, L4_CAP_FPAGE_RWS, 0}); /* Populate the initial environment in the thread. */ st.set_l4re_aux(&_aux); st.set_l4re_env(&_env); + /* Reserve some extra space for capabilities used by this thread. + NOTE: Surely the capability allocator should be able to avoid conflicts, + but concurrency issues have been observed before, leading to various + measures in libipc. */ + + _env.first_free_cap += 0x20; + /* Set the start details. */ err = l4_error(l4_thread_ex_regs(thread, program_start, st.start_address(), 0)); diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/segment.cc --- a/libexec/lib/src/segment.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/lib/src/segment.cc Wed Aug 10 00:25:44 2022 +0200 @@ -31,20 +31,29 @@ -/* Initialise a memory segment. */ +/* Obligatory destructor. */ -Segment::Segment(l4_addr_t base, offset_t size, l4re_rm_flags_t flags, - offset_t file_offset, offset_t file_contents) +Segment::~Segment() +{ +} -: _base(base), _size(size), _flags(flags), _file_offset(file_offset), - _file_contents(file_contents) +/* Common initialisation. */ + +void Segment::init() { - _region_base = trunc(_base, L4_PAGESIZE); - _region_offset = _base - _region_base; + _region_base = trunc(region_address(), L4_PAGESIZE); + _region_content_offset = region_address() - _region_base; /* Expand the region size. */ - _region_size = round(_size + _region_offset, L4_PAGESIZE); + _region_allocated_size = round(region_size() + _region_content_offset, L4_PAGESIZE); +} + +/* Return the capability of the dataspace providing the memory. */ + +l4_cap_idx_t Segment::cap() +{ + return _ds; } /* Return the address of allocated memory. */ @@ -58,53 +67,72 @@ offset_t Segment::size() { - return _size; + return _region_allocated_size; } /* Allocate a writable region for the segment. */ -long Segment::allocate() +long Segment::allocate(bool attach) { - return ipc_allocate_align(_region_size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW, - L4_PAGESHIFT, (void **) &_buf, &_ds); + if (attach) + return ipc_allocate_align(_region_allocated_size, + L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW, + L4_PAGESHIFT, (void **) &_buf, &_ds); + else + return ipc_new_dataspace(_region_allocated_size, + L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW, + L4_PAGESHIFT, &_ds); } /* Fill a segment region with file content. */ -long Segment::fill(file_t *file) +long Segment::fill(file_t *file, bool attach) { - /* Allocate writable memory to populate. */ - - long err = ipc_allocate_align(_region_size, - L4RE_RM_F_SEARCH_ADDR | _flags | L4RE_RM_F_W, - L4_PAGESHIFT, (void **) &_buf, &_ds); - - if (err) - return err; + /* Provide the exposed file contents in a masked memory mapped region, or if + not attaching the region, merely request access via a dataspace. */ - /* Align the exposed file contents to the memory mapped region. - Since the region is page-aligned, the region offset is introduced to map - from an earlier part of the file, if appropriate. */ - - memset(_buf, 0, _region_size); + if (attach) + { + _buf = (char *) client_mmap(file, + file_offset() - _region_content_offset, + _region_allocated_size, + file_offset(), + file_offset() + file_contents(), + region_flags()); + if (_buf == NULL) + return -L4_EIO; + } + else + { + long err = file_mmap_only(file, + file_offset() - _region_content_offset, + _region_allocated_size, + file_offset(), + file_offset() + file_contents()); + if (err) + return err; + } - client_seek(file, _file_offset, SEEK_SET); - offset_t nread = client_read(file, _buf + _region_offset, _file_contents); - - if (nread < _file_contents) - return -L4_EIO; - else - return L4_EOK; + _ds = file->ref; + return L4_EOK; } /* Define and return the mapped region for the segment. */ MappedRegion &Segment::region() { - _region = MappedRegion((l4_addr_t) _buf, _region_size, _flags, _region_base); + _region = MappedRegion((l4_addr_t) _buf, _region_allocated_size, region_flags(), _region_base, _ds); return _region; } +/* Define and return the region dataspace details. */ + +struct exec_region &Segment::exec_region() +{ + _exec_region = (struct exec_region) {_region_base, _region_allocated_size, region_flags(), _ds}; + return _exec_region; +} + /* Return the mapped address corresponding to the given address. */ l4_addr_t Segment::region_address(char *address) @@ -117,16 +145,49 @@ return (address - (l4_addr_t) _buf) + _region_base; } + + +/* Initialise a memory segment using explicit parameters. */ + +ExplicitSegment::ExplicitSegment(l4_addr_t base, offset_t size, + l4re_rm_flags_t flags, offset_t file_offset, + offset_t file_contents) +: _base(base), _size(size), _flags(flags), _file_offset(file_offset), + _file_contents(file_contents) +{ + init(); +} + /* Return the amount of file content loaded into the segment. */ -offset_t Segment::file_contents() +bool ExplicitSegment::loadable() +{ + return false; +} + +offset_t ExplicitSegment::file_contents() { return _file_contents; } +offset_t ExplicitSegment::file_offset() +{ + return _file_offset; +} + +l4_addr_t ExplicitSegment::region_address() +{ + return _base; +} + +offset_t ExplicitSegment::region_size() +{ + return _size; +} + /* Return the region flags for the segment. */ -l4re_rm_flags_t Segment::region_flags() +l4re_rm_flags_t ExplicitSegment::region_flags() { return _flags; } diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/lib/src/stack.cc --- a/libexec/lib/src/stack.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libexec/lib/src/stack.cc Wed Aug 10 00:25:44 2022 +0200 @@ -26,7 +26,6 @@ #include -#include #include #include "stack.h" @@ -39,23 +38,58 @@ : _segment(segment) { _element = (l4_umword_t *) (segment.address() + segment.size()); +} - /* Add a terminator for any additional initial capabilities. */ +/* Set any additional initial capabilities. */ + +void Stack::set_init_caps(l4re_env_cap_entry_t *entries) +{ + _cap_entries = entries; +} + +/* Set any indicated memory regions. */ + +void Stack::set_regions(struct exec_region *regions) +{ + _region_entries = regions; +} - l4re_env_cap_entry_t *entry = (l4re_env_cap_entry_t *) _element; +/* Push any indicated memory regions. */ + +void Stack::push_regions() +{ + struct exec_region *region = (struct exec_region *) _element; + struct exec_region *regions = _region_entries; + + /* Terminate the regions with a null entry. */ - *(--entry) = l4re_env_cap_entry_t(); - _element = (l4_umword_t *) entry; + *(--region) = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; + + for (; (regions != NULL) && (regions->ds != L4_INVALID_CAP); regions++) + { + *(--region) = *regions; + } + + _regions = (l4_addr_t) region; + _element = (l4_umword_t *) region; } /* Push any additional initial capabilities. */ -void Stack::push_cap_entries(l4re_env_cap_entry_t *entries) +void Stack::push_init_caps() { l4re_env_cap_entry_t *entry = (l4re_env_cap_entry_t *) _element; + l4re_env_cap_entry_t *entries = _cap_entries; - while ((entries != NULL) && (entries->cap != L4_INVALID_CAP)) + /* Terminate the capabilities with a null entry. */ + + *(--entry) = l4re_env_cap_entry_t(); + + for (; (entries != NULL) && (entries->flags != ~0UL); entries++) + { *(--entry) = *entries; + _num_caps++; + } _caps = (l4_addr_t) entry; _element = (l4_umword_t *) entry; @@ -102,6 +136,15 @@ void Stack::push_l4re_aux() { + /* Regions for the region mapper. */ + + if (_regions) + *(--_element) = _segment.region_address((char *) _regions); + else + *(--_element) = 0; + + /* Reserve space for the actual L4Re auxiliary structure. */ + _aux = (l4re_aux_t *) _element; _aux--; _element = (l4_umword_t *) _aux; @@ -239,6 +282,11 @@ void Stack::populate(int argc, char *argv[], char *envp[]) { + /* Populate stack with any regions and capabilities. */ + + push_regions(); + push_init_caps(); + /* Populate stack with environment and argument values. */ push_env(envp); diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/rm/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/rm/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,52 @@ +PKGDIR ?= .. +L4DIR ?= $(PKGDIR)/../../.. + +TARGET = exec_region_mapper + +MODE = static + +# Relocate the binary to avoid conflicting with actual payloads. + +DEFAULT_RELOC_x86 := 0xb0000000 +DEFAULT_RELOC_arm := 0xb0000000 +DEFAULT_RELOC_arm64 := 0xc0000000 +DEFAULT_RELOC_ppc32 := 0xb0000000 +DEFAULT_RELOC_amd64 := 0x70000000 +DEFAULT_RELOC_mips := 0x70000000 + +# Locations for interface input and generated output. + +IDL_DIR = $(PKGDIR)/../libsystypes/idl +IDL_MK_DIR = $(L4DIR)/idl4re/mk +IDL_BUILD_DIR = . +IDL_EXPORT_DIR = . + +include $(IDL_MK_DIR)/idl.mk + +# Compound interfaces. + +pager_object_NAME = PagerObject +pager_object_INTERFACES = region_mapper system_pager + +COMP_INTERFACES_CC = pager_object + +# Individual interfaces. + +SERVER_INTERFACES_CC = $(call common_interfaces,$(COMP_INTERFACES_CC)) + +# Generated and plain source files. + +SERVER_INTERFACES_SRC_CC = $(call interfaces_to_server_cc,$(SERVER_INTERFACES_CC) $(COMP_INTERFACES_CC)) + +# Normal source files. + +PLAIN_SRC_CC = region_mapper.cc +SRC_CC = $(PLAIN_SRC_CC) $(SERVER_INTERFACES_SRC_CC) + +REQUIRES_LIBS = libc libstdc++ libexec libipc +PRIVATE_INCDIR = $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR) + +include $(L4DIR)/mk/prog.mk +include $(IDL_MK_DIR)/interface_rules.mk + +$(PLAIN_SRC_CC): $(SERVER_INTERFACES_SRC_CC) diff -r ba6c9728a9ae -r 77fbb52e0a14 libexec/rm/region_mapper.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libexec/rm/region_mapper.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,75 @@ +/* + * A region mapper for deployment in a new task. + * + * Copyright (C) 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include +#include +#include +#include + +#include "pager_object_server.h" + + + +static InternalPager exec_pager; + + + +int main(int argc, char *argv[]) +{ + /* Initialise pager regions from region descriptions obtained via the + auxiliary data. */ + + l4re_aux_t *l4re_aux = exec_get_l4re_aux(argc, argv); + + printf("aux = {%s, %lx, %lx, %lx}\n", l4re_aux->binary, l4re_aux->kip_ds, l4re_aux->dbg_lvl, l4re_aux->ldr_flags); + + /* Skip past the auxiliary structure itself. */ + + struct exec_region *region = (struct exec_region *) *((l4_addr_t *) (l4re_aux + 1)); + + if (!region) + { + printf("Could not find regions.\n"); + return 1; + } + + for (; region && (region->ds != L4_INVALID_CAP); region++) + { + printf("Adding region: {%lx, %lx, %lx, %lx}\n", region->start, region->size, region->flags, region->ds); + exec_pager.add(*region); + } + + /* Start the pager. */ + + printf("Starting pager...\n"); + printf("Pager capability: %lx\n", l4re_env_get_cap("server")); + printf("Main thread: %lx\n", l4re_env()->main_thread); + + ipc_server_loop_for(PagerObject, &exec_pager, "server"); + + printf("Ending pager...\n"); + return 0; +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsclient/include/fsclient/client.h --- a/libfsclient/include/fsclient/client.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsclient/include/fsclient/client.h Wed Aug 10 00:25:44 2022 +0200 @@ -64,7 +64,10 @@ /* File and region operations. */ long client_flush(file_t *file); -void *client_mmap(file_t *file, offset_t position, offset_t length); +void *client_mmap(file_t *file, offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + l4re_rm_flags_t region_flags); +l4re_rm_flags_t client_region_flags(prot_t prot, flags_t flags); /* Pipe region operations. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsclient/include/fsclient/file.h --- a/libfsclient/include/fsclient/file.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsclient/include/fsclient/file.h Wed Aug 10 00:25:44 2022 +0200 @@ -23,6 +23,7 @@ #include +#include #include #include @@ -74,6 +75,10 @@ notify_flags_t notifications; + /* Flags indicated when opening the file. */ + + flags_t flags; + } file_t; @@ -104,7 +109,13 @@ /* File and region operations. */ long file_flush(file_t *file); -long file_mmap(file_t *file, offset_t position, offset_t length); +long file_mmap(file_t *file, offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + l4re_rm_flags_t region_flags); +long file_mmap_only(file_t *file, offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible); +flags_t file_opening_flags(l4re_rm_flags_t rm_flags); +l4re_rm_flags_t file_region_flags(flags_t flags); long file_resize(file_t *file, offset_t size); /* File and region properties. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsclient/lib/src/client.cc --- a/libfsclient/lib/src/client.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsclient/lib/src/client.cc Wed Aug 10 00:25:44 2022 +0200 @@ -41,6 +41,32 @@ +/* Merging of region flags from protection and access flags. */ + +static l4re_rm_flags_t _combine_region_flags(l4re_rm_flags_t region_flags, + flags_t flags) +{ + return region_flags & (file_region_flags(flags) | L4RE_RM_F_X); +} + +/* Conversion of protection and access flags to region flags. */ + +l4re_rm_flags_t client_region_flags(prot_t prot, flags_t flags) +{ + l4re_rm_flags_t rm_flags = 0; + + if (prot & PROT_READ) + rm_flags |= L4RE_RM_F_R; + if (prot & PROT_WRITE) + rm_flags |= L4RE_RM_F_W; + if (prot & PROT_EXEC) + rm_flags |= L4RE_RM_F_X; + + return _combine_region_flags(rm_flags, flags); +} + + + /* Access the given position and synchronise state with the file object. Pipe objects may return busy conditions indicating that the desired access cannot yet be fulfilled. */ @@ -55,7 +81,8 @@ if ((position < file->start_pos) || (position >= file->end_pos)) { - if (file_mmap(file, position, file_span(file))) + if (file_mmap(file, position, file_span(file), 0, 0, + file_region_flags(file->flags))) return -L4_EIO; } @@ -164,7 +191,8 @@ if (file->memory == NULL) { if (file->object_flags & OBJECT_SUPPORTS_MMAP) - return client_mmap(file, client_tell(file), count); + return client_mmap(file, client_tell(file), count, 0, 0, + file_region_flags(file->flags)); else if (pipe_current(file)) return NULL; } @@ -459,9 +487,12 @@ /* Map a memory region to a file. */ -void *client_mmap(file_t *file, offset_t position, offset_t length) +void *client_mmap(file_t *file, offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + l4re_rm_flags_t region_flags) { - if ((file == NULL) || (file_mmap(file, position, length))) + if ((file == NULL) || file_mmap(file, position, length, start_visible, + end_visible, region_flags)) return NULL; return file->memory; diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsclient/lib/src/file.cc --- a/libfsclient/lib/src/file.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsclient/lib/src/file.cc Wed Aug 10 00:25:44 2022 +0200 @@ -21,8 +21,10 @@ #include #include +#include #include +#include #include #include "dataspace_client.h" @@ -88,6 +90,7 @@ file->object_flags = 0; file->can_block = 0; file->notifications = 0; + file->flags = 0; } @@ -255,6 +258,7 @@ long err; file_init(file); + file->flags = O_RDWR; err = opener.context(&file->ref); if (err) @@ -286,6 +290,7 @@ { client_OpenerContext openercontext(context->ref); file_init(file); + file->flags = flags; return openercontext.open(flags, &file->size, &file->ref, &file->object_flags); } @@ -346,19 +351,20 @@ /* Map a region of the given file to a memory region, obtaining an updated file size and populated data details. Unmap any previously mapped region. */ -long file_mmap(file_t *file, offset_t position, offset_t length) +long file_mmap(file_t *file, offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + l4re_rm_flags_t region_flags) { char *memory = file->memory; - client_MappedFile mapped_file(file->ref); - long err = mapped_file.mmap(position, length, &file->start_pos, - &file->end_pos, &file->size); + long err = file_mmap_only(file, position, length, start_visible, end_visible); if (err) return err; - _update_extent(file); - - err = ipc_attach_dataspace(file->ref, file_span(file), (void **) &file->memory); + err = ipc_attach_dataspace_align(file->ref, file_span(file), + L4RE_RM_F_SEARCH_ADDR | region_flags, + L4_PAGESHIFT, + (void **) &file->memory); if (err) return err; @@ -368,6 +374,60 @@ return L4_EOK; } +/* Request access to a region of the given file, obtaining an updated file size + and populated data details. The region is not mapped, however. */ + +long file_mmap_only(file_t *file, offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible) +{ + client_MappedFile mapped_file(file->ref); + long err = mapped_file.mmap(position, length, start_visible, end_visible, + &file->start_pos, &file->end_pos, &file->size); + + if (err) + return err; + + _update_extent(file); + + return L4_EOK; +} + +/* Return opening flags compatible with the given region flags. */ + +flags_t file_opening_flags(l4re_rm_flags_t rm_flags) +{ + if ((rm_flags & L4RE_RM_F_RW) == L4RE_RM_F_RW) + return O_RDWR; + else if (rm_flags & L4RE_RM_F_W) + return O_WRONLY; + else + return O_RDONLY; +} + +/* Return mmap flags corresponding to the file access flags. */ + +l4re_rm_flags_t file_region_flags(flags_t flags) +{ + l4re_rm_flags_t rm_flags; + + switch (flags & 3) + { + case O_WRONLY: + rm_flags = L4RE_RM_F_W; + break; + + case O_RDWR: + rm_flags = L4RE_RM_F_RW; + break; + + default: + rm_flags = L4RE_RM_F_R; + break; + } + + return rm_flags; +} + /* Resize a file, obtaining updated file size and populated data details. */ long file_resize(file_t *file, offset_t size) @@ -624,6 +684,8 @@ file_init(reader); file_init(writer); + reader->flags = O_RDONLY; + writer->flags = O_WRONLY; return opener.pipe(size, &reader->ref, &writer->ref); } @@ -697,6 +759,7 @@ { client_Directory directory(file->ref); file_init(reader); + reader->flags = O_RDONLY; return directory.opendir(&reader->size, &reader->ref, &reader->object_flags); } diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/copied_page_mapper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/include/fsserver/copied_page_mapper.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,81 @@ +/* + * A page mapper providing copied memory pages or deferring to another page + * mapper to satisfy file accesses. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include + +#include +#include +#include + + + +/* A special flexpage class for copied flexpages. */ + +class CopiedFlexpage : public Flexpage +{ +public: + explicit CopiedFlexpage(Region *region = NULL); +}; + + + +/* A file mapper, associating flexpages with file regions. */ + +class CopiedPageMapper : public GenericPageMapper +{ +protected: + GenericPageMapper *_mapper; + offset_t _size; + + /* Copied page support. */ + + AccessMap _map; + MemoryIncremental _memory; + std::list _queue; + + /* Internal flexpage retrieval methods. */ + + Flexpage *find(offset_t offset); + + Flexpage *replicate_flexpage(offset_t offset, Flexpage *flexpage); + +public: + explicit CopiedPageMapper(GenericPageMapper *mapper); + + virtual ~CopiedPageMapper(); + + /* Interface for the pager, implementing GenericPageMapper. */ + + Flexpage *get(offset_t offset, map_flags_t flags); + + void queue(Flexpage *flexpage); + + void flush_all(offset_t start, offset_t size); + + offset_t get_data_size(); + + void set_data_size(offset_t size); +}; + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/directory_provider.h --- a/libfsserver/include/fsserver/directory_provider.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/directory_provider.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * An object providing a directory abstraction with notification facilities. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -41,7 +41,8 @@ virtual DirectoryAccessor *accessor(); - virtual long make_resource(offset_t *size, object_flags_t *object_flags, + virtual long make_resource(flags_t flags, offset_t *size, + object_flags_t *object_flags, Resource **resource); }; diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/file_pager.h --- a/libfsserver/include/fsserver/file_pager.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/file_pager.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * File-specific pager functionality. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -33,6 +33,7 @@ { protected: FileProvider *_provider; + flags_t _flags; /* Notification endpoint for event subscription. */ @@ -42,10 +43,14 @@ bool _resized = false; + /* Helper methods. */ + + bool copy_on_write(); + public: fileid_t fileid; - explicit FilePager(fileid_t fileid, FileProvider *provider, map_flags_t flags); + explicit FilePager(fileid_t fileid, FileProvider *provider, flags_t flags); virtual void close(); @@ -69,8 +74,9 @@ virtual long map(offset_t offset, address_t hot_spot, map_flags_t flags, l4_snd_fpage_t *region); - virtual long mmap(offset_t position, offset_t length, offset_t *start_pos, - offset_t *end_pos, offset_t *size); + virtual long mmap(offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + offset_t *start_pos, offset_t *end_pos, offset_t *size); /* Notification methods. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/file_provider.h --- a/libfsserver/include/fsserver/file_provider.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/file_provider.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * An object encapsulating file resources. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -31,18 +31,18 @@ class FileProvider : public Provider { protected: - map_flags_t _flags; PageMapper *_mapper; public: - explicit FileProvider(fileid_t fileid, map_flags_t flags, - ProviderRegistry *registry, PageMapper *mapper); + explicit FileProvider(fileid_t fileid, ProviderRegistry *registry, + PageMapper *mapper); virtual ~FileProvider(); virtual PageMapper *mapper(); - virtual long make_resource(offset_t *size, object_flags_t *object_flags, + virtual long make_resource(flags_t flags, offset_t *size, + object_flags_t *object_flags, Resource **resource); virtual void remove(); diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/generic_page_mapper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/include/fsserver/generic_page_mapper.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,47 @@ +/* + * A generic page mapper providing memory pages to satisfy file accesses. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include + + + +/* A generic file mapper interface, accessed by the pager, associating flexpages + with file regions. */ + +class GenericPageMapper +{ +public: + virtual ~GenericPageMapper(); + + virtual Flexpage *get(offset_t offset, map_flags_t flags); + + virtual void queue(Flexpage *flexpage); + + virtual void flush_all(offset_t start, offset_t size); + + virtual offset_t get_data_size(); + + virtual void set_data_size(offset_t size); +}; + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/masked_page_mapper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/include/fsserver/masked_page_mapper.h Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,82 @@ +/* + * A page mapper providing memory pages to satisfy file accesses, masking the + * limits of a visible region of the file's contents. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + + + +/* A special flexpage class for masked flexpages. */ + +class MaskedFlexpage : public Flexpage +{ +public: + explicit MaskedFlexpage(Region *region = NULL); +}; + + + +/* A file mapper, associating flexpages with file regions. */ + +class MaskedPageMapper : public GenericPageMapper +{ +protected: + GenericPageMapper *_mapper; + offset_t _size; + + /* Masked region support. */ + + MemoryIncremental _memory; + offset_t _start_visible, _end_visible; + MaskedFlexpage _start_flexpage, _end_flexpage, _zero_flexpage; + + /* Internal flexpage retrieval methods. */ + + Flexpage *get_masked_flexpage(Flexpage *flexpage); + + void allocate_region(Flexpage *flexpage, Flexpage &masked); + + void populate_region(Flexpage *flexpage, Flexpage &masked, + bool has_start, bool has_end); + +public: + explicit MaskedPageMapper(GenericPageMapper *mapper, offset_t start_visible, + offset_t end_visible); + + virtual ~MaskedPageMapper(); + + /* Interface for the pager, implementing GenericPageMapper. */ + + Flexpage *get(offset_t offset, map_flags_t flags); + + void queue(Flexpage *flexpage); + + void flush_all(offset_t start, offset_t size); + + offset_t get_data_size(); + + void set_data_size(offset_t size); +}; + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/page_mapper.h --- a/libfsserver/include/fsserver/page_mapper.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/page_mapper.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A page mapper providing memory pages to satisfy file accesses. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -31,7 +32,7 @@ /* A file mapper, associating flexpages with file regions. */ -class PageMapper : public PageOwner +class PageMapper : public GenericPageMapper, public PageOwner { protected: AccessMap _map; @@ -56,7 +57,7 @@ Accessor *accessor() { return _accessor; } - /* Interface for the pager. */ + /* Interface for the pager, implementing GenericPageMapper. */ Flexpage *get(offset_t offset, map_flags_t flags); diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/pager.h --- a/libfsserver/include/fsserver/pager.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/pager.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Generic pager functionality. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,7 +23,7 @@ #include -#include +#include #include @@ -34,17 +34,17 @@ { protected: offset_t _start, _size; - PageMapper *_mapper; - map_flags_t _flags; + GenericPageMapper *_mapper, *_mapper_base, *_mapper_masked, *_mapper_copied; + map_flags_t _map_flags; public: - explicit Pager(PageMapper *mapper, map_flags_t flags); + explicit Pager(GenericPageMapper *mapper, map_flags_t map_flags); virtual void close(); /* Paging methods. */ - virtual long map(offset_t offset, address_t hot_spot, map_flags_t flags, + virtual long map(offset_t offset, address_t hot_spot, map_flags_t map_flags, l4_snd_fpage_t *region); /* Limit methods. */ @@ -59,8 +59,9 @@ /* Mapped file methods. */ - virtual long mmap(offset_t position, offset_t length, offset_t *start_pos, - offset_t *end_pos, offset_t *size); + virtual long mmap(offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + offset_t *start_pos, offset_t *end_pos, offset_t *size); }; // vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/pages.h --- a/libfsserver/include/fsserver/pages.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/pages.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A page collection abstraction providing pages from a queue to users. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -36,13 +36,13 @@ Memory *_memory; PageQueue *_queue; + virtual Flexpage *remove(); + public: explicit Pages(Memory *memory, PageQueue *queue); virtual ~Pages(); - virtual Flexpage *remove(); - virtual bool reserve(PageOwner *owner, Flexpage *flexpage); virtual Flexpage *flexpage(); diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/include/fsserver/provider.h --- a/libfsserver/include/fsserver/provider.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/include/fsserver/provider.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Filesystem object provider support. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -44,7 +44,8 @@ virtual ProviderRegistry *registry(); - virtual long make_resource(offset_t *size, object_flags_t *object_flags, + virtual long make_resource(flags_t flags, offset_t *size, + object_flags_t *object_flags, Resource **resource) = 0; /* Lifecycle methods. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/Makefile --- a/libfsserver/lib/Makefile Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -76,7 +76,10 @@ generic/resource_server.cc \ generic/simple_pager.cc \ mapping/access_map.cc \ + mapping/copied_page_mapper.cc \ + mapping/generic_page_mapper.cc \ mapping/ipc.cc \ + mapping/masked_page_mapper.cc \ mapping/page_mapper.cc \ pages/page_queue.cc \ pages/page_queue_partitioned.cc \ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/directories/directory_provider.cc --- a/libfsserver/lib/directories/directory_provider.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/directories/directory_provider.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * An object providing access to directory functionality. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -48,10 +48,12 @@ /* Return a directory resource initialised with this provider. */ -long DirectoryProvider::make_resource(offset_t *size, +long DirectoryProvider::make_resource(flags_t flags, offset_t *size, object_flags_t *object_flags, Resource **resource) { + (void) flags; + /* Provide non-file values for certain outputs. */ *size = 0; diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/files/file_pager.cc --- a/libfsserver/lib/files/file_pager.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/files/file_pager.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * File-specific pager functionality. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,18 +19,22 @@ * Boston, MA 02110-1301, USA */ +#include /* file_region_flags */ +#include + +#include "copied_page_mapper.h" #include "file_pager.h" #include "mapped_file_object_server.h" /* Initialise a pager for a file with a unique file identifier, file provider, - mapping flags and a file registry. The provider offers a shared page mapper + opening flags and a file registry. The provider offers a shared page mapper for moderating access to loaded pages. */ -FilePager::FilePager(fileid_t fileid, FileProvider *provider, map_flags_t flags) -: Pager(provider->mapper(), flags), - _provider(provider), fileid(fileid) +FilePager::FilePager(fileid_t fileid, FileProvider *provider, flags_t flags) +: Pager(provider->mapper(), file_region_flags(flags)), + _provider(provider), _flags(flags), fileid(fileid) { } @@ -50,9 +54,10 @@ void FilePager::close() { - /* Notify other users of the file. */ + /* Notify other users of the file and unsubscribe. */ _provider->notify_others(_endpoint, NOTIFY_PEER_CLOSED); + unsubscribe(); /* Detach the pager, potentially removing the file provider. */ @@ -90,21 +95,43 @@ return err; } -long FilePager::mmap(offset_t position, offset_t length, offset_t *start_pos, - offset_t *end_pos, offset_t *size) +long FilePager::mmap(offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + offset_t *start_pos, offset_t *end_pos, offset_t *size) { /* Set the limits of the paged region. */ - return Pager::mmap(position, length, start_pos, end_pos, size); + long err = Pager::mmap(position, length, start_visible, end_visible, start_pos, + end_pos, size); + + if (err) + return err; + + /* Impose copy-on-write semantics where appropriate. */ + + if ((_mapper != _mapper_copied) && copy_on_write()) + { + _mapper_copied = new CopiedPageMapper(_mapper); + _mapper = _mapper_copied; + } + + return L4_EOK; +} + +/* Return whether the pager should employ copy-on-write semantics. */ + +bool FilePager::copy_on_write() +{ + return (_flags == O_RDONLY) && (_map_flags & L4RE_DS_F_W); } /* Generic pager operations. */ -long FilePager::map(offset_t offset, address_t hot_spot, map_flags_t flags, l4_snd_fpage_t *region) +long FilePager::map(offset_t offset, address_t hot_spot, map_flags_t map_flags, l4_snd_fpage_t *region) { - return Pager::map(offset, hot_spot, flags, region); + return Pager::map(offset, hot_spot, map_flags, region); } diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/files/file_provider.cc --- a/libfsserver/lib/files/file_provider.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/files/file_provider.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * An object providing access to file functionality. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,9 +26,9 @@ /* Initialise the provider with a page 'mapper' for the file's contents. */ -FileProvider::FileProvider(fileid_t fileid, map_flags_t flags, - ProviderRegistry *registry, PageMapper *mapper) -: Provider(fileid, registry), _flags(flags), _mapper(mapper) +FileProvider::FileProvider(fileid_t fileid, ProviderRegistry *registry, + PageMapper *mapper) +: Provider(fileid, registry), _mapper(mapper) { } @@ -37,11 +37,12 @@ FileProvider::~FileProvider() { /* Accessors are allocated exclusively for page mappers for files, and - so can be deleted here. In general, the page mapper may not have - exclusive ownership of an accessor. */ + so can be deleted here. */ Accessor *accessor = _mapper->accessor(); delete _mapper; + + accessor->close(); delete accessor; } @@ -54,13 +55,14 @@ /* Return a file pager initialised with a provider, page mapper and accessor. */ -long FileProvider::make_resource(offset_t *size, object_flags_t *object_flags, +long FileProvider::make_resource(flags_t flags, offset_t *size, + object_flags_t *object_flags, Resource **resource) { /* Initialise the pager with the provider and a reference to this object for detaching from the provider. */ - FilePager *pager = new FilePager(_fileid, this, _flags); + FilePager *pager = new FilePager(_fileid, this, flags); /* Obtain the size details from the pager, also providing appropriate flags. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/generic/pager.cc --- a/libfsserver/lib/generic/pager.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/generic/pager.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Generic pager functionality. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,13 +23,19 @@ #include "ipc.h" #include "pager.h" +#include "copied_page_mapper.h" +#include "masked_page_mapper.h" + /* Initialise the pager with a page mapper and the given flags controlling access to a file. */ -Pager::Pager(PageMapper *mapper, map_flags_t flags) -: _start(0), _size(0), _mapper(mapper), _flags(flags) +Pager::Pager(GenericPageMapper *mapper, map_flags_t map_flags) +: _start(0), _size(0), + _mapper(mapper), _mapper_base(mapper), _mapper_masked(NULL), + _mapper_copied(NULL), + _map_flags(map_flags) { } @@ -37,6 +43,17 @@ void Pager::close() { + if (_mapper_masked != NULL) + { + delete _mapper_masked; + _mapper_masked = NULL; + } + + if (_mapper_copied != NULL) + { + delete _mapper_copied; + _mapper_copied = NULL; + } } /* Flush data to the file. */ @@ -61,8 +78,9 @@ /* Expose a region of the file. */ -long Pager::mmap(offset_t position, offset_t length, offset_t *start_pos, - offset_t *end_pos, offset_t *size) +long Pager::mmap(offset_t position, offset_t length, + offset_t start_visible, offset_t end_visible, + offset_t *start_pos, offset_t *end_pos, offset_t *size) { /* Define region characteristics. */ @@ -73,7 +91,21 @@ *start_pos = _start; *end_pos = _start + _size; - *size = _mapper->get_data_size(); + *size = _mapper_base->get_data_size(); + + /* Permit masking of mapped regions. */ + + if ((start_visible || end_visible) && + ((*start_pos != start_visible) || (*end_pos != end_visible))) + { + /* Introduce the masked page and copied page mappers. */ + + _mapper_masked = new MaskedPageMapper(_mapper_base, start_visible, end_visible); + _mapper_copied = new CopiedPageMapper(_mapper_masked); + _mapper = _mapper_copied; + } + else + _mapper = _mapper_base; return L4_EOK; } @@ -81,28 +113,33 @@ /* Map a flexpage corresponding to the dataspace 'offset' involving a 'hot_spot' (flexpage offset). */ -long Pager::map(offset_t offset, address_t hot_spot, map_flags_t flags, +long Pager::map(offset_t offset, address_t hot_spot, map_flags_t map_flags, l4_snd_fpage_t *region) { offset_t file_offset = _start + offset; offset_t max_offset = _start + _size; - /* Prevent access beyond that defined by the pager. */ + /* Prevent access beyond that defined by the pager. + NOTE: Permitting executable requests here. This needs to be configured + when opening the pager or by another means. */ - if (flags & ~_flags) + if (map_flags & (~(_map_flags | L4RE_DS_F_X))) return -L4_EACCESS; - Flexpage *flexpage = _mapper->get(file_offset, flags); + /* Obtain a flexpage from the page mapper. */ + + Flexpage *flexpage = _mapper->get(file_offset, map_flags); /* Issue the flexpage via the IPC system. */ - long err = ipc_prepare_flexpage(flexpage, file_offset, max_offset, hot_spot, - flags, region); + long err = ipc_prepare_flexpage(flexpage, file_offset, max_offset, + hot_spot, map_flags, region); if (!err) err = complete_Dataspace_map(*region); - /* After the flexpage is issued, it is queued for future reuse. */ + /* After the obtained flexpage is issued, it is queued for future reuse, if + appropriate for the mapper concerned. */ _mapper->queue(flexpage); diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/generic/resource_registry.cc --- a/libfsserver/lib/generic/resource_registry.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/generic/resource_registry.cc Wed Aug 10 00:25:44 2022 +0200 @@ -37,15 +37,6 @@ -/* Convert opening flags to map-compatible paging flags. */ - -map_flags_t ResourceRegistry::get_flags(flags_t flags) -{ - return flags & (O_WRONLY | O_RDWR) ? L4RE_DS_MAP_FLAG_RW : L4RE_DS_MAP_FLAG_RO; -} - - - /* Make a directory provider. */ long ResourceRegistry::make_directory_provider(FileOpening *opening, @@ -81,7 +72,7 @@ return err; PageMapper *mapper = new PageMapper(accessor, _pages); - *provider = new FileProvider(fileid, get_flags(flags), this, mapper); + *provider = new FileProvider(fileid, this, mapper); return L4_EOK; } @@ -167,7 +158,7 @@ /* Make a resource for the provider. */ - return provider->make_resource(size, object_flags, resource); + return provider->make_resource(flags, size, object_flags, resource); } /* Request the removal of a filesystem object through any active provider or diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/generic/resource_server.cc --- a/libfsserver/lib/generic/resource_server.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/generic/resource_server.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Resource server functionality. * - * Copyright (C) 2018, 2019, 2020, 2021 Paul Boddie + * Copyright (C) 2018, 2019, 2020, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -68,8 +68,8 @@ return resource_start_config(_config, _resource); } -/* A convenience method starting a thread and providing the server - capability. */ +/* A convenience method starting a thread and returning the server capability + employed via the given parameter. */ long ResourceServer::start_thread(l4_cap_idx_t *server) { diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/mapping/copied_page_mapper.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/lib/mapping/copied_page_mapper.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,163 @@ +/* + * A page mapper providing copied memory pages or deferring to another page + * mapper to satisfy file accesses. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include + +#include "copied_page_mapper.h" + + + +/* Provide mapped pages populated with the given 'mapper', with pages obtained + from the given 'pages' collection. */ + +CopiedPageMapper::CopiedPageMapper(GenericPageMapper *mapper) +: _mapper(mapper) +{ + _size = _mapper->get_data_size(); +} + +/* Upon deallocation, release allocated flexpages. */ + +CopiedPageMapper::~CopiedPageMapper() +{ + while (!_queue.empty()) + { + Flexpage *flexpage = _queue.front(); + + _queue.pop_front(); + _memory.release(flexpage->region); + delete flexpage; + } +} + +/* Interface for the pager. */ + +/* Return a flexpage providing access to the indicated file 'offset'. + + The returned flexpage will either provide direct access to the underlying + file content or be a completely new flexpage modified through writes to + memory. */ + +Flexpage *CopiedPageMapper::get(offset_t offset, map_flags_t flags) +{ + Flexpage *f = find(offset); + + /* Defer to the underlying page mapper if no flexpage was found. */ + + if (f == NULL) + { + f = _mapper->get(offset, flags); + + /* Replicate the underlying flexpage if writing. */ + + if (flags & L4RE_DS_F_W) + { + Flexpage *rf = replicate_flexpage(offset, f); + _mapper->queue(f); + f = rf; + } + } + + /* Upgrade the access flags. */ + + f->upgrade(flags); + return f; +} + +/* Queue the given 'flexpage'. */ + +void CopiedPageMapper::queue(Flexpage *flexpage) +{ + if (dynamic_cast(flexpage) == NULL) + _mapper->queue(flexpage); +} + +/* Flush pages in the given range from 'start' with 'size'. */ + +void CopiedPageMapper::flush_all(offset_t start, offset_t size) +{ + /* NOTE: This might be superfluous since copy-on-write regions should not + update content. */ + + _mapper->flush_all(start, size); +} + +/* Return the maximum extent of the mapped resource. */ + +offset_t CopiedPageMapper::get_data_size() +{ + return _size; +} + +/* Set the maximum extent of the mapped resource. */ + +void CopiedPageMapper::set_data_size(offset_t size) +{ + _size = size; +} + +/* Internal flexpage retrieval methods. */ + +/* Find an existing flexpage for 'offset'. */ + +Flexpage *CopiedPageMapper::find(offset_t offset) +{ + return _map.find(offset); +} + +/* Replicate an underlying flexpage for the file 'offset' using the given + 'flexpage'. */ + +Flexpage *CopiedPageMapper::replicate_flexpage(offset_t offset, Flexpage *flexpage) +{ + /* Obtain a new memory region and a new flexpage. */ + + Region *region = _memory.region(flexpage->size); + Flexpage *replicated = new CopiedFlexpage(region); + + /* Configure the flexpage for the file region. */ + + replicated->reset(offset); + + /* Copy the contents of the underlying flexpage. */ + + memcpy((void *) region->start, (const void *) flexpage->region->start, flexpage->size); + + /* Register the flexpage for future accesses and for deallocation. */ + + _map.insert(replicated); + _queue.push_back(replicated); + return replicated; +} + + + +/* Copied flexpage constructor. */ + +CopiedFlexpage::CopiedFlexpage(Region *region) +: Flexpage(region) +{ +} + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/mapping/generic_page_mapper.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/lib/mapping/generic_page_mapper.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,30 @@ +/* + * A generic page mapper providing memory pages to satisfy file accesses. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include "generic_page_mapper.h" + + + +GenericPageMapper::~GenericPageMapper() +{ +} + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/mapping/ipc.cc --- a/libfsserver/lib/mapping/ipc.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/mapping/ipc.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Interprocess communication utilities. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,12 +28,17 @@ #include "ipc.h" + /* Make an L4 representation of the given flexpage. */ static l4_fpage_t ipc_get_fpage(SendFlexpage *send_flexpage) { - return l4_fpage(send_flexpage->base_addr, send_flexpage->order, - (send_flexpage->flags & L4RE_DS_MAP_FLAG_RW) ? L4_FPAGE_RW : L4_FPAGE_RO); + unsigned char flags = send_flexpage->flags; + + if (!flags) + flags = L4RE_DS_MAP_FLAG_RO; + + return l4_fpage(send_flexpage->base_addr, send_flexpage->order, flags); } /* Make a representation of a flexpage for the IPC system. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/mapping/masked_page_mapper.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfsserver/lib/mapping/masked_page_mapper.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,195 @@ +/* + * A page mapper providing memory pages to satisfy file accesses, masking the + * limits of a visible region of the file's contents. + * + * Copyright (C) 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include "masked_page_mapper.h" + + + +/* Provide mapped pages populated using the given 'mapper', with a visible + region defined that causes the limits of this region to be masked. */ + +MaskedPageMapper::MaskedPageMapper(GenericPageMapper *mapper, + offset_t start_visible, offset_t end_visible) +: _mapper(mapper), _start_visible(start_visible), _end_visible(end_visible) +{ + _size = _mapper->get_data_size(); +} + +MaskedPageMapper::~MaskedPageMapper() +{ + if (_start_flexpage.region != NULL) + _memory.release(_start_flexpage.region); + + if (_end_flexpage.region != NULL) + _memory.release(_end_flexpage.region); + + if (_zero_flexpage.region != NULL) + _memory.release(_zero_flexpage.region); +} + +/* Interface for the pager. */ + +/* Return a flexpage providing access to the indicated file 'offset'. + + The returned flexpage will either provide direct access to the underlying + file content or be a completely new flexpage containing masked content. */ + +Flexpage *MaskedPageMapper::get(offset_t offset, map_flags_t flags) +{ + Flexpage *f = _mapper->get(offset, flags); + Flexpage *mf = get_masked_flexpage(f); + + if (f != mf) + _mapper->queue(f); + + return mf; +} + +/* Queue the given 'flexpage'. */ + +void MaskedPageMapper::queue(Flexpage *flexpage) +{ + if (dynamic_cast(flexpage) == NULL) + _mapper->queue(flexpage); +} + +/* Flush pages in the given range from 'start' with 'size'. */ + +void MaskedPageMapper::flush_all(offset_t start, offset_t size) +{ + /* NOTE: This might be superfluous since masked regions probably should be + read-only or copy-on-write. */ + + _mapper->flush_all(start, size); +} + +/* Return the maximum extent of the mapped resource. */ + +offset_t MaskedPageMapper::get_data_size() +{ + return _size; +} + +/* Set the maximum extent of the mapped resource. */ + +void MaskedPageMapper::set_data_size(offset_t size) +{ + _size = size; +} + +/* Internal flexpage retrieval methods. */ + +/* Detect flexpages with masked content, introducing separate flexpages + offering masked content from the same region. */ + +Flexpage *MaskedPageMapper::get_masked_flexpage(Flexpage *flexpage) +{ + /* Determine whether the flexpage involves the limits of the visible + region or is beyond such limits. */ + + bool has_start = flexpage->supports_position(_start_visible) && + flexpage->base_offset != _start_visible; + bool has_end = flexpage->supports_position(_end_visible); + bool has_zero = (flexpage->base_offset >= _end_visible) || + (flexpage->base_offset + flexpage->size < _start_visible); + + /* Return the original flexpage within the visible limits. */ + + if (!has_start && !has_end && !has_zero) + return flexpage; + + /* Allocate and populate a region in one of the preallocated flexpages for + masked content. */ + + Flexpage &masked = has_start ? _start_flexpage : + has_end ? _end_flexpage : + _zero_flexpage; + + allocate_region(flexpage, masked); + populate_region(flexpage, masked, has_start, has_end); + + /* Associate the preallocated flexpage with the original flexpage. */ + + flexpage->associate(&masked); + + return &masked; +} + +void MaskedPageMapper::allocate_region(Flexpage *flexpage, Flexpage &masked) +{ + offset_t needed = flexpage->region->size(); + + /* Attempt to re-use an existing region. */ + + if (masked.region != NULL) + { + if (masked.region->size() == needed) + return; + else + _memory.release(masked.region); + } + + /* Set the region in the flexpage. */ + + masked.set_region(_memory.region(needed)); + masked.reset(flexpage->base_offset); +} + +void MaskedPageMapper::populate_region(Flexpage *flexpage, Flexpage &masked, + bool has_start, bool has_end) +{ + /* Without any limits involved, provide a zero flexpage. */ + + if (!has_start && !has_end) + { + memset((void *) masked.region->start, 0, masked.size); + return; + } + + /* Determine the content limits given start and/or end offsets. */ + + offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0; + offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size; + + if (has_start) + memset((void *) masked.region->start, 0, start_offset); + + memcpy((void *) (masked.region->start + start_offset), + (const void *) (flexpage->region->start + start_offset), + end_offset - start_offset); + + if (has_end) + memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset); +} + + + +/* Masked flexpage constructor. */ + +MaskedFlexpage::MaskedFlexpage(Region *region) +: Flexpage(region) +{ +} + +// vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/mapping/page_mapper.cc --- a/libfsserver/lib/mapping/page_mapper.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/mapping/page_mapper.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A page mapper providing memory pages to satisfy file accesses. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -130,11 +130,6 @@ { Flexpage *flexpage = _pages->flexpage(); - /* Obtain an existing flexpage and reuse it. */ - - if (flexpage == NULL) - flexpage = _pages->remove(); - flexpage->reset(offset); fill(flexpage); @@ -162,11 +157,31 @@ { if (flexpage->decrement() || purge) { + /* NOTE: Derived flexpages might potentially support their contents + being merged into the flushed data, although this is a + non-trivial problem. */ + if (flexpage->modified()) _accessor->flush(flexpage); + /* Unmap the flexpage, requiring users of its memory to request another + flexpage in future. */ + ipc_unmap_flexpage(flexpage); flexpage->invalidate(); + + /* Unmap all derived flexpages, since these rely on the underlying file + contents. */ + + DerivedFlexpages::iterator it; + + for (it = flexpage->derived.begin(); it != flexpage->derived.end(); it++) + { + ipc_unmap_flexpage(*it); + (*it)->invalidate(); + } + + flexpage->disassociate(); } } diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/pages/pages.cc --- a/libfsserver/lib/pages/pages.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/pages/pages.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A page collection abstraction providing pages from a queue to users. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,8 +19,6 @@ * Boston, MA 02110-1301, USA */ -#include - #include "pages.h" @@ -68,7 +66,7 @@ if (region != NULL) return new Flexpage(region); else - return NULL; + return remove(); } /* Queue an entry associating the given 'owner' and 'flexpage'. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libfsserver/lib/pipes/pipe_pager.cc --- a/libfsserver/lib/pipes/pipe_pager.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libfsserver/lib/pipes/pipe_pager.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A pipe pager providing access to pipe content and navigation support. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -65,9 +65,10 @@ unsigned int attached = _paging->detach(); - /* Notify the other endpoint. */ + /* Notify the other endpoint and unsubscribe. */ _paging->notify_others(_writing ? PipePaging::WRITER : PipePaging::READER, NOTIFY_PEER_CLOSED); + unsubscribe(); /* Deallocate the paging coordinator if no other endpoints are active. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/include/ipc/map.h --- a/libipc/include/ipc/map.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/include/ipc/map.h Wed Aug 10 00:25:44 2022 +0200 @@ -27,13 +27,18 @@ struct ipc_mapped_cap { - l4_umword_t index; + l4_cap_idx_t mapped_cap; l4_cap_idx_t cap; unsigned char rights; + l4_umword_t obj_rights; }; long ipc_map_capability(l4_cap_idx_t task, struct ipc_mapped_cap mapped_cap); -long ipc_map_capabilities(l4_cap_idx_t task, struct ipc_mapped_cap mapped_caps[]); +long ipc_map_capabilities(l4_cap_idx_t task, struct ipc_mapped_cap mapped_caps[], + unsigned int *count); EXTERN_C_END + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/include/ipc/mem_ipc.h --- a/libipc/include/ipc/mem_ipc.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/include/ipc/mem_ipc.h Wed Aug 10 00:25:44 2022 +0200 @@ -32,7 +32,8 @@ long ipc_allocate_align(unsigned long size, l4re_rm_flags_t flags, unsigned char align, void **addr, l4re_ds_t *ds); -long ipc_new_dataspace(l4_cap_idx_t cap, l4_mword_t size, l4_umword_t flags, l4_umword_t align); +long ipc_new_dataspace(l4_mword_t size, l4_umword_t flags, l4_umword_t align, + l4re_ds_t *ds); long ipc_attach_dataspace(l4re_ds_t ds, unsigned long size, void **addr); @@ -45,3 +46,6 @@ long ipc_dataspace_size(l4_cap_idx_t cap, unsigned long *size); EXTERN_C_END + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/include/ipc/message.h --- a/libipc/include/ipc/message.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/include/ipc/message.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Interprocess communication message abstraction. * - * Copyright (C) 2018, 2019, 2021 Paul Boddie + * Copyright (C) 2018, 2019, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,6 +46,10 @@ l4_msgtag_t tag; unsigned int expected_items; + /* Receive item counter. */ + + unsigned int receive_items; + /* Output details. */ unsigned int words; @@ -109,6 +113,7 @@ void ipc_message_add_item(ipc_message_t *msg, l4_cap_idx_t cap); void ipc_message_add_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage); void ipc_message_add_page(ipc_message_t *msg, l4_umword_t hot_spot, l4_fpage_t fpage); +void ipc_message_add_receive_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage); void ipc_message_add_string(ipc_message_t *msg, const char *value); void ipc_message_add_word(ipc_message_t *msg, l4_umword_t value); void ipc_message_propagate_item(ipc_message_t *msg, l4_cap_idx_t cap); @@ -136,6 +141,7 @@ long ipc_message_import_dataspace(ipc_message_t *msg, int item, l4re_ds_t *mem, l4_addr_t *addr); long ipc_message_import_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t *fpage); void ipc_message_propagate_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref); +void ipc_message_receive_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage); l4_msgtag_t ipc_message_reply_tag(ipc_message_t *msg); l4_msgtag_t ipc_message_request_tag(ipc_message_t *msg, int op); diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/include/ipc/server.h --- a/libipc/include/ipc/server.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/include/ipc/server.h Wed Aug 10 00:25:44 2022 +0200 @@ -34,6 +34,12 @@ (ipc_server_handler_type) handle_##TYPE, \ NAME) +/* A convenience macro for initialising a server. */ + +#define ipc_server_init_for(CONFIG, TYPE, POINTER) \ + _ipc_server_init_for(CONFIG, TYPE##_expected_items, (TYPE *) POINTER, \ + (ipc_server_handler_type) handle_##TYPE) + /* A convenience macro for adding a configuration to an existing server. */ #define ipc_server_add_config(CONFIG, TYPE, POINTER, THREAD) \ @@ -124,6 +130,11 @@ long _ipc_server_loop_for(int expected_items, void *handler_obj, ipc_server_handler_type handler, const char *name); +/* Initialise a server. */ + +long _ipc_server_init_for(ipc_server_config_type *config, int expected_items, + void *handler_obj, ipc_server_handler_type handler); + /* Add handling of incoming messages to an existing server. */ long _ipc_server_add_config(ipc_server_config_type *config, int expected_items, @@ -160,6 +171,11 @@ long ipc_server_start_config(ipc_server_config_type *config); +/* Initialise and start a server using the given configuration and thread. */ + +long ipc_server_start_config_thread(ipc_server_config_type *config, + l4_cap_idx_t thread); + EXTERN_C_END /* vim: tabstop=2 expandtab shiftwidth=2 diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/lib/src/map.c --- a/libipc/lib/src/map.c Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/lib/src/map.c Wed Aug 10 00:25:44 2022 +0200 @@ -34,18 +34,24 @@ { return l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP, l4_obj_fpage(mapped_cap.cap, 0, mapped_cap.rights), - l4_map_obj_control(mapped_cap.index, L4_MAP_ITEM_MAP))); + l4_map_obj_control(mapped_cap.mapped_cap, L4_MAP_ITEM_MAP) | + mapped_cap.obj_rights)); } /* Map several capabilities to another task. */ -long ipc_map_capabilities(l4_cap_idx_t task, struct ipc_mapped_cap mapped_caps[]) +long ipc_map_capabilities(l4_cap_idx_t task, struct ipc_mapped_cap mapped_caps[], + unsigned int *count) { long err = L4_EOK; + unsigned int i; - for (int i = 0; l4_is_valid_cap(mapped_caps[i].cap) && !err; i++) + for (i = 0; l4_is_valid_cap(mapped_caps[i].cap) && !err; i++) err = ipc_map_capability(task, mapped_caps[i]); + if (count != NULL) + *count = i; + return err; } diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/lib/src/mem_ipc.c --- a/libipc/lib/src/mem_ipc.c Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/lib/src/mem_ipc.c Wed Aug 10 00:25:44 2022 +0200 @@ -46,16 +46,9 @@ long ipc_allocate_align(unsigned long size, l4re_rm_flags_t flags, unsigned char align, void **addr, l4re_ds_t *ds) { - /* Allocate a capability for the dataspace. */ - - *ds = ipc_cap_alloc(); - - if (l4_is_invalid_cap(*ds)) - return -L4_ENOENT; - /* Allocate and attach the memory for the dataspace. */ - if (ipc_new_dataspace(*ds, size, 0, align)) + if (ipc_new_dataspace(size, 0, align, ds)) { ipc_cap_free_um(*ds); return -L4_ENOMEM; @@ -72,9 +65,17 @@ /* Create a dataspace. Equivalent to l4re_ma_alloc_align. */ -long ipc_new_dataspace(l4_cap_idx_t cap, l4_mword_t size, l4_umword_t flags, l4_umword_t align) +long ipc_new_dataspace(l4_mword_t size, l4_umword_t flags, l4_umword_t align, + l4re_ds_t *ds) { - return l4re_ma_alloc_align(size, cap, flags, align); + /* Allocate a capability for the dataspace. */ + + *ds = ipc_cap_alloc(); + + if (l4_is_invalid_cap(*ds)) + return -L4_ENOENT; + + return l4re_ma_alloc_align(size, *ds, flags, align); } /* Attach a dataspace region. Similar to l4re_rm_attach. */ diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/lib/src/message.c --- a/libipc/lib/src/message.c Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/lib/src/message.c Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Interprocess communication message abstraction. * - * Copyright (C) 2018, 2019, 2021 Paul Boddie + * Copyright (C) 2018, 2019, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -54,16 +54,7 @@ long ipc_message_expect(ipc_message_t *msg, unsigned int expected_items) { - long err = ipc_message_expect_capabilities(msg, expected_items); - - if (err) - return err; - - /* Restore the buffer registers immediately. */ - - ipc_message_restore_buffer_registers(msg); - - return L4_EOK; + return ipc_message_expect_capabilities(msg, expected_items); } /* Free capabilities expected in messages. */ @@ -80,6 +71,7 @@ /* Set a default for expected items. */ msg->expected_items = 0; + msg->receive_items = 0; ipc_message_reset(msg); } @@ -106,8 +98,13 @@ msg->bregs.bdr = bregs->bdr; - for (i = 0; i < msg->expected_items; i++) - msg->bregs.br[i] = bregs->br[i]; + /* NOTE: Support a mixture of expected items and received flexpages. */ + + if ((msg->expected_items) || (msg->receive_items)) + { + for (i = 0; (i < msg->expected_items) || (i < msg->receive_items * 2); i++) + msg->bregs.br[i] = bregs->br[i]; + } } /* Preserve the message registers. */ @@ -151,8 +148,13 @@ bregs->bdr = msg->bregs.bdr; - for (i = 0; i < msg->expected_items; i++) - bregs->br[i] = msg->bregs.br[i]; + /* NOTE: Support a mixture of expected items and received flexpages. */ + + if ((msg->expected_items) || (msg->receive_items)) + { + for (i = 0; (i < msg->expected_items) || (i < msg->receive_items * 2); i++) + bregs->br[i] = msg->bregs.br[i]; + } } /* Restore message registers to communicate recorded data and items. */ @@ -223,6 +225,7 @@ void ipc_message_wait(ipc_message_t *msg, l4_umword_t *label) { + ipc_message_restore_buffer_registers(msg); msg->tag = l4_ipc_wait(l4_utcb(), label, L4_IPC_NEVER); } @@ -233,6 +236,9 @@ void ipc_message_add_capability(ipc_message_t *msg, l4_cap_idx_t cap) { ipc_message_export_capability(msg, msg->items++, cap); + + /* NOTE: Might use the "grant" operation instead of explicitly discarding. */ + if (cap & IPC_DISCARD_CAP_FLAG) ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); } @@ -242,6 +248,9 @@ void ipc_message_add_item(ipc_message_t *msg, l4_cap_idx_t cap) { ipc_message_export_capability(msg, msg->items++, cap); + + /* NOTE: Might use the "grant" operation instead of explicitly discarding. */ + if (cap & IPC_DISCARD_CAP_FLAG) ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); } @@ -251,7 +260,18 @@ void ipc_message_add_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) { - ipc_message_export_fpage(msg, msg->items++, fpage); + /* Test for {0, l4_fpage_invalid()} as the flexpage to avoid setting an + optional flexpage result, useful for the page fault handler. */ + + if (fpage.snd_base || fpage.fpage.raw) + ipc_message_export_fpage(msg, msg->items++, fpage); +} + +/* Add a receive window flexpage item to the message. */ + +void ipc_message_add_receive_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) +{ + ipc_message_receive_fpage(msg, msg->receive_items++, fpage); } /* Add a flexpage to the message. */ @@ -441,6 +461,16 @@ msg->mregs.mr[msg->words + item * 2 + 1] = fpage.raw; } +/* Specify a receive window flexpage item in the message. */ + +void ipc_message_receive_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) +{ + msg->bregs.br[item * 2] = l4_map_control(fpage.snd_base, 0, 0); + msg->bregs.br[item * 2 + 1] = fpage.fpage.raw; +} + + + /* Import from the message the capability at the given item position, updating the buffer registers for future capabilities. */ @@ -483,3 +513,6 @@ ipc_message_export_capability(msg, item, ref); ipc_message_discard_capability(msg, ref); } + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/lib/src/server.c --- a/libipc/lib/src/server.c Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/lib/src/server.c Wed Aug 10 00:25:44 2022 +0200 @@ -166,24 +166,38 @@ return err; } - ipc_server_init_config(&config); - config.expected_items = expected_items; - config.handler = handler; - config.handler_obj = handler_obj; + _ipc_server_init_for(&config, expected_items, handler_obj, handler); return ipc_server_start_config(&config); } +/* Initialise a server for a given object. */ + +long _ipc_server_init_for(ipc_server_config_type *config, int expected_items, + void *handler_obj, ipc_server_handler_type handler) +{ + ipc_server_init_config(config); + + config->expected_items = expected_items; + config->handler = handler; + config->handler_obj = handler_obj; +} + /* Associate a new configuration with an existing server endpoint. */ long _ipc_server_add_config(ipc_server_config_type *config, int expected_items, void *handler_obj, ipc_server_handler_type handler, l4_cap_idx_t thread) { - ipc_server_init_config(config); - config->expected_items = expected_items; - config->handler = handler; - config->handler_obj = handler_obj; + _ipc_server_init_for(config, expected_items, handler_obj, handler); + return ipc_server_start_config_thread(config, thread); +} + +/* Complete initialisation of a server in the given thread. */ + +long ipc_server_start_config_thread(ipc_server_config_type *config, + l4_cap_idx_t thread) +{ config->thread = thread; config->config_thread = 1; @@ -198,7 +212,7 @@ long ipc_server_managed_loop(int expected_items, ipc_server_config_type *config) { ipc_message_t msg; - l4_umword_t label, irq_label = (l4_umword_t) config->irq; + l4_umword_t label, irq_label; /* Permit other endpoints by dynamically interpreting the label. */ @@ -211,11 +225,6 @@ if (err) return err; - /* Unmask the interrupt. */ - - if (l4_is_valid_cap(config->irq)) - ipc_init_irq(config->irq); - /* Wait for an incoming message. */ while (1) @@ -233,7 +242,9 @@ /* Message involves the IPC gate itself. */ - if (label != irq_label) + irq_label = (l4_umword_t) config->irq; + + if (!config->notifications || (config->notifications && (label != irq_label))) { config_from_label = (ipc_server_config_type *) label; config_from_label->handler(&msg, config_from_label->handler_obj); @@ -241,7 +252,7 @@ /* Message involves the IRQ or a termination condition occurred. */ - if ((label == irq_label) || msg.terminating) + else if ((config->notifications && (label == irq_label)) || msg.terminating) break; } @@ -369,6 +380,10 @@ if (err) return err; + + /* Unmask the interrupt. */ + + ipc_init_irq(config->irq); } /* With a separate thread, return the last status value. Otherwise, invoke the diff -r ba6c9728a9ae -r 77fbb52e0a14 libipc/lib/src/util_ipc.c --- a/libipc/lib/src/util_ipc.c Sun Jun 12 17:10:30 2022 +0200 +++ b/libipc/lib/src/util_ipc.c Wed Aug 10 00:25:44 2022 +0200 @@ -30,7 +30,9 @@ -/* Declare expected capabilities. */ +/* Declare expected capabilities. + NOTE: This does not support the mixing of expected capabilities and other + items. */ long _expect_capabilities(l4_buf_regs_t *bregs, int number) { @@ -233,3 +235,6 @@ { return l4_ipc_send(endpoint, l4_utcb(), tag, L4_IPC_NEVER); } + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/Control --- a/libmem/Control Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/Control Wed Aug 10 00:25:44 2022 +0200 @@ -1,3 +1,3 @@ -requires: libstdc++ libc libsystypes +requires: libstdc++ libc libsystypes libipc provides: libmem maintainer: paul@boddie.org.uk diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/include/mem/flexpage.h --- a/libmem/include/mem/flexpage.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/include/mem/flexpage.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A flexpage abstraction. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -21,6 +21,10 @@ #pragma once +#include + +#include + #include #include #include @@ -34,6 +38,16 @@ +/* Forward declaration. */ + +class Flexpage; + +/* Employed data types. */ + +typedef std::list DerivedFlexpages; + + + /* A flexpage abstraction. */ class Flexpage @@ -45,6 +59,10 @@ public: Region *region; + /* Flexpage relationships to support masked regions. */ + + DerivedFlexpages derived; + /* General flexpage characteristics. */ offset_t base_addr, size; @@ -56,10 +74,15 @@ /* Associate a flexpage with a memory 'region'. */ - explicit Flexpage(Region *region) : region(region) + explicit Flexpage(Region *region = NULL) + : region(region) { } + virtual ~Flexpage(); + + void set_region(Region *region); + void reset(offset_t offset); bool decrement(); @@ -80,6 +103,12 @@ offset_t max_offset=0); SendFlexpage to_unmap(); + + /* Associate other flexpages with this flexpage. */ + + void associate(Flexpage *flexpage); + + void disassociate(); }; // vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/include/mem/memory_incremental.h --- a/libmem/include/mem/memory_incremental.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/include/mem/memory_incremental.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A memory pool allocating a region at a time from the system. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -43,6 +43,8 @@ Region *allocate(offset_t size); + void deallocate(Region *region); + public: explicit MemoryIncremental(unsigned int limit, offset_t region_size=PAGE_SIZE); diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/include/mem/region.h --- a/libmem/include/mem/region.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/include/mem/region.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Memory region abstractions. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -59,8 +59,6 @@ explicit Region(offset_t start, offset_t end); - virtual ~Region(); - offset_t size(); int compare(Region *other); diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/lib/src/Makefile --- a/libmem/lib/src/Makefile Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/lib/src/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -8,7 +8,7 @@ flexpage.cc memory_incremental.cc memory_preallocated.cc \ memory_utils.cc region.cc -REQUIRES_LIBS = l4re_c-util libstdc++ libsystypes +REQUIRES_LIBS = l4re_c-util libstdc++ libsystypes libipc PRIVATE_INCDIR = $(PKGDIR)/include $(PKGDIR)/include/mem CONTRIB_INCDIR = libmem diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/lib/src/flexpage.cc --- a/libmem/lib/src/flexpage.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/lib/src/flexpage.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A flexpage abstraction. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,18 +19,25 @@ * Boston, MA 02110-1301, USA */ -#include - #include #include "flexpage.h" +/* Virtual destructor required for introspection. */ + +Flexpage::~Flexpage() +{ +} + /* Reset the flexpage using 'offset', being the file offset. */ void Flexpage::reset(offset_t offset) { + if (region == NULL) + return; + _counter = 0; _flags = 0; @@ -64,6 +71,13 @@ page_addr = base_addr + page_offset; } +/* Set a region. */ + +void Flexpage::set_region(Region *region) +{ + this->region = region; +} + /* Decrement the usage counter, returning whether the flexpage is now no longer used. */ @@ -156,12 +170,16 @@ if (!receive_size) return SendFlexpage(base_addr, page_order(0), flags); - offset_t receive_page_offset = hot_spot % receive_size; + /* Employ the page-aligned hot spot for compatibility with the page + offset, thus handling any non-aligned values sent in map requests. */ + + offset_t hot_spot_page = trunc(hot_spot, PAGE_SIZE); + offset_t receive_page_offset = hot_spot_page % receive_size; while ((receive_size > PAGE_SIZE) && (receive_page_offset != page_offset)) { receive_size /= 2; - receive_page_offset = hot_spot % receive_size; + receive_page_offset = hot_spot_page % receive_size; } /* The flexpage base address is adjusted using the difference in page @@ -180,4 +198,16 @@ return SendFlexpage(base_addr, page_order(size), _flags); } +/* Associate another flexpage with this flexpage. */ + +void Flexpage::associate(Flexpage *flexpage) +{ + derived.push_back(flexpage); +} + +void Flexpage::disassociate() +{ + derived.clear(); +} + // vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/lib/src/memory_incremental.cc --- a/libmem/lib/src/memory_incremental.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/lib/src/memory_incremental.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * A memory pool allocating a region at a time from the system. * - * Copyright (C) 2021 Paul Boddie + * Copyright (C) 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -21,6 +21,8 @@ #include "memory_incremental.h" +#include + #include @@ -45,18 +47,31 @@ { /* Attempt to allocate aligned memory. */ - void *current; + void *current = NULL; + l4re_ds_t ds; /* Make the size appropriate for the invocation. */ size = round_multiple(size, PAGE_SIZE); - if (posix_memalign(¤t, size, size)) + /* Use allocation permitting executable mapping of the memory. + NOTE: Here, it might be beneficial to employ an allocator that obtains + dataspaces and provides multiple blocks from each dataspace. */ + + if (ipc_allocate_align(size, L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RWX, page_order(size), ¤t, &ds)) return NULL; return new Region((offset_t) current, (offset_t) current + size); } +/* Deallocate the given region. */ + +void MemoryIncremental::deallocate(Region *region) +{ + ipc_detach_dataspace((void *) region->start); + delete region; +} + /* Allocate a new region of the given 'size' rounded to the nearest page. */ Region *MemoryIncremental::region(offset_t size) @@ -109,7 +124,7 @@ if (_limited) _limit += pages; - delete region; + deallocate(region); } // vim: tabstop=4 expandtab shiftwidth=4 diff -r ba6c9728a9ae -r 77fbb52e0a14 libmem/lib/src/region.cc --- a/libmem/lib/src/region.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/libmem/lib/src/region.cc Wed Aug 10 00:25:44 2022 +0200 @@ -58,13 +58,6 @@ memset((void *) start, 0, end - start); } -/* Deallocate the region, freeing its memory. */ - -Region::~Region() -{ - free((void *) start); -} - /* Return the size of the region. */ offset_t Region::size() diff -r ba6c9728a9ae -r 77fbb52e0a14 libsystypes/Control --- a/libsystypes/Control Sun Jun 12 17:10:30 2022 +0200 +++ b/libsystypes/Control Wed Aug 10 00:25:44 2022 +0200 @@ -1,2 +1,3 @@ provides: libsystypes +requires: l4re_c-util maintainer: paul@boddie.org.uk diff -r ba6c9728a9ae -r 77fbb52e0a14 libsystypes/idl/dataspace.idl diff -r ba6c9728a9ae -r 77fbb52e0a14 libsystypes/idl/mapped_file.idl --- a/libsystypes/idl/mapped_file.idl Sun Jun 12 17:10:30 2022 +0200 +++ b/libsystypes/idl/mapped_file.idl Wed Aug 10 00:25:44 2022 +0200 @@ -4,10 +4,12 @@ interface MappedFile { - /* Memory-map a file for the given file position and length, obtaining the - limits of the mapped region and the size of the file. */ + /* Memory-map a file for the given file position and length, masking regions + beyond any visible range if indicated, obtaining the limits of the mapped + region and the size of the file. */ [opcode(7)] void mmap(in offset_t position, in offset_t length, + in offset_t start_visible, in offset_t end_visible, out offset_t start_pos, out offset_t end_pos, out offset_t size); }; diff -r ba6c9728a9ae -r 77fbb52e0a14 libsystypes/include/systypes/base.h --- a/libsystypes/include/systypes/base.h Sun Jun 12 17:10:30 2022 +0200 +++ b/libsystypes/include/systypes/base.h Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Base types used by various other types. * - * Copyright (C) 2019, 2021 Paul Boddie + * Copyright (C) 2019, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -63,6 +63,19 @@ OBJECT_HAS_SIZE = 2 }; +/* Memory mapping protection flags compatible with sys/mman.h (and incompatible + with comparable L4Re flags). */ + +typedef unsigned long prot_t; + +enum prot_flags +{ + PROT_NONE = 0, + PROT_READ = 1, + PROT_WRITE = 2, + PROT_EXEC = 4 +}; + /* Equivalent types are defined in sys/types.h typically. In newlib, they are defined in sys/_types.h if not defined elsewhere (such as in machine/_types.h). diff -r ba6c9728a9ae -r 77fbb52e0a14 test_files/Control --- a/test_files/Control Sun Jun 12 17:10:30 2022 +0200 +++ b/test_files/Control Wed Aug 10 00:25:44 2022 +0200 @@ -1,3 +1,3 @@ provides: fstest_files -requires: libc +requires: libc libstdc++ libexec libipc maintainer: paul@boddie.org.uk diff -r ba6c9728a9ae -r 77fbb52e0a14 test_files/Makefile --- a/test_files/Makefile Sun Jun 12 17:10:30 2022 +0200 +++ b/test_files/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -4,18 +4,32 @@ E2ACCESS_DIR = $(PKGDIR)/../libe2access/host TARGET = $(PKGDIR)/../conf/e2test.fs +REQUIRES_LIBS = libc libstdc++ libexec libipc + include $(L4DIR)/mk/Makeconf -include $(OBJ_BASE)/l4defs.mk.inc + +# Attempt to define system details manually, since the following will not work +# until a complete build has been performed. +# +#include $(OBJ_BASE)/l4defs.mk.inc + +L4_SYSTEM = $(BUILD_ARCH)_$(CPU) PROGRAMS_DIR = $(PKGDIR_OBJ)/programs/OBJ-$(L4_SYSTEM)-l4f +LIBEXEC_DIR = $(PKGDIR)/../libexec +RM_PROGRAM_DIR = $(call absfilename,$(OBJ_DIR)/$(LIBEXEC_DIR))/rm/OBJ-$(L4_SYSTEM)-l4f +RM_PROGRAM = $(RM_PROGRAM_DIR)/exec_region_mapper + # Special rules to build the test filesystem. all:: $(TARGET) $(PROGRAMS_DIR) -$(TARGET): $(PROGRAMS_DIR) +$(TARGET): $(PROGRAMS_DIR) $(RM_PROGRAM) $(MAKE) -C $(E2ACCESS_DIR) && \ - $(PKGDIR)/mk_e2test.sh -q $(PKGDIR) $(PROGRAMS_DIR) $(E2ACCESS_DIR) $@ + $(PKGDIR)/mk_e2test.sh -q $(PKGDIR) $(E2ACCESS_DIR) $@ \ + $(PROGRAMS_DIR)/dstest_* \ + $(RM_PROGRAM) $(PROGRAMS_DIR): $(PKGDIR)/programs/*.c* $(MAKE) -C $(PKGDIR)/programs $(MKFLAGS) && \ diff -r ba6c9728a9ae -r 77fbb52e0a14 test_files/mk_e2test.sh --- a/test_files/mk_e2test.sh Sun Jun 12 17:10:30 2022 +0200 +++ b/test_files/mk_e2test.sh Wed Aug 10 00:25:44 2022 +0200 @@ -31,18 +31,19 @@ fi PKGDIR=$(realpath "$1") -PROGRAMS_DIR=$(realpath "$2") -E2ACCESS_DIR=$(realpath "$3") -TARGET=$(realpath "$4") +E2ACCESS_DIR=$(realpath "$2") +TARGET=$(realpath "$3") -if [ ! -e "$PKGDIR" ] || [ ! -e "$PROGRAMS_DIR" ] || [ ! -e "$E2ACCESS_DIR" ] || [ ! "$TARGET" ] ; then +shift 3 + +if [ ! -e "$PKGDIR" ] || [ ! -e "$E2ACCESS_DIR" ] || [ ! "$TARGET" ] ; then cat 1>&2 < +Usage: $PROGNAME [ -q ] [ ... ] Package directory: $PKGDIR -Programs directory: $PROGRAMS_DIR e2access directory: $E2ACCESS_DIR Target filesystem: $TARGET +Programs: $* EOF exit 1 fi @@ -110,7 +111,9 @@ # Put some programs in the same place. -cp "$PROGRAMS_DIR/dstest_"* . +for PROGRAM in $* ; do + cp $(realpath "$PROGRAM") . +done # Leave the filesystem root. diff -r ba6c9728a9ae -r 77fbb52e0a14 test_files/programs/Makefile diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/Makefile --- a/tests/Makefile Sun Jun 12 17:10:30 2022 +0200 +++ b/tests/Makefile Wed Aug 10 00:25:44 2022 +0200 @@ -9,12 +9,14 @@ dstest_file_monitor \ dstest_file_readdir \ dstest_file_readdir_concurrent \ + dstest_file_remove \ dstest_file_rename \ dstest_host_client \ dstest_pipe_client \ dstest_test_client \ dstest_map_test \ - dstest_exec + dstest_exec \ + dstest_file_mapping MODE = static @@ -29,18 +31,22 @@ # Compound interfaces. -pager_object_NAME = PagerObject -pager_object_INTERFACES = region_mapper system_pager +pager_object_NAME = PagerObject +pager_object_INTERFACES = region_mapper system_pager -COMP_INTERFACES_CC = pager_object +COMP_INTERFACES_CC = pager_object # Individual interfaces. -SERVER_INTERFACES_CC = $(call common_interfaces,$(COMP_INTERFACES_CC)) +CLIENT_INTERFACES_CC = dataspace + +SERVER_INTERFACES_CC = $(call common_interfaces,$(COMP_INTERFACES_CC)) # Generated and plain source files. -SERVER_INTERFACES_SRC_CC = $(call interfaces_to_server_cc,$(SERVER_INTERFACES_CC) $(COMP_INTERFACES_CC)) +CLIENT_INTERFACES_SRC_CC = $(call interfaces_to_client_cc,$(CLIENT_INTERFACES_CC)) + +SERVER_INTERFACES_SRC_CC = $(call interfaces_to_server_cc,$(SERVER_INTERFACES_CC) $(COMP_INTERFACES_CC)) # Normal source files. @@ -60,6 +66,8 @@ SRC_CC_dstest_file_readdir_concurrent = dstest_file_readdir_concurrent.cc +SRC_CC_dstest_file_remove = dstest_file_remove.cc + SRC_CC_dstest_file_rename = dstest_file_rename.cc SRC_CC_dstest_host_client = dstest_host_client.cc @@ -71,6 +79,9 @@ PLAIN_SRC_CC_dstest_exec = dstest_exec.cc SRC_CC_dstest_exec = $(PLAIN_SRC_CC_dstest_exec) $(SERVER_INTERFACES_SRC_CC) +PLAIN_SRC_CC_dstest_file_mapping = dstest_file_mapping.cc +SRC_CC_dstest_file_mapping = $(PLAIN_SRC_CC_dstest_file_mapping) $(CLIENT_INTERFACES_SRC_CC) + REQUIRES_LIBS = l4re_c-util libexec libfsclient libmem libipc libstdc++ libsystypes libe2access_blockserver PRIVATE_INCDIR = $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR) @@ -78,3 +89,5 @@ include $(IDL_MK_DIR)/interface_rules.mk $(PLAIN_SRC_CC_dstest_exec): $(SERVER_INTERFACES_SRC_CC) + +$(PLAIN_SRC_CC_dstest_file_mapping): $(CLIENT_INTERFACES_SRC_CC) diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/dstest_block_client.cc --- a/tests/dstest_block_client.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/tests/dstest_block_client.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Test dataspace operations. * - * Copyright (C) 2020, 2021 Paul Boddie + * Copyright (C) 2020, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -45,9 +45,24 @@ unsigned long sample_remaining = remaining < sample ? remaining : sample; printf("%ld bytes from %p...\n", sample_remaining, (file->memory + offset)); - strncpy(buf, (file->memory + offset), sample_remaining); + memcpy(buf, (file->memory + offset), sample_remaining); buf[sample_remaining] = '\0'; - printf("%s\n", buf); + + unsigned long leading = 0; + char *outbuf = buf; + + while ((*outbuf == '\0') && (leading < sample_remaining)) + { + outbuf++; + leading++; + } + + if (leading) + printf("[%ld zero bytes]\n", leading); + + printf("%s\n", outbuf); + + printf("[%ld bytes after string]\n", sample_remaining - leading - strlen(outbuf)); } } @@ -80,9 +95,10 @@ return 1; } - /* A region of the file is mapped. */ + /* A region of the file is mapped. Here, the start and length will not provide + page-aligned offsets, but the region is nevertheless not masked. */ - err = file_mmap(&file, 0, page(10)); + err = file_mmap(&file, 10, 290, 0, 0, file_region_flags(file.flags)); if (err) { @@ -90,16 +106,39 @@ return 1; } + printf("File contents:\n"); + show(&file, step, sample); + printf("File shown.\n"); + + /* A region of the file is mapped. Here, the resulting offsets will be used to + define a masked region. */ + + err = file_mmap(&file, 10, 290, 10, 290, file_region_flags(file.flags)); + + if (err) + { + printf("Could not map file region: %s\n", l4sys_errtostr(err)); + return 1; + } + + printf("File contents:\n"); + + show(&file, step, sample); + + printf("File shown.\n"); + /* Resizing must occur before writing beyond the end of file. Otherwise, the data may get discarded if the supporting flexpage needs to be flushed. */ - offset_t new_region = round(file_populated_span(&file), page(1)); + offset_t old_size = file_populated_span(&file); + offset_t new_region = round(old_size, page(1)); + offset_t new_size = new_region + old_size; - printf("Resize to %ld...\n", new_region + file_populated_span(&file)); + printf("Resize to %ld...\n", new_size); - err = file_resize(&file, new_region + file_populated_span(&file)); + err = file_resize(&file, new_size); if (err) { @@ -109,17 +148,46 @@ printf("Resized file...\n"); + /* Re-map to avoid masking the region. */ + + err = file_mmap(&file, 10, new_size - 20, 0, 0, file_region_flags(file.flags)); + + if (err) + { + printf("Could not map file region: %s\n", l4sys_errtostr(err)); + return 1; + } + /* Copy the sampled data to another file region. */ printf("Copy data to %ld...\n", new_region); - for (unsigned long offset = 0; offset < file_populated_span(&file); offset += step) + for (unsigned long offset = 0; offset < old_size; offset += step) { + printf("Copying to %ld...\n", new_region + offset); memcpy(file.memory + new_region + offset, file.memory + offset, sample); if (step > sample) memset(file.memory + new_region + offset + sample, 0, step - sample); } + printf("File contents:\n"); + + show(&file, step, sample); + + printf("File shown.\n"); + + /* Re-map to mask the region again. */ + + err = file_mmap(&file, 0, new_size, 10, new_size - 20, file_region_flags(file.flags)); + + if (err) + { + printf("Could not map file region: %s\n", l4sys_errtostr(err)); + return 1; + } + + printf("File contents:\n"); + show(&file, step, sample); printf("File shown.\n"); diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/dstest_exec.cc --- a/tests/dstest_exec.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/tests/dstest_exec.cc Wed Aug 10 00:25:44 2022 +0200 @@ -20,291 +20,52 @@ */ #include -#include #include -#include #include #include +#include +#include #include -#include -#include +#include +#include #include -#include -#include - -#include #include -#include -#include #include #include -#include "pager_object_interface.h" #include "pager_object_server.h" -/* A simple system pager also acting as a region mapper. */ - -typedef std::map MappedRegions; - -class ExecPager : public PagerObject -{ -protected: - MappedRegions _regions; - -public: - virtual void add(MappedRegion region) - { - _regions[region.map_start] = region; - } - - /* Notification methods. */ - - virtual long exception(l4_exc_regs_t regs, - l4_snd_fpage_t *region); - - virtual long page_fault(l4_umword_t pfa, l4_umword_t pc, - l4_snd_fpage_t *region); - - /* Region manager/mapper methods. */ - - virtual long attach(address_t *start, offset_t size, map_flags_t flags, - l4_cap_idx_t ds, address_t offset, unsigned char align); - -}; - -/* Handle a general exception. */ - -long ExecPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) -{ - (void) region; - - printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); - - printf("r15 = %lx\n", regs.r15); - printf("r14 = %lx\n", regs.r14); - printf("r13 = %lx\n", regs.r13); - printf("r12 = %lx\n", regs.r12); - printf("r11 = %lx\n", regs.r11); - printf("r10 = %lx\n", regs.r10); - printf("r9 = %lx\n", regs.r9); - printf("r8 = %lx\n", regs.r8); - printf("rdi = %lx\n", regs.rdi); - printf("rsi = %lx\n", regs.rsi); - printf("rbp = %lx\n", regs.rbp); - printf("pfa = %lx\n", regs.pfa); - printf("rbx = %lx\n", regs.rbx); - printf("rdx = %lx\n", regs.rdx); - printf("rcx = %lx\n", regs.rcx); - printf("rax = %lx\n", regs.rax); - printf("trapno = %lx\n", regs.trapno); - printf("err = %lx\n", regs.err); - printf("ip = %lx\n", regs.ip); - printf("flags = %lx\n", regs.flags); - printf("sp = %lx\n", regs.sp); - printf("ss = %lx\n", regs.ss); - printf("fs_base = %lx\n", regs.fs_base); - printf("gs_base = %lx\n", regs.gs_base); - - return L4_EOK; -} - -#define DEBUG 0 - -/* Handle a page fault using any configured regions. */ - -long ExecPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) -{ - l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; - -#if DEBUG - printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); -#endif - - MappedRegions::iterator it = _regions.upper_bound(addr); - - if (it != _regions.begin()) - it--; - else - { - printf("not mapped!\n"); - return -L4_ENOMEM; - } - - MappedRegion &r = it->second; - - if ((addr >= r.map_start) && (addr < r.map_start + r.size)) - { - l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); - - region->fpage = l4_fpage(r.start + (page_addr - r.map_start), L4_PAGESHIFT, r.flags); - region->snd_base = page_addr; - -#if DEBUG - printf("%lx...%lx from %lx...%lx offset %lx size %d rights %x\n", - r.map_start, region->snd_base, - r.start, l4_fpage_memaddr(region->fpage), - addr - r.map_start, - l4_fpage_size(region->fpage), - l4_fpage_rights(region->fpage)); - - printf("%lx -> ", addr); - - for (unsigned int i = 0; i < sizeof(l4_umword_t); i++) - printf("%02x", *((unsigned char *)(r.start + (addr - r.map_start) + i))); - - printf("\n"); -#endif - - if (r.flags & L4RE_RM_F_W) - l4_touch_rw((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); - else - l4_touch_ro((const void *) (r.start + (page_addr - r.map_start)), L4_PAGESIZE); +/* External system-level pager for the region mapper in a created task. */ - return L4_EOK; - } - -#if DEBUG - printf("not mapped!\n"); -#endif - - return -L4_ENOMEM; -} - -/* Attach a region for provision when page faults occur. This is required in - the initialisation of a program by the C library which requires a region - mapper. */ - -long ExecPager::attach(address_t *start, offset_t size, map_flags_t flags, - l4_cap_idx_t ds, address_t offset, unsigned char align) -{ -#if DEBUG - printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); -#endif - - if (align < L4_PAGESHIFT) - align = L4_PAGESHIFT; - - offset_t increment = 1UL << align; - offset_t region_size = round(size, increment); - - /* Either attempt to find an address for the specified region, starting from - any indicated address. */ - - if (flags & L4RE_RM_F_SEARCH_ADDR) - { - address_t region_start = trunc(*start, increment); - MappedRegions::iterator it = _regions.upper_bound(*start); - - if (!region_start) - region_start += increment; - -#if DEBUG - printf("-> search from %lx -> %lx...\n", *start, region_start); -#endif - - /* Before last known region. */ - - while (it != _regions.end()) - { - MappedRegions::iterator next = it; - MappedRegion &r = it->second; - address_t start_limit; - address_t end_limit = r.map_start; - - /* Consider any preceding region. If no such region exists, choose an - address at the start of memory. */ +static ExternalPager exec_pager; - if (it == _regions.begin()) - start_limit = L4_PAGESIZE; - else - { - it--; - MappedRegion &pr = it->second; - start_limit = pr.map_start + pr.size; - it = next; - } - - /* Test against the limits. */ - - if (region_start < start_limit) - region_start = round(start_limit, increment); - - /* Investigate subsequent regions if not enough space exists between the - preceding region (or start of memory) and the current region. */ - - if ((region_start + region_size) > end_limit) - { - it++; - if (it == _regions.end()) - return -L4_ENOMEM; - } - else - break; - } - - /* Attach the provided dataspace. - NOTE: This is only done in this implementation to support the paging - mechanism. In a region mapper residing within the actual task, the - dataspace's map operation would be invoked to obtain mappings. */ - - l4_addr_t ds_start; - - long err = ipc_attach_dataspace(ds, size, (void **) &ds_start); - - if (err) - return err; - - l4_touch_rw((const void *) ds_start, size); - -#if DEBUG - printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size)); -#endif - - add(MappedRegion(ds_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, region_start)); - - *start = region_start; - return L4_EOK; - } - - /* Or attempt to add the specified region at a specific address. */ - - else - { - // NOTE: To be implemented. - -#if DEBUG - printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); -#endif - - return -L4_ENOMEM; - } -} +static const offset_t initial_stack_size = 16 * L4_PAGESIZE; -static ExecPager exec_pager; +/* Start the system pager in a separate thread. */ -static void init_pager(ipc_server_config_type *config) +static long start_pager(ipc_server_config_type &config) { - ipc_server_init_config(config); + pthread_t pager_thread; + pthread_attr_t attr; - config->expected_items = PagerObject_expected_items; - config->handler = (ipc_server_handler_type) handle_PagerObject; - config->handler_obj = static_cast(&exec_pager); -} + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + ipc_server_init_for(&config, PagerObject, &exec_pager); -static long start_pager(ipc_server_config_type *config, pthread_t thread) -{ - config->config_thread = 1; - config->thread = pthread_l4_cap(thread); + long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); - printf("Starting pager thread...\n"); - return ipc_server_start_config(config); + if (err) + return err; + + return ipc_server_start_config_thread(&config, pthread_l4_cap(pager_thread)); } @@ -313,170 +74,71 @@ { long err; - if (argc < 2) + if (argc < 3) { - printf("Need a program to run.\n"); - return 1; - } - - /* Obtain the payload as a dataspace. */ - - file_t *file = client_open(argv[1], O_RDONLY); - - if (file == NULL) - { - printf("Could not read file: %s\n", argv[1]); + printf("Need a program to run as the region mapper and a main program.\n"); return 1; } - /* Obtain metadata from the file. */ + /* Define the different payloads. */ - char buf[4096]; - offset_t nread; + char *rm_filename = argv[1]; + char *program_filename = argv[2]; - nread = client_read(file, buf, EI_NIDENT); - if ((nread < EI_NIDENT) || memcmp(buf, "\x7f" "ELF", 4)) + /* Initialise the memory segments of the region mapper. These are mapped into + this task so that we may access them. */ + + ExplicitSegment rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); + Payload *rm_payload; + + if (exec_get_payload(rm_filename, &rm_payload, true)) { - printf("Not an ELF payload: %s\n", argv[1]); + printf("Could not initialise region mapper: %s\n", rm_filename); return 1; } - Payload *payload = get_payload(buf); - - if (payload == NULL) - { - printf("Unrecognised object size.\n"); - return 1; - } - - client_seek(file, 0, SEEK_SET); - nread = client_read(file, buf, payload->header_extent()); - - if (nread < payload->header_extent()) + if (rm_stack.allocate(true)) { - printf("Header incomplete.\n"); - return 1; - } - - printf("Program start: %lx\n", payload->entry_point()); - - client_seek(file, 0, SEEK_SET); - nread = client_read(file, buf, payload->program_header_extent()); - - if (nread < payload->program_header_extent()) - { - printf("Program headers incomplete.\n"); + printf("Could not allocate region mapper stack.\n"); return 1; } - /* Make appropriate segments, although program segments could be made - interoperable with these. */ - - Segment *segments[payload->segments() + 1]; - - for (unsigned int i = 0; i < payload->segments(); i++) - { - ProgramSegment *ps = payload->segment(i); - - printf("Segment(0x%lx, 0x%lx, 0x%x, 0x%lx, 0x%lx): %s\n", - ps->region_address(), - ps->region_size(), - ps->region_flags(), - ps->file_offset(), - ps->file_contents(), - ps->loadable() ? "loadable" : "other"); - - if (ps->loadable()) - segments[i] = ps->segment(); - else - segments[i] = NULL; - } - - /* Copy the payload regions to new dataspaces. */ - - address_t program_start = payload->entry_point(); - offset_t initial_stack_size = 16 * L4_PAGESIZE; - - Segment stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW); - - segments[payload->segments()] = &stack; - - for (unsigned int i = 0; i < payload->segments() + 1; i++) - { - Segment *segment = segments[i]; - - if (segment == NULL) - continue; - - if (segment->file_contents()) - { - flags_t flags; - - if ((segment->region_flags() & L4RE_RM_F_RW) == L4RE_RM_F_RW) - flags = O_RDWR; - else if (segment->region_flags() & L4RE_RM_F_W) - flags = O_WRONLY; - else - flags = O_RDONLY; - - file_t *file = client_open(argv[1], flags); + /* Initialise the memory segments of the actual program. These are not mapped + into this task, instead being accessed by the region mapper in the new + task. */ - if (file == NULL) - { - printf("Could not open file for segment.\n"); - return 1; - } - - err = segment->fill(file); - - if (err) - { - printf("Could not fill segment from file.\n"); - return 1; - } - } - else - { - err = segment->allocate(); - - if (err) - { - printf("Could not allocate segment.\n"); - return 1; - } - } - } + ExplicitSegment program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW); + Payload *program_payload; - /* Start the pager. */ - - ipc_server_config_type config; - pthread_t pager_thread; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - init_pager(&config); - - for (unsigned int i = 0; i < payload->segments() + 1; i++) + if (exec_get_payload(program_filename, &program_payload, false)) { - Segment *segment = segments[i]; - - if (segment == NULL) - continue; - - exec_pager.add(segment->region()); - } - - err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config); - - if (err) - { - printf("Could not start pager thread.\n"); + printf("Could not initialise program: %s\n", program_filename); return 1; } - err = start_pager(&config, pager_thread); + if (program_stack.allocate(true)) + { + printf("Could not allocate program stack.\n"); + return 1; + } + + /* Initialise pager regions for the region mapper. */ + + for (unsigned int i = 0; i < rm_payload->segments(); i++) + { + if (rm_payload->segment(i)->loadable()) + exec_pager.add(rm_payload->segment(i)->region()); + } + + exec_pager.add(rm_stack.region()); + + /* Start the pager in a separate thread. */ + + ipc_server_config_type config; + + printf("Starting pager thread...\n"); + + err = start_pager(config); if (err) { @@ -484,12 +146,11 @@ return 1; } - /* Configure the environment for the task, specifying the pager (and exception - handler plus region mapper). */ + /* Configure the environment for the task, reserving two threads. */ - Process process; + Process process(2); - err = process.configure(config.server); + err = process.configure_task(); if (err) { @@ -497,30 +158,168 @@ return 1; } - /* Populate a thread stack with argument and environment details. */ + /* Configure the environment for the thread, specifying the pager (and + exception handler plus region mapper). */ + + err = process.configure_thread(config.server); + + if (err) + { + printf("Could not configure thread.\n"); + return 1; + } + + /* Create an unbound IPC gate for the region mapper. */ + + l4_cap_idx_t ipc_gate = ipc_cap_alloc(); + + if (l4_is_invalid_cap(ipc_gate)) + { + printf("Could not allocate IPC gate capability.\n"); + return 1; + } + + err = l4_error(l4_factory_create_gate(l4re_env()->factory, ipc_gate, L4_INVALID_CAP, 0)); + + if (err) + { + printf("Could not create IPC gate.\n"); + return 1; + } + + /* Define regions employing dataspaces to provide program segments. + + Define capabilities for mapping, including region dataspace capabilities, + the stack dataspace capability, and the server capability. + + Here, the arrays are sized for the maximum number of regions and + capabilities, but in practice only the loadable segments are used, leaving + fewer elements utilised. A terminating entry is employed to indicate the + limit of utilised elements. */ + + struct exec_region rm_regions[rm_payload->segments() + 2]; + struct ipc_mapped_cap rm_mapped_caps[rm_payload->segments() + 3]; + l4_cap_idx_t mapped_cap; + unsigned int rm_index = 0; + + for (unsigned int i = 0; i < program_payload->segments(); i++) + { + Segment *s = program_payload->segment(i); - Stack st(stack); + if (s->loadable()) + { + mapped_cap = process.allocate_cap(); + rm_regions[rm_index] = s->exec_region(); + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, rm_regions[rm_index].ds, L4_CAP_FPAGE_RWS, 0}; + rm_regions[rm_index].ds = mapped_cap; + rm_index++; + } + } + + /* Introduce the stack region and capability. */ + + mapped_cap = process.allocate_cap(); + rm_regions[rm_index] = program_stack.exec_region(); + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {mapped_cap, program_stack.exec_region().ds, L4_CAP_FPAGE_RWS, 0}; + rm_regions[rm_index].ds = mapped_cap; + rm_index++; + + /* Terminate the region array. */ + + rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP}; + + /* Introduce the server capability. */ + + l4_cap_idx_t ipc_gate_cap = process.allocate_cap(); + + printf("Mapping %lx to %lx in task.\n", ipc_gate, ipc_gate_cap); + + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {ipc_gate_cap, ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}; + rm_index++; + + /* Terminate the capability array. */ + + rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0}; + + /* Map these additional capabilities. */ + + printf("Map additional capabilities...\n"); + + process.map_capabilities(rm_mapped_caps, false); + + /* Define the IPC gate as an initial capability to be acquired by the region + mapper via the l4re_env API. The capability index is assigned above when + mapping the capability and encoded in the entry below. */ + + l4re_env_cap_entry_t rm_init_caps[] = { + l4re_env_cap_entry_t("server", ipc_gate_cap, L4_CAP_FPAGE_RWS), + l4re_env_cap_entry_t() + }; /* NOTE: Environment vector is currently not defined. */ char *envp[] = {NULL}; - st.populate(argc - 1, argv + 1, envp); + /* Populate a thread stack with argument and environment details for the + region mapper, plus the initial server capability and region details. */ - /* Start the new thread in the given stack. */ + printf("Populating region mapper stack...\n"); + + Stack rm_st(rm_stack); - printf("Run thread...\n"); + rm_st.set_init_caps(rm_init_caps); + rm_st.set_regions(rm_regions); + rm_st.populate(1, argv + 1, envp); - err = process.thread_start(program_start, st); + /* Start the region mapper thread in the appropriate stack. */ + + printf("Run region mapper thread...\n"); + + err = process.thread_start(rm_payload->entry_point(), rm_st); if (err) { - printf("Could not run thread.\n"); + printf("Could not run thread for region mapper.\n"); + return 1; + } + + /* Configure the environment for the thread, specifying the pager (and + exception handler plus region mapper). */ + + err = process.configure_thread(ipc_gate, ipc_gate_cap); + + if (err) + { + printf("Could not configure task.\n"); + return 1; + } + + /* Populate a thread stack with argument and environment details for the + actual program. The server capability should be assigned to the region + mapper capability slot already. */ + + printf("Populating program stack...\n"); + + Stack program_st(program_stack); + + program_st.populate(argc - 2, argv + 2, envp); + + /* Start the program thread in the appropriate stack. */ + + printf("Run program thread...\n"); + + err = process.thread_start(program_payload->entry_point(), program_st); + + if (err) + { + printf("Could not run thread for program.\n"); return 1; } printf("Finished.\n"); - while (1); + + while (1) + l4_sleep_forever(); return 0; } diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/dstest_file_mapping.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dstest_file_mapping.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,117 @@ +/* + * Test dataspace operations. + * + * Copyright (C) 2020, 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "dataspace_client.h" + + + +int main(int argc, char *argv[]) +{ + if (argc < 4) + { + printf("Need filename, step and sample size.\n"); + return 1; + } + + /* Obtain filename and access parameters. */ + + char *filename = argv[1]; + unsigned long step = atoi(argv[2]); + unsigned long sample = atoi(argv[3]); + + /* Allocate a buffer for sampling from the file. */ + + char buf[sample + 1]; + + /* Obtain access to the filesystem. */ + + l4_cap_idx_t server = l4re_env_get_cap("server"); + + /* Invoke the open method to receive the file reference. */ + + file_t file; + long err = file_open(&file, filename, O_RDWR, server); + + if (err) + { + printf("Could not obtain file: %s\n", l4sys_errtostr(err)); + return 1; + } + + /* A region of the file is mapped but not attached. */ + + err = file_mmap_only(&file, 0, page(10), 0, 0); + + if (err) + { + printf("Could not map file region: %s\n", l4sys_errtostr(err)); + return 1; + } + + /* Fix up the file data structure manually. */ + + file.memory = (char *) 0x2000000; + + /* Explicitly map the file into this address space, without region mapper + usage. The receive window base must be a multiple of its size. */ + + client_Dataspace dataspace(file.ref); + l4_snd_fpage_t region = {0, l4_fpage((l4_addr_t) file.memory, L4_PAGESHIFT + 4, 0)}; + + printf("region = {%lx, {%lx, %d}}\n", region.snd_base, l4_fpage_memaddr(region.fpage), l4_fpage_size(region.fpage)); + + err = dataspace.map(0, 0, L4_FPAGE_RO, ®ion); + + if (err) + { + printf("Could not invoke map successfully: %s\n", l4sys_errtostr(err)); + return 1; + } + + for (unsigned long offset = 0; offset < file_populated_span(&file); offset += step) + { + unsigned long remaining = file_populated_span(&file) - offset; + unsigned long sample_remaining = remaining < sample ? remaining : sample; + + printf("%ld bytes from %p...\n", sample_remaining, (file.memory + offset)); + strncpy(buf, (file.memory + offset), sample_remaining); + buf[sample_remaining] = '\0'; + printf("%s\n", buf); + } + + printf("File shown.\n"); + + return 0; +} + +// vim: tabstop=2 expandtab shiftwidth=2 diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/dstest_file_remove.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dstest_file_remove.cc Wed Aug 10 00:25:44 2022 +0200 @@ -0,0 +1,146 @@ +/* + * Test removal operations. + * + * Copyright (C) 2020, 2021, 2022 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include +#include +#include + +#include +#include + + + +static void read_from_file(file_t *file) +{ + char buf[20]; + offset_t nread; + + client_seek(file, 0, SEEK_SET); + + printf("Reading...\n"); + + nread = client_read(file, buf, 20); + + printf("Read %ld bytes...\n", nread); + fwrite(buf, sizeof(char), nread, stdout); + fputs("\n", stdout); +} + +static void write_to_file(file_t *file, const char *buf) +{ + offset_t nwritten; + + client_seek(file, 0, SEEK_SET); + + printf("Writing...\n"); + + nwritten = client_write(file, buf, strlen(buf)); + + printf("Wrote %ld bytes: ", nwritten); + fwrite(buf, sizeof(char), nwritten, stdout); + fputs("\n", stdout); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + printf("Need a file to remove.\n"); + return 1; + } + + char *filename = argv[1]; + + printf("Creating %s...\n", filename); + + file_t *file = client_open(filename, O_WRONLY | O_CREAT); + + write_to_file(file, "An existing file."); + + client_close(file); + + printf("Opening %s...\n", filename); + + file = client_open(filename, O_RDONLY); + + if (file == NULL) + { + printf("Could not open file: %s\n", filename); + return 1; + } + + /* Read from the file, then remove the file, then read again. */ + + read_from_file(file); + + printf("Removing...\n"); + + long err = client_remove(filename); + + if (err) + { + printf("Could not remove file: %s\n", filename); + return 1; + } + + printf("Reading again...\n"); + + read_from_file(file); + + /* Open the file again. */ + + file_t *file_new = client_open(filename, O_RDONLY); + + if (file_new != NULL) + { + printf("File should be absent: %s\n", filename); + return 1; + } + + file_new = client_open(filename, O_RDWR | O_CREAT); + + if (file_new == NULL) + { + printf("File should be present: %s\n", filename); + return 1; + } + + write_to_file(file_new, "New file!"); + + /* Read from the initial file and new file. */ + + read_from_file(file); + read_from_file(file_new); + + /* Close the files. */ + + client_close(file); + client_close(file_new); + + printf("End of tests.\n"); + + return 0; +} + +// vim: tabstop=2 expandtab shiftwidth=2 diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/dstest_host_client.cc --- a/tests/dstest_host_client.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/tests/dstest_host_client.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Test dataspace operations. * - * Copyright (C) 2020, 2021 Paul Boddie + * Copyright (C) 2020, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -68,7 +68,7 @@ /* A region of the file is mapped. */ - err = file_mmap(&file, 0, page(10)); + err = file_mmap(&file, 0, page(10), 0, 0, file_region_flags(file.flags)); if (err) { diff -r ba6c9728a9ae -r 77fbb52e0a14 tests/dstest_test_client.cc --- a/tests/dstest_test_client.cc Sun Jun 12 17:10:30 2022 +0200 +++ b/tests/dstest_test_client.cc Wed Aug 10 00:25:44 2022 +0200 @@ -1,7 +1,7 @@ /* * Test dataspace operations. * - * Copyright (C) 2020, 2021 Paul Boddie + * Copyright (C) 2020, 2021, 2022 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -77,7 +77,7 @@ /* A region of the file is mapped. */ - err = file_mmap(&file, page(start_page), page(MAP_PAGES)); + err = file_mmap(&file, page(start_page), page(MAP_PAGES), 0, 0, file_region_flags(file.flags)); if (err) {