diff --git a/apps/system/CMakeLists.txt b/apps/system/CMakeLists.txt index 7bbe2e2..ce93bea 100644 --- a/apps/system/CMakeLists.txt +++ b/apps/system/CMakeLists.txt @@ -93,10 +93,6 @@ DeclareCAmkESComponent(OpenTitanUARTDriver components/OpenTitanUARTDriver/include ) -DeclareCAmkESComponent(SeL4Debug - SOURCES components/SeL4Debug/src/wrappers.c -) - DeclareCAmkESComponent(VectorCoreDriver SOURCES components/VectorCoreDriver/src/driver.c diff --git a/apps/system/components/DebugConsole/DebugConsole.camkes b/apps/system/components/DebugConsole/DebugConsole.camkes index 9362857..c8c6460 100644 --- a/apps/system/components/DebugConsole/DebugConsole.camkes +++ b/apps/system/components/DebugConsole/DebugConsole.camkes @@ -3,7 +3,6 @@ import ; import ; import ; import ; -import ; import ; component DebugConsole { @@ -22,6 +21,5 @@ component DebugConsole { uses SecurityCoordinatorInterface security; // TODO(b/200707300): for debugging uses StorageInterface storage; - uses SeL4DebugInterface sel4debug; uses MlCoordinatorInterface mlcoord; } diff --git a/apps/system/components/DebugConsole/kata-shell/Cargo.toml b/apps/system/components/DebugConsole/kata-shell/Cargo.toml index 795ea0e..8e7bfec 100644 --- a/apps/system/components/DebugConsole/kata-shell/Cargo.toml +++ b/apps/system/components/DebugConsole/kata-shell/Cargo.toml @@ -10,6 +10,7 @@ hex = { version = "0.4.3", default-features = false, features = ["alloc"] } kata-io = { path = "../kata-io" } kata-line-reader = { path = "../kata-line-reader" } kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" } +kata-os-common = { path = "../../kata-os-common" } kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" } kata-storage-interface = { path = "../../StorageManager/kata-storage-interface" } log = "0.4" diff --git a/apps/system/components/DebugConsole/kata-shell/src/lib.rs b/apps/system/components/DebugConsole/kata-shell/src/lib.rs index a142914..ad15b8c 100644 --- a/apps/system/components/DebugConsole/kata-shell/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-shell/src/lib.rs @@ -15,6 +15,7 @@ use kata_proc_interface::kata_pkg_mgmt_uninstall; use kata_proc_interface::kata_proc_ctrl_get_running_bundles; use kata_proc_interface::kata_proc_ctrl_start; use kata_proc_interface::kata_proc_ctrl_stop; +use kata_os_common::sel4_sys::seL4_DebugDumpScheduler; use kata_storage_interface::kata_storage_delete; use kata_storage_interface::kata_storage_read; use kata_storage_interface::kata_storage_write; @@ -207,12 +208,7 @@ fn rz_command( /// Implements a "ps" command that dumps seL4 scheduler state to the console. fn ps_command() -> Result<(), CommandError> { - extern "C" { - fn sel4debug_dump_scheduler(); - } - unsafe { - sel4debug_dump_scheduler(); - } + unsafe { seL4_DebugDumpScheduler(); } Ok(()) } diff --git a/apps/system/components/ProcessManager/ProcessManager.camkes b/apps/system/components/ProcessManager/ProcessManager.camkes index 952e3a6..10ed4fb 100644 --- a/apps/system/components/ProcessManager/ProcessManager.camkes +++ b/apps/system/components/ProcessManager/ProcessManager.camkes @@ -3,7 +3,6 @@ import ; import ; import ; -import ; import ; component ProcessManager { @@ -11,6 +10,5 @@ component ProcessManager { provides ProcessControlInterface proc_ctrl; uses LoggerInterface logger; - uses SeL4DebugInterface sel4debug; uses SecurityCoordinatorInterface security; } diff --git a/apps/system/components/SeL4Debug/SeL4Debug.camkes b/apps/system/components/SeL4Debug/SeL4Debug.camkes deleted file mode 100644 index 0e87e70..0000000 --- a/apps/system/components/SeL4Debug/SeL4Debug.camkes +++ /dev/null @@ -1,5 +0,0 @@ -import ; - -component SeL4Debug { - provides SeL4DebugInterface sel4debug; -} diff --git a/apps/system/components/SeL4Debug/src/wrappers.c b/apps/system/components/SeL4Debug/src/wrappers.c deleted file mode 100644 index 363d2d6..0000000 --- a/apps/system/components/SeL4Debug/src/wrappers.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include - -void sel4debug_put_string(const char* msg) { -#ifdef CONFIG_PRINTING - char buf[512]; - snprintf(buf, sizeof(buf), "%s\n", msg); - seL4_DebugPutString(buf); -#endif -} - -void sel4debug_dump_scheduler() { -#ifdef CONFIG_PRINTING - seL4_DebugDumpScheduler(); -#endif -} diff --git a/apps/system/components/kata-os-common/Cargo.toml b/apps/system/components/kata-os-common/Cargo.toml new file mode 100644 index 0000000..29ba89c --- /dev/null +++ b/apps/system/components/kata-os-common/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "kata-os-common" +version = "0.1.0" +edition = "2018" + +[dependencies] +sel4-sys = { path = "src/sel4-sys" } diff --git a/apps/system/components/kata-os-common/src/lib.rs b/apps/system/components/kata-os-common/src/lib.rs new file mode 100644 index 0000000..3fcc76b --- /dev/null +++ b/apps/system/components/kata-os-common/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +pub extern crate sel4_sys; diff --git a/apps/system/components/kata-os-common/src/sel4-sys/CONTRIBUTING.md b/apps/system/components/kata-os-common/src/sel4-sys/CONTRIBUTING.md new file mode 100644 index 0000000..804fbb2 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/CONTRIBUTING.md @@ -0,0 +1,187 @@ +Thank you for your interest in contributing to the Robigalia project! There +are many ways to contribute, and we appreciate all of them. + +If you have questions, please send an email to +[robigalia-dev@lists.robigalia.org][list] or hop on [#robigalia][irc]. + +As a reminder, all participants in our community are expected to honor our +[code of conduct][conduct]. + +[list]: mailto:robigalia-dev@lists.robigalia.org +[irc]: https://webchat.freenode.net/?channels=%23robigalia +[conduct]: https://robigalia.org/conduct.html + +## Feature Proposal + +To propose a change to the way that Robigo or any other Robigalia codebase +works, please open an issue directly on the relevant repository. Include the +text, `[feature-proposal]` in the title and someone will come by and re-tag +the issue appropriately. Here's a template you can use to file a feature +proposal, though it's not necessary to use it exactly: + +``` +One-paragraph summary + +# Details + +As many details as you think are necessary to communicate how the feature +should work. + +# Benefits + +Benefits of the feature. + +# Drawbacks + +Drawbacks of the feature. +``` + +There is [an example feature proposal][prop] if you want to see what it looks +like. + +[prop]: https://gitlab.com/robigalia/meta/issues/1 + +## Bug Reports + +While bugs are unfortunate, they're a reality in software. We can't fix what we +don't know about, so please report liberally. If you're not sure if something +is a bug or not, feel free to file a bug anyway. + +**If you believe reporting your bug publicly represents a security risk to +users of software developed by the Robigalia project, +please follow our [instructions for reporting security +vulnerabilities](https://robigalia.org/security.html)**. + +If you have the chance, before reporting a bug, please [search existing +issues](https://gitlab.com/groups/robigalia/issues) as it's possible that +someone else has already reported your error. This doesn't always work, and +sometimes it's hard to know what to search for, so consider this extra credit. +We won't mind if you accidentally file a duplicate report, we'll typically +close it and point out which issue is a duplicate of. + +Opening an issue is as easy as following [this +link](https://gitlab.com/groups/robigalia/issues) and filling out the fields +after clicking "New Issue". Here's a template that you can use to file a bug, +though it's not necessary to use it exactly: + + + + I tried this: + + + + I expected to see this happen: + + Instead, this happened: + +All three components are important: what you did, what you expected, what +happened instead. + + +## Submitting code + +To coordinate changes to our software, we use a feature of GitLab called +"merge requests". Alternatively, you can email patches (ideally with git +send-email) to [the development mailing list][list]. + +### Commit messages + +Commit messages should be self-contained and describe the motivation for a +change. The subject line should be short, with any elaborations in the body. +If the commit closes an issue, write "Closes #issuenumber" at the end of the +body. + +For some guidelines, read [this][chrisbeams] for the basics, and [this][pro] +to become a pro. + +[chrisbeams]: http://chris.beams.io/posts/git-commit/ +[pro]: https://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message + +### Commit signing + +For a [variety of reasons][horror], we use commit signing to verify the +authenticity and authorship of changes to our projects. If you don't already +have one, create a PGP key. There are many ways to do this. [This blog +post][keygen] by Alex Cabal has a decent explanation of how to do it and some +of the tradeoffs involved. + +Once you have a keypair and it is uploaded to the keyservers, [learn how to +use gpg with git][horror2]. Please sign all commits you intend to include in a +merge request. + +[horror]: https://mikegerwitz.com/papers/git-horror-story +[horror2]: https://mikegerwitz.com/papers/git-horror-story#trust-ensure +[keygen]: https://alexcabal.com/creating-the-perfect-gpg-keypair/ + +### Forking the repositories. + +We use a custom system for managing our large amount of repositories. We have +one "master" repository, [devbox][devbox], which contains scripts for fetching +the other repositories. It also includes a [Vagrant][vagrant] configuration, +which is the easiest way to get started with Rust and seL4. See the readme in +that repository to see how to use the scripts and, optionally, set up Vagrant. +Part of the setup is running the `add_remotes.sh` script, which will rename +the official repo to `upstream` and add an `origin` remote pointing to a repo +under your own username. + +To fork a repository, navigate to it on GitLab and click the "Fork project" +button", which is to the left of the repository URL: + +![fork project button location image][forkimg] + +Then, you can interact with that repository using the `origin` remote in the +relevant sub-repository of `devbox`. + +[devbox]: https://gitlab.com/robigalia/devbox +[vagrant]: https://www.vagrantup.com/ +[forkimg]: https://gitlab.com/robigalia/meta/raw/master/fork_button.png + +### Signing the Developer Certificate of Origin + +Before we can accept code you have written into our repositories, you must +agree to the Developers Certificate of Origin. You can view it [in this +repository][DCO]. It is quite minimalist, and is also used by the Linux +kernel. To do this, fork the meta repository and run a command like: + + gpg --sign --armor < DCO.txt > agreements/${your_gitlab_username} + +Create a new commit with that file and send a merge request. + +[DCO]: https://gitlab.com/robigalia/meta/blob/master/DCO.txt + +### Submitting a merge request + +To submit a merge request, push the commits you want to include to a branch on +your fork of the relevant repository. Refer to the [official GitLab +documentation][mergereq] to see how to proceed from there. + +Please open merge requests against the `master` branch. If needed, we will +merge into a separate branch manually. + +[mergereq]: http://doc.gitlab.com/ce/gitlab-basics/add-merge-request.html + +All merge requests are reviewed by another person. + +If you want to request that a specific person reviews your pull request, you +can specify the "assignee" when you create the pull request. For example, +Corey usually reviews changes to sel4-sys, so you could specify him as the +assignee if you send a merge request to sel4-sys. + +## Writing Documentation + +Documentation improvements are very welcome. The source of robigalia.org is +in its [own repository][web], and individual crate documentation is hosted on +[doc.robigalia.org](https://doc.robigalia.org). + +Documentation merge requests function in the same way as other merge requests. + +[web]: https://gitlab.com/robigalia/robigalia.org + +## Helpful Links and Information + +- The [seL4 website](https://sel4.systems/) has things like the manual and how + to build seL4. +- The [Rust website](https://rust-lang.org/) for all your Rust-related + questions. + +*This document adapted from * diff --git a/apps/system/components/kata-os-common/src/sel4-sys/Cargo.toml b/apps/system/components/kata-os-common/src/sel4-sys/Cargo.toml new file mode 100644 index 0000000..4055aea --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/Cargo.toml @@ -0,0 +1,53 @@ +# Copyright (c) 2015 The Robigalia Project Developers +# Licensed under the Apache License, Version 2.0 or the MIT license , at your option. All files in the +# project carrying such notice may not be copied, modified, or distributed +# except according to those terms. +[package] +name = "sel4-sys" +version = "0.1.0" +authors = ["Corey Richardson "] +description = "Rust interface to the seL4 kernel" +documentation = "https://doc.robigalia.org/sel4_sys" +repository = "https://gitlab.com/robigalia/sel4-sys" +readme = "README.md" +license = "MIT/Apache-2.0" +build = "build.rs" + +# NB: used by build.rs +[env] +# Pathname to seL4 kernel for xml files and (eventually) tools scripts, +# defaults to "sel4" +SEL4_DIR = "sel4" +# Name of python3 interpreter executable, defaults to "python3" +PYTHON = "python3" + +# Beware CONFIG_KERNEL_MCS must match the kernel config for syscall numbering +# to line up. Likewise other syscalls are defined only when the associated +# config knobs are set (e.g. CONFIG_PRINTING). +[features] +default = ["CONFIG_PRINTING", "CONFIG_DEBUG_BUILD", "CONFIG_SET_TLS_BASE_SELF"] +#default = ["CONFIG_KERNEL_MCS", "CONFIG_PRINTING", "CONFIG_DEBUG_BUILD", "CONFIG_SET_TLS_BASE_SELF"] +# Changes api, blech +CONFIG_KERNEL_MCS = [] +CONFIG_PRINTING = [] +CONFIG_DEBUG_BUILD = [] +CONFIG_SMP_SUPPORT = [] +CONFIG_DANGEROUS_CODE_INJECTION = [] +CONFIG_ENABLE_BENCHMARKS = [] +CONFIG_BENCHMARK_TRACK_UTILISATION = [] +# TCBSetBreakpoint, TCBUnsetBreakpoint, TCBSetTLSBase +CONFIG_HARDWARE_DEBUG_API = [] +CONFIG_SET_TLS_BASE_SELF = [] +# note: if you're looking at these, be aware that hypervisor support is not +# yet available at all. these features shouldn't be enabled. +CONFIG_VTX = [] +CONFIG_ARM_SMMU = [] +CONFIG_ARM_HYPERVISOR_SUPPORT = [] +unstable = [] # XXX? + +[lib] +path = "lib.rs" + +[dependencies] diff --git a/apps/system/components/kata-os-common/src/sel4-sys/LICENSE-APACHE b/apps/system/components/kata-os-common/src/sel4-sys/LICENSE-APACHE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/system/components/kata-os-common/src/sel4-sys/LICENSE-MIT b/apps/system/components/kata-os-common/src/sel4-sys/LICENSE-MIT new file mode 100644 index 0000000..9789053 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 The Robigalia Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/apps/system/components/kata-os-common/src/sel4-sys/LICENSE_BSD2.txt b/apps/system/components/kata-os-common/src/sel4-sys/LICENSE_BSD2.txt new file mode 100644 index 0000000..85cda3e --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/LICENSE_BSD2.txt @@ -0,0 +1,31 @@ +Files described as being under the "BSD 2-Clause" license fall under the +following license. + +----------------------------------------------------------------------- + +Copyright (c) 2014 National ICT Australia and other contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + diff --git a/apps/system/components/kata-os-common/src/sel4-sys/README.md b/apps/system/components/kata-os-common/src/sel4-sys/README.md new file mode 100644 index 0000000..612436d --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/README.md @@ -0,0 +1,52 @@ +# sel4-sys + +[![Crates.io](https://img.shields.io/crates/v/sel4-sys.svg?style=flat-square)](https://crates.io/crates/sel4-sys) + +[Documentation](https://doc.robigalia.org/sel4_sys) + +A Rust interface to the [seL4 kernel](https://sel4.systems). Raw syscall +bindings, kernel API, and data structure declarations. Provides the same +interface that libsel4 does, with a few C-isms reduced. + +NOTE: be sure to `git submodule update --recursive --init` if you clone this +repository, as we pull in seL4 via a submodule. + +## Updating to a new version of seL4 + +Updating to a new version of seL4 isn't hard, but it can be annoying. +First, cd into the `seL4` submodule, do a `git fetch`, and checkout the new +version you want to evaluate. Then, do a `cargo build`. At that point, you can +try running `cargo build`. It probably won't succeed, due to changes in API +and the Python tools. + +To fix the Python tools, I use a command like: + + diff -u seL4/tools/bitfield_gen.py tools/bitfield_gen.py | pygmentize | less -R + +I then carefully look at the diff to see if there are any meaningful +differences. One challenge when doing this is that a lot of some of the tools +has been ripped out, because it deals with topics Robigalia doesn't need to +care about (bitfield proofs, or declaration order, for example). + +Once you have a successful `cargo build`, you're not done. It's likely that +the kernel added, removed, or otherwise changed various pieces of the ABI. In +particular, inspect `lib.rs` and update for any changes in the IPC buffer +(unlikely) or bootinfo (increasingly unlikely). Update `arch/x86_64.rs` etc +for any changes in the object types. Changes are usually easy to see by a cd +into seL4/libsel4 and a `git diff X.0.0..Y.0.0`. + +As a quick smoketest, go to the `hello-world` repository and compile and run +it with the new kernel and `sel4-sys`. + +After that, it's time to update the `sel4` crate and any other impacted user +components. + +## Status + +Mostly complete, though largely untested. + +## TODO + +- Add support iterating over the `seL4_BootInfoHeader` +- Add automated, comprehensive tests +- Formal verification that the code actually follows the implemented ABI. diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/arm.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/arm.rs new file mode 100644 index 0000000..1aef93b --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/arm.rs @@ -0,0 +1,695 @@ +/* + * Copyright 2015, Killian Coddington + * Copyright 2014, NICTA + * + * This software may be distributed and modified according to the terms of + * the BSD 2-Clause license. Note that NO WARRANTY is provided. + * See "LICENSE_BSD2.txt" for details. + * + * @TAG(NICTA_BSD) + */ + +pub const seL4_WordBits: usize = 32; +pub const seL4_PageBits: usize = 12; +pub const seL4_SlotBits: usize = 4; +pub const seL4_TCBBits: usize = 9; +pub const seL4_EndpointBits: usize = 4; +pub const seL4_PageTableBits: usize = 10; +pub const seL4_PageDirBits: usize = 14; +pub const seL4_ASIDPoolBits: usize = 12; + +pub const seL4_Frame_Args: usize = 4; +pub const seL4_Frame_MRs: usize = 7; +pub const seL4_Frame_HasNPC: usize = 0; + +pub const seL4_GlobalsFrame: *mut u8 = 0xffffc000 as *mut u8; + +pub type seL4_ARM_Page = seL4_CPtr; +pub type seL4_ARM_PageTable = seL4_CPtr; +pub type seL4_ARM_PageDirectory = seL4_CPtr; +pub type seL4_ARM_ASIDControl = seL4_CPtr; +pub type seL4_ARM_ASIDPool = seL4_CPtr; + +error_types!(u32); + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct seL4_UserContext { + pub pc: seL4_Word, + pub sp: seL4_Word, + pub cpsr: seL4_Word, + pub r0: seL4_Word, + pub r1: seL4_Word, + pub r8: seL4_Word, + pub r9: seL4_Word, + pub r10: seL4_Word, + pub r11: seL4_Word, + pub r12: seL4_Word, + pub r2: seL4_Word, + pub r3: seL4_Word, + pub r4: seL4_Word, + pub r5: seL4_Word, + pub r6: seL4_Word, + pub r7: seL4_Word, + pub r14: seL4_Word, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_ARM_VMAttributes { + PageCacheable = 1, + ParityEnabled = 2, + ExecuteNever = 4, +} +pub const Default_VMAttributes: usize = 0; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_ObjectType { + seL4_UntypedObject = 0, + seL4_TCBObject, + seL4_EndpointObject, + seL4_NotificationObject, + seL4_CapTableObject, + seL4_ARM_SmallPageObject, + seL4_ARM_LargePageObject, + seL4_ARM_SectionObject, + seL4_ARM_SuperSectionObject, + seL4_ARM_PageTableObject, + seL4_ARM_PageDirectoryObject, +} + +#[inline(always)] +pub unsafe fn seL4_GetIPCBuffer() -> *mut seL4_IPCBuffer { + *(seL4_GlobalsFrame as *mut *mut seL4_IPCBuffer) +} + +#[inline(always)] +pub unsafe fn seL4_GetTag() -> seL4_MessageInfo { + (*seL4_GetIPCBuffer()).tag +} + +#[inline(always)] +pub unsafe fn seL4_SetTag(tag: seL4_MessageInfo) { + (*seL4_GetIPCBuffer()).tag = tag; +} + +#[inline(always)] +pub unsafe fn seL4_GetMR(regnum: usize) -> seL4_Word { + (*seL4_GetIPCBuffer()).msg[regnum] +} + +#[inline(always)] +pub unsafe fn seL4_SetMR(regnum: usize, value: seL4_Word) { + (*seL4_GetIPCBuffer()).msg[regnum] = value; +} + +#[inline(always)] +pub unsafe fn seL4_GetUserData() -> seL4_Word { + (*seL4_GetIPCBuffer()).userData +} + +#[inline(always)] +pub unsafe fn seL4_SetUserData(data: seL4_Word) { + (*seL4_GetIPCBuffer()).userData = data; +} + +#[inline(always)] +pub unsafe fn seL4_GetBadge(index: usize) -> seL4_CapData { + let mut badge: seL4_CapData = ::core::mem::uninitialized(); + badge.set_Badge((*seL4_GetIPCBuffer()).caps_or_badges[index]); + badge +} + +#[inline(always)] +pub unsafe fn seL4_GetCap(index: usize) -> seL4_CPtr { + (*seL4_GetIPCBuffer()).caps_or_badges[index] as seL4_CPtr +} + +#[inline(always)] +pub unsafe fn seL4_SetCap(index: usize, cptr: seL4_CPtr) { + (*seL4_GetIPCBuffer()).caps_or_badges[index] = cptr as seL4_Word; +} + +#[inline(always)] +pub unsafe fn seL4_GetCapReceivePath(receiveCNode: *mut seL4_CPtr, + receiveIndex: *mut seL4_CPtr, + receiveDepth: *mut seL4_Word) { + let ipcbuffer = seL4_GetIPCBuffer(); + if !receiveCNode.is_null() { + *receiveCNode = (*ipcbuffer).receiveCNode; + } + + if !receiveIndex.is_null() { + *receiveIndex = (*ipcbuffer).receiveIndex; + } + + if !receiveDepth.is_null() { + *receiveDepth = (*ipcbuffer).receiveDepth; + } +} + +#[inline(always)] +pub unsafe fn seL4_SetCapReceivePath(receiveCNode: seL4_CPtr, + receiveIndex: seL4_CPtr, + receiveDepth: seL4_Word) { + let ipcbuffer = seL4_GetIPCBuffer(); + (*ipcbuffer).receiveCNode = receiveCNode; + (*ipcbuffer).receiveIndex = receiveIndex; + (*ipcbuffer).receiveDepth = receiveDepth; +} + +macro_rules! swinum { + ($val:expr) => { + $val as seL4_Word & 0x00ffffff + } +} + +#[inline(always)] +pub unsafe fn seL4_Send(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + let msg0 = seL4_GetMR(0); + let msg1 = seL4_GetMR(1); + let msg2 = seL4_GetMR(2); + let msg3 = seL4_GetMR(3); + let scno = SyscallId::Send as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::Send)), + "{r0}" (dest), + "{r1}" (msgInfo.words[0]), + "{r2}" (msg0), "{r3}" (msg1), + "{r4}" (msg2), "{r5}" (msg3), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); +} + +macro_rules! opt_deref { + ($name:expr) => { + if !$name.is_null() { + *$name + } else { + 0 + } + } +} + +macro_rules! opt_assign { + ($loc:expr, $val:expr) => { + if !$loc.is_null() { + *$loc = $val; + } + } +} + +#[inline(always)] +pub unsafe fn seL4_SendWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word, + ) { + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + let scno = SyscallId::Send as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::Send)), + "{r0}" (dest), + "{r1}" (msgInfo.words[0]), + "{r2}" (msg0), "{r3}" (msg1), + "{r4}" (msg2), "{r5}" (msg3), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_NBSend(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + let msg0 = seL4_GetMR(0); + let msg1 = seL4_GetMR(1); + let msg2 = seL4_GetMR(2); + let msg3 = seL4_GetMR(3); + let scno = SyscallId::NBSend as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::NBSend)), + "{r0}" (dest), + "{r1}" (msgInfo.words[0]), + "{r2}" (msg0), "{r3}" (msg1), + "{r4}" (msg2), "{r5}" (msg3), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); +} +#[inline(always)] +pub unsafe fn seL4_NBSendWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word, + ) { + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + let scno = SyscallId::NBSend as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::NBSend)), + "{r0}" (dest), + "{r1}" (msgInfo.words[0]), + "{r2}" (msg0), "{r3}" (msg1), + "{r4}" (msg2), "{r5}" (msg3), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_Reply(msgInfo: seL4_MessageInfo) { + let msg0 = seL4_GetMR(0); + let msg1 = seL4_GetMR(1); + let msg2 = seL4_GetMR(2); + let msg3 = seL4_GetMR(3); + let scno = SyscallId::Reply as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::Reply)), + "{r1}" (msgInfo.words[0]), + "{r2}" (msg0), "{r3}" (msg1), + "{r4}" (msg2), "{r5}" (msg3), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); +} +#[inline(always)] +pub unsafe fn seL4_ReplyWithMRs(msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word, + ) { + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + let scno = SyscallId::Reply as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::Reply)), + "{r1}" (msgInfo.words[0]), + "{r2}" (msg0), "{r3}" (msg1), + "{r4}" (msg2), "{r5}" (msg3), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); +} + + +#[inline(always)] +pub unsafe fn seL4_Signal(dest: seL4_CPtr) { + let info = seL4_MessageInfo::new(0, 0, 0, 0).words[0]; + let scno = SyscallId::Send as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::Send)), + "{r0}" (dest), + "{r1}" (info), + "{r7}" (scno) + : "memory", "r0", "r1", "r7" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_Recv(mut src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info = seL4_MessageInfo { words: [0] }; + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + let scno = SyscallId::Recv as seL4_Word; + asm!("swi $6" + : "={r0}" (src), + "={r1}" (info.words[0]), + "={r2}" (msg0), "={r3}" (msg1), + "={r4}" (msg2), "={r5}" (msg3) + : "i" (swinum!(SyscallId::Recv)), + "{r7}" (scno) + : "memory" + : "volatile"); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, src); + + return info +} + +#[inline(always)] +pub unsafe fn seL4_NBRecv(mut src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let info: seL4_Word; + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + let scno = SyscallId::NBRecv as seL4_Word; + asm!("swi $6" + : "={r0}" (src) + "={r1}" (info), + "={r2}" (msg0), "={r3}" (msg1), + "={r4}" (msg2), "={r5}" (msg3) + : "i" (swinum!(SyscallId::NBRecv)), + "{r0}" (src), + "{r7}" (scno) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, src); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_Call(dest: seL4_CPtr, mut msgInfo: seL4_MessageInfo) -> seL4_MessageInfo { + let mut msg0 = seL4_GetMR(0); + let mut msg1 = seL4_GetMR(1); + let mut msg2 = seL4_GetMR(2); + let mut msg3 = seL4_GetMR(3); + + let scno = SyscallId::Call as seL4_Word; + asm!("swi $5" + : "={r1}" (msgInfo.words[0]), + "={r2}" (msg0), "={r3}" (msg1), + "={r4}" (msg2), "={r5}" (msg3) + : "i" (swinum!(SyscallId::Call)), + "{r7}" (scno), + "{r0}" (dest) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + msgInfo +} + +#[inline(always)] +pub unsafe fn seL4_CallWithMRs(dest: seL4_CPtr, mut msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word, + ) -> seL4_MessageInfo { + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + let scno = SyscallId::Call as seL4_Word; + asm!("swi $5" + : "={r1}" (msgInfo.words[0]), + "={r2}" (msg0), "={r3}" (msg1), + "={r4}" (msg2), "={r5}" (msg3) + : "i" (swinum!(SyscallId::Call)), + "{r7}" (scno), + "{r0}" (dest), + "{r1}" (msgInfo.words[0]) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + msgInfo +} + +#[inline(always)] +pub unsafe fn seL4_ReplyRecv(mut src: seL4_CPtr, mut msgInfo: seL4_MessageInfo, + sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut msg0 = seL4_GetMR(0); + let mut msg1 = seL4_GetMR(1); + let mut msg2 = seL4_GetMR(2); + let mut msg3 = seL4_GetMR(3); + + let scno = SyscallId::ReplyRecv as seL4_Word; + asm!("swi $6" + : "={r0}" (src), "={r1}" (msgInfo.words[0]), + "={r2}" (msg0), "={r3}" (msg1), + "={r4}" (msg2), "={r5}" (msg3) + : "i" (swinum!(SyscallId::ReplyRecv)), + "{r7}" (scno), + "{r0}" (src), + "{r1}" (msgInfo.words[0]) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, src); + msgInfo +} + +#[inline(always)] +pub unsafe fn seL4_ReplyRecvWithMRs(mut src: seL4_CPtr, mut msgInfo: seL4_MessageInfo, + sender: *mut seL4_Word, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word, + ) -> seL4_MessageInfo { + let mut msg0 = ::core::mem::uninitialized(); + let mut msg1 = ::core::mem::uninitialized(); + let mut msg2 = ::core::mem::uninitialized(); + let mut msg3 = ::core::mem::uninitialized(); + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + let scno = SyscallId::ReplyRecv as seL4_Word; + asm!("swi $6" + : "={r0}" (src), "={r1}" (msgInfo.words[0]), + "={r2}" (msg0), "={r3}" (msg1), + "={r4}" (msg2), "={r5}" (msg3) + : "i" (swinum!(SyscallId::ReplyRecv)), + "{r7}" (scno), + "{r0}" (src), + "{r1}" (msgInfo.words[0]) + : "memory", "r0", "r1", "r2", "r3", "r4", "r5", "r7" + : "volatile"); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, src); + msgInfo +} + +#[inline(always)] +pub unsafe fn seL4_Yield() { + let scno = SyscallId::Yield as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::Yield)), + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugPutChar(c: u8) { + let scno = SyscallId::DebugPutChar as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::DebugPutChar)), + "{r0}" (c) + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugHalt() { + let scno = SyscallId::DebugHalt as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::DebugHalt)), + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugSnapshot() { + let scno = SyscallId::DebugSnapshot as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::DebugSnapshot)), + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugCapIdentify(mut cap: seL4_CPtr) -> u32 { + let scno = SyscallId::DebugCapIdentify as seL4_Word; + asm!("swi $1" + : "={r0}" (cap) + : "i" (swinum!(SyscallId::DebugCapIdentify)), + "{r0}" (cap), + "{r7}" (scno) + : "memory" + : "volatile"); + cap as _ +} + +// Note: name MUST be NUL-terminated. +#[inline(always)] +pub unsafe fn seL4_DebugNameThread(tcb: seL4_CPtr, name: &[u8]) { + core::ptr::copy_nonoverlapping(name.as_ptr() as *mut u8, (&mut (*seL4_GetIPCBuffer()).msg).as_mut_ptr() as *mut u8,name.len()); + let scno = SyscallId::DebugNameThread as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::DebugNameThread)), + "{r0}" (tcb), + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_DANGEROUS_CODE_INJECTION")] +pub unsafe fn seL4_DebugRun(userfn: extern fn(*mut u8), userarg: *mut u8) { + let userfnptr = userfn as *mut (); + let scno = SyscallId::DebugRun as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::DebugRun)), + "{r0}" (userfnptr), + "{r1}" (userarg), + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkResetLog() { + let scno = SyscallId::BenchmarkResetLog as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::BenchmarkResetLog)), + "{r7}" (scno) + : "memory" + : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkDumpLog(mut start: seL4_Word, size: seL4_Word) -> u32 { + let scno = SyscallId::BenchmarkDumpLog as seL4_Word; + asm!("swi $1" + : "={r0}" (start) + : "i" (swinum!(SyscallId::BenchmarkDumpLog)), + "{r0}" (start), + "{r1}" (size), + "{r7}" (scno) + : "memory" + : "volatile"); + start +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkLogSize() -> u32 { + let mut size = 0; + let scno = SyscallId::BenchmarkLogSize as seL4_Word; + asm!("swi $1" + : "={r0}" (size) + : "i" (swinum!(SyscallId::BenchmarkLogSize)), + "{r7}" (scno) + : "memory" + : "volatile"); + size +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkFinalizeLog() { + let scno = SyscallId::BenchmarkFinalizeLog as seL4_Word; + asm!("swi $0" + : + : "i" (swinum!(SyscallId::BenchmarkFinalizeLog)), + "{r7}" (scno) + : "memory" + : "volatile"); +} diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/arm_object_types.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/arm_object_types.rs new file mode 100644 index 0000000..62d0dff --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/arm_object_types.rs @@ -0,0 +1,15 @@ +/* Copyright (c) 2016 The Robigalia Project Developers + * Licensed under the Apache License, Version 2.0 + * or the MIT + * license , + * at your option. All files in the project carrying such + * notice may not be copied, modified, or distributed except + * according to those terms. + */ + seL4_ARM_SmallPageObject, + seL4_ARM_LargePageObject, + seL4_ARM_SectionObject, + seL4_ARM_SuperSectionObject, + seL4_ARM_PageTableObject, + seL4_ARM_PageDirectoryObject, diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/riscv.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/riscv.rs new file mode 100644 index 0000000..6d5ff09 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/riscv.rs @@ -0,0 +1,949 @@ +/* + * Copyright 2015, Killian Coddington + * Copyright 2014, NICTA + * + * This software may be distributed and modified according to the terms of + * the BSD 2-Clause license. Note that NO WARRANTY is provided. + * See "LICENSE_BSD2.txt" for details. + * + * @TAG(NICTA_BSD) + */ + +pub const seL4_WordBits: usize = 32; +pub const seL4_WordSizeBits: usize = 2; +pub const seL4_PageBits: usize = 12; +pub const seL4_SlotBits: usize = 4; +pub const seL4_TCBBits: usize = 9; +pub const seL4_EndpointBits: usize = 4; +pub const seL4_PageTableEntryBits: usize = 2; +pub const seL4_PageTableIndexBits: usize = 10; +pub const seL4_LargePageBits: usize = 22; +pub const seL4_PageTableBits: usize = 12; +pub const seL4_VSpaceBits: usize = seL4_PageTableBits; +pub const seL4_NumASIDPoolBits: usize = 5; +pub const seL4_ASIDPoolIndexBits: usize = 4; +pub const seL4_ASIDPoolBits: usize = 12; + +pub type seL4_RISCV_Page = seL4_CPtr; +pub type seL4_RISCV_PageTable = seL4_CPtr; +pub type seL4_RISCV_ASIDControl = seL4_CPtr; +pub type seL4_RISCV_ASIDPool = seL4_CPtr; + +error_types!(u32); + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct seL4_UserContext { + pub pc: seL4_Word, + pub ra: seL4_Word, + pub sp: seL4_Word, + pub gp: seL4_Word, + pub s0: seL4_Word, + pub s1: seL4_Word, + pub s2: seL4_Word, + pub s3: seL4_Word, + pub s4: seL4_Word, + pub s5: seL4_Word, + pub s6: seL4_Word, + pub s7: seL4_Word, + pub s8: seL4_Word, + pub s9: seL4_Word, + pub s10: seL4_Word, + pub s11: seL4_Word, + + pub a0: seL4_Word, + pub a1: seL4_Word, + pub a2: seL4_Word, + pub a3: seL4_Word, + pub a4: seL4_Word, + pub a5: seL4_Word, + pub a6: seL4_Word, + pub a7: seL4_Word, + + pub t0: seL4_Word, + pub t1: seL4_Word, + pub t2: seL4_Word, + pub t3: seL4_Word, + pub t4: seL4_Word, + pub t5: seL4_Word, + pub t6: seL4_Word, + + pub tp: seL4_Word, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_RISCV_VMAttributes { + ExecuteNever = 0x1, + Default_VMAttributes = 0, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_ObjectType { + seL4_UntypedObject = 0, + seL4_TCBObject, + seL4_EndpointObject, + seL4_NotificationObject, + seL4_CapTableObject, + #[cfg(feature = "CONFIG_KERNEL_MCS")] + seL4_SchedContextObject, + #[cfg(feature = "CONFIG_KERNEL_MCS")] + seL4_ReplyObject, +} + +#[inline(always)] +pub unsafe fn seL4_GetIPCBuffer() -> *mut seL4_IPCBuffer { + // Magic external symbol setup by runtime once TLS is primed + enum c_void {} + extern "C" { + static __sel4_ipc_buffer: *const c_void; + } + __sel4_ipc_buffer as *mut seL4_IPCBuffer +} + +#[inline(always)] +pub unsafe fn seL4_GetMR(regnum: usize) -> seL4_Word { + (*seL4_GetIPCBuffer()).msg[regnum] +} + +#[inline(always)] +pub unsafe fn seL4_SetMR(regnum: usize, value: seL4_Word) { + (*seL4_GetIPCBuffer()).msg[regnum] = value; +} + +#[inline(always)] +pub unsafe fn seL4_GetUserData() -> seL4_Word { + (*seL4_GetIPCBuffer()).userData +} + +#[inline(always)] +pub unsafe fn seL4_SetUserData(data: seL4_Word) { + (*seL4_GetIPCBuffer()).userData = data; +} + +#[inline(always)] +pub unsafe fn seL4_GetBadge(index: usize) -> seL4_Word { + (*seL4_GetIPCBuffer()).caps_or_badges[index] +} + +#[inline(always)] +pub unsafe fn seL4_GetCap(index: usize) -> seL4_CPtr { + (*seL4_GetIPCBuffer()).caps_or_badges[index] as seL4_CPtr +} + +#[inline(always)] +pub unsafe fn seL4_SetCap(index: usize, cptr: seL4_CPtr) { + (*seL4_GetIPCBuffer()).caps_or_badges[index] = cptr as seL4_Word; +} + +#[inline(always)] +pub unsafe fn seL4_GetCapReceivePath( + receiveCNode: *mut seL4_CPtr, + receiveIndex: *mut seL4_CPtr, + receiveDepth: *mut seL4_Word, +) { + let ipcbuffer = seL4_GetIPCBuffer(); + if !receiveCNode.is_null() { + *receiveCNode = (*ipcbuffer).receiveCNode; + } + if !receiveIndex.is_null() { + *receiveIndex = (*ipcbuffer).receiveIndex; + } + if !receiveDepth.is_null() { + *receiveDepth = (*ipcbuffer).receiveDepth; + } +} + +#[inline(always)] +pub unsafe fn seL4_SetCapReceivePath( + receiveCNode: seL4_CPtr, + receiveIndex: seL4_CPtr, + receiveDepth: seL4_Word, +) { + let ipcbuffer = seL4_GetIPCBuffer(); + (*ipcbuffer).receiveCNode = receiveCNode; + (*ipcbuffer).receiveIndex = receiveIndex; + (*ipcbuffer).receiveDepth = receiveDepth; +} + +macro_rules! swinum { + ($val:expr) => { + $val as seL4_Word + }; +} + +#[inline(always)] +pub unsafe fn seL4_Send(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + asm!("ecall", + in("a7") swinum!(SyscallId::Send), + in("a0") dest, + in("a1") msgInfo.words[0], + in("a2") seL4_GetMR(0), + in("a3") seL4_GetMR(1), + in("a4") seL4_GetMR(2), + in("a5") seL4_GetMR(3), + ); +} + +macro_rules! opt_assign { + ($loc:expr, $val:expr) => { + if !$loc.is_null() { + *$loc = $val; + } + }; +} + +#[inline(always)] +pub unsafe fn seL4_SendWithMRs( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) { + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::Send), + in("a0") dest, + in("a1") msgInfo.words[0], + in("a2") msg0, + in("a3") msg1, + in("a4") msg2, + in("a5") msg3, + ); +} + +#[inline(always)] +pub unsafe fn seL4_NBSend(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + asm!("ecall", + in("a7") swinum!(SyscallId::NBSend), + in("a0") dest, + in("a1") msgInfo.words[0], + in("a2") seL4_GetMR(0), + in("a3") seL4_GetMR(1), + in("a4") seL4_GetMR(2), + in("a5") seL4_GetMR(3), + ); +} + +#[inline(always)] +pub unsafe fn seL4_NBSendWithMRs( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) { + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::NBSend), + in("a0") dest, + in("a1") msgInfo.words[0], + in("a2") msg0, + in("a3") msg1, + in("a4") msg2, + in("a5") msg3, + ); +} + +#[cfg(not(feature = "CONFIG_KERNEL_MCS"))] +#[inline(always)] +pub unsafe fn seL4_Reply(msgInfo: seL4_MessageInfo) { + asm!("ecall", + in("a7") swinum!(SyscallId::Reply), + in("a1") msgInfo.words[0], + in("a2") seL4_GetMR(0), + in("a3") seL4_GetMR(1), + in("a4") seL4_GetMR(2), + in("a5") seL4_GetMR(3), + ); +} + +#[cfg(not(feature = "CONFIG_KERNEL_MCS"))] +#[inline(always)] +pub unsafe fn seL4_ReplyWithMRs( + msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) { + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::Reply), + in("a1") msgInfo.words[0], + in("a2") msg0, + in("a3") msg1, + in("a4") msg2, + in("a5") msg3, + ); +} + +#[inline(always)] +pub unsafe fn seL4_Signal(dest: seL4_CPtr) { + let info = seL4_MessageInfo::new(0, 0, 0, 0).words[0]; + asm!("ecall", + in("a7") swinum!(SyscallId::Send), + in("a0") dest, + in("a1") info, + ); +} + +#[inline(always)] +pub unsafe fn seL4_Recv(mut src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + asm!("ecall", + in("a7") swinum!(SyscallId::Recv), + out("a0") src, + out("a1") info, + out("a2") msg0, + out("a3") msg1, + out("a4") msg2, + out("a5") msg3, + ); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, src); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_NBRecv(mut src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + asm!("ecall", + in("a7") swinum!(SyscallId::NBRecv), + inout("a0") src, + out("a1") info, + out("a2") msg0, + out("a3") msg1, + out("a4") msg2, + out("a5") msg3, + ); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, src); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_Call(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut msg0 = seL4_GetMR(0); + let mut msg1 = seL4_GetMR(1); + let mut msg2 = seL4_GetMR(2); + let mut msg3 = seL4_GetMR(3); + + asm!("ecall", + in("a7") swinum!(SyscallId::Call), + in("a0") dest, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_CallWithMRs( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::Call), + in("a0") dest, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_ReplyRecv( + mut src: seL4_CPtr, + msgInfo: seL4_MessageInfo, + sender: *mut seL4_Word, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut msg0 = seL4_GetMR(0); + let mut msg1 = seL4_GetMR(1); + let mut msg2 = seL4_GetMR(2); + let mut msg3 = seL4_GetMR(3); + + asm!("ecall", + in("a7") swinum!(SyscallId::ReplyRecv), + inout("a0") src, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, src); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_ReplyRecvWithMRs( + src: seL4_CPtr, + msgInfo: seL4_MessageInfo, + sender: *mut seL4_Word, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::ReplyRecv), + inout("a0") src => badge, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_NBSendRecv( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + src: seL4_CPtr, + sender: *mut seL4_Word, + reply: seL4_CPtr, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = seL4_GetMR(0); + let mut msg1 = seL4_GetMR(1); + let mut msg2 = seL4_GetMR(2); + let mut msg3 = seL4_GetMR(3); + + asm!("ecall", + in("a7") swinum!(SyscallId::NBSendRecv), + inout("a0") src => badge, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + in("a6") reply, + in("t0") dest, + ); + + /* Write the message back out to memory. */ + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_NBSendRecvWithMRs( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + src: seL4_CPtr, + sender: *mut seL4_Word, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, + reply: seL4_CPtr, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::NBSendRecv), + inout("a0") src => badge, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + in("a6") reply, + in("t0") dest, + ); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_NBSendWait( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + src: seL4_CPtr, + sender: *mut seL4_Word, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = seL4_GetMR(0); + let mut msg1 = seL4_GetMR(1); + let mut msg2 = seL4_GetMR(2); + let mut msg3 = seL4_GetMR(3); + + asm!("ecall", + in("a7") swinum!(SyscallId::NBSendWait), + inout("a0") src => badge, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + in("a6") dest, + in("t0") 0, // XXX dest + ); + + /* Write the message back out to memory. */ + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_NBSendWaitWithMRs( + dest: seL4_CPtr, + msgInfo: seL4_MessageInfo, + src: seL4_CPtr, + sender: *mut seL4_Word, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + if !mr0.is_null() && msgInfo.get_length() > 0 { + msg0 = *mr0; + } + if !mr1.is_null() && msgInfo.get_length() > 1 { + msg1 = *mr1; + } + if !mr2.is_null() && msgInfo.get_length() > 2 { + msg2 = *mr2; + } + if !mr3.is_null() && msgInfo.get_length() > 3 { + msg3 = *mr3; + } + + asm!("ecall", + in("a7") swinum!(SyscallId::NBSendRecv), + inout("a0") src => badge, + inout("a1") msgInfo.words[0] => info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + in("a6") dest, + in("t0") 0, // XXX does this dtrt + ); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[inline(always)] +pub unsafe fn seL4_Yield() { + asm!("ecall", + in("a7") swinum!(SyscallId::Yield), + options(nomem, nostack), + ); +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_Wait(src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + asm!("ecall", + in("a7") swinum!(SyscallId::Wait), + inout("a0") src => badge, + out("a1") info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_WaitWithMRs( + src: seL4_CPtr, + sender: *mut seL4_Word, + mr0: *mut seL4_Word, + mr1: *mut seL4_Word, + mr2: *mut seL4_Word, + mr3: *mut seL4_Word, +) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + asm!("ecall", + in("a7") swinum!(SyscallId::Wait), + inout("a0") src => badge, + out("a1") info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_KERNEL_MCS")] +#[inline(always)] +pub unsafe fn seL4_NBWait(src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_Word; + let mut badge: seL4_Word; + let mut msg0 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg1 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg2 = ::core::mem::MaybeUninit::uninit().assume_init(); + let mut msg3 = ::core::mem::MaybeUninit::uninit().assume_init(); + + asm!("ecall", + in("a7") swinum!(SyscallId::NBWait), + inout("a0") src => badge, + out("a1") info, + inout("a2") msg0, + inout("a3") msg1, + inout("a4") msg2, + inout("a5") msg3, + ); + + seL4_SetMR(0, msg0); + seL4_SetMR(1, msg1); + seL4_SetMR(2, msg2); + seL4_SetMR(3, msg3); + + opt_assign!(sender, badge); + + seL4_MessageInfo { words: [info] } +} + +#[cfg(feature = "CONFIG_PRINTING")] +#[inline(always)] +pub unsafe fn seL4_DebugPutChar(c: u8) { + asm!("ecall", + in("a7") swinum!(SyscallId::DebugPutChar), + in("a0") c, + options(nostack), + ); +} + +#[cfg(feature = "CONFIG_PRINTING")] +#[inline(always)] +pub unsafe fn seL4_DebugDumpScheduler() { + asm!("ecall", + in("a7") swinum!(SyscallId::DebugDumpScheduler), + options(nomem, nostack), + ); +} + +#[cfg(feature = "CONFIG_DEBUG_BUILD")] +#[inline(always)] +pub unsafe fn seL4_DebugHalt() { + asm!("ecall", + in("a7") swinum!(SyscallId::DebugHalt), + options(nomem, nostack), + ); +} + +#[cfg(feature = "CONFIG_DEBUG_BUILD")] +#[inline(always)] +pub unsafe fn seL4_DebugSnapshot() { + asm!("ecall", + in("a7") swinum!(SyscallId::DebugSnapshot), + options(nomem, nostack), + ); +} + +#[cfg(feature = "CONFIG_DEBUG_BUILD")] +#[inline(always)] +pub unsafe fn seL4_DebugCapIdentify(mut cap: seL4_CPtr) -> u32 { + asm!("ecall", + in("a7") swinum!(SyscallId::DebugCapIdentify), + inout("a0") cap, + options(nomem, nostack), + ); + cap as _ +} + +// Note: name MUST be NUL-terminated. +#[cfg(feature = "CONFIG_DEBUG_BUILD")] +#[inline(always)] +pub unsafe fn seL4_DebugNameThread(tcb: seL4_CPtr, name: &[u8]) { + core::ptr::copy_nonoverlapping( + name.as_ptr() as *mut u8, + (&mut (*seL4_GetIPCBuffer()).msg).as_mut_ptr() as *mut u8, + name.len(), + ); + asm!("ecall", + in("a7") swinum!(SyscallId::DebugNameThread), + in("a0") tcb, + ); +} + +#[cfg(feature = "CONFIG_DANGEROUS_CODE_INJECTION")] +#[inline(always)] +pub unsafe fn seL4_DebugRun(userfn: extern "C" fn(*mut u8), userarg: *mut u8) { + let userfnptr = userfn as *mut (); + asm!("ecall", + in("a7") swinum!(SyscallId::DebugRun), + in("a0") userfnptr, + in("a1") userarg, + ); +} + +#[cfg(feature = "CONFIG_ENABLE_BENCHMARKS")] +#[inline(always)] +pub unsafe fn seL4_BenchmarkResetLog() { + asm!("ecall", + in("a7") swinum!(SyscallId::BenchmarkResetLog), + options(nomem, nostack), + ); +} + +#[cfg(feature = "CONFIG_ENABLE_BENCHMARKS")] +#[inline(always)] +pub unsafe fn seL4_BenchmarkFinalizeLog() { + asm!("ecall", + in("a7") swinum!(SyscallId::BenchmarkFinalizeLog), + options(nomem, nostack), + ); +} + +// TODO(sleffler): seL4_BenchmarkSetLogBuffer +// TODO(sleffler): seL4_BenchmarkNullSyscall +// TODO(sleffler): seL4_BenchmarkFlushCaches +// TODO(sleffler): seL4_BenchmarkFlushL1Caches + +#[cfg(feature = "CONFIG_SET_TLS_BASE_SELF")] +pub unsafe fn seL4_SetTLSBase(tls_base: seL4_Word) { + let info: seL4_Word = 0; // XXX does this dtrt? + asm!("ecall", + in("a7") swinum!(SyscallId::SetTLSBase), + in("a0") tls_base, + in("a1") info, + ); +} diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/riscv_object_types.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/riscv_object_types.rs new file mode 100644 index 0000000..c01bab3 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/riscv_object_types.rs @@ -0,0 +1,10 @@ +/* Copyright (c) 2016 The Robigalia Project Developers + * Licensed under the Apache License, Version 2.0 + * or the MIT + * license , + * at your option. All files in the project carrying such + * notice may not be copied, modified, or distributed except + * according to those terms. + */ + seL4_RISCV_PageTableObject, diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/x86.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/x86.rs new file mode 100644 index 0000000..fb9619c --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/x86.rs @@ -0,0 +1,645 @@ +/* + * Copyright 2015, Corey Richardson + * Copyright 2014, NICTA + * + * This software may be distributed and modified according to the terms of + * the BSD 2-Clause license. Note that NO WARRANTY is provided. + * See "LICENSE_BSD2.txt" for details. + * + * @TAG(NICTA_BSD) + */ + +use core::mem::uninitialized; + +pub const seL4_WordBits: usize = 32; +pub const seL4_PageBits: usize = 12; +pub const seL4_SlotBits: usize = 4; +pub const seL4_TCBBits: usize = 11; +pub const seL4_EndpointBits: usize = 4; +pub const seL4_NotificationBits: usize = 4; +pub const seL4_PageTableBits: usize = 12; +pub const seL4_PageDirBits: usize = 12; +pub const seL4_IOPageTableBits: usize = 12; +pub const seL4_ASIDPoolBits: usize = 12; + +pub const seL4_HugePageBits: usize = 30; + +pub const seL4_VCPUBits: usize = 14; +pub const seL4_EPTPML4Bits: usize = 12; +pub const seL4_EPTPDPTBits: usize = 12; +pub const seL4_EPTPDBits: usize = 12; +pub const seL4_EPTPTBits: usize = 12; + +pub const seL4_PDPTBits: usize = 5; +pub const seL4_LargePageBits: usize = 21; + +pub const seL4_MinUntypedBits: usize = 4; +pub const seL4_MaxUntypedBits: usize = 29; + +pub type seL4_X86_ASIDControl = seL4_CPtr; +pub type seL4_X86_ASIDPool = seL4_CPtr; +pub type seL4_X86_IOSpace = seL4_CPtr; +pub type seL4_X86_IOPort = seL4_CPtr; +pub type seL4_X86_Page = seL4_CPtr; +pub type seL4_X86_PageDirectory = seL4_CPtr; +pub type seL4_X86_PageTable = seL4_CPtr; +pub type seL4_X86_IOPageTable = seL4_CPtr; + +error_types!(u32); + +pub const Default_VMAttributes: usize = 0; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_ObjectType { + seL4_UntypedObject = 0, + seL4_TCBObject, + seL4_EndpointObject, + seL4_NotificationObject, + seL4_CapTableObject, + seL4_IA32_PDPTObject, + seL4_X86_4K, + seL4_X86_LargePageObject, + seL4_X86_PageTableObject, + seL4_X86_PageDirectoryObject, + seL4_X86_IOPageTableObject, + seL4_X86_VCPUObject, + seL4_X86_EPTPML4Object, + seL4_X86_EPTPDPTObject, + seL4_X86_EPTPDObject, + seL4_X86_EPTPTObject, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_X86_VMAttributes { + WriteBack = 0, + WriteThrough = 1, + CacheDisabled = 2, + Uncacheable = 3, + WriteCombining = 4, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct seL4_UserContext { + pub eip: seL4_Word, + pub esp: seL4_Word, + pub eflags: seL4_Word, + pub eax: seL4_Word, + pub ebx: seL4_Word, + pub ecx: seL4_Word, + pub edx: seL4_Word, + pub esi: seL4_Word, + pub edi: seL4_Word, + pub ebp: seL4_Word, + pub tls_base: seL4_Word, + pub fs: seL4_Word, + pub gs: seL4_Word, +} + +#[inline(always)] +pub unsafe fn seL4_GetMR(regnum: isize) -> seL4_Word { + let mr; + asm!("movl %fs:4(,$1,0x4), $0" : "=r"(mr) : "r"(regnum) : : "volatile"); + mr +} + +#[inline(always)] +pub unsafe fn seL4_SetMR(regnum: isize, value: seL4_Word) { + asm!("movl $0, %fs:4(,$1,0x4)" : : "r"(value), "r"(regnum) : "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_GetUserData() -> seL4_Word { + let data; + asm!("movl %fs:484, $0" : "=r"(data) : : : "volatile"); + data +} + +#[inline(always)] +pub unsafe fn seL4_GetIPCBuffer() -> *mut seL4_IPCBuffer { + seL4_GetUserData() as isize as *mut seL4_IPCBuffer +} + +#[inline(always)] +pub unsafe fn seL4_SetUserData(data: seL4_Word) { + asm!("movl $0, %fs:484" : : "r"(data) : "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_GetBadge(index: isize) -> seL4_CapData { + let mut badge: seL4_CapData = uninitialized(); + asm!("movl %fs:488(,$1,0x4), $0" : "=r"(badge.words[0]) : "r"(index) : : "volatile"); + badge +} + +#[inline(always)] +pub unsafe fn seL4_GetCap(index: isize) -> seL4_CPtr { + let cptr; + asm!("movl %fs:488(,$1,0x4), $0" : "=r"(cptr) : "r"(index) : : "volatile"); + cptr +} + +#[inline(always)] +pub unsafe fn seL4_SetCap(index: isize, cptr: seL4_CPtr) { + asm!("movl $0, %fs:488(,$1,0x4)" : : "r"(cptr), "r"(index) : "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_GetCapReceivePath(receiveCNode: *mut seL4_CPtr, + receiveIndex: *mut seL4_CPtr, + receiveDepth: *mut seL4_Word) { + if !receiveCNode.is_null() { + asm!("movl %fs:500, $0" : "=r"(*receiveCNode) : : : "volatile"); + } + + if !receiveIndex.is_null() { + asm!("movl %fs:504, $0" : "=r"(*receiveIndex) : : : "volatile"); + } + + if !receiveDepth.is_null() { + asm!("movl %fs:508, $0" : "=r"(*receiveDepth) : : : "volatile"); + } +} + +#[inline(always)] +pub unsafe fn seL4_SetCapReceivePath(receiveCNode: seL4_CPtr, + receiveIndex: seL4_CPtr, + receiveDepth: seL4_Word) { + asm!("movl $0, %fs:500" : : "r"(receiveCNode) : "memory" : "volatile"); + asm!("movl $0, %fs:504" : : "r"(receiveIndex) : "memory" : "volatile"); + asm!("movl $0, %fs:508" : : "r"(receiveDepth) : "memory" : "volatile"); +} + +#[inline(always)] +unsafe fn x86_sys_send(sys: seL4_Word, mut dest: seL4_Word, info: seL4_Word, mr1: seL4_Word, mr2: seL4_Word) { + asm!("pushl %ebp + pushl %ebx + movl %ecx, %ebp + movl %esp, %ecx + movl %edx, %ebx + leal 1f, %edx + 1: + sysenter + popl %ebx + popl %ebp" + : "+{dx}" (dest) + : "{ax}" (sys), + "{dx}" (dest), + "{si}" (info), + "{di}" (mr1), + "{cx}" (mr2) + : "%edx" + : "volatile"); +} + +#[inline(always)] +unsafe fn x86_sys_reply(sys: seL4_Word, info: seL4_Word, mr1: seL4_Word, mr2: seL4_Word) { + asm!("pushl %ebp + pushl %ebx + movl %ecx, %ebp + movl %esp, %ecx + leal 1f, %edx + 1: + sysenter + popl %ebx + popl %ebp" + : + : "{ax}" (sys), + "{si}" (info), + "{di}" (mr1), + "{cx}" (mr2) + : "%edx" + : "volatile"); +} + +#[inline(always)] +unsafe fn x86_sys_send_null(sys: seL4_Word, mut dest: seL4_Word, info: seL4_Word) { + asm!("pushl %ebp + pushl %ebx + movl %esp, %ecx + movl %edx, %ebx + leal 1f, %edx + 1: + sysenter + popl %ebx + popl %ebp" + : "={dx}" (dest) + : "{ax}" (sys), + "{si}" (info), + "{dx}" (dest) + : "%ecx" + : "volatile"); +} + +#[inline(always)] +unsafe fn x86_sys_recv(sys: seL4_Word, src: seL4_Word, out_badge: *mut seL4_Word, out_info: *mut seL4_Word, out_mr1: *mut seL4_Word, out_mr2: *mut seL4_Word) { + asm!("pushl %ebp + pushl %ebx + movl %esx, %ecp + movl %edp, %ebx + leal 1f, %edx + 1: + sysenter + movl %ebx, %edx + popl %ebx + movl %ebp, %ecx + popl %ebp" + : "={si}" (*out_info) + "={di}" (*out_mr1), + "={cx}" (*out_mr2), + "={dx}" (*out_badge) + : "{ax}" (sys), + "{dx}" (src) + : "memory" + : "volatile"); +} + +#[inline(always)] +unsafe fn x86_sys_send_recv(sys: seL4_Word, dest: seL4_Word, out_badge: *mut seL4_Word, info: seL4_Word, out_info: *mut seL4_Word, in_out_mr1: *mut seL4_Word, in_out_mr2: *mut seL4_Word) { + asm!("pushl %ebp + pushl %ebx + movl %ecx, %ebp + movl %esp, %ecx + movl %edx, %ebx + leal 1f, %edx + 1: + sysenter + movl %ebx, %edx + popl %ebx + movl %ebp, %ecx + popl %ebp" + : "={si}" (*out_info) + "={di}" (*in_out_mr1), + "={cx}" (*in_out_mr2), + "={dx}" (*out_badge) + : "{ax}" (sys), + "{si}" (info), + "{di}" (*in_out_mr1), + "{cx}" (*in_out_mr2), + "{dx}" (dest) + : "memory" + : "volatile"); +} + +#[inline(always)] +unsafe fn x86_sys_null(sys: seL4_Word) { + asm!("pushl %ebp + pushl %ebx + movl %esp, %ecx + leal 1f, %edx + 1: + sysenter + popl %ebx + popl %ebp" + : + : "{ax}" (sys) + : "%ecx", "%edx" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_Send(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + x86_sys_send(SyscallId::Send as seL4_Word, dest, msgInfo.words[0], seL4_GetMR(0), seL4_GetMR(1)); +} + +macro_rules! opt_deref { + ($name:expr) => { + if !$name.is_null() { + *$name + } else { + 0 + } + } +} + +macro_rules! opt_assign { + ($loc:expr, $val:expr) => { + if !$loc.is_null() { + *$loc = $val; + } + } +} + +#[inline(always)] +pub unsafe fn seL4_SendWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word) { + x86_sys_send(SyscallId::Send as seL4_Word, dest, msgInfo.words[0], + if mr0.is_null() { 0 } else { *mr0 }, + if mr1.is_null() { 0 } else { *mr1 }); +} + +#[inline(always)] +pub unsafe fn seL4_NBSend(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + x86_sys_send(SyscallId::NBSend as seL4_Word, dest, msgInfo.words[0], seL4_GetMR(0), seL4_GetMR(1)); +} + +#[inline(always)] +pub unsafe fn seL4_NBSendWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word) { + x86_sys_send(SyscallId::NBSend as seL4_Word, dest, msgInfo.words[0], + if mr0.is_null() { 0 } else { *mr0 }, + if mr1.is_null() { 0 } else { *mr1 }); +} + +#[inline(always)] +pub unsafe fn seL4_Reply(msgInfo: seL4_MessageInfo) { + x86_sys_reply(SyscallId::Reply as seL4_Word, msgInfo.words[0], seL4_GetMR(0), seL4_GetMR(1)); + +} +#[inline(always)] +pub unsafe fn seL4_ReplyWithMRs(msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word) { + x86_sys_reply(SyscallId::Reply as seL4_Word, msgInfo.words[0], + if mr0.is_null() { 0 } else { *mr0 }, + if mr1.is_null() { 0 } else { *mr1 }); +} + + +#[inline(always)] +pub unsafe fn seL4_Signal(dest: seL4_CPtr) { + x86_sys_send_null(SyscallId::Send as seL4_Word, dest, seL4_MessageInfo::new(0,0,0,0).words[0]); +} + +#[inline(always)] +pub unsafe fn seL4_Recv(src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0: seL4_Word = uninitialized(); + let mut mr1: seL4_Word = uninitialized(); + + x86_sys_recv(SyscallId::Recv as seL4_Word, src, &mut badge, &mut info.words[0], &mut mr0 as *mut _, &mut mr1); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_RecvWithMRs(src: seL4_CPtr, sender: *mut seL4_Word, + mr0: *mut seL4_Word, mr1: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut msg0: seL4_Word = uninitialized(); + let mut msg1: seL4_Word = uninitialized(); + + x86_sys_recv(SyscallId::Recv as seL4_Word, src, &mut badge, &mut info.words[0], &mut msg0, &mut msg1); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_NBRecv(src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0: seL4_Word = uninitialized(); + let mut mr1: seL4_Word = uninitialized(); + + x86_sys_recv(SyscallId::NBRecv as seL4_Word, src, &mut badge, &mut info.words[0], &mut mr0, &mut mr1); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_Call(mut dest: seL4_CPtr, msgInfo: seL4_MessageInfo) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut mr0 = seL4_GetMR(0); + let mut mr1 = seL4_GetMR(1); + + x86_sys_send_recv(SyscallId::Call as seL4_Word, dest, &mut dest, msgInfo.words[0], &mut info.words[0], &mut mr0, &mut mr1); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + + info +} + +#[inline(always)] +pub unsafe fn seL4_CallWithMRs(mut dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut msg0: seL4_Word = 0; + let mut msg1: seL4_Word = 0; + + if !mr0.is_null() { + if msgInfo.get_length() > 0 { + msg0 = *mr0; + } + } + if !mr1.is_null() { + if msgInfo.get_length() > 1 { + msg1 = *mr1; + } + } + + x86_sys_send_recv(SyscallId::Call as seL4_Word, dest, &mut dest, msgInfo.words[0], &mut info.words[0], &mut msg0, &mut msg1); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + + info +} + +#[inline(always)] +pub unsafe fn seL4_ReplyRecv(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0 = seL4_GetMR(0); + let mut mr1 = seL4_GetMR(1); + + x86_sys_send_recv(SyscallId::ReplyRecv as seL4_Word, dest, &mut badge, msgInfo.words[0], &mut info.words[0], &mut mr0, &mut mr1); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_ReplyWaitWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, sender: *mut seL4_Word, + mr0: *mut seL4_Word, mr1: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut msg0: seL4_Word = 0; + let mut msg1: seL4_Word = 0; + + if !mr0.is_null() { + if msgInfo.get_length() > 0 { + msg0 = *mr0; + } + } + if !mr1.is_null() { + if msgInfo.get_length() > 1 { + msg1 = *mr1; + } + } + + x86_sys_send_recv(SyscallId::ReplyRecv as seL4_Word, dest, &mut badge, msgInfo.words[0], &mut info.words[0], &mut msg0, &mut msg1); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_Yield() { + x86_sys_null(SyscallId::Yield as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_VMEnter(vcpu: seL4_CPtr, sender: *mut seL4_Word) -> seL4_Word { + let mut fault: seL4_Word = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0 = seL4_GetMR(0); + let mut mr1 = seL4_GetMR(1); + + x86_sys_send_recv(SyscallId::VMEnter as seL4_Word, vcpu, &mut badge, 0, &mut fault, &mut mr0, &mut mr1); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + + if fault == 0 && !sender.is_null() { + *sender = badge; + } + + fault +} + +#[inline(always)] +pub unsafe fn seL4_DebugPutChar(c: u8) { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + x86_sys_send_recv(SyscallId::DebugPutChar as seL4_Word, c as seL4_Word, &mut unused0, 0, &mut + unused1, &mut unused2, &mut unused3); +} + +#[inline(always)] +pub unsafe fn seL4_DebugHalt() { + x86_sys_null(SyscallId::DebugHalt as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugSnapshot() { + x86_sys_null(SyscallId::DebugSnapshot as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugCapIdentify(mut cap: seL4_CPtr) -> u32 { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + x86_sys_send_recv(SyscallId::DebugCapIdentify as seL4_Word, + cap, &mut cap, 0, &mut unused0, &mut unused1, &mut unused2); + cap as _ +} + +/// Note: name MUST be NUL-terminated. +#[inline(always)] +pub unsafe fn seL4_DebugNameThread(tcb: seL4_CPtr, name: &[u8]) { + core::ptr::copy_nonoverlapping(name.as_ptr() as *mut u8, (&mut (*seL4_GetIPCBuffer()).msg).as_mut_ptr() as *mut u8,name.len()); + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + x86_sys_send_recv(SyscallId::DebugNameThread as seL4_Word, tcb, &mut unused0, 0, &mut unused1, + &mut unused2, &mut unused3); +} + +#[inline(always)] +#[cfg(feature = "SEL4_DANGEROUS_CODE_INJECTION")] +pub unsafe fn seL4_DebugRun(userfn: extern fn(*mut u8), userarg: *mut u8) { + let userfnptr = userfn as *mut (); + x86_sys_send_null(SyscallId::DebugRun as seL4_Word, userfnptr as seL4_Word, userarg as seL4_Word); + asm!("" ::: "%edi", "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkResetLog() -> seL4_Word { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + + let mut ret = 0; + + x86_sys_send_recv(SyscallId::BenchmarkResetLog as seL4_Word, 0, &mut ret, 0, &mut unused0 as *mut _ as usize as seL4_Word, &mut unused1 as *mut _ as usize as seL4_Word, &mut unused2 as *mut _ as usize as seL4_Word); + + ret +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkFinalizeLog() { + x86_sys_null(SyscallId::BenchmarkFinalizeLog as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkSetLogBuffer(mut frame_cptr: seL4_Word) -> seL4_Word { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + x86_sys_send_recv(SyscallId::BenchmarkSetLogBuffer as seL4_Word, + frame_cptr, &mut cap, 0, &mut unused0 as *mut _ as usize as seL4_Word, &mut unused1 as *mut _ as usize as seL4_Word, &mut unused2 as *mut _ as usize as seL4_Word); + frame_cptr +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkNullSyscall() { + x86_sys_null(SyscallId::BenchmarkNullSyscall as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkFlushCaches() { + x86_sys_null(SyscallId::BenchmarkFlushCaches as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkGetThreadUtilization(tcb: seL4_Word) { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + x86_sys_send_recv(SyscallId::BenchmarkGetThreadUtilisation as seL4_Word, tcb, &mut unused0 as *mut _ as usize as seL4_Word, 0, + &mut unused1 as *mut _ as usize as seL4_Word, &mut unused2 as *mut _ as usize as seL4_Word, &mut unused3 as *mut _ as usize as seL4_Word); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkGetThreadUtilization(tcb: seL4_Word) { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + x86_sys_send_recv(SyscallId::BenchmarkResetThreadUtilisation as seL4_Word, tcb, &mut unused0 as *mut _ as usize as seL4_Word, 0, + &mut unused1 as *mut _ as usize as seL4_Word, &mut unused2 as *mut _ as usize as seL4_Word, &mut unused3 as *mut _ as usize as seL4_Word); +} diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/x86_64.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/x86_64.rs new file mode 100644 index 0000000..f6a1eaf --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/x86_64.rs @@ -0,0 +1,797 @@ + +use core::mem::uninitialized; + +pub const seL4_WordBits: usize = 64; +pub const seL4_PageBits: usize = 12; +pub const seL4_SlotBits: usize = 5; +pub const seL4_TCBBits: usize = 11; +pub const seL4_EndpointBits: usize = 4; +pub const seL4_NotificationBits: usize = 5; +pub const seL4_PageTableBits: usize = 12; +pub const seL4_PageDirBits: usize = 12; +pub const seL4_PDPTBits: usize = 12; +pub const seL4_PML4Bits: usize = 12; +pub const seL4_IOPageTableBits: usize = 12; +pub const seL4_LargePageBits: usize = 21; +pub const seL4_HugePageBits: usize = 30; + +pub const seL4_VCPUBits: usize = 14; +pub const seL4_EPTPTBits: usize = 12; +pub const seL4_EPTPDBits: usize = 12; +pub const seL4_EPTPDPTBits: usize = 12; +pub const seL4_EPTPML4Bits: usize = 12; + +pub const seL4_ASIDPoolBits: usize = 12; + +pub const seL4_MinUntypedBits: usize = 4; +pub const seL4_MaxUntypedBits: usize = 47; + +pub const seL4_NumHWBreakpoints: usize = 4; +pub const seL4_FirstBreakpoint: usize = !1; +pub const seL4_NumExclusiveBreakpoints: usize = 0; +pub const seL4_FirstWatchpoint: usize = !1; +pub const seL4_NumExclusiveWatchpoints: usize = 0; +pub const seL4_FirstDualFunctionMonitor: usize = 0; +pub const seL4_NumDualFunctionMonitors: usize = 4; + + +pub type seL4_X86_ASIDControl = seL4_CPtr; +pub type seL4_X86_ASIDPool = seL4_CPtr; +pub type seL4_X86_IOSpace = seL4_CPtr; +pub type seL4_X86_IOPort = seL4_CPtr; +pub type seL4_X86_Page = seL4_CPtr; +pub type seL4_X86_PageDirectory = seL4_CPtr; +pub type seL4_X86_PageTable = seL4_CPtr; +pub type seL4_X86_IOPageTable = seL4_CPtr; +pub type seL4_X86_PDPT = seL4_CPtr; +pub type seL4_X64_PML4 = seL4_CPtr; + +error_types!(u64); + +pub const Default_VMAttributes: usize = 0; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_ObjectType { + seL4_UntypedObject = 0, + seL4_TCBObject, + seL4_EndpointObject, + seL4_NotificationObject, + seL4_CapTableObject, + seL4_X86_PDPTObject, + seL4_X64_PML4Object, + // seL4_X64_HugePageObject, + seL4_X86_4K, + seL4_X86_LargePageObject, + seL4_X86_PageTableObject, + seL4_X86_PageDirectoryObject, + seL4_X86_IOPageTableObject, + seL4_X86_VCPUObject, + seL4_X86_EPTPML4Object, + seL4_X86_EPTPDPTObject, + seL4_X86_EPTPDObject, + seL4_X86_EPTPTObject, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum seL4_X86_VMAttributes { + WriteBack = 0, + WriteThrough = 1, + CacheDisabled = 2, + Uncacheable = 3, + WriteCombining = 4, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct seL4_UserContext { + pub rip: seL4_Word, + pub rsp: seL4_Word, + pub rflags: seL4_Word, + pub rax: seL4_Word, + pub rbx: seL4_Word, + pub rcx: seL4_Word, + pub rdx: seL4_Word, + pub rsi: seL4_Word, + pub rdi: seL4_Word, + pub rbp: seL4_Word, + pub r8: seL4_Word, + pub r9: seL4_Word, + pub r10: seL4_Word, + pub r11: seL4_Word, + pub r12: seL4_Word, + pub r13: seL4_Word, + pub r14: seL4_Word, + pub r15: seL4_Word, + pub tls_base: seL4_Word, +} + +#[repr(C, packed)] +pub struct seL4_VBEInfoBlock { + pub signature: [u8; 4], + pub version: u16, + pub oemStringPtr: u32, + pub capabilities: u32, + pub modeListPtr: u32, + pub totalMemory: u16, + pub oemSoftwareRev: u16, + pub oemVendorNamePtr: u32, + pub oemProductNamePtr: u32, + pub reserved: [u8; 222], + pub oemData: [u8; 256], +} + +#[repr(C, packed)] +pub struct seL4_VBEModeInfoBlock { + // all revisions + pub modeAttr: u16, + pub winAAttr: u8, + pub winBAttr: u8, + pub winGranularity: u16, + pub winSize: u16, + pub winASeg: u16, + pub winBSeg: u16, + pub winFuncPtr: u32, + pub bytesPerScanLine: u16, + + // 1.2+ + pub xRes: u16, + pub yRes: u16, + pub xCharSize: u8, + pub yCharSize: u8, + pub planes: u8, + pub bitsPerPixel: u8, + pub banks: u8, + pub memoryMmodel: u8, + pub bankSize: u8, + pub imagePages: u8, + pub reserved1: u8, + + pub redLen: u8, + pub redOff: u8, + pub greenLen: u8, + pub greenOff: u8, + pub blueLen: u8, + pub blueOff: u8, + pub rsvdLen: u8, + pub rsvdOff: u8, + pub directColorInfo: u8, + + // 2.0+ + pub physBasePtr: u32, + pub reserved2: [u8; 6], + + // 3.0+ + pub linBytesPerScanLine: u16, + pub bnkImagePages: u8, + pub linImagePages: u8, + pub linRedLen: u8, + pub linRedOff: u8, + pub linGreenLen: u8, + pub linGreenOff: u8, + pub linBlueLen: u8, + pub linBlueOff: u8, + pub linRsvdLen: u8, + pub linRsvdOff: u8, + pub maxPixelClock: u32, + pub modeId: u16, + pub depth: u8, + + pub reserved3: [u8; 187], +} + +#[repr(C, packed)] +pub struct seL4_X86_BootInfo_VBE { + pub header: seL4_BootInfoHeader, + pub vbeInfoBlock: seL4_VBEInfoBlock, + pub vbeModeInfoBlock: seL4_VBEModeInfoBlock, + pub vbeMode: u32, + pub vbeInterfaceSeg: u32, + pub vbeInterfaceOff: u32, + pub vbeInterfaceLen: u32, +} + + +#[inline(always)] +pub unsafe fn seL4_GetMR(regnum: isize) -> seL4_Word { + let mr; + asm!("movq %gs:8(,$1,0x8), $0" : "=r"(mr) : "r"(regnum) : : "volatile"); + mr +} + +#[inline(always)] +pub unsafe fn seL4_SetMR(regnum: isize, value: seL4_Word) { + asm!("movq $0, %gs:8(,$1,0x8)" : : "r"(value), "r"(regnum) : "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_GetUserData() -> seL4_Word { + let data; + asm!("movq %gs:968, $0" : "=r"(data) : : : "volatile"); + data +} + +#[inline(always)] +pub unsafe fn seL4_GetIPCBuffer() -> *mut seL4_IPCBuffer { + seL4_GetUserData() as isize as *mut seL4_IPCBuffer +} + +#[inline(always)] +pub unsafe fn seL4_SetUserData(data: seL4_Word) { + asm!("movq $0, %gs:968" : : "r"(data) : "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_GetBadge(index: isize) -> seL4_CapData { + let mut badge: seL4_CapData = uninitialized(); + asm!("movq %gs:976(,$1,0x8), $0" : "=r"(badge.words[0]) : "r"(index) : : "volatile"); + badge +} + +#[inline(always)] +pub unsafe fn seL4_GetCap(index: isize) -> seL4_CPtr { + let cptr; + asm!("movq %gs:976(,$1,0x8), $0" : "=r"(cptr) : "r"(index) : : "volatile"); + cptr +} + +#[inline(always)] +pub unsafe fn seL4_SetCap(index: isize, cptr: seL4_CPtr) { + asm!("movq $0, %gs:976(,$1,0x8)" : : "r"(cptr), "r"(index) : "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_GetCapReceivePath(receiveCNode: *mut seL4_CPtr, + receiveIndex: *mut seL4_CPtr, + receiveDepth: *mut seL4_Word) { + if !receiveCNode.is_null() { + asm!("movq %gs:1000, $0" : "=r"(*receiveCNode) : : : "volatile"); + } + + if !receiveIndex.is_null() { + asm!("movq %gs:1008, $0" : "=r"(*receiveIndex) : : : "volatile"); + } + + if !receiveDepth.is_null() { + asm!("movq %gs:1016, $0" : "=r"(*receiveDepth) : : : "volatile"); + } +} + +#[inline(always)] +pub unsafe fn seL4_SetCapReceivePath(receiveCNode: seL4_CPtr, + receiveIndex: seL4_CPtr, + receiveDepth: seL4_Word) { + asm!("movq $0, %gs:1000" : : "r"(receiveCNode) : "memory" : "volatile"); + asm!("movq $0, %gs:1008" : : "r"(receiveIndex) : "memory" : "volatile"); + asm!("movq $0, %gs:1016" : : "r"(receiveDepth) : "memory" : "volatile"); +} + +#[inline(always)] +unsafe fn x64_sys_send(sys: seL4_Word, dest: seL4_Word, info: seL4_Word, mr1: seL4_Word, mr2: seL4_Word, mr3: seL4_Word, mr4: seL4_Word) { + asm!("movq %rsp, %rbx + syscall + movq %rbx, %rsp" + : + : "{rdx}" (sys), + "{rdi}" (dest), + "{rsi}" (info), + "{r10}" (mr1), + "{r8}" (mr2), + "{r9}" (mr3), + "{r15}" (mr4) + : "%rcx", "%rbx", "%r11" + : "volatile"); +} + +#[inline(always)] +unsafe fn x64_sys_reply(sys: seL4_Word, info: seL4_Word, mr1: seL4_Word, mr2: seL4_Word, mr3: seL4_Word, mr4: seL4_Word) { + asm!("movq %rsp, %rbx + syscall + movq %rbx, %rsp" + : + : "{rdx}" (sys), + "{rsi}" (info), + "{r10}" (mr1), + "{r8}" (mr2), + "{r9}" (mr3), + "{r15}" (mr4) + : "%rcx", "%rbx", "%r11" + : "volatile"); +} + +#[inline(always)] +unsafe fn x64_sys_send_null(sys: seL4_Word, dest: seL4_Word, info: seL4_Word) { + asm!("movq %rsp, %rbx + syscall + movq %rbx, %rsp" + : + : "{rdx}" (sys), + "{rdi}" (dest), + "{rsi}" (info) + : "%rcx", "%rbx", "%r11" + : "volatile"); +} + +#[inline(always)] +unsafe fn x64_sys_recv(sys: seL4_Word, src: seL4_Word, out_badge: *mut seL4_Word, out_info: *mut seL4_Word, out_mr1: *mut seL4_Word, out_mr2: *mut seL4_Word, out_mr3: *mut seL4_Word, out_mr4: *mut seL4_Word) { + asm!("movq %rsp, %rbx + syscall + movq %rbx, %rsp" + : "={rsi}" (*out_info) + "={r10}" (*out_mr1), + "={r8}" (*out_mr2), + "={r9}" (*out_mr3), + "={r15}" (*out_mr4), + "={rdi}" (*out_badge) + : "{rdx}" (sys), + "{rdi}" (src) + : "memory", "%rcx", "%rbx", "%r11" + : "volatile"); +} + +#[inline(always)] +unsafe fn x64_sys_send_recv(sys: seL4_Word, dest: seL4_Word, out_dest: *mut seL4_Word, info: seL4_Word, out_info: *mut seL4_Word, in_out_mr1: *mut seL4_Word, in_out_mr2: *mut seL4_Word, in_out_mr3: *mut seL4_Word, in_out_mr4: *mut seL4_Word) { + asm!("movq %rsp, %rbx + syscall + movq %rbx, %rsp" + : "={rsi}" (*out_info) + "={r10}" (*in_out_mr1), + "={r8}" (*in_out_mr2), + "={r9}" (*in_out_mr3), + "={r15}" (*in_out_mr4), + "={rdi}" (*out_dest) + : "{rdx}" (sys), + "{rsi}" (info), + "{r10}" (*in_out_mr1), + "{r8}" (*in_out_mr2), + "{r9}" (*in_out_mr3), + "{r15}" (*in_out_mr4), + "{rdi}" (dest) + : "memory", "%rcx", "%rbx", "%r11" + : "volatile"); +} + +#[inline(always)] +unsafe fn x64_sys_null(sys: seL4_Word) { + asm!("movq %rsp, %rbx + syscall + movq %rbx, %rsp" + : + : "{rdx}" (sys) + : "%rcx", "%rbx", "%r11", "%rsi", "%rdi" + : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_Send(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + x64_sys_send(SyscallId::Send as seL4_Word, dest, msgInfo.words[0], seL4_GetMR(0), seL4_GetMR(1), seL4_GetMR(2), seL4_GetMR(3)); +} + +macro_rules! opt_deref { + ($name:expr) => { + if !$name.is_null() { + *$name + } else { + 0 + } + } +} + +macro_rules! opt_assign { + ($loc:expr, $val:expr) => { + if !$loc.is_null() { + *$loc = $val; + } + } +} + +#[inline(always)] +pub unsafe fn seL4_SendWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word) { + x64_sys_send(SyscallId::Send as seL4_Word, dest, msgInfo.words[0], + if mr0.is_null() { 0 } else { *mr0 }, + if mr1.is_null() { 0 } else { *mr1 }, + if mr2.is_null() { 0 } else { *mr2 }, + if mr3.is_null() { 0 } else { *mr3 }, + ); +} + +#[inline(always)] +pub unsafe fn seL4_NBSend(dest: seL4_CPtr, msgInfo: seL4_MessageInfo) { + x64_sys_send(SyscallId::NBSend as seL4_Word, dest, msgInfo.words[0], seL4_GetMR(0), seL4_GetMR(1), seL4_GetMR(2), seL4_GetMR(3)); +} + +#[inline(always)] +pub unsafe fn seL4_NBSendWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word) { + x64_sys_send(SyscallId::NBSend as seL4_Word, dest, msgInfo.words[0], + if mr0.is_null() { 0 } else { *mr0 }, + if mr1.is_null() { 0 } else { *mr1 }, + if mr2.is_null() { 0 } else { *mr2 }, + if mr3.is_null() { 0 } else { *mr3 }); +} + +#[inline(always)] +pub unsafe fn seL4_Reply(msgInfo: seL4_MessageInfo) { + x64_sys_reply(SyscallId::Reply as seL4_Word, msgInfo.words[0], seL4_GetMR(0), seL4_GetMR(1), seL4_GetMR(2), seL4_GetMR(3)); + +} +#[inline(always)] +pub unsafe fn seL4_ReplyWithMRs(msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word) { + x64_sys_reply(SyscallId::Reply as seL4_Word, msgInfo.words[0], + if mr0.is_null() { 0 } else { *mr0 }, + if mr1.is_null() { 0 } else { *mr1 }, + if mr2.is_null() { 0 } else { *mr2 }, + if mr3.is_null() { 0 } else { *mr3 }); +} + + +#[inline(always)] +pub unsafe fn seL4_Signal(dest: seL4_CPtr) { + x64_sys_send_null(SyscallId::Send as seL4_Word, dest, seL4_MessageInfo::new(0,0,0,0).words[0]); +} + +#[inline(always)] +pub unsafe fn seL4_Recv(src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0: seL4_Word = uninitialized(); + let mut mr1: seL4_Word = uninitialized(); + let mut mr2: seL4_Word = uninitialized(); + let mut mr3: seL4_Word = uninitialized(); + + x64_sys_recv(SyscallId::Recv as seL4_Word, src, &mut badge, &mut info.words[0], &mut mr0, &mut mr1, &mut mr2, &mut mr3); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + seL4_SetMR(2, mr2); + seL4_SetMR(3, mr3); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_RecvWithMRs(src: seL4_CPtr, sender: *mut seL4_Word, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut msg0: seL4_Word = uninitialized(); + let mut msg1: seL4_Word = uninitialized(); + let mut msg2: seL4_Word = uninitialized(); + let mut msg3: seL4_Word = uninitialized(); + + x64_sys_recv(SyscallId::Recv as seL4_Word, src, &mut badge, &mut info.words[0], &mut msg0, &mut msg1, &mut msg2, &mut msg3); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_NBRecv(src: seL4_CPtr, sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0: seL4_Word = uninitialized(); + let mut mr1: seL4_Word = uninitialized(); + let mut mr2: seL4_Word = uninitialized(); + let mut mr3: seL4_Word = uninitialized(); + + x64_sys_recv(SyscallId::NBRecv as seL4_Word, src, &mut badge, &mut info.words[0], &mut mr0, &mut mr1, &mut mr2, &mut mr3); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + seL4_SetMR(2, mr2); + seL4_SetMR(3, mr3); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_Call(mut dest: seL4_CPtr, msgInfo: seL4_MessageInfo) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut mr0 = seL4_GetMR(0); + let mut mr1 = seL4_GetMR(1); + let mut mr2 = seL4_GetMR(2); + let mut mr3 = seL4_GetMR(3); + + x64_sys_send_recv(SyscallId::Call as seL4_Word, dest, &mut dest, msgInfo.words[0], &mut info.words[0], &mut mr0, &mut mr1, &mut mr2, &mut mr3); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + seL4_SetMR(2, mr2); + seL4_SetMR(3, mr3); + + info +} + +#[inline(always)] +pub unsafe fn seL4_CallWithMRs(mut dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut msg0: seL4_Word = 0; + let mut msg1: seL4_Word = 0; + let mut msg2: seL4_Word = 0; + let mut msg3: seL4_Word = 0; + + if !mr0.is_null() { + if msgInfo.get_length() > 0 { + msg0 = *mr0; + } + } + if !mr1.is_null() { + if msgInfo.get_length() > 1 { + msg1 = *mr1; + } + } + if !mr2.is_null() { + if msgInfo.get_length() > 2 { + msg2 = *mr2; + } + } + if !mr3.is_null() { + if msgInfo.get_length() > 3 { + msg3 = *mr3; + } + } + + x64_sys_send_recv(SyscallId::Call as seL4_Word, dest, &mut dest, msgInfo.words[0], &mut info.words[0], &mut msg0, &mut msg1, &mut msg2, &mut msg3); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + info +} + +#[inline(always)] +pub unsafe fn seL4_ReplyRecv(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, + sender: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0 = seL4_GetMR(0); + let mut mr1 = seL4_GetMR(1); + let mut mr2 = seL4_GetMR(2); + let mut mr3 = seL4_GetMR(3); + + x64_sys_send_recv(SyscallId::ReplyRecv as seL4_Word, dest, &mut badge, msgInfo.words[0], &mut info.words[0], &mut mr0, &mut mr1, &mut mr2, &mut mr3); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + seL4_SetMR(2, mr2); + seL4_SetMR(3, mr3); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_ReplyWaitWithMRs(dest: seL4_CPtr, msgInfo: seL4_MessageInfo, sender: *mut seL4_Word, + mr0: *mut seL4_Word, mr1: *mut seL4_Word, + mr2: *mut seL4_Word, mr3: *mut seL4_Word) -> seL4_MessageInfo { + let mut info: seL4_MessageInfo = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut msg0: seL4_Word = 0; + let mut msg1: seL4_Word = 0; + let mut msg2: seL4_Word = 0; + let mut msg3: seL4_Word = 0; + + if !mr0.is_null() { + if msgInfo.get_length() > 0 { + msg0 = *mr0; + } + } + if !mr1.is_null() { + if msgInfo.get_length() > 1 { + msg1 = *mr1; + } + } + if !mr2.is_null() { + if msgInfo.get_length() > 2 { + msg2 = *mr2; + } + } + if !mr3.is_null() { + if msgInfo.get_length() > 3 { + msg3 = *mr3; + } + } + + x64_sys_send_recv(SyscallId::ReplyRecv as seL4_Word, dest, &mut badge, msgInfo.words[0], &mut info.words[0], &mut msg0, &mut msg1, &mut msg2, &mut msg3); + + opt_assign!(mr0, msg0); + opt_assign!(mr1, msg1); + opt_assign!(mr2, msg2); + opt_assign!(mr3, msg3); + + opt_assign!(sender, badge); + + info +} + +#[inline(always)] +pub unsafe fn seL4_Yield() { + x64_sys_null(SyscallId::Yield as seL4_Word); + asm!("" ::: "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_VMEnter(vcpu: seL4_CPtr, sender: *mut seL4_Word) -> seL4_Word { + let mut fault: seL4_Word = uninitialized(); + let mut badge: seL4_Word = uninitialized(); + let mut mr0 = seL4_GetMR(0); + let mut mr1 = seL4_GetMR(1); + let mut mr2 = seL4_GetMR(2); + let mut mr3 = seL4_GetMR(3); + + x64_sys_send_recv(SyscallId::VMEnter as seL4_Word, vcpu, &mut badge, 0, &mut fault, &mut mr0, &mut mr1, &mut mr2, &mut mr3); + + seL4_SetMR(0, mr0); + seL4_SetMR(1, mr1); + seL4_SetMR(2, mr2); + seL4_SetMR(3, mr3); + + if fault == 0 && !sender.is_null() { + *sender = badge; + } + + fault +} + +//#[inline(always)] +pub unsafe fn seL4_DebugPutChar(c: u8) { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + let mut unused5 = 0; + x64_sys_send_recv(SyscallId::DebugPutChar as seL4_Word, c as seL4_Word, &mut unused0, 0, &mut + unused1, &mut unused2, &mut unused3, &mut unused4, &mut unused5); +} + +#[inline(always)] +pub unsafe fn seL4_DebugHalt() { + x64_sys_null(SyscallId::DebugHalt as seL4_Word); + asm!("" ::: "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugSnapshot() { + x64_sys_null(SyscallId::DebugSnapshot as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +pub unsafe fn seL4_DebugCapIdentify(mut cap: seL4_CPtr) -> u32 { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + x64_sys_send_recv(SyscallId::DebugCapIdentify as seL4_Word, + cap, &mut cap, 0, &mut unused0, &mut unused1, &mut unused2, &mut unused3, &mut unused4); + cap as u32 +} + +/// Note: name MUST be NUL-terminated. +#[inline(always)] +pub unsafe fn seL4_DebugNameThread(tcb: seL4_CPtr, name: &[u8]) { + core::ptr::copy_nonoverlapping(name.as_ptr() as *mut u8, (&mut (*seL4_GetIPCBuffer()).msg).as_mut_ptr() as *mut u8,name.len()); + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + let mut unused5 = 0; + x64_sys_send_recv(SyscallId::DebugNameThread as seL4_Word, tcb, &mut unused0, 0, &mut unused1, + &mut unused2, &mut unused3, &mut unused4, &mut unused5); +} + +#[inline(always)] +#[cfg(feature = "SEL4_DANGEROUS_CODE_INJECTION")] +pub unsafe fn seL4_DebugRun(userfn: extern fn(*mut u8), userarg: *mut u8) { + let userfnptr = userfn as *mut (); + x64_sys_send_null(SyscallId::DebugRun as seL4_Word, userfnptr as seL4_Word, userarg as seL4_Word); + asm!("" ::: "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkResetLog() -> seL4_Word { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + + let mut ret = 0; + + x64_sys_send_recv(SyscallId::BenchmarkResetLog as seL4_Word, 0, &mut ret, 0, &mut unused0, &mut unused1, &mut unused2, &mut unused3, &mut unused4); + + ret +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkFinalizeLog() { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + let mut index_ret = 0; + x64_sys_send_recv(SyscallId::BenchmarkFinalizeLog as seL4_Word, 0, &mut index_ret, &mut unused0, &mut unused1, &mut unused2, &mut unused3, &mut unused4); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkSetLogBuffer(mut frame_cptr: seL4_Word) -> seL4_Word { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + + x64_sys_send_recv(SyscallId::BenchmarkSetLogBuffer as seL4_Word, + frame_cptr, &mut frame_cptr, 0, &mut unused0, &mut unused1, &mut unused2, &mut unused3, &mut unused4); + frame_cptr +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkNullSyscall() { + x64_sys_null(SyscallId::BenchmarkNullSyscall as seL4_Word); + asm!("" ::: "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkFlushCaches() { + x64_sys_null(SyscallId::BenchmarkFlushCaches as seL4_Word); + asm!("" ::: "%esi", "%edi", "memory" : "volatile"); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkGetThreadUtilization(tcb: seL4_Word) { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + let mut unused5 = 0; + x64_sys_send_recv(SyscallId::BenchmarkGetThreadUtilisation as seL4_Word, tcb, &mut unused0, 0, + &mut unused1, &mut unused2, &mut unused3, &mut unused3, &mut unused4, &mut unused5); +} + +#[inline(always)] +#[cfg(feature = "SEL4_CONFIG_BENCHMARK")] +pub unsafe fn seL4_BenchmarkGetThreadUtilization(tcb: seL4_Word) { + let mut unused0 = 0; + let mut unused1 = 0; + let mut unused2 = 0; + let mut unused3 = 0; + let mut unused4 = 0; + let mut unused5 = 0; + + x64_sys_send_recv(SyscallId::BenchmarkResetThreadUtilisation as seL4_Word, tcb, &mut unused0, 0, + &mut unused1, &mut unused2, &mut unused3, &mut unused4, &mut unused5); +} diff --git a/apps/system/components/kata-os-common/src/sel4-sys/arch/x86_object_types.rs b/apps/system/components/kata-os-common/src/sel4-sys/arch/x86_object_types.rs new file mode 100644 index 0000000..d12b93c --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/arch/x86_object_types.rs @@ -0,0 +1,15 @@ +/* Copyright (c) 2015 The Robigalia Project Developers + * Licensed under the Apache License, Version 2.0 + * or the MIT + * license , + * at your option. All files in the project carrying such + * notice may not be copied, modified, or distributed except + * according to those terms. + */ + seL4_IA32_4K, + seL4_IA32_LargePage, + seL4_IA32_PageTableObject, + seL4_IA32_PageDirectoryObject, + seL4_IA32_PDPTObject, + seL4_IA32_IOPageTableObject, diff --git a/apps/system/components/kata-os-common/src/sel4-sys/build.rs b/apps/system/components/kata-os-common/src/sel4-sys/build.rs new file mode 100644 index 0000000..55fa846 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/build.rs @@ -0,0 +1,131 @@ +/* Copyright (c) 2015 The Robigalia Project Developers + * Licensed under the Apache License, Version 2.0 + * or the MIT + * license , + * at your option. All files in the project carrying such + * notice may not be copied, modified, or distributed except + * according to those terms. + */ + +use std::env; +use std::fs::File; +use std::os::unix::prelude::*; +use std::process::{Command, Stdio}; + +fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + let cargo_target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let cargo_target_pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap(); + println!("target_arch = {} target_pointer_width = {}", + cargo_target_arch, cargo_target_pointer_width); + + // Default to python3 (maybe necessary for code divergence) + let python_bin = env::var("PYTHON").unwrap_or_else(|_| "python3".to_string()); + + // Default to "seL4" for backwards compat; can either use git submodule or + // symbolic link (neither recommended) + let sel4_dir = env::var("SEL4_DIR").unwrap_or_else(|_| "seL4".to_string()); + + // Use CARGO_TARGET_ARCH and CARGO_TARGET_POINTER_WIDTH + // to select the target architecture; + // NB: this mimics the logic in lib.rs + let (arch, archdir) = match cargo_target_arch.as_str() { + "x86" => ("ia32", "x86"), + "x86_64" => ("x86_64", "x86"), + "arm" => match cargo_target_pointer_width.as_str() { + "32" => ("aarch32", "arm"), + "64" => ("aarch64", "arm"), + _ => { panic!("Unsupported arm word size {}", cargo_target_pointer_width); } + } + "riscv32" => ("riscv32", "riscv"), + "riscv64" => ("riscv64", "riscv"), + _ => { panic!("Unsupported architecture {}", cargo_target_arch); } + }; + + let xml_interfaces_file = format!("{}/libsel4/include/interfaces/sel4.xml", sel4_dir); + let outfile = format!("{}/{}_syscall_stub.rs", out_dir, arch); + let xml_arch_file = &*format!( + "{}/libsel4/arch_include/{}/interfaces/sel4arch.xml", + sel4_dir, archdir + ); + let xml_sel4_arch_file = format!( + "{}/libsel4/sel4_arch_include/{}/interfaces/sel4arch.xml", + sel4_dir, arch + ); + let args = vec![ + "tools/syscall_stub_gen.py", + "-a", + arch, + "-w", + cargo_target_pointer_width.as_str(), + "--buffer", + #[cfg(feature = "CONFIG_KERNEL_MCS")] + "--mcs", + "-o", + &*outfile, + &*xml_interfaces_file, + &*xml_arch_file, + &*xml_sel4_arch_file, + ]; + + let mut cmd = Command::new("/usr/bin/env"); + cmd.arg(&python_bin).args(&args); + + println!("Running: {:?}", cmd); + assert!(cmd.status().unwrap().success()); + + // TODO(sleffler): requires pip install tempita + let xml_arch_file = &*format!( + "{}/libsel4/arch_include/{}/interfaces/sel4arch.xml", + sel4_dir, archdir + ); + let xml_sel4_arch_file = format!( + "{}/libsel4/sel4_arch_include/{}/interfaces/sel4arch.xml", + sel4_dir, arch + ); + let mut cmd = Command::new("/usr/bin/env"); + cmd.arg(&python_bin).args(&[ + "tools/invocation_header_gen.py", + "--dest", + &*format!("{}/{}_invocation.rs", out_dir, arch), + &*xml_interfaces_file, + &*xml_arch_file, + &*xml_sel4_arch_file, + ]); + println!("Running {:?}", cmd); + assert!(cmd.status().unwrap().success()); + + // TODO(sleffler): requires pip install tempita + let mut cmd = Command::new("/usr/bin/env"); + cmd.arg(&python_bin).args(&[ + "tools/syscall_header_gen.py", + #[cfg(feature = "CONFIG_KERNEL_MCS")] + "--mcs", + "--xml", + &*format!("{}/libsel4/include/api/syscall.xml", sel4_dir), + "--dest", + &*format!("{}/syscalls.rs", out_dir), + ]); + println!("Running {:?}", cmd); + assert!(cmd.status().unwrap().success()); + + let bfin = File::open(&*format!( + "{}/libsel4/mode_include/{}/sel4/shared_types.bf", + sel4_dir, cargo_target_pointer_width + )) + .unwrap(); + println!("{}/types{}.rs", out_dir, cargo_target_pointer_width); + let bfout = File::create(&*format!("{}/types{}.rs", out_dir, cargo_target_pointer_width)).unwrap(); + let mut cmd = Command::new("/usr/bin/env"); + cmd.arg(&python_bin) + .arg("tools/bitfield_gen.py") + .arg("--language=rust") + // .arg("--word-size=32") + .stdin(unsafe { Stdio::from_raw_fd(bfin.as_raw_fd()) }) + .stdout(unsafe { Stdio::from_raw_fd(bfout.as_raw_fd()) }); + println!("Running {:?}", cmd); + assert!(cmd.status().unwrap().success()); + std::mem::forget(bfin); + std::mem::forget(bfout); +} diff --git a/apps/system/components/kata-os-common/src/sel4-sys/lib.rs b/apps/system/components/kata-os-common/src/sel4-sys/lib.rs new file mode 100644 index 0000000..6bce49f --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/lib.rs @@ -0,0 +1,290 @@ +/* Copyright (c) 2015 The Robigalia Project Developers + * Licensed under the Apache License, Version 2.0 + * or the MIT + * license , + * at your option. All files in the project carrying such + * notice may not be copied, modified, or distributed except + * according to those terms. + */ +#![no_std] +#![feature(asm)] +#![allow(bad_style, unused_parens, unused_assignments)] + +// NB: this mimics the logic in build.rs +#[cfg(not(any( + all(target_arch = "arm", target_pointer_width = "32"), + all(target_arch = "riscv32"), + all(target_arch = "x86"), + all(target_arch = "x86_64"), +)))] +use architecture_not_supported_sorry; + +pub use seL4_BreakpointAccess::*; +pub use seL4_BreakpointType::*; +pub use seL4_Error::*; +pub use seL4_LookupFailureType::*; +pub use seL4_ObjectType::*; + +use core::mem::size_of; + +// XXX: These can't be repr(C), but it needs to "match an int" according to the comments on +// SEL4_FORCE_LONG_ENUM. There's no single type that matches in Rust, so it needs to be +// per-architecture. We use a macro to define them all in one whack, with the invoker providing +// only what the size of the enums ought to be. Each arch then invokes it. +macro_rules! error_types { + ($int_width:ident) => { + #[repr($int_width)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum seL4_Error { + seL4_NoError = 0, + seL4_InvalidArgument, + seL4_InvalidCapability, + seL4_IllegalOperation, + seL4_RangeError, + seL4_AlignmentError, + seL4_FailedLookup, + seL4_TruncatedMessage, + seL4_DeleteFirst, + seL4_RevokeFirst, + seL4_NotEnoughMemory, + // XXX: Code depends on this being the last variant + } + + #[repr($int_width)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum seL4_LookupFailureType { + seL4_NoFailure = 0, + seL4_InvalidRoot, + seL4_MissingCapability, + seL4_DepthMismatch, + seL4_GuardMismatch, + // XXX: Code depends on this being the last variant + } + + #[repr($int_width)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum seL4_BreakpointType { + seL4_DataBreakpoint = 0, + seL4_InstructionBreakpoint, + seL4_SingleStep, + seL4_SoftwareBreakRequest, + } + + #[repr($int_width)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum seL4_BreakpointAccess { + seL4_BreakOnRead = 0, + seL4_BreakOnWrite, + seL4_BreakOnReadWrite, + } + }; +} + +pub type seL4_Word = usize; +pub type seL4_CPtr = usize; + +#[cfg(target_arch = "x86")] +include!("arch/x86.rs"); + +#[cfg(target_arch = "x86_64")] +include!("arch/x86_64.rs"); + +#[cfg(all(target_arch = "arm", target_pointer_width = "32"))] +include!("arch/arm.rs"); + +#[cfg(target_arch = "riscv32")] +include!("arch/riscv.rs"); + +#[cfg(all(target_arch = "x86"))] +include!(concat!(env!("OUT_DIR"), "/ia32_invocation.rs")); + +#[cfg(all(target_arch = "x86_64"))] +include!(concat!(env!("OUT_DIR"), "/x86_64_invocation.rs")); + +#[cfg(all(target_arch = "arm", target_pointer_width = "32"))] +include!(concat!(env!("OUT_DIR"), "/aarch32_invocation.rs")); + +#[cfg(target_arch = "riscv32")] +include!(concat!(env!("OUT_DIR"), "/riscv32_invocation.rs")); + +#[cfg(all(target_arch = "x86"))] +include!(concat!(env!("OUT_DIR"), "/ia32_syscall_stub.rs")); + +#[cfg(all(target_arch = "x86_64"))] +include!(concat!(env!("OUT_DIR"), "/x86_64_syscall_stub.rs")); + +#[cfg(all(target_arch = "arm", target_pointer_width = "32"))] +include!(concat!(env!("OUT_DIR"), "/aarch32_syscall_stub.rs")); + +#[cfg(target_arch = "riscv32")] +include!(concat!(env!("OUT_DIR"), "/riscv32_syscall_stub.rs")); + +#[cfg(target_pointer_width = "32")] +include!(concat!(env!("OUT_DIR"), "/types32.rs")); + +#[cfg(target_pointer_width = "64")] +include!(concat!(env!("OUT_DIR"), "/types64.rs")); + +include!(concat!(env!("OUT_DIR"), "/syscalls.rs")); + +// Well-known types from libsel4/include/sel4/types.h + +pub type seL4_NodeId = seL4_Word; +pub type seL4_PAddr = seL4_Word; +pub type seL4_Domain = seL4_Word; + +pub type seL4_CNode = seL4_CPtr; +pub type seL4_IRQHandler = seL4_CPtr; +pub type seL4_IRQControl = seL4_CPtr; +pub type seL4_TCB = seL4_CPtr; +pub type seL4_Untyped = seL4_CPtr; +pub type seL4_DomainSet = seL4_CPtr; +pub type seL4_SchedContext = seL4_CPtr; +pub type seL4_SchedControl = seL4_CPtr; + +// TODO(sleffler): seL4 uses seL4_Uint64 but it's not defined for us +pub type seL4_Time = u64; + +pub const seL4_MsgLengthBits: usize = 7; +pub const seL4_MsgMaxLength: usize = 120; +pub const seL4_MsgExtraCapBits: usize = 2; +pub const seL4_MsgMaxExtraCaps: usize = (1usize << seL4_MsgExtraCapBits) - 1; + +#[derive(Copy)] +/// Buffer used to store received IPC messages +pub struct seL4_IPCBuffer { + /// Message tag + /// + /// The kernel does not initialize this. + pub tag: seL4_MessageInfo, + /// Message contents + /// + /// The kernel only initializes the bytes which were not able to fit into physical registers. + pub msg: [seL4_Word; seL4_MsgMaxLength], + /// Arbitrary user data. + /// + /// The seL4 C libraries expect this to be a pointer to the IPC buffer in the thread's VSpace., + /// but this doesn't really matter. + pub userData: seL4_Word, + /// Capabilities to transfer (if sending) or unwrapped badges + pub caps_or_badges: [seL4_Word; seL4_MsgMaxExtraCaps], + /// CPtr to a CNode in the thread's CSpace from which to find the receive slot + pub receiveCNode: seL4_CPtr, + /// CPtr to the receive slot, relative to receiveCNode + pub receiveIndex: seL4_CPtr, + /// Number of bits of receiveIndex to use + pub receiveDepth: seL4_Word, +} + +impl ::core::clone::Clone for seL4_IPCBuffer { + fn clone(&self) -> Self { + *self + } +} + +/* bootinfo */ + +pub static seL4_CapNull: seL4_Word = 0; /* null cap */ +pub static seL4_CapInitThreadTCB: seL4_Word = 1; /* initial thread's TCB cap */ +pub static seL4_CapInitThreadCNode: seL4_Word = 2; /* initial thread's root CNode cap */ +pub static seL4_CapInitThreadVSpace: seL4_Word = 3; /* initial thread's VSpace cap */ +pub static seL4_CapIRQControl: seL4_Word = 4; /* global IRQ controller cap */ +pub static seL4_CapASIDControl: seL4_Word = 5; /* global ASID controller cap */ +pub static seL4_CapInitThreadASIDPool: seL4_Word = 6; /* initial thread's ASID pool cap */ +pub static seL4_CapIOPort: seL4_Word = 7; /* global IO port cap (null cap if not supported) */ +pub static seL4_CapIOSpace: seL4_Word = 8; /* global IO space cap (null cap if no IOMMU support) */ +pub static seL4_CapBootInfoFrame: seL4_Word = 9; /* bootinfo frame cap */ +pub static seL4_CapInitThreadIPCBuffer: seL4_Word = 10; /* initial thread's IPC buffer frame cap */ +pub static seL4_CapDomain: seL4_Word = 11; /* global domain controller cap */ + +#[repr(C, packed)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// A half-open [start..end) range of slots +pub struct seL4_SlotRegion { + /// First CNode slot position of the region + pub start: seL4_Word, + /// First CNode slot position after the region + pub end: seL4_Word, /* first CNode slot position AFTER region */ +} + +#[repr(C, packed)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct seL4_UntypedDesc { + /// Physical address corresponding of the untyped object's backing memory + pub paddr: seL4_Word, + pub padding1: u8, + pub padding2: u8, + /// log2 size of the region of memory backing the untyped object + pub sizeBits: u8, + /// Whether the backing memory corresponds to some device memory + pub isDevice: u8, +} + +// explicitly *not* Copy. the array at the end is tricky to handle. + +#[repr(C, packed)] +// #[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) +//#[derive(Debug, PartialEq, Eq)] +pub struct seL4_BootInfo { + /// Length of any additional bootinfo information + pub extraLen: seL4_Word, + /// ID [0..numNodes-1] of the current node (0 if uniprocessor) + pub nodeID: seL4_Word, + /// Number of seL4 nodes (1 if uniprocessor) + pub numNodes: seL4_Word, + /// Number of IOMMU PT levels (0 if no IOMMU support) + pub numIOPTLevels: seL4_Word, + /// pointer to root task's IPC buffer */ + pub ipcBuffer: *mut seL4_IPCBuffer, + /// Empty slots (null caps) + pub empty: seL4_SlotRegion, + /// Frames shared between nodes + pub sharedFrames: seL4_SlotRegion, + /// Frame caps used for the loaded ELF image of the root task + pub userImageFrames: seL4_SlotRegion, + /// PD caps used for the loaded ELF image of the root task + pub userImagePaging: seL4_SlotRegion, + /// IOSpace caps for ARM SMMU + pub ioSpaceCaps: seL4_SlotRegion, + /// Caps fr anypages used to back the additional bootinfo information + pub extraBIPages: seL4_SlotRegion, + /// log2 size of root task's CNode + pub initThreadCNodeSizeBits: u8, + /// Root task's domain ID + pub initThreadDomain: u32, + + #[cfg(feature = "SEL4_CONFIG_KERNEL_MCS")] + // Caps to sched_control for each node + pub schedcontrol: seL4_SlotRegion, + + /// Untyped object caps + pub untyped: seL4_SlotRegion, + /// Information about each untyped cap + /// + /// *Note*! This is actually an array! The actual length depends on kernel configuration which + /// we have no way of knowing at this point. Use the `untyped_descs` method. + pub untypedList: seL4_UntypedDesc, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct seL4_BootInfoHeader { + /// Identifier of the following chunk + pub id: seL4_Word, + /// Length of the chunk + pub len: seL4_Word, +} + +impl seL4_BootInfo { + /// This is safe if you don't mutate the `untyped` field and corrupt its length. + pub unsafe fn untyped_descs(&self) -> &[seL4_UntypedDesc] { + let len = self.untyped.end - self.untyped.start; + // sanity check that the number of untypeds doesn't extend past the end of the page + debug_assert!( + len <= (4096 - size_of::() + size_of::()) + / size_of::() + ); + core::slice::from_raw_parts(&self.untypedList, len) + } +} diff --git a/apps/system/components/kata-os-common/src/sel4-sys/seL4 b/apps/system/components/kata-os-common/src/sel4-sys/seL4 new file mode 120000 index 0000000..ed7725c --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/seL4 @@ -0,0 +1 @@ +../../../../../../../../kernel \ No newline at end of file diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/bitfield_gen.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/bitfield_gen.py new file mode 100755 index 0000000..d59cec5 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/bitfield_gen.py @@ -0,0 +1,3600 @@ +#!/usr/bin/env python3 +# +# Copyright 2015, Corey Richardson +# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) +# +# SPDX-License-Identifier: BSD-2-Clause +# + +## +# A tool for generating bitfield structures with get/set/new methods +# including Isabelle/HOL specifications and correctness proofs. +## + +from __future__ import print_function, division +import sys +import os.path +import optparse +import re +import itertools +import tempfile + +from six.moves import range +from functools import reduce + +import lex +from ply import yacc + +import umm + +# Whether debugging is enabled (turn on with command line option --debug). +DEBUG = False + +# name of locale the bitfield proofs should be in +loc_name = 'kernel_all_substitute' + +# The C parser emits variables with a suffix to indicate their types; this lets +# the C parser distinguish `char x` from `int x`. +# +# The suffix map should match the C parser types for `uint{8,16,32,64}_t` in +# `include/stdint.h`. +var_size_suffix_map = {k: 'unsigned' + v for (k, v) in + {8: '_char', 16: '_short', 32: '', + 64: '_longlong'}.items()} + + +def return_name(base): + # name of return value for standard word sizes + return 'ret__' + var_size_suffix_map[base] + + +def var_name(name, base): + # converts C variable name to Isabelle variable name via type mangling. + return name + '___' + var_size_suffix_map[base] + + +# Headers to include depending on which environment we are generating code for. +C_INCLUDES = { + 'sel4': ['assert.h', 'config.h', 'stdint.h', 'util.h'], + 'libsel4': ['autoconf.h', 'sel4/simple_types.h', 'sel4/debug_assert.h'], +} + +C_ASSERTS = { + 'sel4': 'debug_assert!', # 'assert', + 'libsel4': 'seL4_DebugAssert' +} + +C_INLINE = { + 'sel4': 'static inline', + 'libsel4': 'LIBSEL4_INLINE_FUNC' +} + +C_TYPES = { + "sel4": { + 8: "uint8_t", + 16: "uint16_t", + 32: "uint32_t", + 64: "uint64_t" + }, + + "libsel4": { + 8: "seL4_Uint8", + 16: "seL4_Uint16", + 32: "seL4_Uint32", + 64: "seL4_Uint64" + } +} + +RUST_INCLUDES = { + 'sel4': [], +} + +RUST_ASSERTS = { + 'sel4': 'debug_assert!', +} + +RUST_INLINE = { + 'sel4': '#[inline(always)]', +} + +RUST_TYPES = { + "sel4": { + 8: "u8", + 16: "u16", + 32: "u32", + 64: "u64" + }, +} + +# Parser + +reserved = ('BLOCK', 'BASE', 'FIELD', 'FIELD_HIGH', 'MASK', 'PADDING', + 'TAGGED_UNION', 'TAG') + +tokens = reserved + ('IDENTIFIER', 'INTLIT', 'LBRACE', 'RBRACE', + 'LPAREN', 'RPAREN', 'COMMA') + +t_LBRACE = r'{' +t_RBRACE = r'}' +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_COMMA = r',' + +reserved_map = dict((r.lower(), r) for r in reserved) + + +def t_IDENTIFIER(t): + r'[A-Za-z_]\w+|[A-Za-z]' + t.type = reserved_map.get(t.value, 'IDENTIFIER') + return t + + +def t_INTLIT(t): + r'([1-9][0-9]*|0[oO]?[0-7]+|0[xX][0-9a-fA-F]+|0[bB][01]+|0)[lL]?' + t.value = int(t.value, 0) + return t + + +def t_NEWLINE(t): + r'\n+' + t.lexer.lineno += len(t.value) + + +def t_comment(t): + r'--.*|\#.*' + + +t_ignore = ' \t' + + +def t_error(t): + print("%s: Unexpected character '%s'" % (sys.argv[0], t.value[0]), + file=sys.stderr) + if DEBUG: + print('Token: %s' % str(t), file=sys.stderr) + sys.exit(1) + + +def p_start(t): + """start : entity_list""" + t[0] = t[1] + + +def p_entity_list_empty(t): + """entity_list : """ + t[0] = (None, {}, {}) + + +def p_entity_list_base(t): + """entity_list : entity_list base""" + current_base, block_map, union_map = t[1] + block_map.setdefault(t[2], {}) + union_map.setdefault(t[2], {}) + t[0] = (t[2], block_map, union_map) + + +def p_entity_list_block(t): + """entity_list : entity_list block""" + current_base, block_map, union_map = t[1] + block_map[current_base][t[2].name] = t[2] + t[0] = (current_base, block_map, union_map) + + +def p_entity_list_union(t): + """entity_list : entity_list tagged_union""" + current_base, block_map, union_map = t[1] + union_map[current_base][t[2].name] = t[2] + t[0] = (current_base, block_map, union_map) + + +def p_base_simple(t): + """base : BASE INTLIT""" + t[0] = (t[2], t[2], 0) + + +def p_base_mask(t): + """base : BASE INTLIT LPAREN INTLIT COMMA INTLIT RPAREN""" + t[0] = (t[2], t[4], t[6]) + + +def p_block(t): + """block : BLOCK IDENTIFIER opt_visible_order_spec""" \ + """ LBRACE fields RBRACE""" + t[0] = Block(name=t[2], fields=t[5], visible_order=t[3]) + + +def p_opt_visible_order_spec_empty(t): + """opt_visible_order_spec : """ + t[0] = None + + +def p_opt_visible_order_spec(t): + """opt_visible_order_spec : LPAREN visible_order_spec RPAREN""" + t[0] = t[2] + + +def p_visible_order_spec_empty(t): + """visible_order_spec : """ + t[0] = [] + + +def p_visible_order_spec_single(t): + """visible_order_spec : IDENTIFIER""" + t[0] = [t[1]] + + +def p_visible_order_spec(t): + """visible_order_spec : visible_order_spec COMMA IDENTIFIER""" + t[0] = t[1] + [t[3]] + + +def p_fields_empty(t): + """fields : """ + t[0] = [] + + +def p_fields_field(t): + """fields : fields FIELD IDENTIFIER INTLIT""" + t[0] = t[1] + [(t[3], t[4], False)] + + +def p_fields_field_high(t): + """fields : fields FIELD_HIGH IDENTIFIER INTLIT""" + t[0] = t[1] + [(t[3], t[4], True)] + + +def p_fields_padding(t): + """fields : fields PADDING INTLIT""" + t[0] = t[1] + [(None, t[3], False)] + + +def p_tagged_union(t): + """tagged_union : TAGGED_UNION IDENTIFIER IDENTIFIER""" \ + """ LBRACE masks tags RBRACE""" + t[0] = TaggedUnion(name=t[2], tagname=t[3], classes=t[5], tags=t[6]) + + +def p_tags_empty(t): + """tags :""" + t[0] = [] + + +def p_tags(t): + """tags : tags TAG IDENTIFIER INTLIT""" + t[0] = t[1] + [(t[3], t[4])] + + +def p_masks_empty(t): + """masks :""" + t[0] = [] + + +def p_masks(t): + """masks : masks MASK INTLIT INTLIT""" + t[0] = t[1] + [(t[3], t[4])] + + +def p_error(t): + print("Syntax error at token '%s'" % t.value, file=sys.stderr) + sys.exit(1) + +# Templates + +## Rust templates + +rust_type_template = \ +""" +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct %(name)s { + words: [%(type)s; %(multiple)d], +}""" + +rust_generator_template = \ +"""impl %(block)s { + #[inline(always)] + pub fn new(%(args)s) -> %(block)s { + let mut %(block)s: %(block)s = unsafe { ::core::mem::zeroed() }; + +%(word_inits)s +%(field_inits)s + + %(block)s + } +}""" + +rust_ptr_generator_template = \ +"""impl %(block)s { + #[inline(always)] + pub fn ptr_new(%(args)s) { +%(word_inits)s + +%(field_inits)s + } +}""" + +rust_reader_template = \ +"""impl %(block)s { + #[inline(always)] + pub fn get_%(field)s(&self) -> %(type)s { + let mut ret; + ret = (self.words[%(index)d] & 0x%(mask)x%(suf)s) %(r_shift_op)s %(shift)d; + /* Possibly sign extend */ + if (0 != (ret & (1%(suf)s << (%(extend_bit)d)))) { + ret |= 0x%(high_bits)x; + } + ret + } +}""" + +rust_writer_template = \ +"""impl %(block)s { + #[inline(always)] + pub fn set_%(field)s(&mut self, v: %(type)s) { + /* fail if user has passed bits that we will override */ + %(assert)s(((!0x%(mask)x %(r_shift_op)s %(shift)d) & v) == + (if (0 != (v & (1%(suf)s << (%(extend_bit)d)))) { 0x%(high_bits)x } else { 0 })); + self.words[%(index)d] &= !0x%(mask)x%(suf)s; + self.words[%(index)d] |= (v %(w_shift_op)s %(shift)d) & 0x%(mask)x%(suf)s; + } +}""" + +rust_tag_reader_header_template = \ +"""impl %(union)s { + #[inline(always)] + pub fn get_%(tagname)s(&self) -> %(type)s { +""" + +rust_tag_reader_entry_template = \ +""" if (self.words[%(index)d] & 0x%(classmask)x) != 0x%(classmask)x) { + (self.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s + }""" + +rust_tag_reader_final_template = \ +""" (self.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s""" + +rust_tag_reader_footer_template = \ +"""} +}""" + +rust_tag_eq_reader_header_template = \ +"""impl %(union)s { + #[inline(always)] + pub fn %(tagname)s_equals(&self, %(union)s_type_tag: %(type)s) -> bool { +""" + +rust_tag_eq_reader_entry_template = \ +""" if ((%(union)s_type_tag & 0x%(classmask)x) != 0x%(classmask)x) { + ((self.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s) == %(union)s_type_tag; + }""" + +rust_tag_eq_reader_final_template = \ +""" ((self.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s) == %(union)s_type_tag""" + +rust_tag_eq_reader_footer_template = \ +"""} +}""" + +rust_tag_writer_template = \ +"""impl %(union)s { + #[inline(always)] + pub fn set_%(tagname)s(&mut self, v: %(type)s) { + /* fail if user has passed bits that we will override */ + %(assert)s(((!0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d) & v) == (0 != (v & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + + self.words[%(index)d] &= !0x%(mask)x%(suf)s; + self.words[%(index)d] |= (v << %(shift)d) & 0x%(mask)x%(suf)s; + } +}""" + +# C templates + + +c_typedef_template = \ + """struct %(name)s { + %(type)s words[%(multiple)d]; +}; +c_typedef struct %(name)s %(name)s_t;""" + +c_generator_template = \ + """%(inline)s %(block)s_t CONST +%(block)s_new(%(gen_params)s) { + %(block)s_t %(block)s; + +%(asserts)s + +%(gen_inits)s + + return %(block)s; +}""" + +c_ptr_generator_template = \ + """%(inline)s void +%(block)s_ptr_new(%(ptr_params)s) { +%(asserts)s + +%(ptr_inits)s +}""" + +c_reader_template = \ + """%(inline)s %(type)s CONST +%(block)s_get_%(field)s(%(block)s_t %(block)s) { + %(type)s ret; + ret = (%(block)s.words[%(index)d] & 0x%(mask)x%(suf)s) %(r_shift_op)s %(shift)d; + /* Possibly sign extend */ + if (__builtin_expect(!!(%(sign_extend)d && (ret & (1%(suf)s << (%(extend_bit)d)))), %(sign_extend)d)) { + ret |= 0x%(high_bits)x; + } + return ret; +}""" + +c_ptr_reader_template = \ + """%(inline)s %(type)s PURE +%(block)s_ptr_get_%(field)s(%(block)s_t *%(block)s_ptr) { + %(type)s ret; + ret = (%(block)s_ptr->words[%(index)d] & 0x%(mask)x%(suf)s) """ \ + """%(r_shift_op)s %(shift)d; + /* Possibly sign extend */ + if (__builtin_expect(!!(%(sign_extend)d && (ret & (1%(suf)s << (%(extend_bit)d)))), %(sign_extend)d)) { + ret |= 0x%(high_bits)x; + } + return ret; +}""" + +c_writer_template = \ + """%(inline)s %(block)s_t CONST +%(block)s_set_%(field)s(%(block)s_t %(block)s, %(type)s v%(base)d) { + /* fail if user has passed bits that we will override */ + %(assert)s((((~0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d ) | 0x%(high_bits)x) & v%(base)d) == ((%(sign_extend)d && (v%(base)d & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + %(block)s.words[%(index)d] &= ~0x%(mask)x%(suf)s; + %(block)s.words[%(index)d] |= (v%(base)d %(w_shift_op)s %(shift)d) & 0x%(mask)x%(suf)s; + return %(block)s; +}""" + +c_ptr_writer_template = \ + """%(inline)s void +%(block)s_ptr_set_%(field)s(%(block)s_t *%(block)s_ptr, %(type)s v%(base)d) { + /* fail if user has passed bits that we will override */ + %(assert)s((((~0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d) | 0x%(high_bits)x) & v%(base)d) == ((%(sign_extend)d && (v%(base)d & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + %(block)s_ptr->words[%(index)d] &= ~0x%(mask)x%(suf)s; + %(block)s_ptr->words[%(index)d] |= (v%(base)d %(w_shift_op)s """ \ + """%(shift)d) & 0x%(mask)x; +}""" + +c_union_generator_template = \ + """%(inline)s %(union)s_t CONST +%(union)s_%(block)s_new(%(gen_params)s) { + %(union)s_t %(union)s; + +%(asserts)s + +%(gen_inits)s + + return %(union)s; +}""" + +c_ptr_union_generator_template = \ + """%(inline)s void +%(union)s_%(block)s_ptr_new(%(ptr_params)s) { +%(asserts)s + +%(ptr_inits)s +}""" + +c_union_reader_template = \ + """%(inline)s %(type)s CONST +%(union)s_%(block)s_get_%(field)s(%(union)s_t %(union)s) { + %(type)s ret; + %(assert)s(((%(union)s.words[%(tagindex)d] >> %(tagshift)d) & 0x%(tagmask)x) == + %(union)s_%(block)s); + + ret = (%(union)s.words[%(index)d] & 0x%(mask)x%(suf)s) %(r_shift_op)s %(shift)d; + /* Possibly sign extend */ + if (__builtin_expect(!!(%(sign_extend)d && (ret & (1%(suf)s << (%(extend_bit)d)))), %(sign_extend)d)) { + ret |= 0x%(high_bits)x; + } + return ret; +}""" + +c_ptr_union_reader_template = \ + """%(inline)s %(type)s PURE +%(union)s_%(block)s_ptr_get_%(field)s(%(union)s_t *%(union)s_ptr) { + %(type)s ret; + %(assert)s(((%(union)s_ptr->words[%(tagindex)d] >> """ \ + """%(tagshift)d) & 0x%(tagmask)x) == + %(union)s_%(block)s); + + ret = (%(union)s_ptr->words[%(index)d] & 0x%(mask)x%(suf)s) """ \ + """%(r_shift_op)s %(shift)d; + /* Possibly sign extend */ + if (__builtin_expect(!!(%(sign_extend)d && (ret & (1%(suf)s << (%(extend_bit)d)))), %(sign_extend)d)) { + ret |= 0x%(high_bits)x; + } + return ret; +}""" + +c_union_writer_template = \ + """%(inline)s %(union)s_t CONST +%(union)s_%(block)s_set_%(field)s(%(union)s_t %(union)s, %(type)s v%(base)d) { + %(assert)s(((%(union)s.words[%(tagindex)d] >> %(tagshift)d) & 0x%(tagmask)x) == + %(union)s_%(block)s); + /* fail if user has passed bits that we will override */ + %(assert)s((((~0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d ) | 0x%(high_bits)x) & v%(base)d) == ((%(sign_extend)d && (v%(base)d & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + + %(union)s.words[%(index)d] &= ~0x%(mask)x%(suf)s; + %(union)s.words[%(index)d] |= (v%(base)d %(w_shift_op)s %(shift)d) & 0x%(mask)x%(suf)s; + return %(union)s; +}""" + +c_ptr_union_writer_template = \ + """%(inline)s void +%(union)s_%(block)s_ptr_set_%(field)s(%(union)s_t *%(union)s_ptr, + %(type)s v%(base)d) { + %(assert)s(((%(union)s_ptr->words[%(tagindex)d] >> """ \ + """%(tagshift)d) & 0x%(tagmask)x) == + %(union)s_%(block)s); + + /* fail if user has passed bits that we will override */ + %(assert)s((((~0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d) | 0x%(high_bits)x) & v%(base)d) == ((%(sign_extend)d && (v%(base)d & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + + %(union)s_ptr->words[%(index)d] &= ~0x%(mask)x%(suf)s; + %(union)s_ptr->words[%(index)d] |= """ \ + """(v%(base)d %(w_shift_op)s %(shift)d) & 0x%(mask)x%(suf)s; +}""" + +c_tag_reader_header_template = \ + """%(inline)s %(type)s CONST +%(union)s_get_%(tagname)s(%(union)s_t %(union)s) { +""" + +c_tag_reader_entry_template = \ + """ if ((%(union)s.words[%(index)d] & 0x%(classmask)x) != 0x%(classmask)x) + return (%(union)s.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s; +""" + +c_tag_reader_final_template = \ + """ return (%(union)s.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s;""" + +c_tag_reader_footer_template = \ + """ +}""" + +c_tag_eq_reader_header_template = \ + """%(inline)s int CONST +%(union)s_%(tagname)s_equals(%(union)s_t %(union)s, %(type)s %(union)s_type_tag) { +""" + +c_tag_eq_reader_entry_template = \ + """ if ((%(union)s_type_tag & 0x%(classmask)x) != 0x%(classmask)x) + return ((%(union)s.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s) == %(union)s_type_tag; +""" + +c_tag_eq_reader_final_template = \ + """ return ((%(union)s.words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s) == %(union)s_type_tag;""" + +c_tag_eq_reader_footer_template = \ + """ +}""" + +c_ptr_tag_reader_header_template = \ + """%(inline)s %(type)s PURE +%(union)s_ptr_get_%(tagname)s(%(union)s_t *%(union)s_ptr) { +""" + +c_ptr_tag_reader_entry_template = \ + """ if ((%(union)s_ptr->words[%(index)d] & 0x%(classmask)x) != 0x%(classmask)x) + return (%(union)s_ptr->words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s; +""" + +c_ptr_tag_reader_final_template = \ + """ return (%(union)s_ptr->words[%(index)d] >> %(shift)d) & 0x%(mask)x%(suf)s;""" + +ptr_tag_reader_footer_template = \ + """ +}""" + +c_tag_writer_template = \ + """%(inline)s %(union)s_t CONST +%(union)s_set_%(tagname)s(%(union)s_t %(union)s, %(type)s v%(base)d) { + /* fail if user has passed bits that we will override */ + %(assert)s((((~0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d) | 0x%(high_bits)x) & v%(base)d) == ((%(sign_extend)d && (v%(base)d & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + + %(union)s.words[%(index)d] &= ~0x%(mask)x%(suf)s; + %(union)s.words[%(index)d] |= (v%(base)d << %(shift)d) & 0x%(mask)x%(suf)s; + return %(union)s; +}""" + +c_ptr_tag_writer_template = \ + """%(inline)s void +%(union)s_ptr_set_%(tagname)s(%(union)s_t *%(union)s_ptr, %(type)s v%(base)d) { + /* fail if user has passed bits that we will override */ + %(assert)s((((~0x%(mask)x%(suf)s %(r_shift_op)s %(shift)d) | 0x%(high_bits)x) & v%(base)d) == ((%(sign_extend)d && (v%(base)d & (1%(suf)s << (%(extend_bit)d)))) ? 0x%(high_bits)x : 0)); + + %(union)s_ptr->words[%(index)d] &= ~0x%(mask)x%(suf)s; + %(union)s_ptr->words[%(index)d] |= (v%(base)d << %(shift)d) & 0x%(mask)x%(suf)s; +}""" + +# HOL definition templates + +lift_def_template = \ + '''definition + %(name)s_lift :: "%(name)s_C \ %(name)s_CL" +where + "%(name)s_lift %(name)s \ \ + %(fields)s \"''' + +block_lift_def_template = \ + '''definition %(union)s_%(block)s_lift :: ''' \ + '''"%(union)s_C \ %(union)s_%(block)s_CL" +where + "%(union)s_%(block)s_lift %(union)s \ + case (%(union)s_lift %(union)s) of ''' \ + '''Some (%(generator)s rec) \ rec"''' + +block_lift_lemma_template = \ + '''lemma %(union)s_%(block)s_lift: + "(%(union)s_get_tag c = scast %(union)s_%(block)s) = ''' \ + '''(%(union)s_lift c = Some (%(generator)s (%(union)s_%(block)s_lift c)))" + unfolding %(union)s_lift_def %(union)s_%(block)s_lift_def + by (clarsimp simp: %(union)s_tag_defs Let_def)''' + +union_get_tag_def_header_template = \ + '''definition + %(name)s_get_tag :: "%(name)s_C \ word%(base)d" +where + "%(name)s_get_tag %(name)s \ + ''' + +union_get_tag_def_entry_template = \ + '''if ((index (%(name)s_C.words_C %(name)s) %(tag_index)d)''' \ + ''' AND 0x%(classmask)x \ 0x%(classmask)x) + then ((index (%(name)s_C.words_C %(name)s) %(tag_index)d)'''\ +''' >> %(tag_shift)d) AND mask %(tag_size)d + else ''' + +union_get_tag_def_final_template = \ + '''((index (%(name)s_C.words_C %(name)s) %(tag_index)d)'''\ + ''' >> %(tag_shift)d) AND mask %(tag_size)d''' + +union_get_tag_def_footer_template = '''"''' + +union_get_tag_eq_x_def_header_template = \ + '''lemma %(name)s_get_tag_eq_x: + "(%(name)s_get_tag c = x) = ((''' + +union_get_tag_eq_x_def_entry_template = \ + '''if ((x << %(tag_shift)d) AND 0x%(classmask)x \ 0x%(classmask)x) + then ((index (%(name)s_C.words_C c) %(tag_index)d)''' \ +''' >> %(tag_shift)d) AND mask %(tag_size)d + else ''' + +union_get_tag_eq_x_def_final_template = \ + '''((index (%(name)s_C.words_C c) %(tag_index)d)''' \ + ''' >> %(tag_shift)d) AND mask %(tag_size)d''' + +union_get_tag_eq_x_def_footer_template = ''') = x)" + by (auto simp add: %(name)s_get_tag_def mask_def word_bw_assocs)''' + +union_tag_mask_helpers_header_template = \ + '''lemma %(name)s_%(block)s_tag_mask_helpers:''' + +union_tag_mask_helpers_entry_template = ''' + "w && %(full_mask)s = %(full_value)s \ w'''\ +''' && %(part_mask)s = %(part_value)s" +''' + +union_tag_mask_helpers_footer_template = \ + ''' by (auto elim: word_sub_mask simp: mask_def)''' + +union_lift_def_template = \ + '''definition + %(name)s_lift :: "%(name)s_C \ %(name)s_CL option" +where + "%(name)s_lift %(name)s \ + (let tag = %(name)s_get_tag %(name)s in + %(tag_cases)s + else None)"''' + +union_access_def_template = \ + '''definition + %(union)s_%(block)s_access :: "(%(union)s_%(block)s_CL \ 'a) + \ %(union)s_CL \ 'a" +where + "%(union)s_%(block)s_access f %(union)s \ + (case %(union)s of %(generator)s rec \ f rec)"''' + +union_update_def_template = \ + '''definition + %(union)s_%(block)s_update :: "(%(union)s_%(block)s_CL \''' \ + ''' %(union)s_%(block)s_CL) \ + %(union)s_CL \ %(union)s_CL" +where + "%(union)s_%(block)s_update f %(union)s \ + (case %(union)s of %(generator)s rec \ + %(generator)s (f rec))"''' + +# HOL proof templates + +# FIXME: avoid [simp] +struct_lemmas_template = \ + ''' +lemmas %(name)s_ptr_guards[simp] = + %(name)s_ptr_words_NULL + %(name)s_ptr_words_aligned + %(name)s_ptr_words_ptr_safe''' + +# FIXME: move to global theory +defs_global_lemmas = ''' +lemma word_sub_mask: + "\ w && m1 = v1; m1 && m2 = m2; v1 && m2 = v2 \ + \ w && m2 = v2" + by (clarsimp simp: word_bw_assocs) +''' + +# Proof templates are stored as a list of +# (header, body, stuff to go afterwards). +# This makes it easy to replace the proof body with a sorry. + +# ptrname should be a function of s + + +def ptr_basic_template(name, ptrname, retval, args, post): + return ('''lemma (in ''' + loc_name + ''') %(name)s_ptr_''' + name + '''_spec: + defines "ptrval s \ cslift s ''' + ptrname + '''" + shows "\s. \ \ \s. s \\<^sub>c ''' + ptrname + '''\ + ''' + retval + '''PROC %(name)s_ptr_''' + name + '''(\%(name)s_ptr''' + args + ''') + ''' + post + ''' " ''') + + +def ptr_union_basic_template(name, ptrname, retval, args, pre, post): + return ('''lemma (in ''' + loc_name + ''') %(name)s_%(block)s_ptr_''' + name + '''_spec: + defines "ptrval s \ cslift s ''' + ptrname + '''" + shows "\s. \ \ \s. s \\<^sub>c ''' + ptrname + " " + pre + '''\ + ''' + retval + '''PROC %(name)s_%(block)s_ptr_''' + name + '''(\%(name)s_ptr''' + args + ''') + ''' + post + ''' " ''') + + +direct_ptr_name = '\<^bsup>s\<^esup>%(name)s_ptr' +path_ptr_name = '(cparent \<^bsup>s\<^esup>%(name)s_ptr [%(path)s] :: %(toptp)s ptr)' + + +def ptr_get_template(ptrname): + return ptr_basic_template('get_%(field)s', ptrname, '\%(ret_name)s :== ', '', + '''\\%(ret_name)s = ''' + '''%(name)s_CL.%(field)s_CL ''' + '''(%(name)s_lift (%(access_path)s))\''') + + +def ptr_set_template(name, ptrname): + return ptr_basic_template(name, ptrname, '', ', \v%(base)d', + '''{t. \%(name)s. + %(name)s_lift %(name)s = + %(name)s_lift (%(access_path)s) \ %(name)s_CL.%(field)s_CL ''' + ''':= %(sign_extend)s(\<^bsup>s\<^esup>v%(base)d AND %(mask)s) \ \ + t_hrs_' (globals t) = hrs_mem_update (heap_update + (''' + ptrname + ''') + %(update_path)s) + (t_hrs_' (globals s)) + }''') + + +def ptr_new_template(ptrname): + return ptr_basic_template('new', ptrname, '', ', %(args)s', + '''{t. \%(name)s. %(name)s_lift %(name)s = \ + %(field_eqs)s \ \ + t_hrs_' (globals t) = hrs_mem_update (heap_update + (''' + ptrname + ''') + %(update_path)s) + (t_hrs_' (globals s)) + }''') + + +def ptr_get_tag_template(ptrname): + return ptr_basic_template('get_%(tagname)s', ptrname, '\%(ret_name)s :== ', '', + '''\\%(ret_name)s = %(name)s_get_tag (%(access_path)s)\''') + + +def ptr_empty_union_new_template(ptrname): + return ptr_union_basic_template('new', ptrname, '', '', '', + '''{t. \%(name)s. ''' + '''%(name)s_get_tag %(name)s = scast %(name)s_%(block)s \ + t_hrs_' (globals t) = hrs_mem_update (heap_update + (''' + ptrname + ''') + %(update_path)s) + (t_hrs_' (globals s)) + }''') + + +def ptr_union_new_template(ptrname): + return ptr_union_basic_template('new', ptrname, '', ', %(args)s', '', + '''{t. \%(name)s. ''' + '''%(name)s_%(block)s_lift %(name)s = \ + %(field_eqs)s \ \ + %(name)s_get_tag %(name)s = scast %(name)s_%(block)s \ + t_hrs_' (globals t) = hrs_mem_update (heap_update + (''' + ptrname + ''') + %(update_path)s) + (t_hrs_' (globals s)) + }''') + + +def ptr_union_get_template(ptrname): + return ptr_union_basic_template('get_%(field)s', ptrname, + '\%(ret_name)s :== ', '', + '\ %(name)s_get_tag %(access_path)s = scast %(name)s_%(block)s', + '''\\%(ret_name)s = ''' + '''%(name)s_%(block)s_CL.%(field)s_CL ''' + '''(%(name)s_%(block)s_lift %(access_path)s)\''') + + +def ptr_union_set_template(ptrname): + return ptr_union_basic_template('set_%(field)s', ptrname, '', ', \v%(base)d', + '\ %(name)s_get_tag %(access_path)s = scast %(name)s_%(block)s', + '''{t. \%(name)s. ''' + '''%(name)s_%(block)s_lift %(name)s = + %(name)s_%(block)s_lift %(access_path)s ''' + '''\ %(name)s_%(block)s_CL.%(field)s_CL ''' + ''':= %(sign_extend)s(\<^bsup>s\<^esup>v%(base)d AND %(mask)s) \ \ + %(name)s_get_tag %(name)s = scast %(name)s_%(block)s \ + t_hrs_' (globals t) = hrs_mem_update (heap_update + (''' + ptrname + ''') + %(update_path)s) + (t_hrs_' (globals s)) + }''') + + +proof_templates = { + + 'lift_collapse_proof': [ + '''lemma %(name)s_lift_%(block)s: + "%(name)s_get_tag %(name)s = scast %(name)s_%(block)s \ + %(name)s_lift %(name)s = + Some (%(value)s)"''', + ''' apply(simp add:%(name)s_lift_def %(name)s_tag_defs) +done'''], + + 'words_NULL_proof': [ + '''lemma %(name)s_ptr_words_NULL: + "c_guard (p::%(name)s_C ptr) \ + 0 < &(p\[''words_C''])"''', + ''' apply(fastforce intro:c_guard_NULL_fl simp:typ_uinfo_t_def) +done'''], + + 'words_aligned_proof': [ + '''lemma %(name)s_ptr_words_aligned: + "c_guard (p::%(name)s_C ptr) \ + ptr_aligned ((Ptr &(p\[''words_C'']))::''' + '''((word%(base)d[%(words)d]) ptr))"''', + ''' apply(fastforce intro:c_guard_ptr_aligned_fl simp:typ_uinfo_t_def) +done'''], + + 'words_ptr_safe_proof': [ + '''lemma %(name)s_ptr_words_ptr_safe: + "ptr_safe (p::%(name)s_C ptr) d \ + ptr_safe (Ptr &(p\[''words_C''])::''' + '''((word%(base)d[%(words)d]) ptr)) d"''', + ''' apply(fastforce intro:ptr_safe_mono simp:typ_uinfo_t_def) +done'''], + + 'get_tag_fun_spec_proof': [ + '''lemma (in ''' + loc_name + ''') fun_spec: + "\ \ {\} + \ret__%(rtype)s :== PROC %(name)s_get_%(tag_name)s(''' + ''' \%(name)) + \\ret__%(rtype)s = %(name)s_get_tag''' + '''\<^bsup>\\<^esup>\"''', + ''' apply(rule allI, rule conseqPre, vcg) + apply(clarsimp) + apply(simp add:$(name)s_get_tag_def word_sle_def + mask_def ucast_def) +done'''], + + 'const_modifies_proof': [ + '''lemma (in ''' + loc_name + ''') %(fun_name)s_modifies: + "\ s. \ \\<^bsub>/UNIV\<^esub> {s} + PROC %(fun_name)s(%(args)s) + {t. t may_not_modify_globals s}"''', + ''' by (vcg spec=modifies strip_guards=true)'''], + + 'ptr_set_modifies_proof': [ + '''lemma (in ''' + loc_name + ''') %(fun_name)s_modifies: + "\s. \ \\<^bsub>/UNIV\<^esub> {s} + PROC %(fun_name)s(%(args)s) + {t. t may_only_modify_globals s in [t_hrs]}"''', + ''' by (vcg spec=modifies strip_guards=true)'''], + + + 'new_spec': [ + '''lemma (in ''' + loc_name + ''') %(name)s_new_spec: + "\ s. \ \ {s} + \ret__struct_%(name)s_C :== PROC %(name)s_new(%(args)s) + \ %(name)s_lift \ret__struct_%(name)s_C = \ + %(field_eqs)s \ \"''', + ''' apply (rule allI, rule conseqPre, vcg) + apply (clarsimp simp: guard_simps) + apply (simp add: %(name)s_lift_def) + apply ((intro conjI sign_extend_eq)?; + (simp add: mask_def shift_over_ao_dists multi_shift_simps word_size + word_ao_dist word_bw_assocs word_and_max_simps)?) + done'''], + + 'ptr_new_spec_direct': [ + ptr_new_template(direct_ptr_name), + '''sorry (* ptr_new_spec_direct *)'''], + + 'ptr_new_spec_path': [ + ptr_new_template(path_ptr_name), + '''sorry (* ptr_new_spec_path *)'''], + + + 'get_spec': [ + '''lemma (in ''' + loc_name + ''') %(name)s_get_%(field)s_spec: + "\s. \ \ {s} + \%(ret_name)s :== ''' + '''PROC %(name)s_get_%(field)s(\%(name)s) + \\%(ret_name)s = ''' + '''%(name)s_CL.%(field)s_CL ''' + '''(%(name)s_lift \<^bsup>s\<^esup>%(name)s)\"''', + ''' apply (rule allI, rule conseqPre, vcg) + apply clarsimp + apply (simp add: %(name)s_lift_def mask_shift_simps guard_simps) + apply (simp add: sign_extend_def' mask_def nth_is_and_neq_0 word_bw_assocs + shift_over_ao_dists word_oa_dist word_and_max_simps)? + done'''], + + 'set_spec': [ + '''lemma (in ''' + loc_name + ''') %(name)s_set_%(field)s_spec: + "\s. \ \ {s} + \ret__struct_%(name)s_C :== ''' + '''PROC %(name)s_set_%(field)s(\%(name)s, \v%(base)d) + \%(name)s_lift \ret__struct_%(name)s_C = ''' + '''%(name)s_lift \<^bsup>s\<^esup>%(name)s \ ''' + '''%(name)s_CL.%(field)s_CL ''' + ''':= %(sign_extend)s (\<^bsup>s\<^esup>v%(base)d AND %(mask)s) \\"''', + ''' apply(rule allI, rule conseqPre, vcg) + apply(clarsimp simp: guard_simps ucast_id + %(name)s_lift_def + mask_def shift_over_ao_dists + multi_shift_simps word_size + word_ao_dist word_bw_assocs + NOT_eq) + apply (simp add: sign_extend_def' mask_def nth_is_and_neq_0 word_bw_assocs + shift_over_ao_dists word_and_max_simps)? + done'''], + + # where the top level type is the bitfield type --- these are split because they have different proofs + 'ptr_get_spec_direct': [ + ptr_get_template(direct_ptr_name), + ''' unfolding ptrval_def + apply (rule allI, rule conseqPre, vcg) + apply (clarsimp simp: h_t_valid_clift_Some_iff) + apply (simp add: %(name)s_lift_def guard_simps mask_def typ_heap_simps ucast_def) + apply (simp add: sign_extend_def' mask_def nth_is_and_neq_0 word_bw_assocs + shift_over_ao_dists word_oa_dist word_and_max_simps)? + done'''], + + 'ptr_get_spec_path': [ + ptr_get_template(path_ptr_name), + ''' unfolding ptrval_def + apply (rule allI, rule conseqPre, vcg) + apply (clarsimp simp: guard_simps) + apply (frule iffD1[OF h_t_valid_clift_Some_iff], rule exE, assumption, simp) + apply (frule clift_subtype, simp, simp, simp) + apply (simp add: typ_heap_simps) + apply (simp add: thread_state_lift_def) + apply (simp add: sign_extend_def' mask_def nth_is_and_neq_0 word_bw_assocs + shift_over_ao_dists word_oa_dist word_and_max_simps)? + apply (simp add: mask_shift_simps)? + done'''], + + 'ptr_set_spec_direct': [ + ptr_set_template('set_%(field)s', direct_ptr_name), + ''' unfolding ptrval_def + apply (rule allI, rule conseqPre, vcg) + apply (clarsimp simp: guard_simps) + apply (clarsimp simp add: packed_heap_update_collapse_hrs typ_heap_simps)? + apply (rule exI, rule conjI[rotated], rule refl) + apply (clarsimp simp: h_t_valid_clift_Some_iff %(name)s_lift_def typ_heap_simps) + apply ((intro conjI sign_extend_eq)?; + (simp add: mask_def shift_over_ao_dists multi_shift_simps word_size + word_ao_dist word_bw_assocs word_and_max_simps))? + done'''], + + 'ptr_set_spec_path': [ + ptr_set_template('set_%(field)s', path_ptr_name), + ''' (* Invoke vcg *) + unfolding ptrval_def + apply (rule allI, rule conseqPre, vcg) + apply (clarsimp) + + (* Infer h_t_valid for all three levels of indirection *) + apply (frule h_t_valid_c_guard_cparent, simp, simp add: typ_uinfo_t_def) + apply (frule h_t_valid_c_guard_field[where f="[''words_C'']"], + simp, simp add: typ_uinfo_t_def) + + (* Discharge guards, including c_guard for pointers *) + apply (simp add: h_t_valid_c_guard guard_simps) + + (* Lift field updates to bitfield struct updates *) + apply (simp add: heap_update_field_hrs h_t_valid_c_guard typ_heap_simps) + + (* Collapse multiple updates *) + apply(simp add: packed_heap_update_collapse_hrs) + + (* Instantiate the toplevel object *) + apply(frule iffD1[OF h_t_valid_clift_Some_iff], rule exE, assumption, simp) + + (* Instantiate the next-level object in terms of the last *) + apply(frule clift_subtype, simp+) + + (* Resolve pointer accesses *) + apply(simp add: h_val_field_clift') + + (* Rewrite bitfield struct updates as enclosing struct updates *) + apply(frule h_t_valid_c_guard) + apply(simp add: parent_update_child) + + (* Equate the updated values *) + apply(rule exI, rule conjI[rotated], simp add: h_val_clift') + + (* Rewrite struct updates *) + apply(simp add: o_def %(name)s_lift_def) + + (* Solve bitwise arithmetic *) + apply ((intro conjI sign_extend_eq)?; + (simp add: mask_def shift_over_ao_dists multi_shift_simps word_size + word_ao_dist word_bw_assocs word_and_max_simps))? + done'''], + + + 'get_tag_spec': [ + '''lemma (in ''' + loc_name + ''') %(name)s_get_%(tagname)s_spec: + "\s. \ \ {s} + \%(ret_name)s :== ''' \ + '''PROC %(name)s_get_%(tagname)s(\%(name)s) + \\%(ret_name)s = ''' \ + '''%(name)s_get_tag \<^bsup>s\<^esup>%(name)s\"''', + ''' apply(rule allI, rule conseqPre, vcg) + apply(clarsimp) + apply(simp add:%(name)s_get_tag_def + mask_shift_simps + guard_simps) +done'''], + + 'get_tag_equals_spec': [ + '''lemma (in ''' + loc_name + ''') %(name)s_%(tagname)s_equals_spec: + "\s. \ \ {s} + \ret__int :== + PROC %(name)s_%(tagname)s_equals(\%(name)s, \%(name)s_type_tag) + \\ret__int = of_bl [%(name)s_get_tag \<^bsup>s\<^esup>%(name)s = \<^bsup>s\<^esup>%(name)s_type_tag]\"''', + ''' apply(rule allI, rule conseqPre, vcg) + apply(clarsimp) + apply(simp add:%(name)s_get_tag_eq_x + mask_shift_simps + guard_simps) +done'''], + + 'ptr_get_tag_spec_direct': [ + ptr_get_tag_template(direct_ptr_name), + ''' unfolding ptrval_def + apply(rule allI, rule conseqPre, vcg) + apply(clarsimp simp:guard_simps) + apply(frule h_t_valid_field[where f="[''words_C'']"], simp+) + apply(frule iffD1[OF h_t_valid_clift_Some_iff], rule exE, assumption, simp) + apply(simp add:h_val_clift' clift_field) + apply(simp add:%(name)s_get_tag_def) + apply(simp add:mask_shift_simps)? +done'''], + + 'ptr_get_tag_spec_path': [ + ptr_get_tag_template(path_ptr_name), + ''' unfolding ptrval_def + apply(rule allI, rule conseqPre, vcg) + apply(clarsimp) + apply(frule h_t_valid_c_guard_cparent, simp, simp add: typ_uinfo_t_def) + apply(clarsimp simp: typ_heap_simps h_t_valid_clift_Some_iff) + apply(frule clift_subtype, simp+) + apply(simp add: %(name)s_get_tag_def mask_shift_simps guard_simps) +done'''], + + + 'empty_union_new_spec': [ + '''lemma (in ''' + loc_name + ''') ''' \ + '''%(name)s_%(block)s_new_spec: + "\s. \ \ {s} + \ret__struct_%(name)s_C :== ''' \ + '''PROC %(name)s_%(block)s_new() + \%(name)s_get_tag \ret__struct_%(name)s_C = ''' \ + '''scast %(name)s_%(block)s\"''', + ''' apply(rule allI, rule conseqPre, vcg) + apply(clarsimp simp:guard_simps + %(name)s_lift_def + Let_def + %(name)s_get_tag_def + mask_shift_simps + %(name)s_tag_defs + word_of_int_hom_syms) +done'''], + + 'union_new_spec': [ + '''lemma (in ''' + loc_name + ''') ''' \ + '''%(name)s_%(block)s_new_spec: + "\s. \ \ {s} + \ret__struct_%(name)s_C :== ''' \ + '''PROC %(name)s_%(block)s_new(%(args)s) + \%(name)s_%(block)s_lift ''' \ + '''\ret__struct_%(name)s_C = \ + %(field_eqs)s \ \ + %(name)s_get_tag \ret__struct_%(name)s_C = ''' \ + '''scast %(name)s_%(block)s\"''', + ''' apply (rule allI, rule conseqPre, vcg) + apply (clarsimp simp: guard_simps o_def mask_def shift_over_ao_dists) + apply (rule context_conjI[THEN iffD1[OF conj_commute]], + fastforce simp: %(name)s_get_tag_eq_x %(name)s_%(block)s_def + mask_def shift_over_ao_dists word_bw_assocs word_ao_dist) + apply (simp add: %(name)s_%(block)s_lift_def) + apply (erule %(name)s_lift_%(block)s[THEN subst[OF sym]]; simp?) + apply ((intro conjI sign_extend_eq)?; + (simp add: mask_def shift_over_ao_dists multi_shift_simps word_size + word_ao_dist word_bw_assocs word_and_max_simps))? + done'''], + + 'ptr_empty_union_new_spec_direct': [ + ptr_empty_union_new_template(direct_ptr_name), + '''sorry (* ptr_empty_union_new_spec_direct *)'''], + + 'ptr_empty_union_new_spec_path': [ + ptr_empty_union_new_template(path_ptr_name), + ''' unfolding ptrval_def + apply(rule allI, rule conseqPre, vcg) + apply(clarsimp) + apply(frule h_t_valid_c_guard_cparent, simp, simp add: typ_uinfo_t_def) + apply(clarsimp simp: h_t_valid_clift_Some_iff) + apply(frule clift_subtype, simp+) + apply(clarsimp simp: typ_heap_simps c_guard_clift + packed_heap_update_collapse_hrs) + + apply(simp add: parent_update_child[OF c_guard_clift] + typ_heap_simps c_guard_clift) + + apply((simp add: o_def)?, rule exI, rule conjI[OF _ refl]) + + apply(simp add: %(name)s_get_tag_def %(name)s_tag_defs + guard_simps mask_shift_simps) +done +'''], + + 'ptr_union_new_spec_direct': [ + ptr_union_new_template(direct_ptr_name), + '''sorry (* ptr_union_new_spec_direct *)'''], + + 'ptr_union_new_spec_path': [ + ptr_union_new_template(path_ptr_name), + ''' unfolding ptrval_def + apply (rule allI, rule conseqPre, vcg) + apply (clarsimp) + apply (frule h_t_valid_c_guard_cparent, simp, simp add: typ_uinfo_t_def) + apply (drule h_t_valid_clift_Some_iff[THEN iffD1], erule exE) + apply (frule clift_subtype, simp, simp) + apply (clarsimp simp: typ_heap_simps c_guard_clift + packed_heap_update_collapse_hrs) + apply (simp add: guard_simps mask_shift_simps + %(name)s_tag_defs[THEN tag_eq_to_tag_masked_eq])? + apply (simp add: parent_update_child[OF c_guard_clift] + typ_heap_simps c_guard_clift) + apply (simp add: o_def %(name)s_%(block)s_lift_def) + apply (simp only: %(name)s_lift_%(block)s cong: rev_conj_cong) + apply (rule exI, rule conjI[rotated], rule conjI[OF _ refl]) + apply (simp_all add: %(name)s_get_tag_eq_x %(name)s_tag_defs mask_shift_simps) + apply (intro conjI sign_extend_eq; simp add: mask_def word_ao_dist word_bw_assocs)? + done'''], + + 'union_get_spec': [ + '''lemma (in ''' + loc_name + ''') ''' \ + '''%(name)s_%(block)s_get_%(field)s_spec: + "\s. \ \ ''' \ +'''\s. %(name)s_get_tag \%(name)s = ''' \ + '''scast %(name)s_%(block)s\ + \%(ret_name)s :== ''' \ + '''PROC %(name)s_%(block)s_get_%(field)s(\%(name)s) + \\%(ret_name)s = ''' \ + '''%(name)s_%(block)s_CL.%(field)s_CL ''' \ + '''(%(name)s_%(block)s_lift \<^bsup>s\<^esup>%(name)s)''' \ + '''\"''', + ''' apply(rule allI, rule conseqPre, vcg) + apply(clarsimp simp:guard_simps) + apply(simp add:%(name)s_%(block)s_lift_def) + apply(subst %(name)s_lift_%(block)s) + apply(simp add:o_def + %(name)s_get_tag_def + %(name)s_%(block)s_def + mask_def + word_size + shift_over_ao_dists) + apply(subst %(name)s_lift_%(block)s, simp)? + apply(simp add:o_def + %(name)s_get_tag_def + %(name)s_%(block)s_def + mask_def + word_size + shift_over_ao_dists + multi_shift_simps + word_bw_assocs + word_oa_dist + word_and_max_simps + ucast_def + sign_extend_def' + nth_is_and_neq_0) +done'''], + + 'union_set_spec': [ + '''lemma (in ''' + loc_name + ''') ''' \ + '''%(name)s_%(block)s_set_%(field)s_spec: + "\s. \ \ ''' \ +'''\s. %(name)s_get_tag \%(name)s = ''' \ + '''scast %(name)s_%(block)s\ + \ret__struct_%(name)s_C :== ''' \ + '''PROC %(name)s_%(block)s_set_%(field)s(\%(name)s, \v%(base)d) + \%(name)s_%(block)s_lift \ret__struct_%(name)s_C = ''' \ + '''%(name)s_%(block)s_lift \<^bsup>s\<^esup>%(name)s \ ''' \ + '''%(name)s_%(block)s_CL.%(field)s_CL ''' \ + ''':= %(sign_extend)s (\<^bsup>s\<^esup>v%(base)d AND %(mask)s)\ \ + %(name)s_get_tag \ret__struct_%(name)s_C = ''' \ + '''scast %(name)s_%(block)s\"''', + ''' apply (rule allI, rule conseqPre, vcg) + apply clarsimp + apply (rule context_conjI[THEN iffD1[OF conj_commute]], + fastforce simp: %(name)s_get_tag_eq_x %(name)s_lift_def %(name)s_tag_defs + mask_def shift_over_ao_dists multi_shift_simps word_size + word_ao_dist word_bw_assocs) + apply (simp add: %(name)s_%(block)s_lift_def %(name)s_lift_def %(name)s_tag_defs) + apply ((intro conjI sign_extend_eq)?; + (simp add: mask_def shift_over_ao_dists multi_shift_simps word_size + word_ao_dist word_bw_assocs word_and_max_simps))? + done'''], + + 'ptr_union_get_spec_direct': [ + ptr_union_get_template(direct_ptr_name), + ''' unfolding ptrval_def + apply(rule allI, rule conseqPre, vcg) + apply(clarsimp simp: typ_heap_simps h_t_valid_clift_Some_iff guard_simps + mask_shift_simps sign_extend_def' nth_is_and_neq_0 + %(name)s_lift_%(block)s %(name)s_%(block)s_lift_def) +done +'''], + + 'ptr_union_get_spec_path': [ + ptr_union_get_template(path_ptr_name), + '''unfolding ptrval_def + apply(rule allI, rule conseqPre, vcg) + apply(clarsimp) + apply(frule h_t_valid_c_guard_cparent, simp, simp add: typ_uinfo_t_def) + apply(drule h_t_valid_clift_Some_iff[THEN iffD1], erule exE) + apply(frule clift_subtype, simp, simp) + apply(clarsimp simp: typ_heap_simps c_guard_clift) + apply(simp add: guard_simps mask_shift_simps) + apply(simp add:%(name)s_%(block)s_lift_def) + apply(subst %(name)s_lift_%(block)s) + apply(simp add: mask_def)+ + done + (* ptr_union_get_spec_path *)'''], + + 'ptr_union_set_spec_direct': [ + ptr_union_set_template(direct_ptr_name), + '''sorry (* ptr_union_set_spec_direct *)'''], + + + 'ptr_union_set_spec_path': [ + ptr_union_set_template(path_ptr_name), + ''' unfolding ptrval_def + apply (rule allI, rule conseqPre, vcg) + apply (clarsimp) + apply (frule h_t_valid_c_guard_cparent, simp, simp add: typ_uinfo_t_def) + apply (drule h_t_valid_clift_Some_iff[THEN iffD1], erule exE) + apply (frule clift_subtype, simp, simp) + apply (clarsimp simp: typ_heap_simps c_guard_clift + packed_heap_update_collapse_hrs) + apply (simp add: guard_simps mask_shift_simps + %(name)s_tag_defs[THEN tag_eq_to_tag_masked_eq])? + apply (simp add: parent_update_child[OF c_guard_clift] + typ_heap_simps c_guard_clift) + apply (simp add: o_def %(name)s_%(block)s_lift_def) + apply (simp only: %(name)s_lift_%(block)s cong: rev_conj_cong) + apply (rule exI, rule conjI[rotated], rule conjI[OF _ refl]) + apply (simp_all add: %(name)s_get_tag_eq_x %(name)s_tag_defs mask_shift_simps) + apply (intro conjI sign_extend_eq; simp add: mask_def word_ao_dist word_bw_assocs)? + done'''], + +} + + +def make_proof(name, substs, sorry=False): + result = proof_templates[name][0] % substs + '\n' + + if sorry: + result += '\nsorry' + else: + result += proof_templates[name][1] % substs + + if len(proof_templates[name]) > 2: + result += '\n' + '\n'.join(proof_templates[name][2:]) % substs + + return result + +# AST objects + + +def emit_named(name, params, string): + # Emit a named definition/proof, only when the given name is in + # params.names + + if(name in params.names): + print(string, file=params.output) + print(file=params.output) + +# This calculates substs for each proof, which is probably inefficient. Meh + + +def emit_named_ptr_proof(fn_name, params, name, type_map, toptps, prf_prefix, substs): + name_C = name + '_C' + + if name_C in type_map: + toptp, path = type_map[name_C] + + substs['access_path'] = '(' + reduce(lambda x, y: y + + ' (' + x + ')', ['the (ptrval s)'] + path) + ')' + + if len(path) == 0: + substs['update_path'] = name + emit_named(fn_name, params, make_proof(prf_prefix + '_direct', substs, params.sorry)) + else: + substs['toptp'] = toptp + # the split here gives us the field name (less any qualifiers) as the typ_heap + # stuff doesn't use the qualifier + substs['path'] = ', '.join(map(lambda x: "''%s''" % x.split('.')[-1], path)) + + # The self.name here is the variable name (so not _C) + path.reverse() + substs['update_path'] = '(' + reduce(lambda x, y: y + '_update (' + x + ')', + ['\\_. ' + name] + path) + '(the (ptrval s))' + ')' + emit_named(fn_name, params, make_proof(prf_prefix + '_path', substs, params.sorry)) + + +def field_mask_proof(base, base_bits, sign_extend, high, size): + if high: + if base_bits == base or sign_extend: + # equivalent to below, but nicer in proofs + return "NOT (mask %d)" % (base_bits - size) + else: + return "(mask %d << %d)" % (size, base_bits - size) + else: + return "mask %d" % size + + +def sign_extend_proof(high, base_bits, base_sign_extend): + if high and base_sign_extend: + return "sign_extend %d " % (base_bits - 1) + else: + return "" + + +def det_values(*dicts): + """Deterministically iterate over the values of each dict in `dicts`.""" + def values(d): + return (d[key] for key in sorted(d.keys())) + return itertools.chain(*(values(d) for d in dicts)) + + +# This calculates substs for each proof, which is probably inefficient. Meh +def emit_named_ptr_proof(fn_name, params, name, type_map, toptps, prf_prefix, substs): + name_C = name + '_C' + + if name_C in type_map: + toptp, path = type_map[name_C] + + substs['access_path'] = '(' + reduce(lambda x, y: y + ' (' + x + ')', ['the (ptrval s)'] + path) + ')' + + if len(path) == 0: + substs['update_path'] = name + emit_named(fn_name, params, make_proof(prf_prefix + '_direct', substs, params.sorry)) + else: + substs['toptp'] = toptp + # the split here gives us the field name (less any qualifiers) as the typ_heap + # stuff doesn't use the qualifier + substs['path'] = ', '.join(map(lambda x: "''%s''" % x.split('.')[-1], path)) + + # The self.name here is the variable name (so not _C) + path.reverse() + substs['update_path'] = '(' + reduce(lambda x, y: y + '_update (' + x + ')', + ['\\_. ' + name] + path) + '(the (ptrval s))' + ')' + emit_named(fn_name, params, make_proof(prf_prefix + '_path', substs, params.sorry)) + +class TaggedUnion: + def __init__(self, name, tagname, classes, tags): + self.name = name + self.tagname = tagname + self.constant_suffix = '' + + # Check for duplicate tags + used_names = set() + used_values = set() + for name, value in tags: + if name in used_names: + raise ValueError("Duplicate tag name %s" % name) + if value in used_values: + raise ValueError("Duplicate tag value %d" % value) + + used_names.add(name) + used_values.add(value) + self.classes = dict(classes) + self.tags = tags + + def resolve(self, params, symtab): + # Grab block references for tags + self.tags = [(name, value, symtab[name]) for name, value in self.tags] + self.make_classes(params) + + # Ensure that block sizes and tag size & position match for + # all tags in the union + union_base = None + union_size = None + for name, value, ref in self.tags: + _tag_offset, _tag_size, _tag_high = ref.field_map[self.tagname] + + if union_base is None: + union_base = ref.base + elif union_base != ref.base: + raise ValueError("Base mismatch for element %s" + " of tagged union %s" % (name, self.name)) + + if union_size is None: + union_size = ref.size + elif union_size != ref.size: + raise ValueError("Size mismatch for element %s" + " of tagged union %s" % (name, self.name)) + + if _tag_offset != self.tag_offset[_tag_size]: + raise ValueError("Tag offset mismatch for element %s" + " of tagged union %s" % (name, self.name)) + + self.assert_value_in_class(name, value, _tag_size) + + if _tag_high: + raise ValueError("Tag field is high-aligned for element %s" + " of tagged union %s" % (name, self.name)) + + # Flag block as belonging to a tagged union + ref.tagged = True + + self.union_base = union_base + self.union_size = union_size + + def set_base(self, base, base_bits, base_sign_extend, suffix): + self.base = base + self.multiple = self.union_size // base + self.constant_suffix = suffix + self.base_bits = base_bits + self.base_sign_extend = base_sign_extend + + tag_index = None + for w in self.tag_offset: + tag_offset = self.tag_offset[w] + + if tag_index is None: + tag_index = tag_offset // base + + if (tag_offset // base) != tag_index: + raise ValueError( + "The tag field of tagged union %s" + " is in a different word (%s) to the others (%s)." + % (self.name, hex(tag_offset // base), hex(tag_index))) + + def generate_hol_proofs(self, params, type_map): + output = params.output + + # Add fixed simp rule for struct + print("lemmas %(name)s_C_words_C_fl_simp[simp] = " + "%(name)s_C_words_C_fl[simplified]" % + {"name": self.name}, file=output) + print(file=output) + + # Generate struct field pointer proofs + substs = {"name": self.name, + "words": self.multiple, + "base": self.base} + + print(make_proof('words_NULL_proof', + substs, params.sorry), file=output) + print(file=output) + + print(make_proof('words_aligned_proof', + substs, params.sorry), file=output) + print(file=output) + + print(make_proof('words_ptr_safe_proof', + substs, params.sorry), file=output) + print(file=output) + + # Generate struct lemmas + print(struct_lemmas_template % {"name": self.name}, + file=output) + print(file=output) + + # Generate get_tag specs + substs = {"name": self.name, + "tagname": self.tagname, + "ret_name": return_name(self.base)} + + if not params.skip_modifies: + emit_named("%(name)s_get_%(tagname)s" % substs, params, + make_proof('const_modifies_proof', + {"fun_name": "%(name)s_get_%(tagname)s" % substs, + "args": ', '.join(["\ret__unsigned_long", + "\%(name)s" % substs])}, + params.sorry)) + emit_named("%(name)s_ptr_get_%(tagname)s" % substs, params, + make_proof('const_modifies_proof', + {"fun_name": "%(name)s_ptr_get_%(tagname)s" % substs, + "args": ', '.join(["\ret__unsigned_long", + "\%(name)s_ptr" % substs])}, + params.sorry)) + + emit_named("%s_get_%s" % (self.name, self.tagname), params, + make_proof('get_tag_spec', substs, params.sorry)) + + emit_named("%s_%s_equals" % (self.name, self.tagname), params, + make_proof('get_tag_equals_spec', substs, params.sorry)) + + # Only generate ptr lemmas for those types reachable from top level types + emit_named_ptr_proof("%s_ptr_get_%s" % (self.name, self.tagname), params, self.name, + type_map, params.toplevel_types, + 'ptr_get_tag_spec', substs) + + for name, value, ref in self.tags: + # Generate struct_new specs + arg_list = ["\" + field + for field in ref.visible_order + if field != self.tagname] + + # Generate modifies proof + if not params.skip_modifies: + emit_named("%s_%s_new" % (self.name, ref.name), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_%s_new" % + (self.name, ref.name), + "args": ', '.join([ + "\ret__struct_%(name)s_C" % substs] + + arg_list)}, + params.sorry)) + + emit_named("%s_%s_ptr_new" % (self.name, ref.name), params, + make_proof('ptr_set_modifies_proof', + {"fun_name": "%s_%s_ptr_new" % + (self.name, ref.name), + "args": ', '.join([ + "\ret__struct_%(name)s_C" % substs] + + arg_list)}, + params.sorry)) + + if len(arg_list) == 0: + # For an empty block: + emit_named("%s_%s_new" % (self.name, ref.name), params, + make_proof('empty_union_new_spec', + {"name": self.name, + "block": ref.name}, + params.sorry)) + + emit_named_ptr_proof("%s_%s_ptr_new" % (self.name, ref.name), params, self.name, + type_map, params.toplevel_types, + 'ptr_empty_union_new_spec', + {"name": self.name, + "block": ref.name}) + else: + field_eq_list = [] + for field in ref.visible_order: + offset, size, high = ref.field_map[field] + + if field == self.tagname: + continue + + mask = field_mask_proof(self.base, self.base_bits, + self.base_sign_extend, high, size) + sign_extend = sign_extend_proof(high, self.base_bits, self.base_sign_extend) + field_eq_list.append( + "%s_%s_CL.%s_CL = %s(\<^bsup>s\<^esup>%s AND %s)" % + (self.name, ref.name, field, sign_extend, + var_name(field, self.base), mask)) + field_eqs = ',\n '.join(field_eq_list) + + emit_named("%s_%s_new" % (self.name, ref.name), params, + make_proof('union_new_spec', + {"name": self.name, + "block": ref.name, + "args": ', '.join(arg_list), + "field_eqs": field_eqs}, + params.sorry)) + + emit_named_ptr_proof("%s_%s_ptr_new" % (self.name, ref.name), params, self.name, + type_map, params.toplevel_types, + 'ptr_union_new_spec', + {"name": self.name, + "block": ref.name, + "args": ', '.join(arg_list), + "field_eqs": field_eqs}) + + _, size, _ = ref.field_map[self.tagname] + if any([w for w in self.widths if w < size]): + tag_mask_helpers = ("%s_%s_tag_mask_helpers" + % (self.name, ref.name)) + else: + tag_mask_helpers = "" + + # Generate get/set specs + for (field, offset, size, high) in ref.fields: + if field == self.tagname: + continue + + mask = field_mask_proof(self.base, self.base_bits, + self.base_sign_extend, high, size) + sign_extend = sign_extend_proof(high, self.base_bits, self.base_sign_extend) + + substs = {"name": self.name, + "block": ref.name, + "field": field, + "mask": mask, + "sign_extend": sign_extend, + "tag_mask_helpers": tag_mask_helpers, + "ret_name": return_name(self.base), + "base": self.base} + + # Get modifies spec + if not params.skip_modifies: + emit_named("%s_%s_get_%s" % (self.name, ref.name, field), + params, + make_proof('const_modifies_proof', + {"fun_name": "%s_%s_get_%s" % + (self.name, ref.name, field), + "args": ', '.join([ + "\ret__unsigned_long", + "\%s" % self.name])}, + params.sorry)) + + emit_named("%s_%s_ptr_get_%s" % (self.name, ref.name, field), + params, + make_proof('const_modifies_proof', + {"fun_name": "%s_%s_ptr_get_%s" % + (self.name, ref.name, field), + "args": ', '.join([ + "\ret__unsigned_long", + "\%s_ptr" % self.name])}, + params.sorry)) + + # Get spec + emit_named("%s_%s_get_%s" % (self.name, ref.name, field), + params, + make_proof('union_get_spec', substs, params.sorry)) + + # Set modifies spec + if not params.skip_modifies: + emit_named("%s_%s_set_%s" % (self.name, ref.name, field), + params, + make_proof('const_modifies_proof', + {"fun_name": "%s_%s_set_%s" % + (self.name, ref.name, field), + "args": ', '.join([ + "\ret__struct_%s_C" % self.name, + "\%s" % self.name, + "\v%(base)d"])}, + params.sorry)) + + emit_named("%s_%s_ptr_set_%s" % (self.name, ref.name, field), + params, + make_proof('ptr_set_modifies_proof', + {"fun_name": "%s_%s_ptr_set_%s" % + (self.name, ref.name, field), + "args": ', '.join([ + "\%s_ptr" % self.name, + "\v%(base)d"])}, + params.sorry)) + + # Set spec + emit_named("%s_%s_set_%s" % (self.name, ref.name, field), + params, + make_proof('union_set_spec', substs, params.sorry)) + + # Pointer get spec + emit_named_ptr_proof("%s_%s_ptr_get_%s" % (self.name, ref.name, field), + params, self.name, type_map, params.toplevel_types, + 'ptr_union_get_spec', substs) + + # Pointer set spec + emit_named_ptr_proof("%s_%s_ptr_set_%s" % (self.name, ref.name, field), + params, self.name, type_map, params.toplevel_types, + 'ptr_union_set_spec', substs) + + def generate_hol_defs(self, params): + output = params.output + + empty_blocks = {} + + def gen_name(ref_name, capitalise=False): + # Create datatype generator/type name for a block + if capitalise: + return "%s_%s" % \ + (self.name[0].upper() + self.name[1:], ref_name) + else: + return "%s_%s" % (self.name, ref_name) + + # Generate block records with tag field removed + for name, value, ref in self.tags: + if ref.generate_hol_defs(params, + suppressed_field=self.tagname, + prefix="%s_" % self.name, + in_union=True): + empty_blocks[ref] = True + + constructor_exprs = [] + for name, value, ref in self.tags: + if ref in empty_blocks: + constructor_exprs.append(gen_name(name, True)) + else: + constructor_exprs.append("%s %s_CL" % + (gen_name(name, True), gen_name(name))) + + print("datatype %s_CL =\n %s\n" % + (self.name, '\n | '.join(constructor_exprs)), + file=output) + + # Generate get_tag definition + subs = {"name": self.name, + "base": self.base} + + templates = ([union_get_tag_def_entry_template] * (len(self.widths) - 1) + + [union_get_tag_def_final_template]) + + fs = (union_get_tag_def_header_template % subs + + "".join([template % + dict(subs, + tag_size=width, + classmask=self.word_classmask(width), + tag_index=self.tag_offset[width] // self.base, + tag_shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + union_get_tag_def_footer_template % subs) + + print(fs, file=output) + print(file=output) + + # Generate get_tag_eq_x lemma + templates = ([union_get_tag_eq_x_def_entry_template] + * (len(self.widths) - 1) + + [union_get_tag_eq_x_def_final_template]) + + fs = (union_get_tag_eq_x_def_header_template % subs + + "".join([template % + dict(subs, + tag_size=width, + classmask=self.word_classmask(width), + tag_index=self.tag_offset[width] // self.base, + tag_shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + union_get_tag_eq_x_def_footer_template % subs) + + print(fs, file=output) + print(file=output) + + # Generate mask helper lemmas + + for name, value, ref in self.tags: + offset, size, _ = ref.field_map[self.tagname] + part_widths = [w for w in self.widths if w < size] + if part_widths: + subs = {"name": self.name, + "block": name, + "full_mask": hex(2 ** size - 1), + "full_value": hex(value)} + + fs = (union_tag_mask_helpers_header_template % subs + + "".join([union_tag_mask_helpers_entry_template % + dict(subs, part_mask=hex(2 ** pw - 1), + part_value=hex(value & (2 ** pw - 1))) + for pw in part_widths]) + + union_tag_mask_helpers_footer_template) + + print(fs, file=output) + print(file=output) + + # Generate lift definition + collapse_proofs = "" + tag_cases = [] + for name, value, ref in self.tags: + field_inits = [] + + for field in ref.visible_order: + offset, size, high = ref.field_map[field] + + if field == self.tagname: + continue + + index = offset // self.base + sign_extend = "" + + if high: + shift_op = "<<" + shift = self.base_bits - size - (offset % self.base) + if shift < 0: + shift = -shift + shift_op = ">>" + if self.base_sign_extend: + sign_extend = "sign_extend %d " % (self.base_bits - 1) + else: + shift_op = ">>" + shift = offset % self.base + + initialiser = \ + "%s_CL.%s_CL = %s(((index (%s_C.words_C %s) %d) %s %d)" % \ + (gen_name(name), field, sign_extend, self.name, self.name, + index, shift_op, shift) + + if size < self.base: + mask = field_mask_proof(self.base, self.base_bits, + self.base_sign_extend, high, size) + initialiser += " AND " + mask + + field_inits.append("\n " + initialiser + ")") + + if len(field_inits) == 0: + value = gen_name(name, True) + else: + value = "%s \ %s \" % \ + (gen_name(name, True), ','.join(field_inits)) + + tag_cases.append("if tag = scast %s then Some (%s)" % + (gen_name(name), value)) + + collapse_proofs += \ + make_proof("lift_collapse_proof", + {"name": self.name, + "block": name, + "value": value}, + params.sorry) + collapse_proofs += "\n\n" + + print(union_lift_def_template % + {"name": self.name, + "tag_cases": '\n else '.join(tag_cases)}, + file=output) + print(file=output) + + print(collapse_proofs, file=output) + + block_lift_lemmas = "lemmas %s_lifts = \n" % self.name + # Generate lifted access/update definitions, and abstract lifters + for name, value, ref in self.tags: + # Don't generate accessors if the block (minus tag) is empty + if ref in empty_blocks: + continue + + substs = {"union": self.name, + "block": name, + "generator": gen_name(name, True)} + + for t in [union_access_def_template, union_update_def_template]: + print(t % substs, file=output) + print(file=output) + + print(block_lift_def_template % substs, file=output) + print(file=output) + + print(block_lift_lemma_template % substs, file=output) + print(file=output) + + block_lift_lemmas += "\t%(union)s_%(block)s_lift\n" % substs + + print(block_lift_lemmas, file=output) + print(file=output) + + def generate_hol_defs(self, params, suppressed_field=None, \ + prefix="", in_union = False): + output = params.output + + # Don't generate raw records for blocks in tagged unions + if self.tagged and not in_union: return + + _name = prefix + self.name + + # Generate record def + out = "record %s_CL =\n" % _name + + empty = True + + for field in self.visible_order: + if suppressed_field == field: + continue + + empty = False + + out += ' %s_CL :: "word%d"\n' % (field, self.base) + + word_updates = "" + + if not empty: + print(out, file=output) + + # Generate lift definition + if not in_union: + field_inits = [] + + for name in self.visible_order: + offset, size, high = self.field_map[name] + + index = offset // self.base + + if high: + shift_op = "<<" + shift = self.base - size - (offset % self.base) + else: + shift_op = ">>" + shift = offset % self.base + + initialiser = \ + "%s_CL.%s_CL = ((index (%s_C.words_C %s) %d) %s %d)" % \ + (self.name, name, self.name, self.name, \ + index, shift_op, shift) + + if size < self.base: + if high: + mask = ((1 << size) - 1) << (self.base_bits - size) + else: + mask = (1 << size) - 1 + + initialiser += " AND %d" % mask + + field_inits.append(initialiser) + + print(lift_def_template % \ + {"name": self.name, \ + "fields": ',\n '.join(field_inits)}, + file=output) + print(file=output) + + return empty + + def generate_hol_proofs(self, params, type_map): + output = params.output + + if self.tagged: return + + # Add fixed simp rule for struct + print("lemmas %(name)s_C_words_C_fl_simp[simp] = "\ + "%(name)s_C_words_C_fl[simplified]" % \ + {"name": self.name}, file=output) + print(file=output) + + # Generate struct field pointer proofs + substs = {"name": self.name, + "words": self.multiple} + + print(make_proof('words_NULL_proof', + substs, params.sorry), file=output) + print(file=output) + + print(make_proof('words_aligned_proof', + substs, params.sorry), file=output) + print(file=output) + + print(make_proof('words_ptr_safe_proof', + substs, params.sorry), file=output) + print(file=output) + + # Generate struct lemmas + print(struct_lemmas_template % {"name": self.name}, + file=output) + print(file=output) + + # Generate struct_new specs + arg_list = ["\" + field for + (field, offset, size, high) in self.fields] + + if not params.skip_modifies: + emit_named("%s_new" % self.name, params, + make_proof('const_modifies_proof', + {"fun_name": "%s_new" % self.name, \ + "args": ', '.join(["\ret__struct_%s_C" % \ + self.name] + \ + arg_list)}, + params.sorry)) + # FIXME: ptr_new (doesn't seem to be used) + + field_eq_list = [] + for (field, offset, size, high) in self.fields: + if high: + mask = "NOT (mask %d)" % (self.base - size) + else: + mask = "(mask %d)" % size + + field_eq_list.append("%s_CL.%s_CL = \<^bsup>s\<^esup>%s AND %s" % \ + (self.name, field, field, mask)) + field_eqs = ',\n '.join(field_eq_list) + + emit_named("%s_new" % self.name, params, + make_proof('new_spec', + {"name": self.name, \ + "args": ', '.join(arg_list), \ + "field_eqs": field_eqs}, + params.sorry)) + + emit_named_ptr_proof("%s_ptr_new" % self.name, params, self.name, + type_map, params.toplevel_types, + 'ptr_new_spec', + {"name": self.name, \ + "args": ', '.join(arg_list), \ + "field_eqs": field_eqs}) + + # Generate get/set specs + for (field, offset, size, high) in self.fields: + if high: + mask = "NOT (mask %d)" % (self.base - size) + else: + mask = "(mask %d)" % size + + substs = {"name": self.name, \ + "field": field, \ + "mask": mask, + "ret_name": return_name} + + if not params.skip_modifies: + # Get modifies spec + emit_named("%s_get_%s" % (self.name, field), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_get_%s" % (self.name, field), \ + "args": ', '.join([ + "\ret__unsigned_long", + "\%s" % self.name] )}, + params.sorry)) + + # Ptr get modifies spec + emit_named("%s_ptr_get_%s" % (self.name, field), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_ptr_get_%s" % (self.name, field), \ + "args": ', '.join([ + "\ret__unsigned_long", + "\%s_ptr" % self.name] )}, + params.sorry)) + + + # Get spec + emit_named("%s_get_%s" % (self.name, field), params, + make_proof('get_spec', substs, params.sorry)) + + if not params.skip_modifies: + # Set modifies spec + emit_named("%s_set_%s" % (self.name, field), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_set_%s" % (self.name, field), \ + "args": ', '.join([ + "\ret__struct_%s_C" % self.name, + "\%s" % self.name, + "\v"] )}, + params.sorry)) + + emit_named("%s_ptr_set_%s" % (self.name, field), params, + make_proof('ptr_set_modifies_proof', + {"fun_name": "%s_ptr_set_%s" % (self.name, field), \ + "args": ', '.join([ + "\%s_ptr" % self.name, + "\v"] )}, + params.sorry)) + + + # Set spec + emit_named("%s_set_%s" % (self.name, field), params, + make_proof('set_spec', substs, params.sorry)) + + emit_named_ptr_proof("%s_ptr_get_%s" % (self.name, field), params, self.name, + type_map, params.toplevel_types, + 'ptr_get_spec', substs) + emit_named_ptr_proof("%s_ptr_set_%s" % (self.name, field), params, self.name, + type_map, params.toplevel_types, + 'ptr_set_spec', substs) + + def rust_generate(self, params): + output = params.output + + # Generate type + print(rust_type_template % \ + {"type": "usize", \ + "name": self.name, \ + "multiple": self.multiple}, file=output) + print(file=output) + + # Generate tag enum + print("#[repr(C)]") + print("pub enum %sTag {" % self.name, file=output) + if len(self.tags) > 0: + for name, value, ref in self.tags[:-1]: + print(" %s_%s = %d," % (self.name, name, value), file=output) + name, value, ref = self.tags[-1]; + print(" %s_%s = %d" % (self.name, name, value), file=output) + print("}", file=output) + print(file=output) + + subs = {\ + 'union': self.name, \ + 'type': "usize", \ + 'tagname': self.tagname, \ + 'suf' : self.constant_suffix} + + # Generate tag reader + templates = ([rust_tag_reader_entry_template] * (len(self.widths) - 1) + + [rust_tag_reader_final_template]) + + fs = (rust_tag_reader_header_template % subs + + "".join([template % + dict(subs, + mask=2 ** width - 1, + classmask=self.word_classmask(width), + index=self.tag_offset[width] // self.base, + shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + rust_tag_reader_footer_template % subs) + + emit_named("%s_get_%s" % (self.name, self.tagname), params, fs) + + # Generate tag eq reader + + templates = ([rust_tag_eq_reader_entry_template] * (len(self.widths) - 1) + + [rust_tag_eq_reader_final_template]) + + fs = (rust_tag_eq_reader_header_template % subs + + "".join([template % + dict(subs, + mask=2 ** width - 1, + classmask=self.word_classmask(width), + index=self.tag_offset[width] // self.base, + shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + rust_tag_eq_reader_footer_template % subs) + + emit_named("%s_%s_equals" % (self.name, self.tagname), params, fs) + + for name, value, ref in self.tags: + # Generate generators + arg_list = ["%s %s" % ("usize", field) for \ + field in ref.visible_order if + field != self.tagname] + + if len(arg_list) == 0: + args = 'void' + else: + args = ', '.join(arg_list) + + ptr_args = ', '.join(["%s_ptr: *mut %s" % (self.name, self.name)] + \ + arg_list) + + word_inits = [" %s.words[%d] = 0;" % (self.name, i) \ + for i in range(self.multiple)] + + ptr_word_inits = [" unsafe { (*%s_ptr).words[%d] = 0 };" % (self.name, i) \ + for i in range(self.multiple)] + + field_inits = [] + ptr_field_inits = [] + for field in ref.visible_order: + offset, size, high = ref.field_map[field] + + if field == self.tagname: + f_value = "(%s)%s_%s" % ("usize", self.name, name) + else: + f_value = field + + index = offset // self.base + if high: + shift_op = ">>" + shift = self.base_bits - size - (offset % self.base) + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << (self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + if shift < 0: + shift = -shift + shift_op = "<<" + else: + shift_op = "<<" + shift = offset % self.base + high_bits = 0 + if size < self.base: + if high: + mask = ((1 << size) - 1) << (self.base_bits - size) + else: + mask = (1 << size) - 1 + suf = self.constant_suffix + + field_inits.append( + " /* fail if user has passed bits that we will override */") + field_inits.append( + " %s!((%s & !0x%x%s) == (if (0 != (%s & (1%s << %d))) { 0x%x } else { 0 }));" % (RUST_ASSERTS[options.environment], f_value, mask, suf, f_value, suf, self.base_bits - 1, high_bits)) + field_inits.append( + " %s.words[%d] |= (%s & 0x%x%s) %s %d;" % \ + (self.name, index, f_value, mask, suf, shift_op, shift)) + + ptr_field_inits.append( + " /* fail if user has passed bits that we will override */") + ptr_field_inits.append( + " %s!((%s & !0x%x%s) == (if (0 != (%s & (1%s << %d))) { 0x%x } else { 0 }));" % (RUST_ASSERTS[options.environment], f_value, mask, suf, f_value, suf, self.base_bits - 1, high_bits)) + ptr_field_inits.append( + " unsafe { (*%s_ptr).words[%d] |= (%s & 0x%x%s) %s %d };" % \ + (self.name, index, f_value, mask, suf, shift_op, shift)) + else: + field_inits.append( + " %s.words[%d] |= %s %s %d;" % \ + (self.name, index, f_value, shift_op, shift)) + + ptr_field_inits.append( + " unsafe { (*%s_ptr).words[%d] |= %s %s %d };" % \ + (self.name, index, f_value, shift_op, shift)) + + # Generate field readers/writers + tagnameoffset, tagnamesize, _ = ref.field_map[self.tagname] + tagmask = (2 ** tagnamesize) - 1 + for field, offset, size, high in ref.fields: + # Don't duplicate tag accessors + if field == self.tagname: continue + + index = offset // self.base + if high: + write_shift = ">>" + read_shift = "<<" + shift = self.base_bits - size - (offset % self.base) + if shift < 0: + shift = -shift + write_shift = "<<" + read_shift = ">>" + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << (self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + else: + write_shift = "<<" + read_shift = ">>" + shift = offset % self.base + high_bits = 0 + mask = ((1 << size) - 1) << (offset % self.base) + + subs = {\ + "block": self.name, \ + "field": field, \ + "type": "usize", \ + "assert": RUST_ASSERTS[options.environment], \ + "index": index, \ + "shift": shift, \ + "r_shift_op": read_shift, \ + "w_shift_op": write_shift, \ + "mask": mask, \ + "tagindex": tagnameoffset // self.base, \ + "tagshift": tagnameoffset % self.base, \ + "tagmask": tagmask, \ + "union": self.name, \ + "suf": self.constant_suffix, + "high_bits": high_bits, + "sign_extend": self.base_sign_extend and high, + "extend_bit": self.base_bits - 1} + + emit_named("%s_%s_get_%s" % (self.name, ref.name, field), + params, rust_reader_template % subs) + emit_named("%s_%s_set_%s" % (self.name, ref.name, field), + params, rust_writer_template % subs) + + + def c_generate(self, params): + output = params.output + + # Generate typedef + print(c_typedef_template % + {"type": C_TYPES[options.environment][self.base], + "name": self.name, + "multiple": self.multiple}, file=output) + print(file=output) + + # Generate tag enum + print("enum %s_tag {" % self.name, file=output) + if len(self.tags) > 0: + for name, value, ref in self.tags[:-1]: + print(" %s_%s = %d," % (self.name, name, value), + file=output) + name, value, ref = self.tags[-1] + print(" %s_%s = %d" % (self.name, name, value), + file=output) + print("};", file=output) + print("typedef enum %s_tag %s_tag_t;" % + (self.name, self.name), file=output) + print(file=output) + + subs = { + 'inline': C_INLINE[options.environment], + 'union': self.name, + 'type': C_TYPES[options.environment][self.union_base], + 'tagname': self.tagname, + 'suf': self.constant_suffix} + + # Generate tag reader + templates = ([c_tag_reader_entry_template] * (len(self.widths) - 1) + + c_[tag_reader_final_template]) + + fs = (c_tag_reader_header_template % subs + + "".join([template % + dict(subs, + mask=2 ** width - 1, + classmask=self.word_classmask(width), + index=self.tag_offset[width] // self.base, + shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + c_tag_reader_footer_template % subs) + + emit_named("%s_get_%s" % (self.name, self.tagname), params, fs) + + # Generate tag eq reader + templates = ([c_tag_eq_reader_entry_template] * (len(self.widths) - 1) + + [c_tag_eq_reader_final_template]) + + fs = (c_tag_eq_reader_header_template % subs + + "".join([template % + dict(subs, + mask=2 ** width - 1, + classmask=self.word_classmask(width), + index=self.tag_offset[width] // self.base, + shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + c_tag_eq_reader_footer_template % subs) + + emit_named("%s_%s_equals" % (self.name, self.tagname), params, fs) + + # Generate pointer lifted tag reader + templates = ([c_ptr_tag_reader_entry_template] * (len(self.widths) - 1) + + [c_ptr_tag_reader_final_template]) + + fs = (c_ptr_tag_reader_header_template % subs + + "".join([template % + dict(subs, + mask=2 ** width - 1, + classmask=self.word_classmask(width), + index=self.tag_offset[width] // self.base, + shift=self.tag_offset[width] % self.base) + for template, width in zip(templates, self.widths)]) + + c_ptr_tag_reader_footer_template % subs) + + emit_named("%s_ptr_get_%s" % (self.name, self.tagname), params, fs) + + for name, value, ref in self.tags: + # Generate generators + param_fields = [field for field in ref.visible_order if field != self.tagname] + param_list = ["%s %s" % (C_TYPES[options.environment][self.base], field) + for field in param_fields] + + if len(param_list) == 0: + gen_params = 'void' + else: + gen_params = ', '.join(param_list) + + ptr_params = ', '.join(["%s_t *%s_ptr" % (self.name, self.name)] + param_list) + + field_updates = {word: [] for word in range(self.multiple)} + field_asserts = [" /* fail if user has passed bits that we will override */"] + + for field in ref.visible_order: + offset, size, high = ref.field_map[field] + + if field == self.tagname: + f_value = "(%s)%s_%s" % (C_TYPES[options.environment][self.base], self.name, name) + else: + f_value = field + + index = offset // self.base + if high: + shift_op = ">>" + shift = self.base_bits - size - (offset % self.base) + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << ( + self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + if shift < 0: + shift = -shift + shift_op = "<<" + else: + shift_op = "<<" + shift = offset % self.base + high_bits = 0 + if size < self.base: + if high: + mask = ((1 << size) - 1) << (self.base_bits - size) + else: + mask = (1 << size) - 1 + suf = self.constant_suffix + + field_asserts.append( + " %s((%s & ~0x%x%s) == ((%d && (%s & (1%s << %d))) ? 0x%x : 0));" + % (C_ASSERTS[options.environment], f_value, mask, suf, self.base_sign_extend, + f_value, suf, self.base_bits - 1, high_bits)) + + field_updates[index].append( + "(%s & 0x%x%s) %s %d" % (f_value, mask, suf, shift_op, shift)) + + else: + field_updates[index].append("%s %s %d" % (f_value, shift_op, shift)) + + word_inits = [ + ("words[%d] = 0" % index) + ''.join(["\n | %s" % up for up in ups]) + ';' + for (index, ups) in field_updates.items()] + + def mk_inits(prefix): + return '\n'.join([" %s%s" % (prefix, word_init) for word_init in word_inits]) + + print_params = { + "inline": C_INLINE[options.environment], + "block": name, + "union": self.name, + "gen_params": gen_params, + "ptr_params": ptr_params, + "gen_inits": mk_inits("%s." % self.name), + "ptr_inits": mk_inits("%s_ptr->" % self.name), + "asserts": ' \n'.join(field_asserts) + } + + generator = c_union_generator_template % print_params + ptr_generator = c_ptr_union_generator_template % print_params + + emit_named("%s_%s_new" % (self.name, name), params, generator) + emit_named("%s_%s_ptr_new" % (self.name, name), params, ptr_generator) + + # Generate field readers/writers + tagnameoffset, tagnamesize, _ = ref.field_map[self.tagname] + tagmask = (2 ** tagnamesize) - 1 + for field, offset, size, high in ref.fields: + # Don't duplicate tag accessors + if field == self.tagname: + continue + + index = offset // self.base + if high: + write_shift = ">>" + read_shift = "<<" + shift = self.base_bits - size - (offset % self.base) + if shift < 0: + shift = -shift + write_shift = "<<" + read_shift = ">>" + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << ( + self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + else: + write_shift = "<<" + read_shift = ">>" + shift = offset % self.base + high_bits = 0 + mask = ((1 << size) - 1) << (offset % self.base) + + subs = { + "inline": C_INLINE[options.environment], + "block": ref.name, + "field": field, + "type": C_TYPES[options.environment][ref.base], + "assert": C_ASSERTS[options.environment], + "index": index, + "shift": shift, + "r_shift_op": read_shift, + "w_shift_op": write_shift, + "mask": mask, + "tagindex": tagnameoffset // self.base, + "tagshift": tagnameoffset % self.base, + "tagmask": tagmask, + "union": self.name, + "suf": self.constant_suffix, + "high_bits": high_bits, + "sign_extend": self.base_sign_extend and high, + "extend_bit": self.base_bits - 1, + "base": self.base} + + # Reader + emit_named("%s_%s_get_%s" % (self.name, ref.name, field), + params, c_union_reader_template % subs) + + # Writer + emit_named("%s_%s_set_%s" % (self.name, ref.name, field), + params, c_union_writer_template % subs) + + # Pointer lifted reader + emit_named("%s_%s_ptr_get_%s" % (self.name, ref.name, field), + params, c_ptr_union_reader_template % subs) + + # Pointer lifted writer + emit_named("%s_%s_ptr_set_%s" % (self.name, ref.name, field), + params, c_ptr_union_writer_template % subs) + + def make_names(self): + "Return the set of candidate function names for a union" + + substs = {"union": self.name, + "tagname": self.tagname} + names = [t % substs for t in [ + "%(union)s_get_%(tagname)s", + "%(union)s_ptr_get_%(tagname)s", + "%(union)s_%(tagname)s_equals"]] + + for name, value, ref in self.tags: + names += ref.make_names(self) + + return names + + def represent_value(self, value, width): + max_width = max(self.classes.keys()) + + tail_str = ("{:0{}b}".format(value, width) + + "_" * (self.tag_offset[width] - self.class_offset)) + head_str = "_" * ((max_width + self.tag_offset[max_width] + - self.class_offset) - len(tail_str)) + + return head_str + tail_str + + def represent_class(self, width): + max_width = max(self.classes.keys()) + + cmask = self.classes[width] + return ("{:0{}b}".format(cmask, max_width).replace("0", "-") + + " ({:#x})".format(cmask)) + + def represent_field(self, width): + max_width = max(self.classes.keys()) + offset = self.tag_offset[width] - self.class_offset + + return ("{:0{}b}".format((2 ** width - 1) << offset, max_width) + .replace("0", "-").replace("1", "#")) + + def assert_value_in_class(self, name, value, width): + max_width = max(self.classes.keys()) + ovalue = value << self.tag_offset[width] + cvalue = value << (self.tag_offset[width] - self.class_offset) + + offset_field = (2 ** width - 1) << self.tag_offset[width] + if (ovalue | offset_field) != offset_field: + raise ValueError( + "The value for element %s of tagged union %s,\n" + " %s\nexceeds the field bounds\n" + " %s." + % (name, self.name, + self.represent_value(value, width), + self.represent_field(width))) + + for w, mask in [(lw, self.classes[lw]) + for lw in self.widths if lw < width]: + if (cvalue & mask) != mask: + raise ValueError( + "The value for element %s of tagged union %s,\n" + " %s\nis invalid: it has %d bits but fails" + " to match the earlier mask at %d bits,\n" + " %s." + % (name, self.name, + self.represent_value(value, width), + width, w, self.represent_class(w))) + + if (self.widths.index(width) + 1 < len(self.widths) and + (cvalue & self.classes[width]) == self.classes[width]): + raise ValueError( + "The value for element %s of tagged union %s,\n" + " %s (%d/%s)\nis invalid: it must not match the " + "mask for %d bits,\n %s." + % (name, self.name, + ("{:0%db}" % width).format(cvalue), + value, hex(value), + width, + self.represent_class(width))) + + def word_classmask(self, width): + "Return a class mask for testing a whole word, i.e., one." + "that is positioned absolutely relative to the lsb of the" + "relevant word." + + return (self.classes[width] << (self.class_offset % self.base)) + + def make_classes(self, params): + "Calculate an encoding for variable width tagnames" + + # Check self.classes, which maps from the bit width of tagname in a + # particular block to a classmask that identifies when a value belongs + # to wider tagname. + # + # For example, given three possible field widths -- 4, 8, and 12 bits -- + # one possible encoding is: + # + # * * _ _ (** != 11) + # 0 _ _ _ 1 1 _ _ + # _ _ _ _ 1 _ _ _ 1 1 _ _ + # + # where the 3rd and 4th lsbs signify whether the field should be + # interpreted using a 4-bit mask (if 00, 01, or 10) or as an 8 or 16 bit + # mask (if 11). And, in the latter case, the 8th lsb signifies whether + # to intrepret it as an 8 bit field (if 0) or a 16 bit field (if 1). + # + # In this example we have: + # 4-bit class: classmask = 0b00001100 + # 8-bit class: classmask = 0b10001100 + # 16-bit class: classmask = 0b10001100 + # + # More generally, the fields need not all start at the same offset + # (measured "left" from the lsb), for example: + # + # ...# ###. .... .... 4-bit field at offset 9 + # ..## #### ##.. .... 8-bit field at offset 6 + # #### #### #### .... 12-bit field at offset 4 + # + # In this case, the class_offset is the minimum offset (here 4). + # Classmasks are declared relative to the field, but converted + # internally to be relative to the class_offset; tag_offsets + # are absolute (relative to the lsb); values are relative to + # the tag_offset (i.e., within the field). for example: + # + # ...1 100. .... 4-bit class: classmask=0xc tag_offset=9 + # ..01 1000 10.. 8-bit class: classmask=0x62 tag_offset=6 + # 0001 1000 1000 16-bit class: classmask=0x188 tag_offset=4 + + used = set() + self.tag_offset = {} + for name, _, ref in self.tags: + offset, size, _ = ref.field_map[self.tagname] + used.add(size) + self.tag_offset[size] = offset + + self.class_offset = min(self.tag_offset.values()) + + # internally, classmasks are relative to the class_offset, so + # that we can compare them to each other. + for w in self.classes: + self.classes[w] <<= self.tag_offset[w] - self.class_offset + + used_widths = sorted(list(used)) + assert(len(used_widths) > 0) + + if not self.classes: + self.classes = {used_widths[0]: 0} + + # sanity checks on classes + classes = self.classes + widths = sorted(self.classes.keys()) + context = "masks for %s.%s" % (self.name, self.tagname) + class_offset = self.class_offset + + for uw in used_widths: + if uw not in classes: + raise ValueError("%s: none defined for a field of %d bits." + % (context, uw)) + + for mw in classes.keys(): + if mw not in used_widths: + raise ValueError( + "%s: there is a mask with %d bits but no corresponding fields." + % (context, mw)) + + for w in widths: + offset_field = (2 ** w - 1) << self.tag_offset[w] + if (classes[w] << class_offset) | offset_field != offset_field: + raise ValueError( + "{:s}: the mask for {:d} bits:\n {:s}\n" + "exceeds the field bounds:\n {:s}." + .format(context, w, self.represent_class(w), + self.represent_field(w))) + + if len(widths) > 1 and classes[widths[0]] == 0: + raise ValueError("%s: the first (width %d) is zero." % ( + context, widths[0])) + + if any([classes[widths[i-1]] == classes[widths[i]] + for i in range(1, len(widths) - 1)]): + raise ValueError("%s: there is a non-final duplicate!" % context) + + # smaller masks are included within larger masks + pre_mask = None + pre_width = None + for w in widths: + if pre_mask is not None and (classes[w] & pre_mask) != pre_mask: + raise ValueError( + "{:s}: the mask\n 0b{:b} for width {:d} does not include " + "the mask\n 0b{:b} for width {:d}.".format( + context, classes[w], w, pre_mask, pre_width)) + pre_width = w + pre_mask = classes[w] + + if params.showclasses: + print("-----%s.%s" % (self.name, self.tagname), file=sys.stderr) + for w in widths: + print("{:2d} = {:s}".format(w, self.represent_class(w)), + file=sys.stderr) + + self.widths = widths + + +class Block: + def __init__(self, name, fields, visible_order): + offset = 0 + _fields = [] + self.size = sum(size for _name, size, _high in fields) + offset = self.size + self.constant_suffix = '' + + if visible_order is None: + self.visible_order = [] + + for _name, _size, _high in fields: + offset -= _size + if not _name is None: + if visible_order is None: + self.visible_order.append(_name) + _fields.append((_name, offset, _size, _high)) + + self.name = name + self.tagged = False + self.fields = _fields + self.field_map = dict((name, (offset, size, high)) + for name, offset, size, high in _fields) + + if not visible_order is None: + missed_fields = set(self.field_map.keys()) + + for _name in visible_order: + if _name not in self.field_map: + raise ValueError("Nonexistent field '%s' in visible_order" + % _name) + missed_fields.remove(_name) + + if len(missed_fields) > 0: + raise ValueError("Fields %s missing from visible_order" % + str([x for x in missed_fields])) + + self.visible_order = visible_order + + def set_base(self, base, base_bits, base_sign_extend, suffix): + self.base = base + self.constant_suffix = suffix + self.base_bits = base_bits + self.base_sign_extend = base_sign_extend + if self.size % base != 0: + raise ValueError("Size of block %s not a multiple of base" + % self.name) + self.multiple = self.size // base + for name, offset, size, high in self.fields: + if offset // base != (offset+size-1) // base: + raise ValueError("Field %s of block %s " + "crosses a word boundary" + % (name, self.name)) + + def generate_hol_defs(self, params, suppressed_field=None, + prefix="", in_union=False): + output = params.output + + # Don't generate raw records for blocks in tagged unions + if self.tagged and not in_union: + return + + _name = prefix + self.name + + # Generate record def + out = "record %s_CL =\n" % _name + + empty = True + + for field in self.visible_order: + if suppressed_field == field: + continue + + empty = False + + out += ' %s_CL :: "word%d"\n' % (field, self.base) + + word_updates = "" + + if not empty: + print(out, file=output) + + # Generate lift definition + if not in_union: + field_inits = [] + + for name in self.visible_order: + offset, size, high = self.field_map[name] + + index = offset // self.base + sign_extend = "" + + if high: + shift_op = "<<" + shift = self.base_bits - size - (offset % self.base) + if shift < 0: + shift = -shift + shift_op = ">>" + if self.base_sign_extend: + sign_extend = "sign_extend %d " % (self.base_bits - 1) + else: + shift_op = ">>" + shift = offset % self.base + + initialiser = \ + "%s_CL.%s_CL = %s(((index (%s_C.words_C %s) %d) %s %d)" % \ + (self.name, name, sign_extend, self.name, self.name, + index, shift_op, shift) + + if size < self.base: + mask = field_mask_proof(self.base, self.base_bits, + self.base_sign_extend, high, size) + + initialiser += " AND " + mask + + field_inits.append(initialiser + ")") + + print(lift_def_template % + {"name": self.name, + "fields": ',\n '.join(field_inits)}, + file=output) + print(file=output) + + return empty + + def generate_hol_proofs(self, params, type_map): + output = params.output + + if self.tagged: + return + + # Add fixed simp rule for struct + print("lemmas %(name)s_C_words_C_fl_simp[simp] = " + "%(name)s_C_words_C_fl[simplified]" % + {"name": self.name}, file=output) + print(file=output) + + # Generate struct field pointer proofs + substs = {"name": self.name, + "words": self.multiple, + "base": self.base} + + print(make_proof('words_NULL_proof', + substs, params.sorry), file=output) + print(file=output) + + print(make_proof('words_aligned_proof', + substs, params.sorry), file=output) + print(file=output) + + print(make_proof('words_ptr_safe_proof', + substs, params.sorry), file=output) + print(file=output) + + # Generate struct lemmas + print(struct_lemmas_template % {"name": self.name}, + file=output) + print(file=output) + + # Generate struct_new specs + arg_list = ["\" + field for field in self.visible_order] + + if not params.skip_modifies: + emit_named("%s_new" % self.name, params, + make_proof('const_modifies_proof', + {"fun_name": "%s_new" % self.name, + "args": ', '.join(["\ret__struct_%s_C" % + self.name] + + arg_list)}, + params.sorry)) + # FIXME: ptr_new (doesn't seem to be used) + + field_eq_list = [] + for field in self.visible_order: + offset, size, high = self.field_map[field] + mask = field_mask_proof(self.base, self.base_bits, self.base_sign_extend, high, size) + sign_extend = sign_extend_proof(high, self.base_bits, self.base_sign_extend) + + field_eq_list.append("%s_CL.%s_CL = %s(\<^bsup>s\<^esup>%s AND %s)" % + (self.name, field, sign_extend, field, mask)) + field_eqs = ',\n '.join(field_eq_list) + + emit_named("%s_new" % self.name, params, + make_proof('new_spec', + {"name": self.name, + "args": ', '.join(arg_list), + "field_eqs": field_eqs}, + params.sorry)) + + emit_named_ptr_proof("%s_ptr_new" % self.name, params, self.name, + type_map, params.toplevel_types, + 'ptr_new_spec', + {"name": self.name, + "args": ', '.join(arg_list), + "field_eqs": field_eqs}) + + # Generate get/set specs + for (field, offset, size, high) in self.fields: + mask = field_mask_proof(self.base, self.base_bits, self.base_sign_extend, high, size) + sign_extend = sign_extend_proof(high, self.base_bits, self.base_sign_extend) + + substs = {"name": self.name, + "field": field, + "mask": mask, + "sign_extend": sign_extend, + "ret_name": return_name(self.base), + "base": self.base} + + if not params.skip_modifies: + # Get modifies spec + emit_named("%s_get_%s" % (self.name, field), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_get_%s" % (self.name, field), + "args": ', '.join([ + "\ret__unsigned_long", + "\%s" % self.name])}, + params.sorry)) + + # Ptr get modifies spec + emit_named("%s_ptr_get_%s" % (self.name, field), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_ptr_get_%s" % (self.name, field), + "args": ', '.join([ + "\ret__unsigned_long", + "\%s_ptr" % self.name])}, + params.sorry)) + + # Get spec + emit_named("%s_get_%s" % (self.name, field), params, + make_proof('get_spec', substs, params.sorry)) + + if not params.skip_modifies: + # Set modifies spec + emit_named("%s_set_%s" % (self.name, field), params, + make_proof('const_modifies_proof', + {"fun_name": "%s_set_%s" % (self.name, field), + "args": ', '.join([ + "\ret__struct_%s_C" % self.name, + "\%s" % self.name, + "\v%(base)d"])}, + params.sorry)) + + emit_named("%s_ptr_set_%s" % (self.name, field), params, + make_proof('ptr_set_modifies_proof', + {"fun_name": "%s_ptr_set_%s" % (self.name, field), + "args": ', '.join([ + "\%s_ptr" % self.name, + "\v%(base)d"])}, + params.sorry)) + + # Set spec + emit_named("%s_set_%s" % (self.name, field), params, + make_proof('set_spec', substs, params.sorry)) + + emit_named_ptr_proof("%s_ptr_get_%s" % (self.name, field), params, self.name, + type_map, params.toplevel_types, + 'ptr_get_spec', substs) + emit_named_ptr_proof("%s_ptr_set_%s" % (self.name, field), params, self.name, + type_map, params.toplevel_types, + 'ptr_set_spec', substs) + + def rust_generate(self, params): + output = params.output + + # Don't generate raw accessors for blocks in tagged unions + if self.tagged: return + + # Type definition + print(rust_type_template % \ + {"type": "usize", \ + "name": self.name, \ + "multiple": self.multiple}, file=output) + print(file=output) + + # Generator + arg_list = ["%s: %s" % (field, "usize") for \ + field in self.visible_order] + if len(arg_list) == 0: + args = 'void' + else: + args = ', '.join(arg_list) + + ptr_args = ', '.join(["%s_ptr: *mut %s" % (self.name, self.name)] + \ + arg_list) + + word_inits = [" %s.words[%d] = 0;" % (self.name, i) \ + for i in range(self.multiple)] + + ptr_word_inits = [" unsafe { (*%s_ptr).words[%d] = 0 };" % (self.name, i) \ + for i in range(self.multiple)] + + field_inits = [] + ptr_field_inits = [] + for field, offset, size, high in self.fields: + index = offset // self.base + if high: + shift_op = ">>" + shift = self.base_bits - size - (offset % self.base) + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << (self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + if shift < 0: + shift = -shift + shift_op = "<<" + else: + shift_op = "<<" + shift = offset % self.base + high_bits = 0 + if size < self.base: + if high: + mask = ((1 << size) - 1) << (self.base_bits - size) + else: + mask = (1 << size) - 1 + suf = self.constant_suffix + + field_inits.append( + " /* fail if user has passed bits that we will override */") + field_inits.append( + " %s((%s & !0x%x%s) == (if (0 != (%s & (1%s << %d))) { 0x%x } else { 0 }));" % (RUST_ASSERTS[options.environment], field, mask, suf, field, suf, self.base_bits - 1, high_bits)) + field_inits.append( + " %s.words[%d] |= (%s & 0x%x%s) %s %d;" % \ + (self.name, index, field, mask, suf, shift_op, shift)) + + ptr_field_inits.append( + " /* fail if user has passed bits that we will override */") + ptr_field_inits.append( + " %s((%s & !0x%x%s) == (if (0 != (%s & (1%s << %d))) { 0x%x } else { 0 }));" % (RUST_ASSERTS[options.environment], field, mask, suf, field, suf, self.base_bits - 1, high_bits)) + ptr_field_inits.append( + " unsafe { (*%s_ptr).words[%d] |= (%s & 0x%x%s) %s %d };" % \ + (self.name, index, field, mask, suf, shift_op, shift)) + else: + field_inits.append( + " %s.words[%d] |= %s %s %d;" % \ + (self.name, index, field, shift_op, shift)) + + ptr_field_inits.append( + " unsafe { (*%s_ptr).words[%d] |= %s %s %d };" % \ + (self.name, index, field, shift_op, shift)) + + generator = rust_generator_template % \ + {"block": self.name, \ + "args": args, \ + "word_inits": '\n'.join(word_inits), \ + "field_inits": '\n'.join(field_inits)} + + ptr_generator = rust_ptr_generator_template % \ + {"block": self.name, \ + "args": ptr_args, \ + "word_inits": '\n'.join(ptr_word_inits), \ + "field_inits": '\n'.join(ptr_field_inits)} + + emit_named("%s_new" % self.name, params, generator) + emit_named("%s_ptr_new" % self.name, params, ptr_generator) + + # Accessors + for field, offset, size, high in self.fields: + index = offset // self.base + if high: + write_shift = ">>" + read_shift = "<<" + shift = self.base_bits - size - (offset % self.base) + if shift < 0: + shift = -shift + write_shift = "<<" + read_shift = ">>" + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << (self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + else: + write_shift = "<<" + read_shift = ">>" + shift = offset % self.base + high_bits = 0 + mask = ((1 << size) - 1) << (offset % self.base) + + subs = {\ + "block": self.name, \ + "field": field, \ + "type": "usize", \ + "assert": RUST_ASSERTS[options.environment], \ + "index": index, \ + "shift": shift, \ + "r_shift_op": read_shift, \ + "w_shift_op": write_shift, \ + "mask": mask, \ + "suf": self.constant_suffix, \ + "high_bits": high_bits, \ + "sign_extend": self.base_sign_extend and high, + "extend_bit": self.base_bits - 1} + + # Reader + emit_named("%s_get_%s" % (self.name, field), params, + rust_reader_template % subs) + + # Writer + emit_named("%s_set_%s" % (self.name, field), params, + rust_writer_template % subs) + + def c_generate(self, params): + output = params.output + + # Don't generate raw accessors for blocks in tagged unions + if self.tagged: + return + + # Type definition + print(c_typedef_template % + {"type": C_TYPES[options.environment][self.base], + "name": self.name, + "multiple": self.multiple}, file=output) + print(file=output) + + # Generator + param_fields = [field for field in self.visible_order] + param_list = ["%s %s" % (C_TYPES[options.environment][self.base], field) + for field in param_fields] + + if len(param_list) == 0: + gen_params = 'void' + else: + gen_params = ', '.join(param_list) + + ptr_params = ', '.join(["%s_t *%s_ptr" % (self.name, self.name)] + param_list) + + field_updates = {word: [] for word in range(self.multiple)} + field_asserts = [" /* fail if user has passed bits that we will override */"] + + for field, offset, size, high in self.fields: + index = offset // self.base + if high: + shift_op = ">>" + shift = self.base_bits - size - (offset % self.base) + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << ( + self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + if shift < 0: + shift = -shift + shift_op = "<<" + else: + shift_op = "<<" + shift = offset % self.base + high_bits = 0 + if size < self.base: + if high: + mask = ((1 << size) - 1) << (self.base_bits - size) + else: + mask = (1 << size) - 1 + suf = self.constant_suffix + + field_asserts.append( + " %s((%s & ~0x%x%s) == ((%d && (%s & (1%s << %d))) ? 0x%x : 0));" + % (C_ASSERTS[options.environment], field, mask, suf, self.base_sign_extend, + field, suf, self.base_bits - 1, high_bits)) + + field_updates[index].append( + "(%s & 0x%x%s) %s %d" % (field, mask, suf, shift_op, shift)) + + else: + field_updates[index].append("%s %s %d;" % (field, shift_op, shift)) + + word_inits = [ + ("words[%d] = 0" % index) + ''.join(["\n | %s" % up for up in ups]) + ';' + for (index, ups) in field_updates.items()] + + def mk_inits(prefix): + return '\n'.join([" %s%s" % (prefix, word_init) for word_init in word_inits]) + + print_params = { + "inline": C_INLINE[options.environment], + "block": self.name, + "gen_params": gen_params, + "ptr_params": ptr_params, + "gen_inits": mk_inits("%s." % self.name), + "ptr_inits": mk_inits("%s_ptr->" % self.name), + "asserts": ' \n'.join(field_asserts) + } + + generator = c_generator_template % print_params + ptr_generator = c_ptr_generator_template % print_params + + emit_named("%s_new" % self.name, params, generator) + emit_named("%s_ptr_new" % self.name, params, ptr_generator) + + # Accessors + for field, offset, size, high in self.fields: + index = offset // self.base + if high: + write_shift = ">>" + read_shift = "<<" + shift = self.base_bits - size - (offset % self.base) + if shift < 0: + shift = -shift + write_shift = "<<" + read_shift = ">>" + if self.base_sign_extend: + high_bits = ((self.base_sign_extend << ( + self.base - self.base_bits)) - 1) << self.base_bits + else: + high_bits = 0 + else: + write_shift = "<<" + read_shift = ">>" + shift = offset % self.base + high_bits = 0 + mask = ((1 << size) - 1) << (offset % self.base) + + subs = { + "inline": C_INLINE[options.environment], + "block": self.name, + "field": field, + "type": C_TYPES[options.environment][self.base], + "assert": C_ASSERTS[options.environment], + "index": index, + "shift": shift, + "r_shift_op": read_shift, + "w_shift_op": write_shift, + "mask": mask, + "suf": self.constant_suffix, + "high_bits": high_bits, + "sign_extend": self.base_sign_extend and high, + "extend_bit": self.base_bits - 1, + "base": self.base} + + # Reader + emit_named("%s_get_%s" % (self.name, field), params, + c_reader_template % subs) + + # Writer + emit_named("%s_set_%s" % (self.name, field), params, + c_writer_template % subs) + + # Pointer lifted reader + emit_named("%s_ptr_get_%s" % (self.name, field), params, + c_ptr_reader_template % subs) + + # Pointer lifted writer + emit_named("%s_ptr_set_%s" % (self.name, field), params, + c_ptr_writer_template % subs) + + # Pointer lifted reader + emit_named("%s_ptr_get_%s" % (self.name, field), params, + c_ptr_reader_template % subs) + + # Pointer lifted writer + emit_named("%s_ptr_set_%s" % (self.name, field), params, + c_ptr_writer_template % subs) + + def make_names(self, union=None): + "Return the set of candidate function names for a block" + + if union is None: + # Don't generate raw accessors for blocks in tagged unions + if self.tagged: + return [] + + substs = {"block": self.name} + + # A standalone block + field_templates = [ + "%(block)s_get_%(field)s", + "%(block)s_set_%(field)s", + "%(block)s_ptr_get_%(field)s", + "%(block)s_ptr_set_%(field)s"] + + names = [t % substs for t in [ + "%(block)s_new", + "%(block)s_ptr_new"]] + else: + substs = {"block": self.name, + "union": union.name} + + # A tagged union block + field_templates = [ + "%(union)s_%(block)s_get_%(field)s", + "%(union)s_%(block)s_set_%(field)s", + "%(union)s_%(block)s_ptr_get_%(field)s", + "%(union)s_%(block)s_ptr_set_%(field)s"] + + names = [t % substs for t in [ + "%(union)s_%(block)s_new", + "%(union)s_%(block)s_ptr_new"]] + + for field, offset, size, high in self.fields: + if not union is None and field == union.tagname: + continue + + substs["field"] = field + names += [t % substs for t in field_templates] + + return names + + +temp_output_files = [] + + +class OutputFile(object): + def __init__(self, filename, mode='w', atomic=True): + """Open an output file for writing, recording its filename. + If atomic is True, use a temporary file for writing. + Call finish_output to finalise all temporary files.""" + self.filename = os.path.abspath(filename) + if atomic: + dirname, basename = os.path.split(self.filename) + self.file = tempfile.NamedTemporaryFile( + mode=mode, dir=dirname, prefix=basename + '.', delete=False) + if DEBUG: + print('Temp file: %r -> %r' % (self.file.name, self.filename), file=sys.stderr) + global temp_output_files + temp_output_files.append(self) + else: + self.file = open(filename, mode) + + def write(self, *args, **kwargs): + self.file.write(*args, **kwargs) + + +def finish_output(): + global temp_output_files + for f in temp_output_files: + os.rename(f.file.name, f.filename) + temp_output_files = [] + + +# Toplevel +if __name__ == '__main__': + # Parse arguments to set mode and grab I/O filenames + params = {} + in_filename = None + in_file = sys.stdin + out_file = sys.stdout + mode = 'c_defs' + + parser = optparse.OptionParser() + parser.add_option('--c_defs', action='store_true', default=False) + parser.add_option('--language', action='store', default='rust', # XXX 'c' + choices=list(['c', 'rust'])) + parser.add_option('--environment', action='store', default='sel4', + choices=list(C_INCLUDES.keys())) + parser.add_option('--hol_defs', action='store_true', default=False) + parser.add_option('--hol_proofs', action='store_true', default=False) + parser.add_option('--sorry_lemmas', action='store_true', + dest='sorry', default=False) + parser.add_option('--prune', action='append', + dest="prune_files", default=[]) + parser.add_option('--toplevel', action='append', + dest="toplevel_types", default=[]) + parser.add_option('--umm_types', action='store', + dest="umm_types_file", default=None) + parser.add_option('--multifile_base', action='store', default=None) + parser.add_option('--cspec-dir', action='store', default=None, + help="Location of the 'cspec' directory containing 'KernelState_C'.") + parser.add_option('--thy-output-path', action='store', default=None, + help="Path that the output theory files will be located in.") + parser.add_option('--skip_modifies', action='store_true', default=False) + parser.add_option('--showclasses', action='store_true', default=False) + parser.add_option('--debug', action='store_true', default=False) + + options, args = parser.parse_args() + DEBUG = options.debug + + if len(args) > 0: + in_filename = args[0] + in_file = open(in_filename) + + if len(args) > 1: + out_file = OutputFile(args[1]) + + # + # If generating Isabelle scripts, ensure we have enough information for + # relative paths required by Isabelle. + # + if options.hol_defs or options.hol_proofs: + # Ensure directory that we need to include is known. + if options.cspec_dir is None: + parser.error("'cspec_dir' not defined.") + + # Ensure that if an output file was not specified, an output path was. + if len(args) <= 1: + if options.thy_output_path is None: + parser.error("Theory output path was not specified") + if out_file == sys.stdout: + parser.error( + 'Output file name must be given when generating HOL definitions or proofs') + out_file.filename = os.path.abspath(options.thy_output_path) + + if options.hol_proofs and not options.umm_types_file: + parser.error('--umm_types must be specified when generating HOL proofs') + + del parser + + options.output = out_file + + # Parse the spec + lexer = lex.lex() + yacc.yacc(debug=0, write_tables=0) + blocks = {} + unions = {} + _, block_map, union_map = yacc.parse(input=in_file.read(), lexer=lexer) + base_list = [8, 16, 32, 64] + # assumes that unsigned int = 32 bit on 32-bit and 64-bit platforms, + # and that unsigned long long = 64 bit on 64-bit platforms. + # Should still work fine if ull = 128 bit, but will not work + # if unsigned int is less than 32 bit. + suffix_map = {} + if options.language == 'rust': + suffix_map = {8 : '', 16 : '', 32 : '', 64 : ''} + elif options.language == 'c': + suffix_map = {8: 'u', 16: 'u', 32: 'u', 64: 'ull'} + for base_info, block_list in block_map.items(): + base, base_bits, base_sign_extend = base_info + for name, b in block_list.items(): + if not base in base_list: + raise ValueError("Invalid base size: %d" % base) + suffix = suffix_map[base] + b.set_base(base, base_bits, base_sign_extend, suffix) + blocks[name] = b + + symtab = {} + symtab.update(blocks) + for base, union_list in union_map.items(): + unions.update(union_list) + symtab.update(unions) + for base_info, union_list in union_map.items(): + base, base_bits, base_sign_extend = base_info + for u in union_list.values(): + if not base in base_list: + raise ValueError("Invalid base size: %d" % base) + suffix = suffix_map[base] + u.resolve(options, symtab) + u.set_base(base, base_bits, base_sign_extend, suffix) + + if not in_filename is None: + base_filename = os.path.basename(in_filename).split('.')[0] + + # Generate the module name from the input filename + module_name = base_filename + + # Prune list of names to generate + name_list = [] + for e in det_values(blocks, unions): + name_list += e.make_names() + + name_list = set(name_list) + if len(options.prune_files) > 0: + search_re = re.compile('[a-zA-Z0-9_]+') + + pruned_names = set() + for filename in options.prune_files: + f = open(filename) + string = f.read() + + matched_tokens = set(search_re.findall(string)) + pruned_names.update(matched_tokens & name_list) + else: + pruned_names = name_list + + options.names = pruned_names + + # Generate the output + if options.hol_defs: + # Fetch kernel + if options.multifile_base is None: + print("theory %s_defs" % module_name, file=out_file) + print("imports \"%s/KernelState_C\"" % ( + os.path.relpath(options.cspec_dir, + os.path.dirname(out_file.filename))), file=out_file) + print("begin", file=out_file) + print(file=out_file) + + print(defs_global_lemmas, file=out_file) + print(file=out_file) + + for e in det_values(blocks, unions): + e.generate_hol_defs(options) + + print("end", file=out_file) + else: + print("theory %s_defs" % module_name, file=out_file) + print("imports", file=out_file) + print(" \"%s/KernelState_C\"" % ( + os.path.relpath(options.cspec_dir, + os.path.dirname(out_file.filename))), file=out_file) + for e in det_values(blocks, unions): + print(" %s_%s_defs" % (module_name, e.name), + file=out_file) + print("begin", file=out_file) + print("end", file=out_file) + + for e in det_values(blocks, unions): + base_filename = \ + os.path.basename(options.multifile_base).split('.')[0] + submodule_name = base_filename + "_" + \ + e.name + "_defs" + out_file = OutputFile(options.multifile_base + "_" + + e.name + "_defs" + ".thy") + + print("theory %s imports \"%s/KernelState_C\" begin" % ( + submodule_name, os.path.relpath(options.cspec_dir, + os.path.dirname(out_file.filename))), + file=out_file) + print(file=out_file) + + options.output = out_file + e.generate_hol_defs(options) + + print("end", file=out_file) + elif options.hol_proofs: + def is_bit_type(tp): + return umm.is_base(tp) & (umm.base_name(tp) in + map(lambda e: e.name + '_C', det_values(blocks, unions))) + + tps = umm.build_types(options.umm_types_file) + type_map = {} + + # invert type map + for toptp in options.toplevel_types: + paths = umm.paths_to_type(tps, is_bit_type, toptp) + + for path, tp in paths: + tp = umm.base_name(tp) + + if tp in type_map: + raise ValueError("Type %s has multiple parents" % tp) + + type_map[tp] = (toptp, path) + + if options.multifile_base is None: + print("theory %s_proofs" % module_name, file=out_file) + print("imports %s_defs" % module_name, file=out_file) + print("begin", file=out_file) + print(file=out_file) + print(file=out_file) + + for e in det_values(blocks, unions): + e.generate_hol_proofs(options, type_map) + + print("end", file=out_file) + else: + # top types are broken here. + print("theory %s_proofs" % module_name, file=out_file) + print("imports", file=out_file) + for e in det_values(blocks, unions): + print(" %s_%s_proofs" % (module_name, e.name), + file=out_file) + print("begin", file=out_file) + print("end", file=out_file) + + for e in det_values(blocks, unions): + base_filename = \ + os.path.basename(options.multifile_base).split('.')[0] + submodule_name = base_filename + "_" + \ + e.name + "_proofs" + out_file = OutputFile(options.multifile_base + "_" + + e.name + "_proofs" + ".thy") + + print(("theory %s imports " + + "%s_%s_defs begin") % ( + submodule_name, base_filename, e.name), + file=out_file) + print(file=out_file) + + options.output = out_file + e.generate_hol_proofs(options, type_map) + + print("end", file=out_file) + else: + if options.language == 'c': + guard = re.sub(r'[^a-zA-Z0-9_]', '_', out_file.filename.upper()) + print("#ifndef %(guard)s\n#define %(guard)s\n" % + {'guard': guard}, file=out_file) + print('\n'.join(map(lambda x: '#include <%s>' % x, + C_INCLUDES[options.environment])), file=out_file) + for e in det_values(blocks, unions): + e.c_generate(options) + print("#endif", file=out_file) + elif options.language == 'rust': + for e in det_values(blocks, unions): + e.rust_generate(options) + + finish_output() diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/invocation_header_gen.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/invocation_header_gen.py new file mode 100755 index 0000000..8b72bdf --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/invocation_header_gen.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright 2015, Corey Richardson +# Copyright 2014, NICTA +# +# This software may be distributed and modified according to the terms of +# the BSD 2-Clause license. Note that NO WARRANTY is provided. +# See "LICENSE_BSD2.txt" for details. +# +# @TAG(NICTA_BSD) +# + +# seL4 Invocation ID Generator +# ============================ + +import argparse +import sys +from itertools import chain +# install tempita using sudo apt-get install python-tempita or similar for your distro +import tempita +import xml.dom.minidom + +COMMON_HEADER = """ +/* @LICENSE(NICTA) */ + +/* This header was generated by kernel/tools/invocation_header_gen.py. + * + * To add an invocation call number, edit libsel4/include/interfaces/sel4.xml. + * + */""" + +INVOCATION_TEMPLATE = COMMON_HEADER + """ +#[repr(C)] +pub enum InvocationLabel { + InvalidInvocation = 0, + {{for loop, list in looper(invocations)}} + {{py:label, condition = list}} + {{if condition}} + #[cfg({{condition}})] + {{endif}} + {{label}} = {{loop.index+1}}, + {{endfor}} +} +""" + +def parse_args(): + parser = argparse.ArgumentParser(description='Generate seL4 invocation API \ + constants and header files') + parser.add_argument('--dest', type=argparse.FileType('w'), + help='Name of file to create', required=True) + parser.add_argument('files', nargs='+', help='XML files to parse invocations from') + + return parser.parse_args() + +def parse_xml(xml_file): + try: + doc = xml.dom.minidom.parse(xml_file) + except: + print >> sys.stderr, "Error: invalid xml file" + sys.exit(-1) + + invocation_labels = [] + for method in doc.getElementsByTagName("method"): + condition = method.getAttribute("condition") + # HACK: ugly hacks to handle simple CPP expressions (very fragile) + if condition == "(!defined CONFIG_KERNEL_MCS) && CONFIG_MAX_NUM_NODES > 1": + # NB: CONFIG_MAX_NUM_NODES > 1 =>'s CONFIG_SMP_SUPPORT + condition = 'all(not(feature = "CONFIG_KERNEL_MCS"), feature = "CONFIG_SMP_SUPPORT")' + elif condition == "CONFIG_MAX_NUM_NODES > 1": + condition = 'feature = "CONFIG_SMP_SUPPORT"' + elif condition: + condition = condition.replace('defined', '') + condition = condition.replace('(', '') + condition = condition.replace(')', '') + if 'CONFIG_' in condition: + condition = 'feature = "' + condition + '"' + if '!' in condition: + condition = 'not(%s)' % condition.replace('!', '') + + invocation_labels.append((str(method.getAttribute("id")), + str(condition))) + + return invocation_labels + +def generate(args, invocations): + + header_title = "LIBRUSTSEL4" + + template = tempita.Template(INVOCATION_TEMPLATE) + + args.dest.write(template.substitute(header_title=header_title, + invocations=invocations)) + + args.dest.close() + +if __name__ == "__main__": + args = parse_args() + + invocations = chain.from_iterable(parse_xml(xml) for xml in args.files) + + generate(args, invocations) + diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/lex.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/lex.py new file mode 100644 index 0000000..c180e37 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/lex.py @@ -0,0 +1,1060 @@ +# +# @TAG(OTHER_BSD) +# +# ----------------------------------------------------------------------------- +# ply: lex.py +# +# Copyright (C) 2001-2009, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__version__ = "3.2" +__tabversion__ = "3.2" # Version of table file used + +import re, sys, types, copy, os + +# This tuple contains known string types +try: + # Python 2.6 + StringTypes = (types.StringType, types.UnicodeType) +except AttributeError: + # Python 3.0 + StringTypes = (str, bytes) + +# Extract the code attribute of a function. Different implementations +# are for Python 2/3 compatibility. + +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# This regular expression is used to match valid token names +_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') + +# Exception thrown when invalid token encountered and no default error +# handler is defined. + +class LexError(Exception): + def __init__(self,message,s): + self.args = (message,) + self.text = s + +# Token class. This class is used to represent the tokens produced. +class LexToken(object): + def __str__(self): + return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) + def __repr__(self): + return str(self) + +# This object is a stand-in for a logging object created by the +# logging module. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def critical(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + info = critical + debug = critical + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# ----------------------------------------------------------------------------- +# === Lexing Engine === +# +# The following Lexer class implements the lexer runtime. There are only +# a few public methods and attributes: +# +# input() - Store a new string in the lexer +# token() - Get the next token +# clone() - Clone the lexer +# +# lineno - Current line number +# lexpos - Current position in the input string +# ----------------------------------------------------------------------------- + +class Lexer: + def __init__(self): + self.lexre = None # Master regular expression. This is a list of + # tuples (re,findex) where re is a compiled + # regular expression and findex is a list + # mapping regex group numbers to rules + self.lexretext = None # Current regular expression strings + self.lexstatere = {} # Dictionary mapping lexer states to master regexs + self.lexstateretext = {} # Dictionary mapping lexer states to regex strings + self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names + self.lexstate = "INITIAL" # Current lexer state + self.lexstatestack = [] # Stack of lexer states + self.lexstateinfo = None # State information + self.lexstateignore = {} # Dictionary of ignored characters for each state + self.lexstateerrorf = {} # Dictionary of error functions for each state + self.lexreflags = 0 # Optional re compile flags + self.lexdata = None # Actual input data (as a string) + self.lexpos = 0 # Current position in input text + self.lexlen = 0 # Length of the input text + self.lexerrorf = None # Error rule (if any) + self.lextokens = None # List of valid tokens + self.lexignore = "" # Ignored characters + self.lexliterals = "" # Literal characters that can be passed through + self.lexmodule = None # Module + self.lineno = 1 # Current line number + self.lexoptimize = 0 # Optimized mode + + def clone(self,object=None): + c = copy.copy(self) + + # If the object parameter has been supplied, it means we are attaching the + # lexer to a new object. In this case, we have to rebind all methods in + # the lexstatere and lexstateerrorf tables. + + if object: + newtab = { } + for key, ritem in self.lexstatere.items(): + newre = [] + for cre, findex in ritem: + newfindex = [] + for f in findex: + if not f or not f[0]: + newfindex.append(f) + continue + newfindex.append((getattr(object,f[0].__name__),f[1])) + newre.append((cre,newfindex)) + newtab[key] = newre + c.lexstatere = newtab + c.lexstateerrorf = { } + for key, ef in self.lexstateerrorf.items(): + c.lexstateerrorf[key] = getattr(object,ef.__name__) + c.lexmodule = object + return c + + # ------------------------------------------------------------ + # writetab() - Write lexer information to a table file + # ------------------------------------------------------------ + def writetab(self,tabfile,outputdir=""): + if isinstance(tabfile,types.ModuleType): + return + basetabfilename = tabfile.split(".")[-1] + filename = os.path.join(outputdir,basetabfilename)+".py" + tf = open(filename,"w") + tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__)) + tf.write("_tabversion = %s\n" % repr(__version__)) + tf.write("_lextokens = %s\n" % repr(self.lextokens)) + tf.write("_lexreflags = %s\n" % repr(self.lexreflags)) + tf.write("_lexliterals = %s\n" % repr(self.lexliterals)) + tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo)) + + tabre = { } + # Collect all functions in the initial state + initial = self.lexstatere["INITIAL"] + initialfuncs = [] + for part in initial: + for f in part[1]: + if f and f[0]: + initialfuncs.append(f) + + for key, lre in self.lexstatere.items(): + titem = [] + for i in range(len(lre)): + titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i]))) + tabre[key] = titem + + tf.write("_lexstatere = %s\n" % repr(tabre)) + tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore)) + + taberr = { } + for key, ef in self.lexstateerrorf.items(): + if ef: + taberr[key] = ef.__name__ + else: + taberr[key] = None + tf.write("_lexstateerrorf = %s\n" % repr(taberr)) + tf.close() + + # ------------------------------------------------------------ + # readtab() - Read lexer information from a tab file + # ------------------------------------------------------------ + def readtab(self,tabfile,fdict): + if isinstance(tabfile,types.ModuleType): + lextab = tabfile + else: + if sys.version_info[0] < 3: + exec("import %s as lextab" % tabfile) + else: + env = { } + exec("import %s as lextab" % tabfile, env,env) + lextab = env['lextab'] + + if getattr(lextab,"_tabversion","0.0") != __version__: + raise ImportError("Inconsistent PLY version") + + self.lextokens = lextab._lextokens + self.lexreflags = lextab._lexreflags + self.lexliterals = lextab._lexliterals + self.lexstateinfo = lextab._lexstateinfo + self.lexstateignore = lextab._lexstateignore + self.lexstatere = { } + self.lexstateretext = { } + for key,lre in lextab._lexstatere.items(): + titem = [] + txtitem = [] + for i in range(len(lre)): + titem.append((re.compile(lre[i][0],lextab._lexreflags),_names_to_funcs(lre[i][1],fdict))) + txtitem.append(lre[i][0]) + self.lexstatere[key] = titem + self.lexstateretext[key] = txtitem + self.lexstateerrorf = { } + for key,ef in lextab._lexstateerrorf.items(): + self.lexstateerrorf[key] = fdict[ef] + self.begin('INITIAL') + + # ------------------------------------------------------------ + # input() - Push a new string into the lexer + # ------------------------------------------------------------ + def input(self,s): + # Pull off the first character to see if s looks like a string + c = s[:1] + if not isinstance(c,StringTypes): + raise ValueError("Expected a string") + self.lexdata = s + self.lexpos = 0 + self.lexlen = len(s) + + # ------------------------------------------------------------ + # begin() - Changes the lexing state + # ------------------------------------------------------------ + def begin(self,state): + if not state in self.lexstatere: + raise ValueError("Undefined state") + self.lexre = self.lexstatere[state] + self.lexretext = self.lexstateretext[state] + self.lexignore = self.lexstateignore.get(state,"") + self.lexerrorf = self.lexstateerrorf.get(state,None) + self.lexstate = state + + # ------------------------------------------------------------ + # push_state() - Changes the lexing state and saves old on stack + # ------------------------------------------------------------ + def push_state(self,state): + self.lexstatestack.append(self.lexstate) + self.begin(state) + + # ------------------------------------------------------------ + # pop_state() - Restores the previous state + # ------------------------------------------------------------ + def pop_state(self): + self.begin(self.lexstatestack.pop()) + + # ------------------------------------------------------------ + # current_state() - Returns the current lexing state + # ------------------------------------------------------------ + def current_state(self): + return self.lexstate + + # ------------------------------------------------------------ + # skip() - Skip ahead n characters + # ------------------------------------------------------------ + def skip(self,n): + self.lexpos += n + + # ------------------------------------------------------------ + # opttoken() - Return the next token from the Lexer + # + # Note: This function has been carefully implemented to be as fast + # as possible. Don't make changes unless you really know what + # you are doing + # ------------------------------------------------------------ + def token(self): + # Make local copies of frequently referenced attributes + lexpos = self.lexpos + lexlen = self.lexlen + lexignore = self.lexignore + lexdata = self.lexdata + + while lexpos < lexlen: + # This code provides some short-circuit code for whitespace, tabs, and other ignored characters + if lexdata[lexpos] in lexignore: + lexpos += 1 + continue + + # Look for a regular expression match + for lexre,lexindexfunc in self.lexre: + m = lexre.match(lexdata,lexpos) + if not m: continue + + # Create a token for return + tok = LexToken() + tok.value = m.group() + tok.lineno = self.lineno + tok.lexpos = lexpos + + i = m.lastindex + func,tok.type = lexindexfunc[i] + + if not func: + # If no token type was set, it's an ignored token + if tok.type: + self.lexpos = m.end() + return tok + else: + lexpos = m.end() + break + + lexpos = m.end() + + # If token is processed by a function, call it + + tok.lexer = self # Set additional attributes useful in token rules + self.lexmatch = m + self.lexpos = lexpos + + newtok = func(tok) + + # Every function must return a token, if nothing, we just move to next token + if not newtok: + lexpos = self.lexpos # This is here in case user has updated lexpos. + lexignore = self.lexignore # This is here in case there was a state change + break + + # Verify type of the token. If not in the token map, raise an error + if not self.lexoptimize: + if not newtok.type in self.lextokens: + raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( + func_code(func).co_filename, func_code(func).co_firstlineno, + func.__name__, newtok.type),lexdata[lexpos:]) + + return newtok + else: + # No match, see if in literals + if lexdata[lexpos] in self.lexliterals: + tok = LexToken() + tok.value = lexdata[lexpos] + tok.lineno = self.lineno + tok.type = tok.value + tok.lexpos = lexpos + self.lexpos = lexpos + 1 + return tok + + # No match. Call t_error() if defined. + if self.lexerrorf: + tok = LexToken() + tok.value = self.lexdata[lexpos:] + tok.lineno = self.lineno + tok.type = "error" + tok.lexer = self + tok.lexpos = lexpos + self.lexpos = lexpos + newtok = self.lexerrorf(tok) + if lexpos == self.lexpos: + # Error method didn't change text position at all. This is an error. + raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) + lexpos = self.lexpos + if not newtok: continue + return newtok + + self.lexpos = lexpos + raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) + + self.lexpos = lexpos + 1 + if self.lexdata is None: + raise RuntimeError("No input string given with input()") + return None + + # Iterator interface + def __iter__(self): + return self + + def next(self): + t = self.token() + if t is None: + raise StopIteration + return t + + __next__ = next + +# ----------------------------------------------------------------------------- +# ==== Lex Builder === +# +# The functions and classes below are used to collect lexing information +# and build a Lexer object from it. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# _funcs_to_names() +# +# Given a list of regular expression functions, this converts it to a list +# suitable for output to a table file +# ----------------------------------------------------------------------------- + +def _funcs_to_names(funclist,namelist): + result = [] + for f,name in zip(funclist,namelist): + if f and f[0]: + result.append((name, f[1])) + else: + result.append(f) + return result + +# ----------------------------------------------------------------------------- +# _names_to_funcs() +# +# Given a list of regular expression function names, this converts it back to +# functions. +# ----------------------------------------------------------------------------- + +def _names_to_funcs(namelist,fdict): + result = [] + for n in namelist: + if n and n[0]: + result.append((fdict[n[0]],n[1])) + else: + result.append(n) + return result + +# ----------------------------------------------------------------------------- +# _form_master_re() +# +# This function takes a list of all of the regex components and attempts to +# form the master regular expression. Given limitations in the Python re +# module, it may be necessary to break the master regex into separate expressions. +# ----------------------------------------------------------------------------- + +def _form_master_re(relist,reflags,ldict,toknames): + if not relist: return [] + regex = "|".join(relist) + try: + lexre = re.compile(regex,re.VERBOSE | reflags) + + # Build the index to function map for the matching engine + lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) + lexindexnames = lexindexfunc[:] + + for f,i in lexre.groupindex.items(): + handle = ldict.get(f,None) + if type(handle) in (types.FunctionType, types.MethodType): + lexindexfunc[i] = (handle,toknames[f]) + lexindexnames[i] = f + elif handle is not None: + lexindexnames[i] = f + if f.find("ignore_") > 0: + lexindexfunc[i] = (None,None) + else: + lexindexfunc[i] = (None, toknames[f]) + + return [(lexre,lexindexfunc)],[regex],[lexindexnames] + except Exception: + m = int(len(relist)/2) + if m == 0: m = 1 + llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) + rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) + return llist+rlist, lre+rre, lnames+rnames + +# ----------------------------------------------------------------------------- +# def _statetoken(s,names) +# +# Given a declaration name s of the form "t_" and a dictionary whose keys are +# state names, this function returns a tuple (states,tokenname) where states +# is a tuple of state names and tokenname is the name of the token. For example, +# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') +# ----------------------------------------------------------------------------- + +def _statetoken(s,names): + nonstate = 1 + parts = s.split("_") + for i in range(1,len(parts)): + if not parts[i] in names and parts[i] != 'ANY': break + if i > 1: + states = tuple(parts[1:i]) + else: + states = ('INITIAL',) + + if 'ANY' in states: + states = tuple(names) + + tokenname = "_".join(parts[i:]) + return (states,tokenname) + + +# ----------------------------------------------------------------------------- +# LexerReflect() +# +# This class represents information needed to build a lexer as extracted from a +# user's input file. +# ----------------------------------------------------------------------------- +class LexerReflect(object): + def __init__(self,ldict,log=None,reflags=0): + self.ldict = ldict + self.error_func = None + self.tokens = [] + self.reflags = reflags + self.stateinfo = { 'INITIAL' : 'inclusive'} + self.files = {} + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_tokens() + self.get_literals() + self.get_states() + self.get_rules() + + # Validate all of the information + def validate_all(self): + self.validate_tokens() + self.validate_literals() + self.validate_rules() + return self.error + + # Get the tokens map + def get_tokens(self): + tokens = self.ldict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + terminals = {} + for n in self.tokens: + if not _is_identifier.match(n): + self.log.error("Bad token name '%s'",n) + self.error = 1 + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the literals specifier + def get_literals(self): + self.literals = self.ldict.get("literals","") + + # Validate literals + def validate_literals(self): + try: + for c in self.literals: + if not isinstance(c,StringTypes) or len(c) > 1: + self.log.error("Invalid literal %s. Must be a single character", repr(c)) + self.error = 1 + continue + + except TypeError: + self.log.error("Invalid literals specification. literals must be a sequence of characters") + self.error = 1 + + def get_states(self): + self.states = self.ldict.get("states",None) + # Build statemap + if self.states: + if not isinstance(self.states,(tuple,list)): + self.log.error("states must be defined as a tuple or list") + self.error = 1 + else: + for s in self.states: + if not isinstance(s,tuple) or len(s) != 2: + self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s)) + self.error = 1 + continue + name, statetype = s + if not isinstance(name,StringTypes): + self.log.error("State name %s must be a string", repr(name)) + self.error = 1 + continue + if not (statetype == 'inclusive' or statetype == 'exclusive'): + self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name) + self.error = 1 + continue + if name in self.stateinfo: + self.log.error("State '%s' already defined",name) + self.error = 1 + continue + self.stateinfo[name] = statetype + + # Get all of the symbols with a t_ prefix and sort them into various + # categories (functions, strings, error functions, and ignore characters) + + def get_rules(self): + tsymbols = [f for f in self.ldict if f[:2] == 't_' ] + + # Now build up a list of functions and a list of strings + + self.toknames = { } # Mapping of symbols to token names + self.funcsym = { } # Symbols defined as functions + self.strsym = { } # Symbols defined as strings + self.ignore = { } # Ignore strings by state + self.errorf = { } # Error functions by state + + for s in self.stateinfo: + self.funcsym[s] = [] + self.strsym[s] = [] + + if len(tsymbols) == 0: + self.log.error("No rules of the form t_rulename are defined") + self.error = 1 + return + + for f in tsymbols: + t = self.ldict[f] + states, tokname = _statetoken(f,self.stateinfo) + self.toknames[f] = tokname + + if hasattr(t,"__call__"): + if tokname == 'error': + for s in states: + self.errorf[s] = t + elif tokname == 'ignore': + line = func_code(t).co_firstlineno + file = func_code(t).co_filename + self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__) + self.error = 1 + else: + for s in states: + self.funcsym[s].append((f,t)) + elif isinstance(t, StringTypes): + if tokname == 'ignore': + for s in states: + self.ignore[s] = t + if "\\" in t: + self.log.warning("%s contains a literal backslash '\\'",f) + + elif tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", f) + self.error = 1 + else: + for s in states: + self.strsym[s].append((f,t)) + else: + self.log.error("%s not defined as a function or string", f) + self.error = 1 + + # Sort the functions by line number + for f in self.funcsym.values(): + if sys.version_info[0] < 3: + f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno)) + else: + # Python 3.0 + f.sort(key=lambda x: func_code(x[1]).co_firstlineno) + + # Sort the strings by regular expression length + for s in self.strsym.values(): + if sys.version_info[0] < 3: + s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) + else: + # Python 3.0 + s.sort(key=lambda x: len(x[1]),reverse=True) + + # Validate all of the t_rules collected + def validate_rules(self): + for state in self.stateinfo: + # Validate all rules defined by functions + + + + for fname, f in self.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + tokname = self.toknames[fname] + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + continue + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + continue + + if not f.__doc__: + self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags) + if c.match(""): + self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e) + if '#' in f.__doc__: + self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__) + self.error = 1 + + # Validate all rules defined by strings + for name,r in self.strsym[state]: + tokname = self.toknames[name] + if tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", name) + self.error = 1 + continue + + if not tokname in self.tokens and tokname.find("ignore_") < 0: + self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags) + if (c.match("")): + self.log.error("Regular expression for rule '%s' matches empty string",name) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("Invalid regular expression for rule '%s'. %s",name,e) + if '#' in r: + self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name) + self.error = 1 + + if not self.funcsym[state] and not self.strsym[state]: + self.log.error("No rules defined for state '%s'",state) + self.error = 1 + + # Validate the error function + efunc = self.errorf.get(state,None) + if efunc: + f = efunc + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + + for f in self.files: + self.validate_file(f) + + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This checks to see if there are duplicated t_rulename() functions or strings + # in the parser input file. This is done using a simple regular expression + # match on each line in the given file. + # ----------------------------------------------------------------------------- + + def validate_file(self,filename): + import os.path + base,ext = os.path.splitext(filename) + if ext != '.py': return # No idea what the file is. Return OK + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + return # Couldn't find the file. Don't worry about it + + fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') + sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') + + counthash = { } + linen = 1 + for l in lines: + m = fre.match(l) + if not m: + m = sre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev) + self.error = 1 + linen += 1 + +# ----------------------------------------------------------------------------- +# lex(module) +# +# Build all of the regular expression rules from definitions in the supplied module +# ----------------------------------------------------------------------------- +def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None): + global lexer + ldict = None + stateinfo = { 'INITIAL' : 'inclusive'} + lexobj = Lexer() + lexobj.lexoptimize = optimize + global token,input + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + if debug: + if debuglog is None: + debuglog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the lexer + if object: module = object + + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + ldict = dict(_items) + else: + ldict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + linfo = LexerReflect(ldict,log=errorlog,reflags=reflags) + linfo.get_all() + if not optimize: + if linfo.validate_all(): + raise SyntaxError("Can't build lexer") + + if optimize and lextab: + try: + lexobj.readtab(lextab,ldict) + token = lexobj.token + input = lexobj.input + lexer = lexobj + return lexobj + + except ImportError: + pass + + # Dump some basic debugging information + if debug: + debuglog.info("lex: tokens = %r", linfo.tokens) + debuglog.info("lex: literals = %r", linfo.literals) + debuglog.info("lex: states = %r", linfo.stateinfo) + + # Build a dictionary of valid token names + lexobj.lextokens = { } + for n in linfo.tokens: + lexobj.lextokens[n] = 1 + + # Get literals specification + if isinstance(linfo.literals,(list,tuple)): + lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) + else: + lexobj.lexliterals = linfo.literals + + # Get the stateinfo dictionary + stateinfo = linfo.stateinfo + + regexs = { } + # Build the master regular expressions + for state in stateinfo: + regex_list = [] + + # Add rules defined by functions first + for fname, f in linfo.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state) + + # Now add all of the simple rules + for name,r in linfo.strsym[state]: + regex_list.append("(?P<%s>%s)" % (name,r)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state) + + regexs[state] = regex_list + + # Build the master regular expressions + + if debug: + debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====") + + for state in regexs: + lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames) + lexobj.lexstatere[state] = lexre + lexobj.lexstateretext[state] = re_text + lexobj.lexstaterenames[state] = re_names + if debug: + for i in range(len(re_text)): + debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i]) + + # For inclusive states, we need to add the regular expressions from the INITIAL state + for state,stype in stateinfo.items(): + if state != "INITIAL" and stype == 'inclusive': + lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) + lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) + lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) + + lexobj.lexstateinfo = stateinfo + lexobj.lexre = lexobj.lexstatere["INITIAL"] + lexobj.lexretext = lexobj.lexstateretext["INITIAL"] + + # Set up ignore variables + lexobj.lexstateignore = linfo.ignore + lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") + + # Set up error functions + lexobj.lexstateerrorf = linfo.errorf + lexobj.lexerrorf = linfo.errorf.get("INITIAL",None) + if not lexobj.lexerrorf: + errorlog.warning("No t_error rule is defined") + + # Check state information for ignore and error rules + for s,stype in stateinfo.items(): + if stype == 'exclusive': + if not s in linfo.errorf: + errorlog.warning("No error rule is defined for exclusive state '%s'", s) + if not s in linfo.ignore and lexobj.lexignore: + errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) + elif stype == 'inclusive': + if not s in linfo.errorf: + linfo.errorf[s] = linfo.errorf.get("INITIAL",None) + if not s in linfo.ignore: + linfo.ignore[s] = linfo.ignore.get("INITIAL","") + + # Create global versions of the token() and input() functions + token = lexobj.token + input = lexobj.input + lexer = lexobj + + # If in optimize mode, we write the lextab + if lextab and optimize: + lexobj.writetab(lextab,outputdir) + + return lexobj + +# ----------------------------------------------------------------------------- +# runmain() +# +# This runs the lexer as a main program +# ----------------------------------------------------------------------------- + +def runmain(lexer=None,data=None): + if not data: + try: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + except IndexError: + sys.stdout.write("Reading from standard input (type EOF to end):\n") + data = sys.stdin.read() + + if lexer: + _input = lexer.input + else: + _input = input + _input(data) + if lexer: + _token = lexer.token + else: + _token = token + + while 1: + tok = _token() + if not tok: break + sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos)) + +# ----------------------------------------------------------------------------- +# @TOKEN(regex) +# +# This decorator function can be used to set the regex expression on a function +# when its docstring might need to be set in an alternative way +# ----------------------------------------------------------------------------- + +def TOKEN(r): + def set_doc(f): + if hasattr(r,"__call__"): + f.__doc__ = r.__doc__ + else: + f.__doc__ = r + return f + return set_doc + +# Alternative spelling of the TOKEN decorator +Token = TOKEN + diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/parsetab.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/parsetab.py new file mode 100644 index 0000000..1248ded --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/parsetab.py @@ -0,0 +1,51 @@ + +# parsetab.py +# This file is automatically generated. Do not edit. +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'BLOCK BASE FIELD FIELD_HIGH MASK PADDING TAGGED_UNION TAG IDENTIFIER INTLIT LBRACE RBRACE LPAREN RPAREN COMMAstart : entity_listentity_list : entity_list : entity_list baseentity_list : entity_list blockentity_list : entity_list tagged_unionbase : BASE INTLITbase : BASE INTLIT LPAREN INTLIT COMMA INTLIT RPARENblock : BLOCK IDENTIFIER opt_visible_order_spec LBRACE fields RBRACEopt_visible_order_spec : opt_visible_order_spec : LPAREN visible_order_spec RPARENvisible_order_spec : visible_order_spec : IDENTIFIERvisible_order_spec : visible_order_spec COMMA IDENTIFIERfields : fields : fields FIELD IDENTIFIER INTLITfields : fields FIELD_HIGH IDENTIFIER INTLITfields : fields PADDING INTLITtagged_union : TAGGED_UNION IDENTIFIER IDENTIFIER LBRACE masks tags RBRACEtags :tags : tags TAG IDENTIFIER INTLITmasks :masks : masks MASK INTLIT INTLIT' + +_lr_action_items = {'BASE':([0,2,3,4,5,9,27,34,38,],[-2,6,-3,-4,-5,-6,-8,-7,-18,]),'BLOCK':([0,2,3,4,5,9,27,34,38,],[-2,7,-3,-4,-5,-6,-8,-7,-18,]),'TAGGED_UNION':([0,2,3,4,5,9,27,34,38,],[-2,8,-3,-4,-5,-6,-8,-7,-18,]),'$end':([0,1,2,3,4,5,9,27,34,38,],[-2,0,-1,-3,-4,-5,-6,-8,-7,-18,]),'INTLIT':([6,12,21,30,33,35,36,40,43,],[9,16,26,37,40,41,42,44,45,]),'IDENTIFIER':([7,8,11,14,24,28,29,39,],[10,11,15,19,31,35,36,43,]),'LPAREN':([9,10,],[12,14,]),'LBRACE':([10,13,15,23,],[-9,17,20,-10,]),'RPAREN':([14,18,19,26,31,],[-11,23,-12,34,-13,]),'COMMA':([14,16,18,19,31,],[-11,21,24,-12,-13,]),'RBRACE':([17,20,22,25,32,37,41,42,44,45,],[-14,-21,27,-19,38,-17,-15,-16,-22,-20,]),'FIELD':([17,22,37,41,42,],[-14,28,-17,-15,-16,]),'FIELD_HIGH':([17,22,37,41,42,],[-14,29,-17,-15,-16,]),'PADDING':([17,22,37,41,42,],[-14,30,-17,-15,-16,]),'MASK':([20,25,44,],[-21,33,-22,]),'TAG':([20,25,32,44,45,],[-21,-19,39,-22,-20,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'start':([0,],[1,]),'entity_list':([0,],[2,]),'base':([2,],[3,]),'block':([2,],[4,]),'tagged_union':([2,],[5,]),'opt_visible_order_spec':([10,],[13,]),'visible_order_spec':([14,],[18,]),'fields':([17,],[22,]),'masks':([20,],[25,]),'tags':([25,],[32,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> start","S'",1,None,None,None), + ('start -> entity_list','start',1,'p_start','bitfield_gen.py',83), + ('entity_list -> ','entity_list',0,'p_entity_list_empty','bitfield_gen.py',87), + ('entity_list -> entity_list base','entity_list',2,'p_entity_list_base','bitfield_gen.py',91), + ('entity_list -> entity_list block','entity_list',2,'p_entity_list_block','bitfield_gen.py',98), + ('entity_list -> entity_list tagged_union','entity_list',2,'p_entity_list_union','bitfield_gen.py',104), + ('base -> BASE INTLIT','base',2,'p_base_simple','bitfield_gen.py',110), + ('base -> BASE INTLIT LPAREN INTLIT COMMA INTLIT RPAREN','base',7,'p_base_mask','bitfield_gen.py',114), + ('block -> BLOCK IDENTIFIER opt_visible_order_spec LBRACE fields RBRACE','block',6,'p_block','bitfield_gen.py',118), + ('opt_visible_order_spec -> ','opt_visible_order_spec',0,'p_opt_visible_order_spec_empty','bitfield_gen.py',123), + ('opt_visible_order_spec -> LPAREN visible_order_spec RPAREN','opt_visible_order_spec',3,'p_opt_visible_order_spec','bitfield_gen.py',127), + ('visible_order_spec -> ','visible_order_spec',0,'p_visible_order_spec_empty','bitfield_gen.py',131), + ('visible_order_spec -> IDENTIFIER','visible_order_spec',1,'p_visible_order_spec_single','bitfield_gen.py',135), + ('visible_order_spec -> visible_order_spec COMMA IDENTIFIER','visible_order_spec',3,'p_visible_order_spec','bitfield_gen.py',139), + ('fields -> ','fields',0,'p_fields_empty','bitfield_gen.py',143), + ('fields -> fields FIELD IDENTIFIER INTLIT','fields',4,'p_fields_field','bitfield_gen.py',147), + ('fields -> fields FIELD_HIGH IDENTIFIER INTLIT','fields',4,'p_fields_field_high','bitfield_gen.py',151), + ('fields -> fields PADDING INTLIT','fields',3,'p_fields_padding','bitfield_gen.py',155), + ('tagged_union -> TAGGED_UNION IDENTIFIER IDENTIFIER LBRACE masks tags RBRACE','tagged_union',7,'p_tagged_union','bitfield_gen.py',159), + ('tags -> ','tags',0,'p_tags_empty','bitfield_gen.py',164), + ('tags -> tags TAG IDENTIFIER INTLIT','tags',4,'p_tags','bitfield_gen.py',168), + ('masks -> ','masks',0,'p_masks_empty','bitfield_gen.py',172), + ('masks -> masks MASK INTLIT INTLIT','masks',4,'p_masks','bitfield_gen.py',176), +] diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/syscall_header_gen.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/syscall_header_gen.py new file mode 100755 index 0000000..d85cf5f --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/syscall_header_gen.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# +# Copyright 2015, Corey Richardson +# Copyright 2014, NICTA +# +# This software may be distributed and modified according to the terms of +# the BSD 2-Clause license. Note that NO WARRANTY is provided. +# See "LICENSE_BSD2.txt" for details. +# +# @TAG(NICTA_BSD) +# + +# seL4 System Call ID Generator +# ============================== + +from __future__ import division, print_function +import argparse +import re +import sys +# install tempita using pip install tempita +# install tempita using sudo apt-get install python-tempita or similar for your distro +import tempita +import xml.dom.minidom + +common_header = """ + +/* This header was generated by kernel/tools/syscall_header_gen.py. + * + * To add a system call number, edit kernel/include/api/syscall.xml + * + */""" + +libsel4_header_template = \ +"""/* @LICENSE(NICTA) */""" + common_header + """ +#[repr(isize)] +pub enum SyscallId { +{{py:syscall_number = -1}} +{{for name, condition, list in enum}} + {{for syscall in list}} + {{if len(condition) > 0}} + #[cfg({{condition}})] + {{endif}} + {{syscall}} = {{syscall_number}}, + {{py:syscall_number -= 1}} + {{endfor}} +{{endfor}} +} +""" + +def parse_args(): + parser = argparse.ArgumentParser(description="""Generate seL4 syscall API constants + and associated header files""") + parser.add_argument('--xml', type=argparse.FileType('r'), + help='Name of xml file with syscall name definitions', required=True) + parser.add_argument('--mcs', action='store_true', + help='Generate MCS api') + parser.add_argument('--dest', type=argparse.FileType('w'), + help='Name of file to generate for librustsel4', required=True) + + result = parser.parse_args() + + return result + +def parse_syscall_list(element): + syscalls = [] + for config in element.getElementsByTagName("config"): + condition = config.getAttribute("condition") + # HACK: ugly hacks to handle simple CPP expressions (very fragile) + # NB: CONFIG_MAX_NUM_NODES > 1 =>'s CONFIG_SMP_SUPPORT + condition = condition.replace('CONFIG_MAX_NUM_NODES > 1', 'CONFIG_SMP_SUPPORT') + if condition == "defined CONFIG_DEBUG_BUILD && CONFIG_SMP_SUPPORT": + condition = 'all(feature = "CONFIG_DEBUG_BUILD", feature = "CONFIG_SMP_SUPPORT")' + elif condition == "defined CONFIG_DEBUG_BUILD && defined CONFIG_BENCHMARK_TRACK_UTILISATION": + condition = 'all(feature = "CONFIG_DEBUG_BUILD", feature = "CONFIG_BENCHMARK_TRACK_UTILISATION")' + elif condition: + condition = condition.replace('defined', '') + condition = condition.replace('(', '') + condition = condition.replace(')', '') + condition = condition.replace(' ', '') + if 'CONFIG_' in condition: + condition = 'feature = "' + condition + '"' + if '!' in condition: + condition = 'not(%s)' % condition.replace('!', '') + + config_name = config.getAttribute("name") + config_syscalls = [] + for syscall in config.getElementsByTagName("syscall"): + name = str(syscall.getAttribute("name")) + config_syscalls.append(name) + syscalls.append((config_name, condition, config_syscalls)) + + # sanity check + assert len(syscalls) != 0 + + return syscalls + + +def parse_xml(xml_file, mcs): + # first check if the file is valid xml + try: + doc = xml.dom.minidom.parse(xml_file) + except: + print("Error: invalid xml file.", file=sys.stderr) + sys.exit(-1) + + tag = "api-mcs" if mcs else "api-master" + api = doc.getElementsByTagName(tag) + if len(api) != 1: + print("Error: malformed xml. Only one api element allowed", + file=sys.stderr) + sys.exit(-1) + + configs = api[0].getElementsByTagName("config") + if len(configs) != 1: + print("Error: api element only supports 1 config element", + file=sys.stderr) + sys.exit(-1) + + if len(configs[0].getAttribute("name")) != 0: + print("Error: api element config only supports an empty name", + file=sys.stderr) + sys.exit(-1) + + # debug elements are optional + debug = doc.getElementsByTagName("debug") + if len(debug) != 1: + debug_element = None + else: + debug_element = debug[0] + + api_elements = parse_syscall_list(api[0]) + debug = parse_syscall_list(debug_element) + + return (api_elements, debug) + +def generate_libsel4_file(libsel4_header, syscalls): + + tmpl = tempita.Template(libsel4_header_template) + libsel4_header.write(tmpl.substitute(enum=syscalls)) + +if __name__ == "__main__": + args = parse_args() + + (api, debug) = parse_xml(args.xml, args.mcs) + args.xml.close() + + generate_libsel4_file(args.dest, api + debug) + args.dest.close() diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/syscall_stub_gen.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/syscall_stub_gen.py new file mode 100644 index 0000000..cba61fa --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/syscall_stub_gen.py @@ -0,0 +1,1096 @@ +#!/usr/bin/env python +# +# Copyright 2017, Corey Richardson +# Copyright 2014, NICTA +# +# This software may be distributed and modified according to the terms of +# the BSD 2-Clause license. Note that NO WARRANTY is provided. +# See "LICENSE_BSD2.txt" for details. +# +# @TAG(NICTA_BSD) +# + +from __future__ import division, print_function +from functools import reduce + +# +# seL4 System Call Stub Generator +# =============================== +# +# 2009 David Greenaway +# +# This script generates system call stubs based on an XML specification of the +# objects that the kernel exports (and the methods those objects export). +# +# Previously, Magpie (an IDL compiler) was used to generate these stubs. As +# Magpie development progressed, support for a fixed ABI (i.e., the ABI +# implemented by the seL4 kernel) was lost, and support for generating +# alignment-safe code (required by platforms such as ARM) was also removed. +# +# This script is a stop-gap until these features can be restored in Magpie +# once again. +# +# The script has certain limitations: +# +# * It must be told the size of all types. This includes complex types +# such as structures. +# +# We generate code that will cause compilation to fail if we get any +# object's size wrong, which should help mitigate the number of bugs caused +# because of this script becoming out of date compared to the source files. +# +# * The script has only been tested on the actual seL4 API XML description. +# +# No stress testing has taken place; there may be bugs if new and wonderful +# XML method descriptions are added. +# + +import operator +import itertools +import xml.dom.minidom +from argparse import ArgumentParser +import sys + +# Number of bits in a standard word +WORD_SIZE_BITS_ARCH = { + "aarch32": 32, + "ia32": 32, + "aarch64": 64, + "ia64": 64, + "x86_64": 64, + "arm_hyp": 32, + "riscv32": 32, + "riscv64": 64, +} + +MESSAGE_REGISTERS_FOR_ARCH = { + "aarch32": 4, + "aarch64": 4, + "ia32": 2, + "ia32-mcs": 1, + "x86_64": 4, + "arm_hyp": 4, + "riscv32": 4, + "riscv64": 4, +} + +WORD_CONST_SUFFIX_BITS = { + 32: "", + 64: "", +} + +# Maximum number of words that will be in a message. +MAX_MESSAGE_LENGTH = 64 + +TYPE_TRANS = { + "int": "isize", + "seL4_Uint8": "u8", + "seL4_Uint16": "u16", + "seL4_Uint32": "u32", + "seL4_Uint64": "u64", + "seL4_Bool": "u8", + "seL4_CapData_t": "seL4_CapData", + "seL4_PrioProps_t": "seL4_PrioProps", + "seL4_CapRights_t": "seL4_CapRights", + "seL4_RISCV_Page_GetAddress_t": "seL4_RISCV_Page_GetAddress", +} + +# Headers to include +INCLUDES = [ + 'autoconf.h', 'sel4/types.h' +] + +def translate_type(name): + if name in TYPE_TRANS: + return TYPE_TRANS[name] + else: + return name + +def translate_expr(name): + if name == "type": + return "type_" + return name + +def construction(expr, param): + if isinstance(param.type, StructType): + return "%s { words: [%s] }" % (param.type.name, expr) + else: + return "%s as %s" % (expr, translate_type(param.type.name)) + +TYPES = { + 8: "u8", + 16: "u16", + 32: "u32", + 64: "u64" +} + +DEFAULT_CAP_DESC = "The capability to the %(interface_manual_name)s which is being operated on." + +class Type(object): + """ + This class represents a C type (such as an 'int', structure or + pointer. + """ + + def __init__(self, name, size_bits, wordsize, double_word=False, native_size_bits=None): + """ + Define a new type, named 'name' that is 'size_bits' bits + long. + """ + + self.name = name + self.size_bits = size_bits + self.wordsize = wordsize + self.double_word = double_word + + # + # Store the number of bits C will use for this type + # in its native unpacked form. + # + # Required for 'bool', for example, which only uses 1 + # bit when packed, but 32 bits when unpacked. + # + if native_size_bits: + self.native_size_bits = native_size_bits + else: + self.native_size_bits = size_bits + + def pass_by_reference(self): + return self.size_bits > self.wordsize and not self.double_word + + def render_parameter_name(self, name): + """ + Return a string of C code that would be used in a function + parameter declaration. + """ + if name == "type": + name = "type_" + return "%s: %s" % (name, translate_type(self.name)) + + def pointer(self): + """ + Return a new Type class representing a pointer to this + object. + """ + return PointerType(self, self.wordsize) + + def c_expression(self, var_name, word_num=0): + """ + Return code for a C expression that gets word 'word_num' + of this type. + """ + assert word_num == 0 + return "%s" % var_name + + def double_word_expression(self, var_name, word_num, word_size): + + assert word_num == 0 or word_num == 1 + + if word_num == 0: + return "{1} as {0}".format(TYPES[self.size_bits], var_name) + elif word_num == 1: + return "{1}.wrapping_shr({2}) as {0}".format(TYPES[self.size_bits], var_name, + word_size) + + +class PointerType(Type): + """ + A pointer to a standard type. + """ + + def __init__(self, base_type, wordsize): + Type.__init__(self, base_type.name, wordsize, wordsize) + self.base_type = base_type + + def render_parameter_name(self, name): + if name == "type": + name = "type_" + return "%s: *mut %s" % (name, translate_type(self.name)) + + def c_expression(self, var_name, word_num=0): + assert word_num == 0 + return "unsafe { *%s }" % var_name + + def pointer(self): + raise NotImplementedError() + + +class CapType(Type): + """ + A type that is just a typedef of seL4_CPtr. + """ + + def __init__(self, name, wordsize): + Type.__init__(self, name, wordsize, wordsize) + + +class StructType(Type): + """ + A C 'struct' definition. + """ + + def __init__(self, name, size_bits, wordsize): + Type.__init__(self, name, size_bits, wordsize) + + def c_expression(self, var_name, word_num, member_name): + assert word_num < self.size_bits // self.wordsize + + # Multiword structure. + assert self.pass_by_reference() + return "(*%s).%s" % (var_name, member_name[word_num]) + + +class BitFieldType(Type): + """ + A special C 'struct' generated by the bitfield generator + """ + + def __init__(self, name, size_bits, wordsize): + Type.__init__(self, name, size_bits, wordsize) + + def c_expression(self, var_name, word_num=0): + + return "%s.words[%d]" % (var_name, word_num) + + +class Parameter(object): + def __init__(self, name, type): + self.name = name + self.type = type + + +class Api(object): + def __init__(self, name): + self.name = name + +# +# Types +# + + +def init_data_types(wordsize): + types = [ + # Simple Types + Type("int", 32, wordsize), + Type("long", wordsize, wordsize), + + Type("seL4_Uint8", 8, wordsize), + Type("seL4_Uint16", 16, wordsize), + Type("seL4_Uint32", 32, wordsize), + Type("seL4_Uint64", 64, wordsize, double_word=(wordsize == 32)), + Type("seL4_Time", 64, wordsize, double_word=(wordsize == 32)), + Type("seL4_Word", wordsize, wordsize), + Type("seL4_Bool", 1, wordsize, native_size_bits=8), + + # seL4 Structures + BitFieldType("seL4_CapRights_t", wordsize, wordsize), + + # Object types + CapType("seL4_CPtr", wordsize), + CapType("seL4_CNode", wordsize), + CapType("seL4_IRQHandler", wordsize), + CapType("seL4_IRQControl", wordsize), + CapType("seL4_TCB", wordsize), + CapType("seL4_Untyped", wordsize), + CapType("seL4_DomainSet", wordsize), + CapType("seL4_SchedContext", wordsize), + CapType("seL4_SchedControl", wordsize), + ] + + return types + + +def init_arch_types(wordsize): + arm_smmu = [ + CapType("seL4_ARM_SIDControl", wordsize), + CapType("seL4_ARM_SID", wordsize), + CapType("seL4_ARM_CBControl", wordsize), + CapType("seL4_ARM_CB", wordsize), + ] + arch_types = { + "aarch32": [ + Type("seL4_ARM_VMAttributes", wordsize, wordsize), + CapType("seL4_ARM_Page", wordsize), + CapType("seL4_ARM_PageTable", wordsize), + CapType("seL4_ARM_PageDirectory", wordsize), + CapType("seL4_ARM_ASIDControl", wordsize), + CapType("seL4_ARM_ASIDPool", wordsize), + CapType("seL4_ARM_VCPU", wordsize), + CapType("seL4_ARM_IOSpace", wordsize), + CapType("seL4_ARM_IOPageTable", wordsize), + StructType("seL4_UserContext", wordsize * 19, wordsize), + ] + arm_smmu, + + "aarch64": [ + Type("seL4_ARM_VMAttributes", wordsize, wordsize), + CapType("seL4_ARM_Page", wordsize), + CapType("seL4_ARM_PageTable", wordsize), + CapType("seL4_ARM_PageDirectory", wordsize), + CapType("seL4_ARM_PageUpperDirectory", wordsize), + CapType("seL4_ARM_PageGlobalDirectory", wordsize), + CapType("seL4_ARM_VSpace", wordsize), + CapType("seL4_ARM_ASIDControl", wordsize), + CapType("seL4_ARM_ASIDPool", wordsize), + CapType("seL4_ARM_VCPU", wordsize), + CapType("seL4_ARM_IOSpace", wordsize), + CapType("seL4_ARM_IOPageTable", wordsize), + StructType("seL4_UserContext", wordsize * 36, wordsize), + ] + arm_smmu, + + "arm_hyp": [ + Type("seL4_ARM_VMAttributes", wordsize, wordsize), + CapType("seL4_ARM_Page", wordsize), + CapType("seL4_ARM_PageTable", wordsize), + CapType("seL4_ARM_PageDirectory", wordsize), + CapType("seL4_ARM_ASIDControl", wordsize), + CapType("seL4_ARM_ASIDPool", wordsize), + CapType("seL4_ARM_VCPU", wordsize), + CapType("seL4_ARM_IOSpace", wordsize), + CapType("seL4_ARM_IOPageTable", wordsize), + StructType("seL4_UserContext", wordsize * 19, wordsize), + ] + arm_smmu, + + "ia32": [ + Type("seL4_X86_VMAttributes", wordsize, wordsize), + CapType("seL4_X86_IOPort", wordsize), + CapType("seL4_X86_IOPortControl", wordsize), + CapType("seL4_X86_ASIDControl", wordsize), + CapType("seL4_X86_ASIDPool", wordsize), + CapType("seL4_X86_IOSpace", wordsize), + CapType("seL4_X86_Page", wordsize), + CapType("seL4_X86_PageDirectory", wordsize), + CapType("seL4_X86_PageTable", wordsize), + CapType("seL4_X86_IOPageTable", wordsize), + CapType("seL4_X86_VCPU", wordsize), + CapType("seL4_X86_EPTPML4", wordsize), + CapType("seL4_X86_EPTPDPT", wordsize), + CapType("seL4_X86_EPTPD", wordsize), + CapType("seL4_X86_EPTPT", wordsize), + StructType("seL4_VCPUContext", wordsize * 7, wordsize), + StructType("seL4_UserContext", wordsize * 12, wordsize), + ], + + "x86_64": [ + Type("seL4_X86_VMAttributes", wordsize, wordsize), + CapType("seL4_X86_IOPort", wordsize), + CapType("seL4_X86_IOPortControl", wordsize), + CapType("seL4_X86_ASIDControl", wordsize), + CapType("seL4_X86_ASIDPool", wordsize), + CapType("seL4_X86_IOSpace", wordsize), + CapType("seL4_X86_Page", wordsize), + CapType("seL4_X64_PML4", wordsize), + CapType("seL4_X86_PDPT", wordsize), + CapType("seL4_X86_PageDirectory", wordsize), + CapType("seL4_X86_PageTable", wordsize), + CapType("seL4_X86_IOPageTable", wordsize), + CapType("seL4_X86_VCPU", wordsize), + CapType("seL4_X86_EPTPML4", wordsize), + CapType("seL4_X86_EPTPDPT", wordsize), + CapType("seL4_X86_EPTPD", wordsize), + CapType("seL4_X86_EPTPT", wordsize), + StructType("seL4_VCPUContext", wordsize * 7, wordsize), + StructType("seL4_UserContext", wordsize * 20, wordsize), + ], + "riscv32": [ + Type("seL4_RISCV_VMAttributes", wordsize, wordsize), + CapType("seL4_RISCV_Page", wordsize), + CapType("seL4_RISCV_PageTable", wordsize), + CapType("seL4_RISCV_ASIDControl", wordsize), + CapType("seL4_RISCV_ASIDPool", wordsize), + StructType("seL4_UserContext", wordsize * 32, wordsize), + ], + "riscv64": [ + Type("seL4_RISCV_VMAttributes", wordsize, wordsize), + CapType("seL4_RISCV_Page", wordsize), + CapType("seL4_RISCV_PageTable", wordsize), + CapType("seL4_RISCV_ASIDControl", wordsize), + CapType("seL4_RISCV_ASIDPool", wordsize), + StructType("seL4_UserContext", wordsize * 32, wordsize), + ] + } + + return arch_types + +# Retrieve a member list for a given struct type + + +def struct_members(typ, structs): + members = [member for struct_name, member in structs if struct_name == typ.name] + assert len(members) == 1 + return members[0] + +# Keep increasing the given number 'x' until 'x % a == 0'. + + +def align_up(x, a): + if x % a == 0: + return x + return x + a - (x % a) + + +def get_parameter_positions(parameters, wordsize): + """ + Determine where each parameter should be packed in the generated message. + We generate a list of: + + (param_name, param_type, first_bit, num_bits) + + tuples. + + We guarantee that either (num_words == 1) or (bit_offset == 0). + """ + bits_used = 0 + results = [] + + for param in parameters: + # How big are we? + type_size = param.type.size_bits + + # We need everything to be a power of two, or word sized. + assert ((type_size & (type_size - 1)) == 0) or (type_size % wordsize == 0) + + # Align up to our own size, or the next word. (Whichever is smaller) + bits_used = align_up(bits_used, min(type_size, wordsize)) + + # Place ourself. + results.append((param, bits_used, type_size)) + bits_used += type_size + + return results + + +def generate_param_list(input_params, output_params): + # Generate parameters + params = [] + for param in input_params: + if not param.type.pass_by_reference(): + params.append(param.type.render_parameter_name(param.name)) + else: + params.append(param.type.pointer().render_parameter_name(param.name)) + for param in output_params: + if param.type.pass_by_reference(): + params.append(param.type.pointer().render_parameter_name(param.name)) + + return ", ".join(params) + + +def generate_marshal_expressions(params, num_mrs, structs, wordsize): + """ + Generate marshalling expressions for the given set of inputs. + + We return a list of expressions; one expression per word required + to marshal all the inputs. + """ + + def generate_param_code(param, first_bit, num_bits, word_array, wordsize): + """ + Generate code to marshal the given parameter into the correct + location in the message. + + 'word_array' is an array of the final contents of the message. + word_array[k] contains what should be placed in the k'th message + register, and is an array of expressions that will (eventually) + be bitwise-or'ed into it. + """ + + target_word = first_bit // wordsize + target_offset = first_bit % wordsize + + # double word type + if param.type.double_word: + word_array[target_word].append(param.type.double_word_expression(param.name, 0, wordsize)) + word_array[target_word + 1].append(param.type.double_word_expression(param.name, 1, wordsize)) + return + + # Single full word? + if num_bits == wordsize: + assert target_offset == 0 + expr = param.type.c_expression(param.name) + word_array[target_word].append(expr) + return + + # Part of a word? + if num_bits < wordsize: + expr = param.type.c_expression(param.name) + expr = "(%s & %#x%s)" % (expr, (1 << num_bits) - 1, + WORD_CONST_SUFFIX_BITS[wordsize]) + if target_offset: + expr = "(%s as seL4_Word).wrapping_shl(%d)" % (expr, target_offset) + word_array[target_word].append(expr) + return + + # Multiword array + assert target_offset == 0 + num_words = num_bits // wordsize + for i in range(num_words): + expr = param.type.c_expression(param.name, i, struct_members(param.type, structs)) + word_array[target_word + i].append(expr) + + # Get their marshalling positions + positions = get_parameter_positions(params, wordsize) + + # Generate marshal code. + words = [[] for _ in range(num_mrs, MAX_MESSAGE_LENGTH)] + for (param, first_bit, num_bits) in positions: + generate_param_code(param, first_bit, num_bits, words, wordsize) + + # Return list of expressions. + return [" | ".join(map(lambda x: "(" + translate_expr(x) + " as seL4_Word)", x)) for x in words if len(x) > 0] + + +def generate_unmarshal_expressions(params, wordsize): + """ + Generate unmarshalling expressions for the given set of outputs. + + We return a list of list of expressions; one list per variable, containing + expressions for the words in it that must be unmarshalled. The expressions + will have tokens of the form: + "%(w0)s" + in them, indicating a read from a word in the message. + """ + + def unmarshal_single_param(first_bit, num_bits, wordsize): + """ + Unmarshal a single parameter. + """ + first_word = first_bit // wordsize + bit_offset = first_bit % wordsize + + # Multiword type? + if num_bits > wordsize: + result = [] + for x in range(num_bits // wordsize): + result.append("%%(w%d)s" % (x + first_word)) + return result + + # Otherwise, bit packed. + if num_bits == wordsize: + return ["%%(w%d)s" % first_word] + elif bit_offset == 0: + return ["(%%(w%d)s & %#x)" % ( + first_word, (1 << num_bits) - 1)] + else: + return ["(%%(w%d)s.wrapping_shr(%d)) & %#x" % ( + first_word, bit_offset, (1 << num_bits) - 1)] + + # Get their marshalling positions + positions = get_parameter_positions(params, wordsize) + + # Generate the unmarshal code. + results = [] + for (param, first_bit, num_bits) in positions: + results.append((param, unmarshal_single_param(first_bit, num_bits, wordsize))) + return results + +def generate_result_struct(interface_name, method_name, output_params): + """ + Generate a structure definition to be returned by the system call stubs to + the user. + + We have a few constraints: + + * We always need an 'error' output parameter, even though it won't + appear in the list 'output_params' given to us. + + * Output parameters may be marked as 'pass_by_reference', indicating + that we only ever see pointers to the item. + + If no structure is needed (i.e., we just return an error code), we return + 'None'. + """ + + # Do we actually need a structure? + if len([x for x in output_params if not x.type.pass_by_reference()]) == 0: + return None + + # + # Generate the structure: + # + # struct seL4_CNode_Copy { + # int error; + # seL4_Word foo; + # }; + # typedef struct seL4_CNode_Copy seL4_CNode_Copy_t; + # + result = [] + result.append("#[repr(C)]") + result.append("#[derive(Debug, Clone, Copy, PartialEq, Eq)]") + result.append("pub struct %s_%s {" % (interface_name, method_name)) + result.append("\tpub error: isize,") + for i in output_params: + if not i.type.pass_by_reference(): + result.append("\tpub %s," % i.type.render_parameter_name(i.name)) + result.append("}") + result.append("") + + return "\n".join(result) + + +def generate_stub(arch, wordsize, interface_name, method_name, method_id, input_params, output_params, structs, use_only_ipc_buffer, comment, mcs): + result = [] + + if use_only_ipc_buffer: + num_mrs = 0 + else: + if mcs and "%s-mcs" % arch in MESSAGE_REGISTERS_FOR_ARCH: + num_mrs = MESSAGE_REGISTERS_FOR_ARCH["%s-mcs" % arch] + else: + num_mrs = MESSAGE_REGISTERS_FOR_ARCH[arch] + + # Split out cap parameters and standard parameters + standard_params = [] + cap_params = [] + for x in input_params: + if isinstance(x.type, CapType): + cap_params.append(x) + else: + standard_params.append(x) + + # Determine if we are returning a structure, or just the error code. + returning_struct = False + results_structure = generate_result_struct(interface_name, method_name, output_params) + if results_structure: + return_type = "%s_%s" % (interface_name, method_name) + returning_struct = True + else: + return_type = "isize" + + # + # Print doxygen comment. + # + result.append(comment) + + # + # Print function header. + # + # static inline int + # seL4_Untyped_Retype(...) + # { + # + result.append("#[inline(always)]") + result.append("pub unsafe fn %s_%s(%s) -> %s" % (interface_name, method_name, + generate_param_list(input_params, output_params), return_type)) + result.append("{") + + # + # Get a list of expressions for our caps and inputs. + # + input_expressions = generate_marshal_expressions(standard_params, num_mrs, + structs, wordsize) + cap_expressions = [x.name for x in cap_params] + service_cap = cap_expressions[0] + cap_expressions = cap_expressions[1:] + + # + # Compute how many words the inputs and output will require. + # + input_param_words = len(input_expressions) + output_param_words = sum([p.type.size_bits for p in output_params]) // wordsize + + # + # Setup variables we will need. + # + result.append("\tlet mut result: %s = ::core::mem::zeroed();" % return_type) + result.append("\tlet tag = seL4_MessageInfo::new(InvocationLabel::%s as seL4_Word, 0, %d, %d);" % (method_id, len(cap_expressions), len(input_expressions))) + result.append("\tlet output_tag;") + for i in range(num_mrs): + result.append("\tlet mut mr%d: seL4_Word = 0;" % i) + result.append("") + + # + # Copy capabilities. + # + # /* Setup input capabilities. */ + # seL4_SetCap(i, cap); + # + if len(cap_expressions) > 0: + result.append("\t/* Setup input capabilities. */") + for i in range(len(cap_expressions)): + result.append("\tseL4_SetCap(%d, %s);" % (i, cap_expressions[i])) + result.append("") + + # + # Copy in the inputs. + # + # /* Marshal input parameters. */ + # seL4_SetMR(i, v); + # ... + # + if max(num_mrs, len(input_expressions)) > 0: + result.append("\t/* Marshal and initialize parameters. */") + # Initialize in-register parameters + for i in range(num_mrs): + if i < len(input_expressions): + result.append("\tmr%d = %s as seL4_Word;" % (i, input_expressions[i])) + else: + result.append("\tmr%d = 0;" % i) + # Initialize buffered parameters + for i in range(num_mrs, len(input_expressions)): + if input_expressions[i] == "type": + input_expressions[i] = "type_" + result.append("\tseL4_SetMR(%d, %s);" % (i, input_expressions[i])) + result.append("") + + # + # Generate the call. + # + if use_only_ipc_buffer: + result.append("\t/* Perform the call. */") + result.append("\toutput_tag = seL4_Call(%s, tag);" % service_cap) + else: + result.append("\t/* Perform the call, passing in-register arguments directly. */") + result.append("\toutput_tag = seL4_CallWithMRs(%s, tag," % (service_cap)) + result.append("\t\t%s);" % ', '.join( + ("&mut mr%d" % i) for i in range(num_mrs))) + + # + # Prepare the result. + # + label = "result.error" if returning_struct else "result" + result.append("\t%s = output_tag.get_label() as _;" % label); + result.append("") + + if not use_only_ipc_buffer: + result.append("\t/* Unmarshal registers into IPC buffer on error. */") + result.append("\tif (%s != seL4_Error::seL4_NoError as u32) {" % label) + for i in range(num_mrs): + result.append("\t\tseL4_SetMR(%d, mr%d);" % (i, i)) + if returning_struct: + result.append("\t\treturn result;") + result.append("\t}") + result.append("") + + # + # Generate unmarshalling code. + # + if len(output_params) > 0: + result.append("\t/* Unmarshal result. */") + source_words = {} + for i in range(MAX_MESSAGE_LENGTH): + if i < num_mrs: + source_words["w%d" % i] = "mr%d" % i + else: + source_words["w%d" % i] = "seL4_GetMR(%d)" % i + unmashalled_params = generate_unmarshal_expressions(output_params, wordsize) + for (param, words) in unmashalled_params: + param.name = translate_expr(param.name) + if param.type.pass_by_reference(): + members = struct_members(param.type, structs) + for i in range(len(words)): + result.append("\t(*%s).%s = %s;" % + (param.name, members[i], words[i] % source_words)) + else: + if param.type.double_word: + result.append("\tresult.%s = (%s as %s) + ((%s as %s).wrapping_shr(32));" % + (param.name, words[0] % source_words, TYPES[64], + words[1] % source_words, TYPES[64])) + else: + for word in words: + result.append("\tresult.%s = %s;" % (param.name, construction(word % source_words, param))) + + # + # } + # + result.append("\treturn result;") + result.append("}") + + return "\n".join(result) + "\n" + + +def get_xml_element_contents(element): + """ + Converts the contents of an xml element into a string, with all + child xml nodes unchanged. + """ + return "".join([c.toxml() for c in element.childNodes]) + + +def get_xml_element_content_with_xmlonly(element): + """ + Converts the contents of an xml element into a string, wrapping + all child xml nodes in doxygen @xmlonly/@endxmlonly keywords. + """ + + result = [] + prev_element = False + for node in element.childNodes: + if node.nodeType == xml.dom.Node.TEXT_NODE: + if prev_element: + # text node following element node + result.append(" @endxmlonly ") + prev_element = False + else: + if not prev_element: + # element node following text node + result.append(" @xmlonly ") + prev_element = True + + result.append(node.toxml()) + + return "".join(result) + + +def normalise_text(text): + """ + Removes leading and trailing whitespace from each line of text. + Removes leading and trailing blank lines from text. + """ + stripped = text.strip() + stripped_lines = [line.strip() for line in text.split("\n")] + # remove leading and trailing empty lines + stripped_head = list(itertools.dropwhile(lambda s: not s, stripped_lines)) + stripped_tail = itertools.dropwhile(lambda s: not s, reversed(stripped_head)) + return "\n".join(reversed(list(stripped_tail))) + + +def parse_xml_file(input_file, valid_types): + """ + Parse an XML file containing method definitions. + """ + + # Create a dictionary of type name to type. + type_names = {} + for i in valid_types: + type_names[i.name] = i + + # Parse the XML to generate method structures. + methods = [] + structs = [] + doc = xml.dom.minidom.parse(input_file) + + api = Api(doc.getElementsByTagName("api")[0].getAttribute("name")) + + for struct in doc.getElementsByTagName("struct"): + _struct_members = [] + struct_name = struct.getAttribute("name") + for members in struct.getElementsByTagName("member"): + member_name = members.getAttribute("name") + _struct_members.append(member_name) + structs.append((struct_name, _struct_members)) + + for interface in doc.getElementsByTagName("interface"): + interface_name = interface.getAttribute("name") + interface_manual_name = interface.getAttribute("manual_name") or interface_name + + interface_cap_description = interface.getAttribute("cap_description") or DEFAULT_CAP_DESC % {"interface_manual_name": interface_manual_name} + + for method in interface.getElementsByTagName("method"): + method_name = method.getAttribute("name") + method_id = method.getAttribute("id") + method_condition = method.getAttribute("condition") + method_manual_name = method.getAttribute("manual_name") or method_name + method_manual_label = method.getAttribute("manual_label") or \ + "%s_%s" % (interface_manual_name.lower(), method_name.lower()) + + comment_lines = ["@xmlonly @endxmlonly" % + (interface_manual_name, method_manual_name, method_manual_label)] + + method_brief = method.getElementsByTagName("brief") + if method_brief: + method_brief_text = get_xml_element_contents(method_brief[0]) + normalised_method_brief_text = normalise_text(method_brief_text) + comment_lines.append("@brief @xmlonly %s @endxmlonly" % normalised_method_brief_text) + + method_description = method.getElementsByTagName("description") + if method_description: + method_description_text = get_xml_element_contents(method_description[0]) + normalised_method_description_text = normalise_text(method_description_text) + comment_lines.append("\n@xmlonly\n%s\n@endxmlonly\n" % normalised_method_description_text) + + method_return_description = method.getElementsByTagName("return") + if method_return_description: + comment_lines.append("@return @xmlonly %s @endxmlonly" % get_xml_element_contents(method_return_description[0])) + + + # + # Get parameters. + # + # We always have an implicit cap parameter. + # + input_params = [Parameter("_service", type_names[interface_name])] + + cap_description = interface_cap_description + cap_param = method.getElementsByTagName("cap_param") + if cap_param: + append_description = cap_param[0].getAttribute("append_description") + if append_description: + cap_description += append_description + + comment_lines.append("@param[in] _service %s" % cap_description) + output_params = [] + for param in method.getElementsByTagName("param"): + param_name = param.getAttribute("name") + param_type = type_names.get(param.getAttribute("type")) + if not param_type: + raise Exception("Unknown type '%s'." % (param.getAttribute("type"))) + param_dir = param.getAttribute("dir") + assert (param_dir == "in") or (param_dir == "out") + if param_dir == "in": + input_params.append(Parameter(param_name, param_type)) + else: + output_params.append(Parameter(param_name, param_type)) + + if param_dir == "in" or param_type.pass_by_reference(): + param_description = param.getAttribute("description") + if not param_description: + param_description_element = param.getElementsByTagName("description") + if param_description_element: + param_description_text = get_xml_element_content_with_xmlonly( + param_description_element[0]) + param_description = normalise_text(param_description_text) + + comment_lines.append("@param[%s] %s %s " % + (param_dir, param_name, param_description)) + + # split each line on newlines + comment_lines = reduce(operator.add, [l.split("\n") for l in comment_lines], []) + + # place the comment text in a c comment + comment = "\n".join(["/**"] + [" * %s" % l for l in comment_lines] + [" */"]) + + methods.append((interface_name, method_name, method_id, input_params, + output_params, method_condition, comment)) + + return (methods, structs, api) + + +def generate_stub_file(arch, wordsize, input_files, output_file, use_only_ipc_buffer, mcs): + """ + Generate a header file containing system call stubs for seL4. + """ + result = [] + + # Ensure architecture looks sane. + if arch not in WORD_SIZE_BITS_ARCH.keys(): + raise Exception("Invalid architecture.") + + data_types = init_data_types(wordsize) + arch_types = init_arch_types(wordsize) + + # Parse XML + methods = [] + structs = [] + for infile in input_files: + method, struct, _ = parse_xml_file(infile, data_types + arch_types[arch]) + methods += method + structs += struct + + # Print header. + result.append(""" +/* + * Automatically generated system call stubs. + */ + +"""); + + # + # Generate structures needed to return results back to the user. + # + # We can not use pass-by-reference (except for really large objects), as + # the verification framework does not support them. + # + result.append("/*") + result.append(" * Return types for generated methods.") + result.append(" */") + for (interface_name, method_name, _, _, output_params, _, _) in methods: + results_structure = generate_result_struct(interface_name, method_name, output_params) + if results_structure: + result.append(results_structure) + + # + # Generate the actual stub code. + # + result.append("/*") + result.append(" * Generated stubs.") + result.append(" */") + for (interface_name, method_name, method_id, inputs, outputs, condition, comment) in methods: + # HACK: ugly hacks to handle simple CPP expressions (very fragile) + if condition == "(!defined CONFIG_KERNEL_MCS) && CONFIG_MAX_NUM_NODES > 1": + # NB: CONFIG_MAX_NUM_NODES > 1 =>'s CONFIG_SMP_SUPPORT + condition = 'all(not(feature = "CONFIG_KERNEL_MCS"), feature = "CONFIG_SMP_SUPPORT")' + elif condition == "CONFIG_MAX_NUM_NODES > 1": + condition = 'feature = "CONFIG_SMP_SUPPORT"' + elif condition: + condition = condition.replace('defined', '') + condition = condition.replace('(', '') + condition = condition.replace(')', '') + if 'CONFIG_' in condition: + condition = 'feature = "' + condition + '"' + if '!' in condition: + condition = 'not(%s)' % condition.replace('!', '') + if condition: + result.append("#[cfg(%s)]" % condition) + + result.append(generate_stub(arch, wordsize, interface_name, method_name, + method_id, inputs, outputs, structs, use_only_ipc_buffer, comment, mcs)) + + # Write the output + output = open(output_file, "w") + output.write("\n".join(result)) + output.close() + + +def process_args(): + usage_str = """ + %(prog)s [OPTIONS] [FILES] """ + epilog_str = """ + + """ + parser = ArgumentParser(description='seL4 System Call Stub Generator.', + usage=usage_str, + epilog=epilog_str) + parser.add_argument("-o", "--output", dest="output", default="/dev/stdout", + help="Output file to write stub to. (default: %(default)s).") + parser.add_argument("-b", "--buffer", dest="buffer", action="store_true", default=False, + help="Use IPC buffer exclusively, i.e. do not pass syscall arguments by registers. (default: %(default)s)") + parser.add_argument("-a", "--arch", dest="arch", required=True, choices=WORD_SIZE_BITS_ARCH, + help="Architecture to generate stubs for.") + parser.add_argument("--mcs", dest="mcs", action="store_true", + help="Generate MCS api.") + + wsizegroup = parser.add_mutually_exclusive_group() + wsizegroup.add_argument("-w", "--word-size", dest="wsize", + help="Word size(in bits), for the platform.") + wsizegroup.add_argument("-c", "--cfile", dest="cfile", + help="Config file for Kbuild, used to get Word size.") + + parser.add_argument("files", metavar="FILES", nargs="+", + help="Input XML files.") + + return parser + + +def main(): + + parser = process_args() + args = parser.parse_args() + + if not (args.wsize or args.cfile): + parser.error("Require either -w/--word-size or -c/--cfile argument.") + sys.exit(2) + + # Get word size + wordsize = -1 + + #if args.cfile: + # try: + # with open(args.cfile) as conffile: + # for line in conffile: + # if line.startswith('CONFIG_WORD_SIZE'): + # wordsize = int(line.split('=')[1].strip()) + # except IndexError: + # print "Invalid word size in configuration file." + # sys.exit(2) + #else: + wordsize = int(args.wsize) + + if wordsize == -1: + print("Invalid word size: %s" % wordsize) + sys.exit(2) + + # Generate the stubs. + generate_stub_file(args.arch, wordsize, args.files, args.output, args.buffer, args.mcs) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/umm.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/umm.py new file mode 100755 index 0000000..d73ee73 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/umm.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python + +# +# Copyright 2014, NICTA +# +# This software may be distributed and modified according to the terms of +# the BSD 2-Clause license. Note that NO WARRANTY is provided. +# See "LICENSE_BSD2.txt" for details. +# +# @TAG(NICTA_BSD) +# + +import sys +import os.path +import optparse +import re + +## We assume length tp > 0 +def parse_type(tps): + def helper(tps): + tp = tps[0] + rest = tps[1:] + + if tp == 'Word': + return ('Word', rest[0]), rest[1:] + + elif tp == 'Ptr': + tp2, rest = helper(rest) + return ('Ptr', tp2), rest + + elif tp == 'Unit': + return ('Unit',) , rest + + elif tp == 'Array': + tp2, rest = helper(rest) + # arrays are Array ... sz + return ('Array', tp2, rest[0]), rest[1:] + + else: + return ('Base', tp), rest + + return helper(tps)[0] + +def splitBy(f, xs): + def fold(acc, v): + if f(v): + acc[0].append(acc[1]) + return (acc[0], []) + else: + acc[1].append(v) + return acc + + return (reduce(fold, xs, ([], [])))[0] + +def handle_one_struct(s): + def hdl_fld(f): + fl, tp = f.split(':') + return (fl.lstrip(), parse_type(tp.split(' '))) + + name = s[0] + return (name, map(hdl_fld, s[1:])) + +def dict_from_list(ls): + a = {} + for k, v in ls: + a[k] = v + + return a + +def is_base(x): + return (x[0] == 'Base') + +def base_name(x): + return x[1] + +# This assumes that membership is a DAG which is the case in C +# We could memoize, doesn't seem worth it ... +def paths_to_type(mp, f, start): + def handle_one(fld): + name, tp = fld + if f(tp): + return [([start + '.' + name], tp)] + elif is_base(tp): + res = paths_to_type(mp, f, base_name(tp)) + # prepend paths by name (why no Cons in python? grrr) + map(lambda x: (x[0].insert(0, name)), res) + return res + else: + return [] + + # init case + start_tp = ('Base', start) + if f(start_tp): + return [([], start_tp)] + else: + res = map(handle_one, mp[start]) + return (reduce(lambda x, y: x + y, res)) + +def build_types(file): + in_file = open(file, 'r') + + lines = map(lambda x: x.rstrip(), in_file.readlines()) + + in_file.close() + + grps = splitBy(lambda x: x == '', lines) + + # the order information will be important if we want _CL for all types + sts = dict_from_list(map(handle_one_struct, grps)) + + return sts + + +def print_graph(filename, out_file): + mp = build_types(filename) + + print >>out_file, 'digraph types {' + for k, flds in mp.iteritems(): + for fld, tp in flds: + #if is_base(tp): + print >> out_file, '\t "%s" -> "%s" [label="%s"]' % \ + (k, base_name(tp), fld) + print >>out_file, '}' + +## Toplevel + +if __name__ == '__main__': + print_graph('umm_types.txt', sys.stdout) diff --git a/apps/system/components/kata-os-common/src/sel4-sys/tools/yacc.py b/apps/system/components/kata-os-common/src/sel4-sys/tools/yacc.py new file mode 100644 index 0000000..1749b35 --- /dev/null +++ b/apps/system/components/kata-os-common/src/sel4-sys/tools/yacc.py @@ -0,0 +1,3279 @@ +# +# @TAG(OTHER_BSD) +# +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2009, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# +# This implements an LR parser that is constructed from grammar rules defined +# as Python functions. The grammer is specified by supplying the BNF inside +# Python documentation strings. The inspiration for this technique was borrowed +# from John Aycock's Spark parsing system. PLY might be viewed as cross between +# Spark and the GNU bison utility. +# +# The current implementation is only somewhat object-oriented. The +# LR parser itself is defined in terms of an object (which allows multiple +# parsers to co-exist). However, most of the variables used during table +# construction are defined in terms of global variables. Users shouldn't +# notice unless they are trying to define multiple parsers at the same +# time using threads (in which case they should have their head examined). +# +# This implementation supports both SLR and LALR(1) parsing. LALR(1) +# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), +# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, +# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced +# by the more efficient DeRemer and Pennello algorithm. +# +# :::::::: WARNING ::::::: +# +# Construction of LR parsing tables is fairly complicated and expensive. +# To make this module run fast, a *LOT* of work has been put into +# optimization---often at the expensive of readability and what might +# consider to be good Python "coding style." Modify the code at your +# own risk! +# ---------------------------------------------------------------------------- + +__version__ = "3.2" +__tabversion__ = "3.2" # Table version + +#----------------------------------------------------------------------------- +# === User configurable parameters === +# +# Change these to modify the default behavior of yacc (if you wish) +#----------------------------------------------------------------------------- + +yaccdebug = 1 # Debugging mode. If set, yacc generates a + # a 'parser.out' file in the current directory + +debug_file = 'parser.out' # Default name of the debugging file +tab_module = 'parsetab' # Default name of the table module +default_lr = 'LALR' # Default LR table generation method + +error_count = 3 # Number of symbols that must be shifted to leave recovery mode + +yaccdevel = 0 # Set to True if developing yacc. This turns off optimized + # implementations of certain functions. + +resultlimit = 40 # Size limit of results when running in debug mode. + +pickle_protocol = 0 # Protocol to use when writing pickle files + +import re, types, sys, os.path + +# Compatibility function for python 2.6/3.0 +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# Compatibility +try: + MAXINT = sys.maxint +except AttributeError: + MAXINT = sys.maxsize + +# Python 2.x/3.0 compatibility. +def load_ply_lex(): + if sys.version_info[0] < 3: + import lex + else: + import ply.lex as lex + return lex + +# This object is a stand-in for a logging object created by the +# logging module. PLY will use this by default to create things +# such as the parser.out file. If a user wants more detailed +# information, they can create their own logging object and pass +# it into PLY. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def debug(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + info = debug + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + critical = debug + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# Exception raised for yacc-related errors +class YaccError(Exception): pass + +# Format the result message that the parser produces when running in debug mode. +def format_result(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) > resultlimit: + repr_str = repr_str[:resultlimit]+" ..." + result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str) + return result + + +# Format stack entries when the parser is running in debug mode +def format_stack_entry(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) < 16: + return repr_str + else: + return "<%s @ 0x%x>" % (type(r).__name__,id(r)) + +#----------------------------------------------------------------------------- +# === LR Parsing Engine === +# +# The following classes are used for the LR parser itself. These are not +# used during table construction and are independent of the actual LR +# table generation algorithm +#----------------------------------------------------------------------------- + +# This class is used to hold non-terminal grammar symbols during parsing. +# It normally has the following attributes set: +# .type = Grammar symbol type +# .value = Symbol value +# .lineno = Starting line number +# .endlineno = Ending line number (optional, set automatically) +# .lexpos = Starting lex position +# .endlexpos = Ending lex position (optional, set automatically) + +class YaccSymbol: + def __str__(self): return self.type + def __repr__(self): return str(self) + +# This class is a wrapper around the objects actually passed to each +# grammar rule. Index lookup and assignment actually assign the +# .value attribute of the underlying YaccSymbol object. +# The lineno() method returns the line number of a given +# item (or 0 if not defined). The linespan() method returns +# a tuple of (startline,endline) representing the range of lines +# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) +# representing the range of positional information for a symbol. + +class YaccProduction: + def __init__(self,s,stack=None): + self.slice = s + self.stack = stack + self.lexer = None + self.parser= None + def __getitem__(self,n): + if n >= 0: return self.slice[n].value + else: return self.stack[n].value + + def __setitem__(self,n,v): + self.slice[n].value = v + + def __getslice__(self,i,j): + return [s.value for s in self.slice[i:j]] + + def __len__(self): + return len(self.slice) + + def lineno(self,n): + return getattr(self.slice[n],"lineno",0) + + def set_lineno(self,n,lineno): + self.slice[n].lineno = n + + def linespan(self,n): + startline = getattr(self.slice[n],"lineno",0) + endline = getattr(self.slice[n],"endlineno",startline) + return startline,endline + + def lexpos(self,n): + return getattr(self.slice[n],"lexpos",0) + + def lexspan(self,n): + startpos = getattr(self.slice[n],"lexpos",0) + endpos = getattr(self.slice[n],"endlexpos",startpos) + return startpos,endpos + + def error(self): + raise SyntaxError + + +# ----------------------------------------------------------------------------- +# == LRParser == +# +# The LR Parsing engine. +# ----------------------------------------------------------------------------- + +class LRParser: + def __init__(self,lrtab,errorf): + self.productions = lrtab.lr_productions + self.action = lrtab.lr_action + self.goto = lrtab.lr_goto + self.errorfunc = errorf + + def errok(self): + self.errorok = 1 + + def restart(self): + del self.statestack[:] + del self.symstack[:] + sym = YaccSymbol() + sym.type = '$end' + self.symstack.append(sym) + self.statestack.append(0) + + def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + if debug or yaccdevel: + if isinstance(debug,int): + debug = PlyLogger(sys.stderr) + return self.parsedebug(input,lexer,debug,tracking,tokenfunc) + elif tracking: + return self.parseopt(input,lexer,debug,tracking,tokenfunc) + else: + return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parsedebug(). + # + # This is the debugging enabled version of parse(). All changes made to the + # parsing engine should be made here. For the non-debugging version, + # copy this code to a method parseopt() and delete all of the sections + # enclosed in: + # + # #--! DEBUG + # statements + # #--! DEBUG + # + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # --! DEBUG + debug.info("PLY: PARSE DEBUG START") + # --! DEBUG + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = "$end" + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + # --! DEBUG + debug.debug('') + debug.debug('State : %s', state) + # --! DEBUG + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = "$end" + + # --! DEBUG + debug.debug('Stack : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + # --! DEBUG + debug.debug("Action : Shift and goto state %s", t) + # --! DEBUG + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + # --! DEBUG + if plen: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t) + else: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t) + + # --! DEBUG + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n,"value",None) + # --! DEBUG + debug.info("Done : Returning %s", format_result(result)) + debug.info("PLY: PARSE DEBUG END") + # --! DEBUG + return result + + if t == None: + + # --! DEBUG + debug.error('Error : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == "$end": + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != "$end": + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == "$end": + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt(). + # + # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. + # Edit the debug version above, then copy any modifications to the method + # below while removing #--! DEBUG sections. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt_notrack(). + # + # Optimized version of parseopt() with line number tracking removed. + # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove + # code in the #--! TRACKING sections + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + +# ----------------------------------------------------------------------------- +# === Grammar Representation === +# +# The following functions, classes, and variables are used to represent and +# manipulate the rules that make up a grammar. +# ----------------------------------------------------------------------------- + +import re + +# regex matching identifiers +_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') + +# ----------------------------------------------------------------------------- +# class Production: +# +# This class stores the raw information about a single production or grammar rule. +# A grammar rule refers to a specification such as this: +# +# expr : expr PLUS term +# +# Here are the basic attributes defined on all productions +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','PLUS','term'] +# prec - Production precedence level +# number - Production number. +# func - Function that executes on reduce +# file - File where production function is defined +# lineno - Line number where production function is defined +# +# The following attributes are defined or optional. +# +# len - Length of the production (number of symbols on right hand side) +# usyms - Set of unique symbols found in the production +# ----------------------------------------------------------------------------- + +class Production(object): + reduced = 0 + def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0): + self.name = name + self.prod = tuple(prod) + self.number = number + self.func = func + self.callable = None + self.file = file + self.line = line + self.prec = precedence + + # Internal settings used during table construction + + self.len = len(self.prod) # Length of the production + + # Create a list of unique production symbols used in the production + self.usyms = [ ] + for s in self.prod: + if s not in self.usyms: + self.usyms.append(s) + + # List of all LR items for the production + self.lr_items = [] + self.lr_next = None + + # Create a string representation + if self.prod: + self.str = "%s -> %s" % (self.name," ".join(self.prod)) + else: + self.str = "%s -> " % self.name + + def __str__(self): + return self.str + + def __repr__(self): + return "Production("+str(self)+")" + + def __len__(self): + return len(self.prod) + + def __nonzero__(self): + return 1 + + def __getitem__(self,index): + return self.prod[index] + + # Return the nth lr_item from the production (or None if at the end) + def lr_item(self,n): + if n > len(self.prod): return None + p = LRItem(self,n) + + # Precompute the list of productions immediately following. Hack. Remove later + try: + p.lr_after = Prodnames[p.prod[n+1]] + except (IndexError,KeyError): + p.lr_after = [] + try: + p.lr_before = p.prod[n-1] + except IndexError: + p.lr_before = None + + return p + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + +# This class serves as a minimal standin for Production objects when +# reading table data from files. It only contains information +# actually used by the LR parsing engine, plus some additional +# debugging information. +class MiniProduction(object): + def __init__(self,str,name,len,func,file,line): + self.name = name + self.len = len + self.func = func + self.callable = None + self.file = file + self.line = line + self.str = str + def __str__(self): + return self.str + def __repr__(self): + return "MiniProduction(%s)" % self.str + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + + +# ----------------------------------------------------------------------------- +# class LRItem +# +# This class represents a specific stage of parsing a production rule. For +# example: +# +# expr : expr . PLUS term +# +# In the above, the "." represents the current location of the parse. Here +# basic attributes: +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] +# number - Production number. +# +# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' +# then lr_next refers to 'expr -> expr PLUS . term' +# lr_index - LR item index (location of the ".") in the prod list. +# lookaheads - LALR lookahead symbols for this item +# len - Length of the production (number of symbols on right hand side) +# lr_after - List of all productions that immediately follow +# lr_before - Grammar symbol immediately before +# ----------------------------------------------------------------------------- + +class LRItem(object): + def __init__(self,p,n): + self.name = p.name + self.prod = list(p.prod) + self.number = p.number + self.lr_index = n + self.lookaheads = { } + self.prod.insert(n,".") + self.prod = tuple(self.prod) + self.len = len(self.prod) + self.usyms = p.usyms + + def __str__(self): + if self.prod: + s = "%s -> %s" % (self.name," ".join(self.prod)) + else: + s = "%s -> " % self.name + return s + + def __repr__(self): + return "LRItem("+str(self)+")" + +# ----------------------------------------------------------------------------- +# rightmost_terminal() +# +# Return the rightmost terminal from a list of symbols. Used in add_production() +# ----------------------------------------------------------------------------- +def rightmost_terminal(symbols, terminals): + i = len(symbols) - 1 + while i >= 0: + if symbols[i] in terminals: + return symbols[i] + i -= 1 + return None + +# ----------------------------------------------------------------------------- +# === GRAMMAR CLASS === +# +# The following class represents the contents of the specified grammar along +# with various computed properties such as first sets, follow sets, LR items, etc. +# This data is used for critical parts of the table generation process later. +# ----------------------------------------------------------------------------- + +class GrammarError(YaccError): pass + +class Grammar(object): + def __init__(self,terminals): + self.Productions = [None] # A list of all of the productions. The first + # entry is always reserved for the purpose of + # building an augmented grammar + + self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all + # productions of that nonterminal. + + self.Prodmap = { } # A dictionary that is only used to detect duplicate + # productions. + + self.Terminals = { } # A dictionary mapping the names of terminal symbols to a + # list of the rules where they are used. + + for term in terminals: + self.Terminals[term] = [] + + self.Terminals['error'] = [] + + self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list + # of rule numbers where they are used. + + self.First = { } # A dictionary of precomputed FIRST(x) symbols + + self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols + + self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the + # form ('right',level) or ('nonassoc', level) or ('left',level) + + self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer. + # This is only used to provide error checking and to generate + # a warning about unused precedence rules. + + self.Start = None # Starting symbol for the grammar + + + def __len__(self): + return len(self.Productions) + + def __getitem__(self,index): + return self.Productions[index] + + # ----------------------------------------------------------------------------- + # set_precedence() + # + # Sets the precedence for a given terminal. assoc is the associativity such as + # 'left','right', or 'nonassoc'. level is a numeric level. + # + # ----------------------------------------------------------------------------- + + def set_precedence(self,term,assoc,level): + assert self.Productions == [None],"Must call set_precedence() before add_production()" + if term in self.Precedence: + raise GrammarError("Precedence already specified for terminal '%s'" % term) + if assoc not in ['left','right','nonassoc']: + raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") + self.Precedence[term] = (assoc,level) + + # ----------------------------------------------------------------------------- + # add_production() + # + # Given an action function, this function assembles a production rule and + # computes its precedence level. + # + # The production rule is supplied as a list of symbols. For example, + # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and + # symbols ['expr','PLUS','term']. + # + # Precedence is determined by the precedence of the right-most non-terminal + # or the precedence of a terminal specified by %prec. + # + # A variety of error checks are performed to make sure production symbols + # are valid and that %prec is used correctly. + # ----------------------------------------------------------------------------- + + def add_production(self,prodname,syms,func=None,file='',line=0): + + if prodname in self.Terminals: + raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname)) + if prodname == 'error': + raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname)) + if not _is_identifier.match(prodname): + raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname)) + + # Look for literal tokens + for n,s in enumerate(syms): + if s[0] in "'\"": + try: + c = eval(s) + if (len(c) > 1): + raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname)) + if not c in self.Terminals: + self.Terminals[c] = [] + syms[n] = c + continue + except SyntaxError: + pass + if not _is_identifier.match(s) and s != '%prec': + raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname)) + + # Determine the precedence level + if '%prec' in syms: + if syms[-1] == '%prec': + raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line)) + if syms[-2] != '%prec': + raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line)) + precname = syms[-1] + prodprec = self.Precedence.get(precname,None) + if not prodprec: + raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname)) + else: + self.UsedPrecedence[precname] = 1 + del syms[-2:] # Drop %prec from the rule + else: + # If no %prec, precedence is determined by the rightmost terminal symbol + precname = rightmost_terminal(syms,self.Terminals) + prodprec = self.Precedence.get(precname,('right',0)) + + # See if the rule is already in the rulemap + map = "%s -> %s" % (prodname,syms) + if map in self.Prodmap: + m = self.Prodmap[map] + raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) + + "Previous definition at %s:%d" % (m.file, m.line)) + + # From this point on, everything is valid. Create a new Production instance + pnumber = len(self.Productions) + if not prodname in self.Nonterminals: + self.Nonterminals[prodname] = [ ] + + # Add the production number to Terminals and Nonterminals + for t in syms: + if t in self.Terminals: + self.Terminals[t].append(pnumber) + else: + if not t in self.Nonterminals: + self.Nonterminals[t] = [ ] + self.Nonterminals[t].append(pnumber) + + # Create a production and add it to the list of productions + p = Production(pnumber,prodname,syms,prodprec,func,file,line) + self.Productions.append(p) + self.Prodmap[map] = p + + # Add to the global productions list + try: + self.Prodnames[prodname].append(p) + except KeyError: + self.Prodnames[prodname] = [ p ] + return 0 + + # ----------------------------------------------------------------------------- + # set_start() + # + # Sets the starting symbol and creates the augmented grammar. Production + # rule 0 is S' -> start where start is the start symbol. + # ----------------------------------------------------------------------------- + + def set_start(self,start=None): + if not start: + start = self.Productions[1].name + if start not in self.Nonterminals: + raise GrammarError("start symbol %s undefined" % start) + self.Productions[0] = Production(0,"S'",[start]) + self.Nonterminals[start].append(0) + self.Start = start + + # ----------------------------------------------------------------------------- + # find_unreachable() + # + # Find all of the nonterminal symbols that can't be reached from the starting + # symbol. Returns a list of nonterminals that can't be reached. + # ----------------------------------------------------------------------------- + + def find_unreachable(self): + + # Mark all symbols that are reachable from a symbol s + def mark_reachable_from(s): + if reachable[s]: + # We've already reached symbol s. + return + reachable[s] = 1 + for p in self.Prodnames.get(s,[]): + for r in p.prod: + mark_reachable_from(r) + + reachable = { } + for s in list(self.Terminals) + list(self.Nonterminals): + reachable[s] = 0 + + mark_reachable_from( self.Productions[0].prod[0] ) + + return [s for s in list(self.Nonterminals) + if not reachable[s]] + + # ----------------------------------------------------------------------------- + # infinite_cycles() + # + # This function looks at the various parsing rules and tries to detect + # infinite recursion cycles (grammar rules where there is no possible way + # to derive a string of only terminals). + # ----------------------------------------------------------------------------- + + def infinite_cycles(self): + terminates = {} + + # Terminals: + for t in self.Terminals: + terminates[t] = 1 + + terminates['$end'] = 1 + + # Nonterminals: + + # Initialize to false: + for n in self.Nonterminals: + terminates[n] = 0 + + # Then propagate termination until no change: + while 1: + some_change = 0 + for (n,pl) in self.Prodnames.items(): + # Nonterminal n terminates iff any of its productions terminates. + for p in pl: + # Production p terminates iff all of its rhs symbols terminate. + for s in p.prod: + if not terminates[s]: + # The symbol s does not terminate, + # so production p does not terminate. + p_terminates = 0 + break + else: + # didn't break from the loop, + # so every symbol s terminates + # so production p terminates. + p_terminates = 1 + + if p_terminates: + # symbol n terminates! + if not terminates[n]: + terminates[n] = 1 + some_change = 1 + # Don't need to consider any more productions for this n. + break + + if not some_change: + break + + infinite = [] + for (s,term) in terminates.items(): + if not term: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + # s is used-but-not-defined, and we've already warned of that, + # so it would be overkill to say that it's also non-terminating. + pass + else: + infinite.append(s) + + return infinite + + + # ----------------------------------------------------------------------------- + # undefined_symbols() + # + # Find all symbols that were used the grammar, but not defined as tokens or + # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol + # and prod is the production where the symbol was used. + # ----------------------------------------------------------------------------- + def undefined_symbols(self): + result = [] + for p in self.Productions: + if not p: continue + + for s in p.prod: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + result.append((s,p)) + return result + + # ----------------------------------------------------------------------------- + # unused_terminals() + # + # Find all terminals that were defined, but not used by the grammar. Returns + # a list of all symbols. + # ----------------------------------------------------------------------------- + def unused_terminals(self): + unused_tok = [] + for s,v in self.Terminals.items(): + if s != 'error' and not v: + unused_tok.append(s) + + return unused_tok + + # ------------------------------------------------------------------------------ + # unused_rules() + # + # Find all grammar rules that were defined, but not used (maybe not reachable) + # Returns a list of productions. + # ------------------------------------------------------------------------------ + + def unused_rules(self): + unused_prod = [] + for s,v in self.Nonterminals.items(): + if not v: + p = self.Prodnames[s][0] + unused_prod.append(p) + return unused_prod + + # ----------------------------------------------------------------------------- + # unused_precedence() + # + # Returns a list of tuples (term,precedence) corresponding to precedence + # rules that were never used by the grammar. term is the name of the terminal + # on which precedence was applied and precedence is a string such as 'left' or + # 'right' corresponding to the type of precedence. + # ----------------------------------------------------------------------------- + + def unused_precedence(self): + unused = [] + for termname in self.Precedence: + if not (termname in self.Terminals or termname in self.UsedPrecedence): + unused.append((termname,self.Precedence[termname][0])) + + return unused + + # ------------------------------------------------------------------------- + # _first() + # + # Compute the value of FIRST1(beta) where beta is a tuple of symbols. + # + # During execution of compute_first1, the result may be incomplete. + # Afterward (e.g., when called from compute_follow()), it will be complete. + # ------------------------------------------------------------------------- + def _first(self,beta): + + # We are computing First(x1,x2,x3,...,xn) + result = [ ] + for x in beta: + x_produces_empty = 0 + + # Add all the non- symbols of First[x] to the result. + for f in self.First[x]: + if f == '': + x_produces_empty = 1 + else: + if f not in result: result.append(f) + + if x_produces_empty: + # We have to consider the next x in beta, + # i.e. stay in the loop. + pass + else: + # We don't have to consider any further symbols in beta. + break + else: + # There was no 'break' from the loop, + # so x_produces_empty was true for all x in beta, + # so beta produces empty as well. + result.append('') + + return result + + # ------------------------------------------------------------------------- + # compute_first() + # + # Compute the value of FIRST1(X) for all symbols + # ------------------------------------------------------------------------- + def compute_first(self): + if self.First: + return self.First + + # Terminals: + for t in self.Terminals: + self.First[t] = [t] + + self.First['$end'] = ['$end'] + + # Nonterminals: + + # Initialize to the empty set: + for n in self.Nonterminals: + self.First[n] = [] + + # Then propagate symbols until no change: + while 1: + some_change = 0 + for n in self.Nonterminals: + for p in self.Prodnames[n]: + for f in self._first(p.prod): + if f not in self.First[n]: + self.First[n].append( f ) + some_change = 1 + if not some_change: + break + + return self.First + + # --------------------------------------------------------------------- + # compute_follow() + # + # Computes all of the follow sets for every non-terminal symbol. The + # follow set is the set of all symbols that might follow a given + # non-terminal. See the Dragon book, 2nd Ed. p. 189. + # --------------------------------------------------------------------- + def compute_follow(self,start=None): + # If already computed, return the result + if self.Follow: + return self.Follow + + # If first sets not computed yet, do that first. + if not self.First: + self.compute_first() + + # Add '$end' to the follow list of the start symbol + for k in self.Nonterminals: + self.Follow[k] = [ ] + + if not start: + start = self.Productions[1].name + + self.Follow[start] = [ '$end' ] + + while 1: + didadd = 0 + for p in self.Productions[1:]: + # Here is the production set + for i in range(len(p.prod)): + B = p.prod[i] + if B in self.Nonterminals: + # Okay. We got a non-terminal in a production + fst = self._first(p.prod[i+1:]) + hasempty = 0 + for f in fst: + if f != '' and f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if f == '': + hasempty = 1 + if hasempty or i == (len(p.prod)-1): + # Add elements of follow(a) to follow(b) + for f in self.Follow[p.name]: + if f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if not didadd: break + return self.Follow + + + # ----------------------------------------------------------------------------- + # build_lritems() + # + # This function walks the list of productions and builds a complete set of the + # LR items. The LR items are stored in two ways: First, they are uniquely + # numbered and placed in the list _lritems. Second, a linked list of LR items + # is built for each production. For example: + # + # E -> E PLUS E + # + # Creates the list + # + # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] + # ----------------------------------------------------------------------------- + + def build_lritems(self): + for p in self.Productions: + lastlri = p + i = 0 + lr_items = [] + while 1: + if i > len(p): + lri = None + else: + lri = LRItem(p,i) + # Precompute the list of productions immediately following + try: + lri.lr_after = self.Prodnames[lri.prod[i+1]] + except (IndexError,KeyError): + lri.lr_after = [] + try: + lri.lr_before = lri.prod[i-1] + except IndexError: + lri.lr_before = None + + lastlri.lr_next = lri + if not lri: break + lr_items.append(lri) + lastlri = lri + i += 1 + p.lr_items = lr_items + +# ----------------------------------------------------------------------------- +# == Class LRTable == +# +# This basic class represents a basic table of LR parsing information. +# Methods for generating the tables are not defined here. They are defined +# in the derived class LRGeneratedTable. +# ----------------------------------------------------------------------------- + +class VersionError(YaccError): pass + +class LRTable(object): + def __init__(self): + self.lr_action = None + self.lr_goto = None + self.lr_productions = None + self.lr_method = None + + def read_table(self,module): + if isinstance(module,types.ModuleType): + parsetab = module + else: + if sys.version_info[0] < 3: + exec("import %s as parsetab" % module) + else: + env = { } + exec("import %s as parsetab" % module, env, env) + parsetab = env['parsetab'] + + if parsetab._tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + + self.lr_action = parsetab._lr_action + self.lr_goto = parsetab._lr_goto + + self.lr_productions = [] + for p in parsetab._lr_productions: + self.lr_productions.append(MiniProduction(*p)) + + self.lr_method = parsetab._lr_method + return parsetab._lr_signature + + def read_pickle(self,filename): + try: + import cPickle as pickle + except ImportError: + import pickle + + in_f = open(filename,"rb") + + tabversion = pickle.load(in_f) + if tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + self.lr_method = pickle.load(in_f) + signature = pickle.load(in_f) + self.lr_action = pickle.load(in_f) + self.lr_goto = pickle.load(in_f) + productions = pickle.load(in_f) + + self.lr_productions = [] + for p in productions: + self.lr_productions.append(MiniProduction(*p)) + + in_f.close() + return signature + + # Bind all production function names to callable objects in pdict + def bind_callables(self,pdict): + for p in self.lr_productions: + p.bind(pdict) + +# ----------------------------------------------------------------------------- +# === LR Generator === +# +# The following classes and functions are used to generate LR parsing tables on +# a grammar. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# digraph() +# traverse() +# +# The following two functions are used to compute set valued functions +# of the form: +# +# F(x) = F'(x) U U{F(y) | x R y} +# +# This is used to compute the values of Read() sets as well as FOLLOW sets +# in LALR(1) generation. +# +# Inputs: X - An input set +# R - A relation +# FP - Set-valued function +# ------------------------------------------------------------------------------ + +def digraph(X,R,FP): + N = { } + for x in X: + N[x] = 0 + stack = [] + F = { } + for x in X: + if N[x] == 0: traverse(x,N,stack,F,X,R,FP) + return F + +def traverse(x,N,stack,F,X,R,FP): + stack.append(x) + d = len(stack) + N[x] = d + F[x] = FP(x) # F(X) <- F'(x) + + rel = R(x) # Get y's related to x + for y in rel: + if N[y] == 0: + traverse(y,N,stack,F,X,R,FP) + N[x] = min(N[x],N[y]) + for a in F.get(y,[]): + if a not in F[x]: F[x].append(a) + if N[x] == d: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + while element != x: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + +class LALRError(YaccError): pass + +# ----------------------------------------------------------------------------- +# == LRGeneratedTable == +# +# This class implements the LR table generation algorithm. There are no +# public methods except for write() +# ----------------------------------------------------------------------------- + +class LRGeneratedTable(LRTable): + def __init__(self,grammar,method='LALR',log=None): + if method not in ['SLR','LALR']: + raise LALRError("Unsupported method %s" % method) + + self.grammar = grammar + self.lr_method = method + + # Set up the logger + if not log: + log = NullLogger() + self.log = log + + # Internal attributes + self.lr_action = {} # Action table + self.lr_goto = {} # Goto table + self.lr_productions = grammar.Productions # Copy of grammar Production array + self.lr_goto_cache = {} # Cache of computed gotos + self.lr0_cidhash = {} # Cache of closures + + self._add_count = 0 # Internal counter used to detect cycles + + # Diagonistic information filled in by the table generator + self.sr_conflict = 0 + self.rr_conflict = 0 + self.conflicts = [] # List of conflicts + + self.sr_conflicts = [] + self.rr_conflicts = [] + + # Build the tables + self.grammar.build_lritems() + self.grammar.compute_first() + self.grammar.compute_follow() + self.lr_parse_table() + + # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. + + def lr0_closure(self,I): + self._add_count += 1 + + # Add everything in I to J + J = I[:] + didadd = 1 + while didadd: + didadd = 0 + for j in J: + for x in j.lr_after: + if getattr(x,"lr0_added",0) == self._add_count: continue + # Add B --> .G to J + J.append(x.lr_next) + x.lr0_added = self._add_count + didadd = 1 + + return J + + # Compute the LR(0) goto function goto(I,X) where I is a set + # of LR(0) items and X is a grammar symbol. This function is written + # in a way that guarantees uniqueness of the generated goto sets + # (i.e. the same goto set will never be returned as two different Python + # objects). With uniqueness, we can later do fast set comparisons using + # id(obj) instead of element-wise comparison. + + def lr0_goto(self,I,x): + # First we look for a previously cached entry + g = self.lr_goto_cache.get((id(I),x),None) + if g: return g + + # Now we generate the goto set in a way that guarantees uniqueness + # of the result + + s = self.lr_goto_cache.get(x,None) + if not s: + s = { } + self.lr_goto_cache[x] = s + + gs = [ ] + for p in I: + n = p.lr_next + if n and n.lr_before == x: + s1 = s.get(id(n),None) + if not s1: + s1 = { } + s[id(n)] = s1 + gs.append(n) + s = s1 + g = s.get('$end',None) + if not g: + if gs: + g = self.lr0_closure(gs) + s['$end'] = g + else: + s['$end'] = gs + self.lr_goto_cache[(id(I),x)] = g + return g + + # Compute the LR(0) sets of item function + def lr0_items(self): + + C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ] + i = 0 + for I in C: + self.lr0_cidhash[id(I)] = i + i += 1 + + # Loop over the items in C and each grammar symbols + i = 0 + while i < len(C): + I = C[i] + i += 1 + + # Collect all of the symbols that could possibly be in the goto(I,X) sets + asyms = { } + for ii in I: + for s in ii.usyms: + asyms[s] = None + + for x in asyms: + g = self.lr0_goto(I,x) + if not g: continue + if id(g) in self.lr0_cidhash: continue + self.lr0_cidhash[id(g)] = len(C) + C.append(g) + + return C + + # ----------------------------------------------------------------------------- + # ==== LALR(1) Parsing ==== + # + # LALR(1) parsing is almost exactly the same as SLR except that instead of + # relying upon Follow() sets when performing reductions, a more selective + # lookahead set that incorporates the state of the LR(0) machine is utilized. + # Thus, we mainly just have to focus on calculating the lookahead sets. + # + # The method used here is due to DeRemer and Pennelo (1982). + # + # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) + # Lookahead Sets", ACM Transactions on Programming Languages and Systems, + # Vol. 4, No. 4, Oct. 1982, pp. 615-649 + # + # Further details can also be found in: + # + # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", + # McGraw-Hill Book Company, (1985). + # + # ----------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------- + # compute_nullable_nonterminals() + # + # Creates a dictionary containing all of the non-terminals that might produce + # an empty production. + # ----------------------------------------------------------------------------- + + def compute_nullable_nonterminals(self): + nullable = {} + num_nullable = 0 + while 1: + for p in self.grammar.Productions[1:]: + if p.len == 0: + nullable[p.name] = 1 + continue + for t in p.prod: + if not t in nullable: break + else: + nullable[p.name] = 1 + if len(nullable) == num_nullable: break + num_nullable = len(nullable) + return nullable + + # ----------------------------------------------------------------------------- + # find_nonterminal_trans(C) + # + # Given a set of LR(0) items, this functions finds all of the non-terminal + # transitions. These are transitions in which a dot appears immediately before + # a non-terminal. Returns a list of tuples of the form (state,N) where state + # is the state number and N is the nonterminal symbol. + # + # The input C is the set of LR(0) items. + # ----------------------------------------------------------------------------- + + def find_nonterminal_transitions(self,C): + trans = [] + for state in range(len(C)): + for p in C[state]: + if p.lr_index < p.len - 1: + t = (state,p.prod[p.lr_index+1]) + if t[1] in self.grammar.Nonterminals: + if t not in trans: trans.append(t) + state = state + 1 + return trans + + # ----------------------------------------------------------------------------- + # dr_relation() + # + # Computes the DR(p,A) relationships for non-terminal transitions. The input + # is a tuple (state,N) where state is a number and N is a nonterminal symbol. + # + # Returns a list of terminals. + # ----------------------------------------------------------------------------- + + def dr_relation(self,C,trans,nullable): + dr_set = { } + state,N = trans + terms = [] + + g = self.lr0_goto(C[state],N) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index+1] + if a in self.grammar.Terminals: + if a not in terms: terms.append(a) + + # This extra bit is to handle the start state + if state == 0 and N == self.grammar.Productions[0].prod[0]: + terms.append('$end') + + return terms + + # ----------------------------------------------------------------------------- + # reads_relation() + # + # Computes the READS() relation (p,A) READS (t,C). + # ----------------------------------------------------------------------------- + + def reads_relation(self,C, trans, empty): + # Look for empty transitions + rel = [] + state, N = trans + + g = self.lr0_goto(C[state],N) + j = self.lr0_cidhash.get(id(g),-1) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index + 1] + if a in empty: + rel.append((j,a)) + + return rel + + # ----------------------------------------------------------------------------- + # compute_lookback_includes() + # + # Determines the lookback and includes relations + # + # LOOKBACK: + # + # This relation is determined by running the LR(0) state machine forward. + # For example, starting with a production "N : . A B C", we run it forward + # to obtain "N : A B C ." We then build a relationship between this final + # state and the starting state. These relationships are stored in a dictionary + # lookdict. + # + # INCLUDES: + # + # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). + # + # This relation is used to determine non-terminal transitions that occur + # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) + # if the following holds: + # + # B -> LAT, where T -> epsilon and p' -L-> p + # + # L is essentially a prefix (which may be empty), T is a suffix that must be + # able to derive an empty string. State p' must lead to state p with the string L. + # + # ----------------------------------------------------------------------------- + + def compute_lookback_includes(self,C,trans,nullable): + + lookdict = {} # Dictionary of lookback relations + includedict = {} # Dictionary of include relations + + # Make a dictionary of non-terminal transitions + dtrans = {} + for t in trans: + dtrans[t] = 1 + + # Loop over all transitions and compute lookbacks and includes + for state,N in trans: + lookb = [] + includes = [] + for p in C[state]: + if p.name != N: continue + + # Okay, we have a name match. We now follow the production all the way + # through the state machine until we get the . on the right hand side + + lr_index = p.lr_index + j = state + while lr_index < p.len - 1: + lr_index = lr_index + 1 + t = p.prod[lr_index] + + # Check to see if this symbol and state are a non-terminal transition + if (j,t) in dtrans: + # Yes. Okay, there is some chance that this is an includes relation + # the only way to know for certain is whether the rest of the + # production derives empty + + li = lr_index + 1 + while li < p.len: + if p.prod[li] in self.grammar.Terminals: break # No forget it + if not p.prod[li] in nullable: break + li = li + 1 + else: + # Appears to be a relation between (j,t) and (state,N) + includes.append((j,t)) + + g = self.lr0_goto(C[j],t) # Go to next set + j = self.lr0_cidhash.get(id(g),-1) # Go to next state + + # When we get here, j is the final state, now we have to locate the production + for r in C[j]: + if r.name != p.name: continue + if r.len != p.len: continue + i = 0 + # This look is comparing a production ". A B C" with "A B C ." + while i < r.lr_index: + if r.prod[i] != p.prod[i+1]: break + i = i + 1 + else: + lookb.append((j,r)) + for i in includes: + if not i in includedict: includedict[i] = [] + includedict[i].append((state,N)) + lookdict[(state,N)] = lookb + + return lookdict,includedict + + # ----------------------------------------------------------------------------- + # compute_read_sets() + # + # Given a set of LR(0) items, this function computes the read sets. + # + # Inputs: C = Set of LR(0) items + # ntrans = Set of nonterminal transitions + # nullable = Set of empty transitions + # + # Returns a set containing the read sets + # ----------------------------------------------------------------------------- + + def compute_read_sets(self,C, ntrans, nullable): + FP = lambda x: self.dr_relation(C,x,nullable) + R = lambda x: self.reads_relation(C,x,nullable) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # compute_follow_sets() + # + # Given a set of LR(0) items, a set of non-terminal transitions, a readset, + # and an include set, this function computes the follow sets + # + # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} + # + # Inputs: + # ntrans = Set of nonterminal transitions + # readsets = Readset (previously computed) + # inclsets = Include sets (previously computed) + # + # Returns a set containing the follow sets + # ----------------------------------------------------------------------------- + + def compute_follow_sets(self,ntrans,readsets,inclsets): + FP = lambda x: readsets[x] + R = lambda x: inclsets.get(x,[]) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # add_lookaheads() + # + # Attaches the lookahead symbols to grammar rules. + # + # Inputs: lookbacks - Set of lookback relations + # followset - Computed follow set + # + # This function directly attaches the lookaheads to productions contained + # in the lookbacks set + # ----------------------------------------------------------------------------- + + def add_lookaheads(self,lookbacks,followset): + for trans,lb in lookbacks.items(): + # Loop over productions in lookback + for state,p in lb: + if not state in p.lookaheads: + p.lookaheads[state] = [] + f = followset.get(trans,[]) + for a in f: + if a not in p.lookaheads[state]: p.lookaheads[state].append(a) + + # ----------------------------------------------------------------------------- + # add_lalr_lookaheads() + # + # This function does all of the work of adding lookahead information for use + # with LALR parsing + # ----------------------------------------------------------------------------- + + def add_lalr_lookaheads(self,C): + # Determine all of the nullable nonterminals + nullable = self.compute_nullable_nonterminals() + + # Find all non-terminal transitions + trans = self.find_nonterminal_transitions(C) + + # Compute read sets + readsets = self.compute_read_sets(C,trans,nullable) + + # Compute lookback/includes relations + lookd, included = self.compute_lookback_includes(C,trans,nullable) + + # Compute LALR FOLLOW sets + followsets = self.compute_follow_sets(trans,readsets,included) + + # Add all of the lookaheads + self.add_lookaheads(lookd,followsets) + + # ----------------------------------------------------------------------------- + # lr_parse_table() + # + # This function constructs the parse tables for SLR or LALR + # ----------------------------------------------------------------------------- + def lr_parse_table(self): + Productions = self.grammar.Productions + Precedence = self.grammar.Precedence + goto = self.lr_goto # Goto array + action = self.lr_action # Action array + log = self.log # Logger for output + + actionp = { } # Action production array (temporary) + + log.info("Parsing method: %s", self.lr_method) + + # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items + # This determines the number of states + + C = self.lr0_items() + + if self.lr_method == 'LALR': + self.add_lalr_lookaheads(C) + + # Build the parser table, state by state + st = 0 + for I in C: + # Loop over each production in I + actlist = [ ] # List of actions + st_action = { } + st_actionp = { } + st_goto = { } + log.info("") + log.info("state %d", st) + log.info("") + for p in I: + log.info(" (%d) %s", p.number, str(p)) + log.info("") + + for p in I: + if p.len == p.lr_index + 1: + if p.name == "S'": + # Start symbol. Accept! + st_action["$end"] = 0 + st_actionp["$end"] = p + else: + # We are at the end of a production. Reduce! + if self.lr_method == 'LALR': + laheads = p.lookaheads[st] + else: + laheads = self.grammar.Follow[p.name] + for a in laheads: + actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) + r = st_action.get(a,None) + if r is not None: + # Whoa. Have a shift/reduce or reduce/reduce conflict + if r > 0: + # Need to decide on shift or reduce here + # By default we favor shifting. Need to add + # some precedence rules here. + sprec,slevel = Productions[st_actionp[a].number].prec + rprec,rlevel = Precedence.get(a,('right',0)) + if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): + # We really need to reduce here. + st_action[a] = -p.number + st_actionp[a] = p + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + Productions[p.number].reduced += 1 + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the shift + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif r < 0: + # Reduce/reduce conflict. In this case, we favor the rule + # that was defined first in the grammar file + oldp = Productions[-r] + pp = Productions[p.number] + if oldp.line > pp.line: + st_action[a] = -p.number + st_actionp[a] = p + chosenp,rejectp = pp,oldp + Productions[p.number].reduced += 1 + Productions[oldp.number].reduced -= 1 + else: + chosenp,rejectp = oldp,pp + self.rr_conflicts.append((st,chosenp,rejectp)) + log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a]) + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = -p.number + st_actionp[a] = p + Productions[p.number].reduced += 1 + else: + i = p.lr_index + a = p.prod[i+1] # Get symbol right after the "." + if a in self.grammar.Terminals: + g = self.lr0_goto(I,a) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + # We are in a shift state + actlist.append((a,p,"shift and go to state %d" % j)) + r = st_action.get(a,None) + if r is not None: + # Whoa have a shift/reduce or shift/shift conflict + if r > 0: + if r != j: + raise LALRError("Shift/shift conflict in state %d" % st) + elif r < 0: + # Do a precedence check. + # - if precedence of reduce rule is higher, we reduce. + # - if precedence of reduce is same and left assoc, we reduce. + # - otherwise we shift + rprec,rlevel = Productions[st_actionp[a].number].prec + sprec,slevel = Precedence.get(a,('right',0)) + if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): + # We decide to shift here... highest precedence to shift + Productions[st_actionp[a].number].reduced -= 1 + st_action[a] = j + st_actionp[a] = p + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the reduce + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = j + st_actionp[a] = p + + # Print the actions associated with each terminal + _actprint = { } + for a,p,m in actlist: + if a in st_action: + if p is st_actionp[a]: + log.info(" %-15s %s",a,m) + _actprint[(a,m)] = 1 + log.info("") + # Print the actions that were not used. (debugging) + not_used = 0 + for a,p,m in actlist: + if a in st_action: + if p is not st_actionp[a]: + if not (a,m) in _actprint: + log.debug(" ! %-15s [ %s ]",a,m) + not_used = 1 + _actprint[(a,m)] = 1 + if not_used: + log.debug("") + + # Construct the goto table for this state + + nkeys = { } + for ii in I: + for s in ii.usyms: + if s in self.grammar.Nonterminals: + nkeys[s] = None + for n in nkeys: + g = self.lr0_goto(I,n) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + st_goto[n] = j + log.info(" %-30s shift and go to state %d",n,j) + + action[st] = st_action + actionp[st] = st_actionp + goto[st] = st_goto + st += 1 + + + # ----------------------------------------------------------------------------- + # write() + # + # This function writes the LR parsing tables to a file + # ----------------------------------------------------------------------------- + + def write_table(self,modulename,outputdir='',signature=""): + basemodulename = modulename.split(".")[-1] + filename = os.path.join(outputdir,basemodulename) + ".py" + try: + f = open(filename,"w") + + f.write(""" +# %s +# This file is automatically generated. Do not edit. +_tabversion = %r + +_lr_method = %r + +_lr_signature = %r + """ % (filename, __tabversion__, self.lr_method, signature)) + + # Change smaller to 0 to go back to original tables + smaller = 1 + + # Factor out names to try and make smaller + if smaller: + items = { } + + for s,nd in self.lr_action.items(): + for name,v in nd.items(): + i = items.get(name) + if not i: + i = ([],[]) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write("\n_lr_action_items = {") + for k,v in items.items(): + f.write("%r:([" % k) + for i in v[0]: + f.write("%r," % i) + f.write("],[") + for i in v[1]: + f.write("%r," % i) + + f.write("]),") + f.write("}\n") + + f.write(""" +_lr_action = { } +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = { } + _lr_action[_x][_k] = _y +del _lr_action_items +""") + + else: + f.write("\n_lr_action = { "); + for k,v in self.lr_action.items(): + f.write("(%r,%r):%r," % (k[0],k[1],v)) + f.write("}\n"); + + if smaller: + # Factor out names to try and make smaller + items = { } + + for s,nd in self.lr_goto.items(): + for name,v in nd.items(): + i = items.get(name) + if not i: + i = ([],[]) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write("\n_lr_goto_items = {") + for k,v in items.items(): + f.write("%r:([" % k) + for i in v[0]: + f.write("%r," % i) + f.write("],[") + for i in v[1]: + f.write("%r," % i) + + f.write("]),") + f.write("}\n") + + f.write(""" +_lr_goto = { } +for _k, _v in _lr_goto_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_goto: _lr_goto[_x] = { } + _lr_goto[_x][_k] = _y +del _lr_goto_items +""") + else: + f.write("\n_lr_goto = { "); + for k,v in self.lr_goto.items(): + f.write("(%r,%r):%r," % (k[0],k[1],v)) + f.write("}\n"); + + # Write production table + f.write("_lr_productions = [\n") + for p in self.lr_productions: + if p.func: + f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line)) + else: + f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len)) + f.write("]\n") + f.close() + + except IOError: + e = sys.exc_info()[1] + sys.stderr.write("Unable to create '%s'\n" % filename) + sys.stderr.write(str(e)+"\n") + return + + + # ----------------------------------------------------------------------------- + # pickle_table() + # + # This function pickles the LR parsing tables to a supplied file object + # ----------------------------------------------------------------------------- + + def pickle_table(self,filename,signature=""): + try: + import cPickle as pickle + except ImportError: + import pickle + outf = open(filename,"wb") + pickle.dump(__tabversion__,outf,pickle_protocol) + pickle.dump(self.lr_method,outf,pickle_protocol) + pickle.dump(signature,outf,pickle_protocol) + pickle.dump(self.lr_action,outf,pickle_protocol) + pickle.dump(self.lr_goto,outf,pickle_protocol) + + outp = [] + for p in self.lr_productions: + if p.func: + outp.append((p.str,p.name, p.len, p.func,p.file,p.line)) + else: + outp.append((str(p),p.name,p.len,None,None,None)) + pickle.dump(outp,outf,pickle_protocol) + outf.close() + +# ----------------------------------------------------------------------------- +# === INTROSPECTION === +# +# The following functions and classes are used to implement the PLY +# introspection features followed by the yacc() function itself. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# parse_grammar() +# +# This takes a raw grammar rule string and parses it into production data +# ----------------------------------------------------------------------------- +def parse_grammar(doc,file,line): + grammar = [] + # Split the doc string into lines + pstrings = doc.splitlines() + lastp = None + dline = line + for ps in pstrings: + dline += 1 + p = ps.split() + if not p: continue + try: + if p[0] == '|': + # This is a continuation of a previous rule + if not lastp: + raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline)) + prodname = lastp + syms = p[1:] + else: + prodname = p[0] + lastp = prodname + syms = p[2:] + assign = p[1] + if assign != ':' and assign != '::=': + raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline)) + + grammar.append((file,dline,prodname,syms)) + except SyntaxError: + raise + except Exception: + raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip())) + + return grammar + +# ----------------------------------------------------------------------------- +# ParserReflect() +# +# This class represents information extracted for building a parser including +# start symbol, error function, tokens, precedence list, action functions, +# etc. +# ----------------------------------------------------------------------------- +class ParserReflect(object): + def __init__(self,pdict,log=None): + self.pdict = pdict + self.start = None + self.error_func = None + self.tokens = None + self.files = {} + self.grammar = [] + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_start() + self.get_error_func() + self.get_tokens() + self.get_precedence() + self.get_pfunctions() + + # Validate all of the information + def validate_all(self): + self.validate_start() + self.validate_error_func() + self.validate_tokens() + self.validate_precedence() + self.validate_pfunctions() + self.validate_files() + return self.error + + # Compute a signature over the grammar + def signature(self): + try: + from hashlib import md5 + except ImportError: + from md5 import md5 + try: + sig = md5() + if self.start: + sig.update(self.start.encode('latin-1')) + if self.prec: + sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1')) + if self.tokens: + sig.update(" ".join(self.tokens).encode('latin-1')) + for f in self.pfuncs: + if f[3]: + sig.update(f[3].encode('latin-1')) + except (TypeError,ValueError): + pass + return sig.digest() + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This method checks to see if there are duplicated p_rulename() functions + # in the parser module file. Without this function, it is really easy for + # users to make mistakes by cutting and pasting code fragments (and it's a real + # bugger to try and figure out why the resulting parser doesn't work). Therefore, + # we just do a little regular expression pattern matching of def statements + # to try and detect duplicates. + # ----------------------------------------------------------------------------- + + def validate_files(self): + # Match def p_funcname( + fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') + + for filename in self.files.keys(): + base,ext = os.path.splitext(filename) + if ext != '.py': return 1 # No idea. Assume it's okay. + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + continue + + counthash = { } + for linen,l in enumerate(lines): + linen += 1 + m = fre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev) + + # Get the start symbol + def get_start(self): + self.start = self.pdict.get('start') + + # Validate the start symbol + def validate_start(self): + if self.start is not None: + if not isinstance(self.start,str): + self.log.error("'start' must be a string") + + # Look for error handler + def get_error_func(self): + self.error_func = self.pdict.get('p_error') + + # Validate the error function + def validate_error_func(self): + if self.error_func: + if isinstance(self.error_func,types.FunctionType): + ismethod = 0 + elif isinstance(self.error_func, types.MethodType): + ismethod = 1 + else: + self.log.error("'p_error' defined, but is not a function or method") + self.error = 1 + return + + eline = func_code(self.error_func).co_firstlineno + efile = func_code(self.error_func).co_filename + self.files[efile] = 1 + + if (func_code(self.error_func).co_argcount != 1+ismethod): + self.log.error("%s:%d: p_error() requires 1 argument",efile,eline) + self.error = 1 + + # Get the tokens map + def get_tokens(self): + tokens = self.pdict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + # Validate the tokens. + if 'error' in self.tokens: + self.log.error("Illegal token name 'error'. Is a reserved word") + self.error = 1 + return + + terminals = {} + for n in self.tokens: + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the precedence map (if any) + def get_precedence(self): + self.prec = self.pdict.get("precedence",None) + + # Validate and parse the precedence map + def validate_precedence(self): + preclist = [] + if self.prec: + if not isinstance(self.prec,(list,tuple)): + self.log.error("precedence must be a list or tuple") + self.error = 1 + return + for level,p in enumerate(self.prec): + if not isinstance(p,(list,tuple)): + self.log.error("Bad precedence table") + self.error = 1 + return + + if len(p) < 2: + self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p) + self.error = 1 + return + assoc = p[0] + if not isinstance(assoc,str): + self.log.error("precedence associativity must be a string") + self.error = 1 + return + for term in p[1:]: + if not isinstance(term,str): + self.log.error("precedence items must be strings") + self.error = 1 + return + preclist.append((term,assoc,level+1)) + self.preclist = preclist + + # Get all p_functions from the grammar + def get_pfunctions(self): + p_functions = [] + for name, item in self.pdict.items(): + if name[:2] != 'p_': continue + if name == 'p_error': continue + if isinstance(item,(types.FunctionType,types.MethodType)): + line = func_code(item).co_firstlineno + file = func_code(item).co_filename + p_functions.append((line,file,name,item.__doc__)) + + # Sort all of the actions by line number + p_functions.sort() + self.pfuncs = p_functions + + + # Validate all of the p_functions + def validate_pfunctions(self): + grammar = [] + # Check for non-empty symbols + if len(self.pfuncs) == 0: + self.log.error("no rules of the form p_rulename are defined") + self.error = 1 + return + + for line, file, name, doc in self.pfuncs: + func = self.pdict[name] + if isinstance(func, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + if func_code(func).co_argcount > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__) + self.error = 1 + elif func_code(func).co_argcount < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__) + self.error = 1 + elif not func.__doc__: + self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__) + else: + try: + parsed_g = parse_grammar(doc,file,line) + for g in parsed_g: + grammar.append((name, g)) + except SyntaxError: + e = sys.exc_info()[1] + self.log.error(str(e)) + self.error = 1 + + # Looks like a valid grammar rule + # Mark the file in which defined. + self.files[file] = 1 + + # Secondary validation step that looks for p_ definitions that are not functions + # or functions that look like they might be grammar rules. + + for n,v in self.pdict.items(): + if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue + if n[0:2] == 't_': continue + if n[0:2] == 'p_' and n != 'p_error': + self.log.warning("'%s' not defined as a function", n) + if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or + (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)): + try: + doc = v.__doc__.split(" ") + if doc[1] == ':': + self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix", + func_code(v).co_filename, func_code(v).co_firstlineno,n) + except Exception: + pass + + self.grammar = grammar + +# ----------------------------------------------------------------------------- +# yacc(module) +# +# Build a parser +# ----------------------------------------------------------------------------- + +def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, + check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='', + debuglog=None, errorlog = None, picklefile=None): + + global parse # Reference to the parsing method of the last built parser + + # If pickling is enabled, table files are not created + + if picklefile: + write_tables = 0 + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the parser + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + pdict = dict(_items) + else: + pdict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + pinfo = ParserReflect(pdict,log=errorlog) + pinfo.get_all() + + if pinfo.error: + raise YaccError("Unable to build parser") + + # Check signature against table files (if any) + signature = pinfo.signature() + + # Read the tables + try: + lr = LRTable() + if picklefile: + read_signature = lr.read_pickle(picklefile) + else: + read_signature = lr.read_table(tabmodule) + if optimize or (read_signature == signature): + try: + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + parse = parser.parse + return parser + except Exception: + e = sys.exc_info()[1] + errorlog.warning("There was a problem loading the table file: %s", repr(e)) + except VersionError: + e = sys.exc_info() + errorlog.warning(str(e)) + except Exception: + pass + + if debuglog is None: + if debug: + debuglog = PlyLogger(open(debugfile,"w")) + else: + debuglog = NullLogger() + + debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__) + + + errors = 0 + + # Validate the parser information + if pinfo.validate_all(): + raise YaccError("Unable to build parser") + + if not pinfo.error_func: + errorlog.warning("no p_error() function is defined") + + # Create a grammar object + grammar = Grammar(pinfo.tokens) + + # Set precedence level for terminals + for term, assoc, level in pinfo.preclist: + try: + grammar.set_precedence(term,assoc,level) + except GrammarError: + e = sys.exc_info()[1] + errorlog.warning("%s",str(e)) + + # Add productions to the grammar + for funcname, gram in pinfo.grammar: + file, line, prodname, syms = gram + try: + grammar.add_production(prodname,syms,funcname,file,line) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error("%s",str(e)) + errors = 1 + + # Set the grammar start symbols + try: + if start is None: + grammar.set_start(pinfo.start) + else: + grammar.set_start(start) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error(str(e)) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Verify the grammar structure + undefined_symbols = grammar.undefined_symbols() + for sym, prod in undefined_symbols: + errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym) + errors = 1 + + unused_terminals = grammar.unused_terminals() + if unused_terminals: + debuglog.info("") + debuglog.info("Unused terminals:") + debuglog.info("") + for term in unused_terminals: + errorlog.warning("Token '%s' defined, but not used", term) + debuglog.info(" %s", term) + + # Print out all productions to the debug log + if debug: + debuglog.info("") + debuglog.info("Grammar") + debuglog.info("") + for n,p in enumerate(grammar.Productions): + debuglog.info("Rule %-5d %s", n, p) + + # Find unused non-terminals + unused_rules = grammar.unused_rules() + for prod in unused_rules: + errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name) + + if len(unused_terminals) == 1: + errorlog.warning("There is 1 unused token") + if len(unused_terminals) > 1: + errorlog.warning("There are %d unused tokens", len(unused_terminals)) + + if len(unused_rules) == 1: + errorlog.warning("There is 1 unused rule") + if len(unused_rules) > 1: + errorlog.warning("There are %d unused rules", len(unused_rules)) + + if debug: + debuglog.info("") + debuglog.info("Terminals, with rules where they appear") + debuglog.info("") + terms = list(grammar.Terminals) + terms.sort() + for term in terms: + debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]])) + + debuglog.info("") + debuglog.info("Nonterminals, with rules where they appear") + debuglog.info("") + nonterms = list(grammar.Nonterminals) + nonterms.sort() + for nonterm in nonterms: + debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]])) + debuglog.info("") + + if check_recursion: + unreachable = grammar.find_unreachable() + for u in unreachable: + errorlog.warning("Symbol '%s' is unreachable",u) + + infinite = grammar.infinite_cycles() + for inf in infinite: + errorlog.error("Infinite recursion detected for symbol '%s'", inf) + errors = 1 + + unused_prec = grammar.unused_precedence() + for term, assoc in unused_prec: + errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Run the LRGeneratedTable on the grammar + if debug: + errorlog.debug("Generating %s tables", method) + + lr = LRGeneratedTable(grammar,method,debuglog) + + if debug: + num_sr = len(lr.sr_conflicts) + + # Report shift/reduce and reduce/reduce conflicts + if num_sr == 1: + errorlog.warning("1 shift/reduce conflict") + elif num_sr > 1: + errorlog.warning("%d shift/reduce conflicts", num_sr) + + num_rr = len(lr.rr_conflicts) + if num_rr == 1: + errorlog.warning("1 reduce/reduce conflict") + elif num_rr > 1: + errorlog.warning("%d reduce/reduce conflicts", num_rr) + + # Write out conflicts to the output file + if debug and (lr.sr_conflicts or lr.rr_conflicts): + debuglog.warning("") + debuglog.warning("Conflicts:") + debuglog.warning("") + + for state, tok, resolution in lr.sr_conflicts: + debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution) + + already_reported = {} + for state, rule, rejected in lr.rr_conflicts: + if (state,id(rule),id(rejected)) in already_reported: + continue + debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + debuglog.warning("rejected rule (%s) in state %d", rejected,state) + errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + errorlog.warning("rejected rule (%s) in state %d", rejected, state) + already_reported[state,id(rule),id(rejected)] = 1 + + warned_never = [] + for state, rule, rejected in lr.rr_conflicts: + if not rejected.reduced and (rejected not in warned_never): + debuglog.warning("Rule (%s) is never reduced", rejected) + errorlog.warning("Rule (%s) is never reduced", rejected) + warned_never.append(rejected) + + # Write the table file if requested + if write_tables: + lr.write_table(tabmodule,outputdir,signature) + + # Write a pickled version of the tables + if picklefile: + lr.pickle_table(picklefile,signature) + + # Build the parser + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + + parse = parser.parse + return parser diff --git a/apps/system/interfaces/SeL4DebugInterface.camkes b/apps/system/interfaces/SeL4DebugInterface.camkes deleted file mode 100644 index ffac913..0000000 --- a/apps/system/interfaces/SeL4DebugInterface.camkes +++ /dev/null @@ -1,4 +0,0 @@ -procedure SeL4DebugInterface { - void put_string(in string msg); - void dump_scheduler(); -} diff --git a/apps/system/system.camkes b/apps/system/system.camkes index 17826c9..95c6a4f 100644 --- a/apps/system/system.camkes +++ b/apps/system/system.camkes @@ -19,7 +19,6 @@ import "components/OpenTitanUARTDriver/OpenTitanUARTDriver.camkes"; import "components/DebugConsole/DebugConsole.camkes"; import "components/ProcessManager/ProcessManager.camkes"; import "components/MlCoordinator/MlCoordinator.camkes"; -import "components/SeL4Debug/SeL4Debug.camkes"; import "components/StorageManager/StorageManager.camkes"; import "components/SecurityCoordinator/SecurityCoordinator.camkes"; import "components/VectorCoreDriver/VectorCoreDriver.camkes"; @@ -45,8 +44,6 @@ component VectorCoreHw { assembly { composition { - component SeL4Debug sel4debug; - component VectorCoreHw vctop; component VectorCoreDriver vc_drv; @@ -123,16 +120,6 @@ assembly { from security_coordinator.logger, from storage_manager.logger, to debug_console.logger); - - // Connect the SeL4Debug interface of each component that needs access. - // - // (Note that if the LogFibonacci import at the bottom of this file is - // uncommented then there will be another connection to the sel4debug - // procedure. This is fine, but it is important that connection names - // not clash.) - connection seL4RPCCall multi_sel4debug(from debug_console.sel4debug, - from process_manager.sel4debug, - to sel4debug.sel4debug); } configuration {